2001-09-25 16:49:28 +04:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
2001-09-25 16:49:28 +04:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU General Public License v .2 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-09-25 16:49:28 +04:00
*/
2002-11-18 17:04:08 +03:00
# include "lib.h"
2001-10-12 14:32:06 +04:00
# include "device.h"
2001-10-01 19:14:39 +04:00
# include "metadata.h"
2002-02-11 23:50:53 +03:00
# include "toolcontext.h"
2002-02-25 15:56:16 +03:00
# include "lvm-string.h"
2003-07-05 02:34:56 +04:00
# include "lvmcache.h"
# include "memlock.h"
2005-04-20 00:52:35 +04:00
# include "str_list.h"
# include "pv_alloc.h"
2005-10-25 23:08:21 +04:00
# include "activate.h"
2006-11-10 21:24:11 +03:00
# include "display.h"
2001-09-25 16:49:28 +04:00
2006-08-17 23:53:36 +04:00
# include <sys/param.h>
2006-08-17 23:30:59 +04:00
unsigned long pe_align ( void )
{
2006-08-17 23:53:36 +04:00
return MAX ( 65536UL , lvm_getpagesize ( ) ) > > SECTOR_SHIFT ;
2006-08-17 23:30:59 +04:00
}
2002-12-20 02:25:55 +03:00
static int _add_pv_to_vg ( struct format_instance * fid , struct volume_group * vg ,
const char * pv_name )
2001-10-12 18:25:53 +04:00
{
2001-10-15 22:39:40 +04:00
struct pv_list * pvl ;
struct physical_volume * pv ;
2005-10-17 03:03:59 +04:00
struct dm_pool * mem = fid - > fmt - > cmd - > mem ;
2002-11-18 17:04:08 +03:00
struct list mdas ;
2001-10-12 18:25:53 +04:00
2001-10-15 22:39:40 +04:00
log_verbose ( " Adding physical volume '%s' to volume group '%s' " ,
2001-11-10 01:01:04 +03:00
pv_name , vg - > name ) ;
2001-10-15 22:39:40 +04:00
2005-10-17 03:03:59 +04:00
if ( ! ( pvl = dm_pool_zalloc ( mem , sizeof ( * pvl ) ) ) ) {
2001-10-15 22:39:40 +04:00
log_error ( " pv_list allocation for '%s' failed " , pv_name ) ;
2001-10-12 18:25:53 +04:00
return 0 ;
}
2002-11-18 17:04:08 +03:00
list_init ( & mdas ) ;
2004-06-19 23:27:00 +04:00
if ( ! ( pv = pv_read ( fid - > fmt - > cmd , pv_name , & mdas , NULL , 1 ) ) ) {
2003-04-22 20:09:11 +04:00
log_error ( " %s not identified as an existing physical volume " ,
2001-10-15 22:39:40 +04:00
pv_name ) ;
return 0 ;
}
2001-10-12 18:25:53 +04:00
2001-10-15 22:39:40 +04:00
if ( * pv - > vg_name ) {
log_error ( " Physical volume '%s' is already in volume group "
" '%s' " , pv_name , pv - > vg_name ) ;
2001-10-15 16:49:58 +04:00
return 0 ;
}
2001-10-12 18:25:53 +04:00
2002-11-18 17:04:08 +03:00
if ( pv - > fmt ! = fid - > fmt ) {
log_error ( " Physical volume %s is of different format type (%s) " ,
pv_name , pv - > fmt - > name ) ;
return 0 ;
}
2005-10-25 23:08:21 +04:00
/* Ensure PV doesn't depend on another PV already in the VG */
2006-05-11 21:58:58 +04:00
if ( pv_uses_vg ( pv , vg ) ) {
2005-10-25 23:08:21 +04:00
log_error ( " Physical volume %s might be constructed from same "
" volume group %s " , pv_name , vg - > name ) ;
return 0 ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( pv - > vg_name = dm_pool_strdup ( mem , vg - > name ) ) ) {
2001-10-15 22:39:40 +04:00
log_error ( " vg->name allocation failed for '%s' " , pv_name ) ;
2001-10-12 18:25:53 +04:00
return 0 ;
}
2006-04-13 01:23:04 +04:00
memcpy ( & pv - > vgid , & vg - > id , sizeof ( vg - > id ) ) ;
2001-10-16 00:29:15 +04:00
/* Units of 512-byte sectors */
2001-10-12 18:25:53 +04:00
pv - > pe_size = vg - > extent_size ;
2002-04-24 22:20:51 +04:00
/* FIXME Do proper rounding-up alignment? */
/* Reserved space for label; this holds 0 for PVs created by LVM1 */
2006-08-17 23:30:59 +04:00
if ( pv - > pe_start < pe_align ( ) )
pv - > pe_start = pe_align ( ) ;
2002-04-24 22:20:51 +04:00
2001-10-12 18:25:53 +04:00
/*
2006-10-08 03:06:18 +04:00
* pe_count must always be calculated by pv_setup
2001-10-12 18:25:53 +04:00
*/
2002-04-24 22:20:51 +04:00
pv - > pe_alloc_count = 0 ;
2001-10-12 18:25:53 +04:00
2003-03-24 21:08:53 +03:00
if ( ! fid - > fmt - > ops - > pv_setup ( fid - > fmt , UINT64_C ( 0 ) , 0 ,
vg - > extent_size , 0 , UINT64_C ( 0 ) ,
2002-11-18 17:04:08 +03:00
& fid - > metadata_areas , pv , vg ) ) {
2002-01-28 00:30:47 +03:00
log_error ( " Format-specific setup of physical volume '%s' "
2001-10-15 22:39:40 +04:00
" failed. " , pv_name ) ;
return 0 ;
}
if ( find_pv_in_vg ( vg , pv_name ) ) {
log_error ( " Physical volume '%s' listed more than once. " ,
pv_name ) ;
2001-10-12 18:25:53 +04:00
return 0 ;
}
2003-11-06 23:33:34 +03:00
if ( vg - > pv_count & & ( vg - > pv_count = = vg - > max_pv ) ) {
2001-10-15 22:39:40 +04:00
log_error ( " No space for '%s' - volume group '%s' "
" holds max %d physical volume(s). " , pv_name ,
vg - > name , vg - > max_pv ) ;
return 0 ;
}
2005-05-03 21:28:23 +04:00
if ( ! alloc_pv_segment_whole_pv ( mem , pv ) ) {
stack ;
2005-05-09 21:41:36 +04:00
return 0 ;
2005-05-03 21:28:23 +04:00
}
2001-10-15 22:39:40 +04:00
2005-05-03 21:28:23 +04:00
pvl - > pv = pv ;
2001-10-31 15:47:01 +03:00
list_add ( & vg - > pvs , & pvl - > list ) ;
2005-05-03 21:28:23 +04:00
2006-11-10 21:24:11 +03:00
if ( ( uint64_t ) vg - > extent_count + pv - > pe_count > UINT32_MAX ) {
log_error ( " Unable to add %s to %s: new extent count (% "
PRIu64 " ) exceeds limit (% " PRIu32 " ). " ,
pv_name , vg - > name ,
( uint64_t ) vg - > extent_count + pv - > pe_count ,
UINT32_MAX ) ;
return 0 ;
}
2001-10-12 18:25:53 +04:00
vg - > pv_count + + ;
2001-11-06 22:02:26 +03:00
vg - > extent_count + = pv - > pe_count ;
vg - > free_count + = pv - > pe_count ;
2001-10-12 18:25:53 +04:00
return 1 ;
}
2005-04-20 00:44:21 +04:00
static int _copy_pv ( struct physical_volume * pv_to ,
struct physical_volume * pv_from )
2005-04-18 03:59:04 +04:00
{
memcpy ( pv_to , pv_from , sizeof ( * pv_to ) ) ;
2005-04-20 00:52:35 +04:00
if ( ! str_list_dup ( pv_to - > fmt - > cmd - > mem , & pv_to - > tags , & pv_from - > tags ) ) {
log_error ( " PV tags duplication failed " ) ;
return 0 ;
}
if ( ! peg_dup ( pv_to - > fmt - > cmd - > mem , & pv_to - > segments ,
2005-05-11 19:02:49 +04:00
& pv_from - > segments ) ) {
2005-04-20 00:52:35 +04:00
stack ;
return 0 ;
}
return 1 ;
2005-04-18 03:59:04 +04:00
}
2005-04-18 03:57:44 +04:00
int get_pv_from_vg_by_id ( const struct format_type * fmt , const char * vg_name ,
2006-04-13 01:23:04 +04:00
const char * vgid , const char * pvid ,
struct physical_volume * pv )
2005-04-18 03:57:44 +04:00
{
struct volume_group * vg ;
struct pv_list * pvl ;
int consistent = 0 ;
2006-04-13 01:23:04 +04:00
if ( ! ( vg = vg_read ( fmt - > cmd , vg_name , vgid , & consistent ) ) ) {
2005-04-20 00:44:21 +04:00
log_error ( " get_pv_from_vg_by_id: vg_read failed to read VG %s " ,
2005-04-18 03:57:44 +04:00
vg_name ) ;
return 0 ;
}
if ( ! consistent )
log_error ( " Warning: Volume group %s is not consistent " ,
vg_name ) ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( pvl , & vg - > pvs ) {
2006-04-13 01:23:04 +04:00
if ( id_equal ( & pvl - > pv - > id , ( const struct id * ) pvid ) ) {
2005-04-20 00:44:21 +04:00
if ( ! _copy_pv ( pv , pvl - > pv ) ) {
stack ;
return 0 ;
}
2005-04-18 03:57:44 +04:00
return 1 ;
}
}
2005-04-20 00:44:21 +04:00
2005-04-18 03:57:44 +04:00
return 0 ;
}
2002-12-20 02:25:55 +03:00
int vg_rename ( struct cmd_context * cmd , struct volume_group * vg ,
const char * new_name )
{
2005-10-17 03:03:59 +04:00
struct dm_pool * mem = cmd - > mem ;
2005-06-01 20:51:55 +04:00
struct pv_list * pvl ;
2002-12-20 02:25:55 +03:00
2005-10-17 03:03:59 +04:00
if ( ! ( vg - > name = dm_pool_strdup ( mem , new_name ) ) ) {
2002-12-20 02:25:55 +03:00
log_error ( " vg->name allocation failed for '%s' " , new_name ) ;
return 0 ;
}
2005-06-01 20:51:55 +04:00
list_iterate_items ( pvl , & vg - > pvs ) {
2005-10-17 03:03:59 +04:00
if ( ! ( pvl - > pv - > vg_name = dm_pool_strdup ( mem , new_name ) ) ) {
2002-12-20 02:25:55 +03:00
log_error ( " pv->vg_name allocation failed for '%s' " ,
2005-06-01 20:51:55 +04:00
dev_name ( pvl - > pv - > dev ) ) ;
2002-12-20 02:25:55 +03:00
return 0 ;
}
}
return 1 ;
}
2002-11-18 17:04:08 +03:00
int vg_extend ( struct format_instance * fid ,
2001-11-12 15:16:57 +03:00
struct volume_group * vg , int pv_count , char * * pv_names )
2001-10-16 02:04:27 +04:00
{
int i ;
/* attach each pv */
for ( i = 0 ; i < pv_count ; i + + )
2002-11-18 17:04:08 +03:00
if ( ! _add_pv_to_vg ( fid , vg , pv_names [ i ] ) ) {
2001-10-16 02:04:27 +04:00
log_error ( " Unable to add physical volume '%s' to "
" volume group '%s'. " , pv_names [ i ] , vg - > name ) ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
/* FIXME Decide whether to initialise and add new mdahs to format instance */
2001-10-16 02:04:27 +04:00
return 1 ;
}
2001-11-12 18:10:01 +03:00
const char * strip_dir ( const char * vg_name , const char * dev_dir )
2001-11-12 15:16:57 +03:00
{
2002-12-20 02:25:55 +03:00
size_t len = strlen ( dev_dir ) ;
2001-11-12 15:16:57 +03:00
if ( ! strncmp ( vg_name , dev_dir , len ) )
vg_name + = len ;
return vg_name ;
}
2002-04-24 22:20:51 +04:00
struct volume_group * vg_create ( struct cmd_context * cmd , const char * vg_name ,
2002-12-20 02:25:55 +03:00
uint32_t extent_size , uint32_t max_pv ,
2004-05-19 02:12:53 +04:00
uint32_t max_lv , alloc_policy_t alloc ,
int pv_count , char * * pv_names )
2001-10-12 18:25:53 +04:00
{
struct volume_group * vg ;
2005-10-17 03:03:59 +04:00
struct dm_pool * mem = cmd - > mem ;
2002-11-18 17:04:08 +03:00
int consistent = 0 ;
2003-04-30 19:23:43 +04:00
int old_partial ;
2001-10-12 18:25:53 +04:00
2005-10-17 03:03:59 +04:00
if ( ! ( vg = dm_pool_zalloc ( mem , sizeof ( * vg ) ) ) ) {
2001-10-12 18:25:53 +04:00
stack ;
return NULL ;
}
/* is this vg name already in use ? */
2003-04-30 19:23:43 +04:00
old_partial = partial_mode ( ) ;
2002-01-29 20:23:33 +03:00
init_partial ( 1 ) ;
2006-04-13 01:23:04 +04:00
if ( vg_read ( cmd , vg_name , NULL , & consistent ) ) {
2001-10-15 22:39:40 +04:00
log_err ( " A volume group called '%s' already exists. " , vg_name ) ;
2001-10-12 18:25:53 +04:00
goto bad ;
}
2003-04-30 19:23:43 +04:00
init_partial ( old_partial ) ;
2001-10-12 18:25:53 +04:00
if ( ! id_create ( & vg - > id ) ) {
2002-04-24 22:20:51 +04:00
log_err ( " Couldn't create uuid for volume group '%s'. " , vg_name ) ;
2001-10-12 18:25:53 +04:00
goto bad ;
}
2001-11-14 16:52:38 +03:00
/* Strip dev_dir if present */
2002-04-24 22:20:51 +04:00
vg_name = strip_dir ( vg_name , cmd - > dev_dir ) ;
2001-10-15 22:39:40 +04:00
2002-04-24 22:20:51 +04:00
vg - > cmd = cmd ;
2002-01-07 18:27:55 +03:00
2005-10-17 03:03:59 +04:00
if ( ! ( vg - > name = dm_pool_strdup ( mem , vg_name ) ) ) {
2001-10-12 18:25:53 +04:00
stack ;
goto bad ;
}
2002-04-24 22:20:51 +04:00
vg - > seqno = 0 ;
2002-01-11 02:21:07 +03:00
vg - > status = ( RESIZEABLE_VG | LVM_READ | LVM_WRITE ) ;
2006-05-10 21:49:25 +04:00
if ( ! ( vg - > system_id = dm_pool_alloc ( mem , NAME_LEN ) ) ) {
stack ;
goto bad ;
}
2002-01-30 15:47:29 +03:00
* vg - > system_id = ' \0 ' ;
2001-10-12 18:25:53 +04:00
2001-10-15 22:39:40 +04:00
vg - > extent_size = extent_size ;
vg - > extent_count = 0 ;
vg - > free_count = 0 ;
2001-10-12 18:25:53 +04:00
2001-10-15 22:39:40 +04:00
vg - > max_lv = max_lv ;
vg - > max_pv = max_pv ;
2001-10-12 18:25:53 +04:00
2004-05-19 02:12:53 +04:00
vg - > alloc = alloc ;
2001-10-15 22:39:40 +04:00
vg - > pv_count = 0 ;
2001-10-31 15:47:01 +03:00
list_init ( & vg - > pvs ) ;
2001-10-12 18:25:53 +04:00
2001-10-15 22:39:40 +04:00
vg - > lv_count = 0 ;
2001-10-31 15:47:01 +03:00
list_init ( & vg - > lvs ) ;
2001-10-12 18:25:53 +04:00
2002-02-14 18:06:24 +03:00
vg - > snapshot_count = 0 ;
2004-03-08 20:19:15 +03:00
list_init ( & vg - > tags ) ;
2002-04-24 22:20:51 +04:00
if ( ! ( vg - > fid = cmd - > fmt - > ops - > create_instance ( cmd - > fmt , vg_name ,
2006-04-13 01:23:04 +04:00
NULL , NULL ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " Failed to create format instance " ) ;
goto bad ;
}
2003-08-27 01:12:06 +04:00
if ( vg - > fid - > fmt - > ops - > vg_setup & &
! vg - > fid - > fmt - > ops - > vg_setup ( vg - > fid , vg ) ) {
2001-10-15 22:39:40 +04:00
log_error ( " Format specific setup of volume group '%s' failed. " ,
vg_name ) ;
2001-10-12 18:25:53 +04:00
goto bad ;
}
/* attach the pv's */
2002-04-24 22:20:51 +04:00
if ( ! vg_extend ( vg - > fid , vg , pv_count , pv_names ) )
2001-10-16 02:04:27 +04:00
goto bad ;
2001-10-12 18:25:53 +04:00
return vg ;
2002-04-24 22:20:51 +04:00
bad :
2005-10-17 03:03:59 +04:00
dm_pool_free ( mem , vg ) ;
2001-10-12 18:25:53 +04:00
return NULL ;
}
2005-04-18 18:56:42 +04:00
static int _recalc_extents ( uint32_t * extents , const char * desc1 ,
const char * desc2 , uint32_t old_size ,
uint32_t new_size )
{
uint64_t size = ( uint64_t ) old_size * ( * extents ) ;
if ( size % new_size ) {
log_error ( " New size % " PRIu64 " for %s%s not an exact number "
" of new extents. " , size , desc1 , desc2 ) ;
return 0 ;
}
size / = new_size ;
if ( size > UINT32_MAX ) {
log_error ( " New extent count % " PRIu64 " for %s%s exceeds "
" 32 bits. " , size , desc1 , desc2 ) ;
return 0 ;
}
* extents = ( uint32_t ) size ;
return 1 ;
}
int vg_change_pesize ( struct cmd_context * cmd , struct volume_group * vg ,
uint32_t new_size )
{
uint32_t old_size = vg - > extent_size ;
struct pv_list * pvl ;
struct lv_list * lvl ;
struct physical_volume * pv ;
struct logical_volume * lv ;
struct lv_segment * seg ;
2005-05-03 21:28:23 +04:00
struct pv_segment * pvseg ;
2005-04-18 18:56:42 +04:00
uint32_t s ;
vg - > extent_size = new_size ;
if ( vg - > fid - > fmt - > ops - > vg_setup & &
! vg - > fid - > fmt - > ops - > vg_setup ( vg - > fid , vg ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & vg - > extent_count , vg - > name , " " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & vg - > free_count , vg - > name , " free space " ,
old_size , new_size ) ) {
stack ;
return 0 ;
}
/* foreach PV */
list_iterate_items ( pvl , & vg - > pvs ) {
pv = pvl - > pv ;
pv - > pe_size = new_size ;
if ( ! _recalc_extents ( & pv - > pe_count , dev_name ( pv - > dev ) , " " ,
old_size , new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & pv - > pe_alloc_count , dev_name ( pv - > dev ) ,
" allocated space " , old_size , new_size ) ) {
stack ;
return 0 ;
}
2005-05-03 21:28:23 +04:00
2005-05-11 19:02:49 +04:00
/* foreach free PV Segment */
list_iterate_items ( pvseg , & pv - > segments ) {
if ( pvseg - > lvseg )
continue ;
2005-05-03 21:28:23 +04:00
if ( ! _recalc_extents ( & pvseg - > pe , dev_name ( pv - > dev ) ,
" PV segment start " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & pvseg - > len , dev_name ( pv - > dev ) ,
" PV segment length " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
}
2005-04-18 18:56:42 +04:00
}
/* foreach LV */
list_iterate_items ( lvl , & vg - > lvs ) {
lv = lvl - > lv ;
if ( ! _recalc_extents ( & lv - > le_count , lv - > name , " " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
list_iterate_items ( seg , & lv - > segments ) {
if ( ! _recalc_extents ( & seg - > le , lv - > name ,
" segment start " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & seg - > len , lv - > name ,
" segment length " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & seg - > area_len , lv - > name ,
" area length " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents ( & seg - > extents_copied , lv - > name ,
" extents moved " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
/* foreach area */
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
switch ( seg_type ( seg , s ) ) {
2005-04-18 18:56:42 +04:00
case AREA_PV :
if ( ! _recalc_extents
2005-06-01 20:51:55 +04:00
( & seg_pe ( seg , s ) ,
2005-05-03 21:28:23 +04:00
lv - > name ,
" pvseg start " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
if ( ! _recalc_extents
2005-06-01 20:51:55 +04:00
( & seg_pvseg ( seg , s ) - > len ,
2005-05-03 21:28:23 +04:00
lv - > name ,
" pvseg length " , old_size ,
2005-04-18 18:56:42 +04:00
new_size ) ) {
stack ;
return 0 ;
}
break ;
case AREA_LV :
if ( ! _recalc_extents
2005-06-01 20:51:55 +04:00
( & seg_le ( seg , s ) , lv - > name ,
2005-04-18 18:56:42 +04:00
" area start " , old_size ,
new_size ) ) {
stack ;
return 0 ;
}
break ;
2005-06-14 21:54:48 +04:00
case AREA_UNASSIGNED :
log_error ( " Unassigned area %u found in "
" segment " , s ) ;
2005-04-18 18:56:42 +04:00
return 0 ;
}
}
}
}
return 1 ;
}
2002-11-18 17:04:08 +03:00
/* Sizes in sectors */
2002-12-20 02:25:55 +03:00
struct physical_volume * pv_create ( const struct format_type * fmt ,
2002-11-18 17:04:08 +03:00
struct device * dev ,
struct id * id , uint64_t size ,
uint64_t pe_start ,
uint32_t existing_extent_count ,
uint32_t existing_extent_size ,
int pvmetadatacopies ,
uint64_t pvmetadatasize , struct list * mdas )
2001-09-25 16:49:28 +04:00
{
2005-10-17 03:03:59 +04:00
struct dm_pool * mem = fmt - > cmd - > mem ;
2006-04-13 01:23:04 +04:00
struct physical_volume * pv = dm_pool_zalloc ( mem , sizeof ( * pv ) ) ;
2001-10-12 14:32:06 +04:00
if ( ! pv ) {
stack ;
return NULL ;
}
2005-01-20 21:11:53 +03:00
if ( id )
2002-01-16 21:10:08 +03:00
memcpy ( & pv - > id , id , sizeof ( * id ) ) ;
2005-01-20 21:11:53 +03:00
else if ( ! id_create ( & pv - > id ) ) {
log_error ( " Failed to create random uuid for %s. " ,
dev_name ( dev ) ) ;
return NULL ;
}
2002-01-16 21:10:08 +03:00
2002-11-18 17:04:08 +03:00
pv - > dev = dev ;
2001-10-12 14:32:06 +04:00
2005-10-17 03:03:59 +04:00
if ( ! ( pv - > vg_name = dm_pool_zalloc ( mem , NAME_LEN ) ) ) {
2001-10-12 16:21:43 +04:00
stack ;
goto bad ;
}
2002-01-10 18:09:51 +03:00
pv - > status = ALLOCATABLE_PV ;
2001-10-12 14:32:06 +04:00
2002-02-20 21:29:30 +03:00
if ( ! dev_get_size ( pv - > dev , & pv - > size ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " %s: Couldn't get size. " , dev_name ( pv - > dev ) ) ;
2002-02-20 21:29:30 +03:00
goto bad ;
}
if ( size ) {
if ( size > pv - > size )
log_print ( " WARNING: %s: Overriding real size. "
2002-11-18 17:04:08 +03:00
" You could lose data. " , dev_name ( pv - > dev ) ) ;
2002-04-24 22:20:51 +04:00
log_verbose ( " %s: Pretending size is % " PRIu64 " sectors. " ,
2002-11-18 17:04:08 +03:00
dev_name ( pv - > dev ) , size ) ;
2002-02-20 21:29:30 +03:00
pv - > size = size ;
}
2002-04-24 22:20:51 +04:00
2002-02-20 21:29:30 +03:00
if ( pv - > size < PV_MIN_SIZE ) {
2002-11-18 17:04:08 +03:00
log_error ( " %s: Size must exceed minimum of %ld sectors. " ,
dev_name ( pv - > dev ) , PV_MIN_SIZE ) ;
2001-10-12 14:32:06 +04:00
goto bad ;
}
2001-10-15 22:39:40 +04:00
pv - > pe_size = 0 ;
pv - > pe_start = 0 ;
pv - > pe_count = 0 ;
2002-04-24 22:20:51 +04:00
pv - > pe_alloc_count = 0 ;
2002-11-18 17:04:08 +03:00
pv - > fmt = fmt ;
2002-02-15 17:33:59 +03:00
2004-03-08 20:19:15 +03:00
list_init ( & pv - > tags ) ;
2005-04-20 00:52:35 +04:00
list_init ( & pv - > segments ) ;
2004-03-08 20:19:15 +03:00
2002-11-18 17:04:08 +03:00
if ( ! fmt - > ops - > pv_setup ( fmt , pe_start , existing_extent_count ,
existing_extent_size ,
pvmetadatacopies , pvmetadatasize , mdas ,
pv , NULL ) ) {
2002-02-20 21:29:30 +03:00
log_error ( " %s: Format-specific setup of physical volume "
2002-11-18 17:04:08 +03:00
" failed. " , dev_name ( pv - > dev ) ) ;
2002-02-15 17:33:59 +03:00
goto bad ;
}
2001-10-12 14:32:06 +04:00
return pv ;
2001-10-15 22:39:40 +04:00
bad :
2005-10-17 03:03:59 +04:00
dm_pool_free ( mem , pv ) ;
2001-10-12 14:32:06 +04:00
return NULL ;
2001-09-25 16:49:28 +04:00
}
2002-01-21 17:28:12 +03:00
struct pv_list * find_pv_in_vg ( struct volume_group * vg , const char * pv_name )
2001-10-15 22:39:40 +04:00
{
2002-01-21 17:28:12 +03:00
struct pv_list * pvl ;
2001-10-25 18:04:18 +04:00
2005-06-01 20:51:55 +04:00
list_iterate_items ( pvl , & vg - > pvs )
2002-01-25 01:37:24 +03:00
if ( pvl - > pv - > dev = = dev_cache_get ( pv_name , vg - > cmd - > filter ) )
2002-01-21 17:28:12 +03:00
return pvl ;
2001-09-25 16:49:28 +04:00
2001-10-15 22:39:40 +04:00
return NULL ;
2002-11-18 17:04:08 +03:00
}
2003-01-18 00:04:26 +03:00
int pv_is_in_vg ( struct volume_group * vg , struct physical_volume * pv )
{
2005-06-01 20:51:55 +04:00
struct pv_list * pvl ;
2003-01-18 00:04:26 +03:00
2005-06-01 20:51:55 +04:00
list_iterate_items ( pvl , & vg - > pvs )
if ( pv = = pvl - > pv )
2003-01-18 00:04:26 +03:00
return 1 ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
struct physical_volume * find_pv_in_vg_by_uuid ( struct volume_group * vg ,
struct id * id )
{
struct pv_list * pvl ;
2001-10-12 14:32:06 +04:00
2005-06-01 20:51:55 +04:00
list_iterate_items ( pvl , & vg - > pvs )
2002-11-18 17:04:08 +03:00
if ( id_equal ( & pvl - > pv - > id , id ) )
return pvl - > pv ;
return NULL ;
2001-10-15 22:39:40 +04:00
}
2001-10-29 16:52:23 +03:00
2002-01-21 17:28:12 +03:00
struct lv_list * find_lv_in_vg ( struct volume_group * vg , const char * lv_name )
2001-10-29 16:52:23 +03:00
{
2002-01-21 17:28:12 +03:00
struct lv_list * lvl ;
2001-10-29 16:52:23 +03:00
const char * ptr ;
/* Use last component */
if ( ( ptr = strrchr ( lv_name , ' / ' ) ) )
ptr + + ;
else
ptr = lv_name ;
2001-10-31 15:47:01 +03:00
2005-06-01 20:51:55 +04:00
list_iterate_items ( lvl , & vg - > lvs )
2002-01-21 19:49:32 +03:00
if ( ! strcmp ( lvl - > lv - > name , ptr ) )
2002-01-21 17:28:12 +03:00
return lvl ;
2001-10-29 16:52:23 +03:00
2001-11-10 01:01:04 +03:00
return NULL ;
2001-10-29 16:52:23 +03:00
}
2002-12-20 02:25:55 +03:00
struct lv_list * find_lv_in_vg_by_lvid ( struct volume_group * vg ,
const union lvid * lvid )
2002-02-25 15:56:16 +03:00
{
struct lv_list * lvl ;
2005-05-19 20:48:51 +04:00
list_iterate_items ( lvl , & vg - > lvs )
2002-03-05 23:03:09 +03:00
if ( ! strncmp ( lvl - > lv - > lvid . s , lvid - > s , sizeof ( * lvid ) ) )
2002-02-25 15:56:16 +03:00
return lvl ;
return NULL ;
}
2001-10-29 16:52:23 +03:00
struct logical_volume * find_lv ( struct volume_group * vg , const char * lv_name )
{
2002-01-21 17:28:12 +03:00
struct lv_list * lvl = find_lv_in_vg ( vg , lv_name ) ;
2002-01-21 19:49:32 +03:00
return lvl ? lvl - > lv : NULL ;
2001-10-29 16:52:23 +03:00
}
2001-11-28 16:45:50 +03:00
struct physical_volume * find_pv ( struct volume_group * vg , struct device * dev )
2001-10-29 16:52:23 +03:00
{
2005-06-01 20:51:55 +04:00
struct pv_list * pvl ;
2001-11-10 01:01:04 +03:00
2005-06-01 20:51:55 +04:00
list_iterate_items ( pvl , & vg - > pvs )
if ( dev = = pvl - > pv - > dev )
return pvl - > pv ;
2002-01-21 19:05:23 +03:00
2001-11-10 01:01:04 +03:00
return NULL ;
2001-10-29 16:52:23 +03:00
}
2002-02-25 15:56:16 +03:00
2004-05-05 15:04:28 +04:00
struct physical_volume * find_pv_by_name ( struct cmd_context * cmd ,
const char * pv_name )
{
struct physical_volume * pv ;
2004-06-19 23:27:00 +04:00
if ( ! ( pv = pv_read ( cmd , pv_name , NULL , NULL , 1 ) ) ) {
2004-05-05 15:04:28 +04:00
log_error ( " Physical volume %s not found " , pv_name ) ;
return NULL ;
}
if ( ! pv - > vg_name [ 0 ] ) {
log_error ( " Physical volume %s not in a volume group " , pv_name ) ;
return NULL ;
}
return pv ;
}
2003-04-25 02:23:24 +04:00
/* Find segment at a given logical extent in an LV */
struct lv_segment * find_seg_by_le ( struct logical_volume * lv , uint32_t le )
{
struct lv_segment * seg ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv - > segments )
2003-04-25 02:23:24 +04:00
if ( le > = seg - > le & & le < seg - > le + seg - > len )
return seg ;
return NULL ;
}
2005-10-28 16:48:50 +04:00
struct lv_segment * first_seg ( struct logical_volume * lv )
{
struct lv_segment * seg = NULL ;
list_iterate_items ( seg , & lv - > segments )
break ;
return seg ;
}
2005-05-03 21:28:23 +04:00
/* Find segment at a given physical extent in a PV */
struct pv_segment * find_peg_by_pe ( struct physical_volume * pv , uint32_t pe )
{
struct pv_segment * peg ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( peg , & pv - > segments )
2005-05-03 21:28:23 +04:00
if ( pe > = peg - > pe & & pe < peg - > pe + peg - > len )
return peg ;
return NULL ;
}
2002-04-24 22:20:51 +04:00
int vg_remove ( struct volume_group * vg )
{
2002-11-18 17:04:08 +03:00
struct metadata_area * mda ;
2002-04-24 22:20:51 +04:00
/* FIXME Improve recovery situation? */
/* Remove each copy of the metadata */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & vg - > fid - > metadata_areas ) {
2002-11-18 17:04:08 +03:00
if ( mda - > ops - > vg_remove & &
! mda - > ops - > vg_remove ( vg - > fid , vg , mda ) ) {
2002-04-24 22:20:51 +04:00
stack ;
return 0 ;
}
}
return 1 ;
}
2005-07-12 23:40:59 +04:00
int vg_validate ( struct volume_group * vg )
2002-04-24 22:20:51 +04:00
{
2006-08-09 23:33:25 +04:00
struct pv_list * pvl , * pvl2 ;
struct lv_list * lvl , * lvl2 ;
2006-12-01 02:11:42 +03:00
char uuid [ 64 ] __attribute ( ( aligned ( 8 ) ) ) ;
2006-08-09 23:33:25 +04:00
int r = 1 ;
2006-10-06 02:02:52 +04:00
/* FIXME Also check there's no data/metadata overlap */
2006-08-09 23:33:25 +04:00
list_iterate_items ( pvl , & vg - > pvs ) {
list_iterate_items ( pvl2 , & vg - > pvs ) {
if ( pvl = = pvl2 )
break ;
if ( id_equal ( & pvl - > pv - > id ,
& pvl2 - > pv - > id ) ) {
if ( ! id_write_format ( & pvl - > pv - > id , uuid ,
sizeof ( uuid ) ) )
stack ;
log_error ( " Internal error: Duplicate PV id "
" %s detected for %s in %s. " ,
uuid , dev_name ( pvl - > pv - > dev ) ,
vg - > name ) ;
r = 0 ;
}
}
}
2002-04-24 22:20:51 +04:00
2005-05-03 21:28:23 +04:00
if ( ! check_pv_segments ( vg ) ) {
log_error ( " Internal error: PV segments corrupted in %s. " ,
vg - > name ) ;
2006-08-09 23:33:25 +04:00
r = 0 ;
}
list_iterate_items ( lvl , & vg - > lvs ) {
list_iterate_items ( lvl2 , & vg - > lvs ) {
if ( lvl = = lvl2 )
break ;
if ( ! strcmp ( lvl - > lv - > name , lvl2 - > lv - > name ) ) {
log_error ( " Internal error: Duplicate LV name "
" %s detected in %s. " , lvl - > lv - > name ,
vg - > name ) ;
r = 0 ;
}
if ( id_equal ( & lvl - > lv - > lvid . id [ 1 ] ,
& lvl2 - > lv - > lvid . id [ 1 ] ) ) {
if ( ! id_write_format ( & lvl - > lv - > lvid . id [ 1 ] , uuid ,
sizeof ( uuid ) ) )
stack ;
log_error ( " Internal error: Duplicate LV id "
" %s detected for %s and %s in %s. " ,
uuid , lvl - > lv - > name , lvl2 - > lv - > name ,
vg - > name ) ;
r = 0 ;
}
}
2005-05-03 21:28:23 +04:00
}
2005-06-01 20:51:55 +04:00
list_iterate_items ( lvl , & vg - > lvs ) {
2005-10-28 01:51:28 +04:00
if ( ! check_lv_segments ( lvl - > lv , 1 ) ) {
2005-06-01 20:51:55 +04:00
log_error ( " Internal error: LV segments corrupted in %s. " ,
lvl - > lv - > name ) ;
2006-08-09 23:33:25 +04:00
r = 0 ;
2005-06-01 20:51:55 +04:00
}
}
2006-08-09 23:33:25 +04:00
return r ;
2005-07-12 23:40:59 +04:00
}
/*
* After vg_write ( ) returns success ,
* caller MUST call either vg_commit ( ) or vg_revert ( )
*/
int vg_write ( struct volume_group * vg )
{
struct list * mdah ;
struct metadata_area * mda ;
if ( ! vg_validate ( vg ) ) {
stack ;
return 0 ;
}
2002-04-30 21:12:37 +04:00
if ( vg - > status & PARTIAL_VG ) {
log_error ( " Cannot change metadata for partial volume group %s " ,
vg - > name ) ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( list_empty ( & vg - > fid - > metadata_areas ) ) {
log_error ( " Aborting vg_write: No metadata areas to write to! " ) ;
return 0 ;
}
2002-04-24 22:20:51 +04:00
vg - > seqno + + ;
/* Write to each copy of the metadata area */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & vg - > fid - > metadata_areas ) {
2004-03-27 00:07:30 +03:00
if ( ! mda - > ops - > vg_write ) {
2003-08-27 01:12:06 +04:00
log_error ( " Format does not support writing volume "
" group metadata areas " ) ;
/* Revert */
2005-06-01 20:51:55 +04:00
list_uniterate ( mdah , & vg - > fid - > metadata_areas , & mda - > list ) {
mda = list_item ( mdah , struct metadata_area ) ;
2005-04-06 22:59:55 +04:00
2003-08-27 01:12:06 +04:00
if ( mda - > ops - > vg_revert & &
! mda - > ops - > vg_revert ( vg - > fid , vg , mda ) ) {
stack ;
}
}
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( ! mda - > ops - > vg_write ( vg - > fid , vg , mda ) ) {
2002-04-24 22:20:51 +04:00
stack ;
2003-07-05 02:34:56 +04:00
/* Revert */
2005-06-01 20:51:55 +04:00
list_uniterate ( mdah , & vg - > fid - > metadata_areas , & mda - > list ) {
mda = list_item ( mdah , struct metadata_area ) ;
2003-07-05 02:34:56 +04:00
if ( mda - > ops - > vg_revert & &
! mda - > ops - > vg_revert ( vg - > fid , vg , mda ) ) {
stack ;
}
}
2002-04-24 22:20:51 +04:00
return 0 ;
}
}
2005-04-06 22:59:55 +04:00
/* Now pre-commit each copy of the new metadata */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & vg - > fid - > metadata_areas ) {
2005-04-06 22:59:55 +04:00
if ( mda - > ops - > vg_precommit & &
! mda - > ops - > vg_precommit ( vg - > fid , vg , mda ) ) {
stack ;
/* Revert */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & vg - > fid - > metadata_areas ) {
2005-04-06 22:59:55 +04:00
if ( mda - > ops - > vg_revert & &
! mda - > ops - > vg_revert ( vg - > fid , vg , mda ) ) {
stack ;
}
}
return 0 ;
}
}
2003-07-05 02:34:56 +04:00
return 1 ;
}
/* Commit pending changes */
int vg_commit ( struct volume_group * vg )
{
struct metadata_area * mda ;
int cache_updated = 0 ;
int failed = 0 ;
2002-04-24 22:20:51 +04:00
/* Commit to each copy of the metadata area */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & vg - > fid - > metadata_areas ) {
2003-07-05 02:34:56 +04:00
failed = 0 ;
2002-11-18 17:04:08 +03:00
if ( mda - > ops - > vg_commit & &
! mda - > ops - > vg_commit ( vg - > fid , vg , mda ) ) {
2002-04-24 22:20:51 +04:00
stack ;
2003-07-05 02:34:56 +04:00
failed = 1 ;
}
/* Update cache first time we succeed */
if ( ! failed & & ! cache_updated ) {
lvmcache_update_vg ( vg ) ;
cache_updated = 1 ;
}
}
/* If at least one mda commit succeeded, it was committed */
return cache_updated ;
}
/* Don't commit any pending changes */
int vg_revert ( struct volume_group * vg )
{
struct metadata_area * mda ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & vg - > fid - > metadata_areas ) {
2003-07-05 02:34:56 +04:00
if ( mda - > ops - > vg_revert & &
! mda - > ops - > vg_revert ( vg - > fid , vg , mda ) ) {
stack ;
2002-04-24 22:20:51 +04:00
}
}
return 1 ;
}
2002-11-18 17:04:08 +03:00
/* Make orphan PVs look like a VG */
2002-12-20 02:25:55 +03:00
static struct volume_group * _vg_read_orphans ( struct cmd_context * cmd )
2002-11-18 17:04:08 +03:00
{
2003-07-05 02:34:56 +04:00
struct lvmcache_vginfo * vginfo ;
2005-06-01 20:51:55 +04:00
struct lvmcache_info * info ;
2002-11-18 17:04:08 +03:00
struct pv_list * pvl ;
struct volume_group * vg ;
struct physical_volume * pv ;
2006-04-12 21:54:11 +04:00
if ( ! ( vginfo = vginfo_from_vgname ( ORPHAN , NULL ) ) ) {
2002-11-18 17:04:08 +03:00
stack ;
return NULL ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( vg = dm_pool_zalloc ( cmd - > mem , sizeof ( * vg ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " vg allocation failed " ) ;
return NULL ;
}
list_init ( & vg - > pvs ) ;
list_init ( & vg - > lvs ) ;
2004-03-08 20:19:15 +03:00
list_init ( & vg - > tags ) ;
2002-11-18 17:04:08 +03:00
vg - > cmd = cmd ;
2005-10-17 03:03:59 +04:00
if ( ! ( vg - > name = dm_pool_strdup ( cmd - > mem , ORPHAN ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " vg name allocation failed " ) ;
return NULL ;
}
2005-06-01 20:51:55 +04:00
list_iterate_items ( info , & vginfo - > infos ) {
if ( ! ( pv = pv_read ( cmd , dev_name ( info - > dev ) , NULL , NULL , 1 ) ) ) {
2002-11-18 17:04:08 +03:00
continue ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( pvl = dm_pool_zalloc ( cmd - > mem , sizeof ( * pvl ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " pv_list allocation failed " ) ;
return NULL ;
}
pvl - > pv = pv ;
list_add ( & vg - > pvs , & pvl - > list ) ;
vg - > pv_count + + ;
}
return vg ;
}
2007-02-07 16:29:52 +03:00
static int _update_pv_list ( struct list * all_pvs , struct volume_group * vg )
{
struct pv_list * pvl , * pvl2 ;
list_iterate_items ( pvl , & vg - > pvs ) {
list_iterate_items ( pvl2 , all_pvs ) {
if ( pvl - > pv - > dev = = pvl2 - > pv - > dev )
goto next_pv ;
}
/* PV is not on list so add it. Note that we don't copy it. */
if ( ! ( pvl2 = dm_pool_zalloc ( vg - > cmd - > mem , sizeof ( * pvl2 ) ) ) ) {
log_error ( " pv_list allocation for '%s' failed " ,
dev_name ( pvl - > pv - > dev ) ) ;
return 0 ;
}
pvl2 - > pv = pvl - > pv ;
list_add ( all_pvs , & pvl2 - > list ) ;
next_pv :
;
}
return 1 ;
}
2002-11-18 17:04:08 +03:00
/* Caller sets consistent to 1 if it's safe for vg_read to correct
* inconsistent metadata on disk ( i . e . the VG write lock is held ) .
* This guarantees only consistent metadata is returned unless PARTIAL_VG .
* If consistent is 0 , caller must check whether consistent = = 1 on return
* and take appropriate action if it isn ' t ( e . g . abort ; get write lock
* and call vg_read again ) .
2005-10-31 23:15:28 +03:00
*
* If precommitted is set , use precommitted metadata if present .
2002-11-18 17:04:08 +03:00
*/
2005-04-06 22:59:55 +04:00
static struct volume_group * _vg_read ( struct cmd_context * cmd ,
const char * vgname ,
2006-04-13 01:23:04 +04:00
const char * vgid ,
2005-04-06 22:59:55 +04:00
int * consistent , int precommitted )
2002-04-24 22:20:51 +04:00
{
struct format_instance * fid ;
2002-12-20 02:25:55 +03:00
const struct format_type * fmt ;
struct volume_group * vg , * correct_vg = NULL ;
2002-11-18 17:04:08 +03:00
struct metadata_area * mda ;
2002-12-20 02:25:55 +03:00
int inconsistent = 0 ;
2007-02-07 16:29:52 +03:00
int inconsistent_vgid = 0 ;
2005-10-31 23:15:28 +03:00
int use_precommitted = precommitted ;
2006-04-21 23:12:41 +04:00
struct list * pvids ;
2007-02-07 16:29:52 +03:00
struct pv_list * pvl , * pvl2 ;
struct list all_pvs ;
char uuid [ 64 ] __attribute ( ( aligned ( 8 ) ) ) ;
2002-04-24 22:20:51 +04:00
2002-11-18 17:04:08 +03:00
if ( ! * vgname ) {
2005-10-31 23:15:28 +03:00
if ( use_precommitted ) {
2005-04-06 22:59:55 +04:00
log_error ( " Internal error: vg_read requires vgname "
" with pre-commit. " ) ;
return NULL ;
}
2002-11-18 17:04:08 +03:00
* consistent = 1 ;
return _vg_read_orphans ( cmd ) ;
}
/* Find the vgname in the cache */
/* If it's not there we must do full scan to be completely sure */
2006-04-13 01:23:04 +04:00
if ( ! ( fmt = fmt_from_vgname ( vgname , vgid ) ) ) {
2003-07-05 02:34:56 +04:00
lvmcache_label_scan ( cmd , 0 ) ;
2006-04-13 01:23:04 +04:00
if ( ! ( fmt = fmt_from_vgname ( vgname , vgid ) ) ) {
2003-07-05 02:34:56 +04:00
if ( memlock ( ) ) {
stack ;
return NULL ;
}
2005-03-08 16:46:17 +03:00
lvmcache_label_scan ( cmd , 2 ) ;
2006-04-13 01:23:04 +04:00
if ( ! ( fmt = fmt_from_vgname ( vgname , vgid ) ) ) {
2002-11-18 17:04:08 +03:00
stack ;
return NULL ;
}
2002-04-24 22:20:51 +04:00
}
}
2005-10-31 23:15:28 +03:00
if ( use_precommitted & & ! ( fmt - > features & FMT_PRECOMMIT ) )
use_precommitted = 0 ;
2005-04-06 22:59:55 +04:00
2006-04-21 23:12:41 +04:00
/* Store pvids for later so we can check if any are missing */
if ( ! ( pvids = lvmcache_get_pvids ( cmd , vgname , vgid ) ) ) {
stack ;
return NULL ;
}
2002-11-18 17:04:08 +03:00
/* create format instance with appropriate metadata area */
2006-04-13 01:23:04 +04:00
if ( ! ( fid = fmt - > ops - > create_instance ( fmt , vgname , vgid , NULL ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " Failed to create format instance " ) ;
return NULL ;
}
/* Ensure contents of all metadata areas match - else do recovery */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & fid - > metadata_areas ) {
2005-10-31 23:15:28 +03:00
if ( ( use_precommitted & &
2005-04-06 22:59:55 +04:00
! ( vg = mda - > ops - > vg_read_precommit ( fid , vgname , mda ) ) ) | |
2005-10-31 23:15:28 +03:00
( ! use_precommitted & &
2005-04-06 22:59:55 +04:00
! ( vg = mda - > ops - > vg_read ( fid , vgname , mda ) ) ) ) {
2002-11-18 17:04:08 +03:00
inconsistent = 1 ;
continue ;
2002-04-24 22:20:51 +04:00
}
2002-12-20 02:25:55 +03:00
if ( ! correct_vg ) {
2002-04-24 22:20:51 +04:00
correct_vg = vg ;
continue ;
}
2002-11-18 17:04:08 +03:00
/* FIXME Also ensure contents same - checksum compare? */
2002-04-24 22:20:51 +04:00
if ( correct_vg - > seqno ! = vg - > seqno ) {
inconsistent = 1 ;
if ( vg - > seqno > correct_vg - > seqno )
correct_vg = vg ;
}
}
2006-04-21 23:12:41 +04:00
/* Ensure every PV in the VG was in the cache */
if ( correct_vg ) {
if ( list_size ( & correct_vg - > pvs ) ! = list_size ( pvids ) ) {
log_debug ( " Cached VG %s had incorrect PV list " ,
2006-09-22 00:25:54 +04:00
vgname ) ;
2006-07-04 23:36:49 +04:00
if ( memlock ( ) )
inconsistent = 1 ;
else
correct_vg = NULL ;
2006-04-21 23:12:41 +04:00
} else list_iterate_items ( pvl , & correct_vg - > pvs ) {
if ( ! str_list_match_item ( pvids , pvl - > pv - > dev - > pvid ) ) {
log_debug ( " Cached VG %s had incorrect PV list " ,
2006-09-22 00:25:54 +04:00
vgname ) ;
2006-04-21 23:12:41 +04:00
correct_vg = NULL ;
break ;
}
}
}
2007-02-07 16:29:52 +03:00
list_init ( & all_pvs ) ;
2005-03-22 01:40:35 +03:00
/* Failed to find VG where we expected it - full scan and retry */
2002-12-20 02:25:55 +03:00
if ( ! correct_vg ) {
2005-03-22 01:40:35 +03:00
inconsistent = 0 ;
2006-07-04 23:36:49 +04:00
if ( memlock ( ) ) {
stack ;
return NULL ;
}
2005-03-22 01:40:35 +03:00
lvmcache_label_scan ( cmd , 2 ) ;
2006-04-13 01:23:04 +04:00
if ( ! ( fmt = fmt_from_vgname ( vgname , vgid ) ) ) {
2005-03-22 01:40:35 +03:00
stack ;
return NULL ;
}
2005-10-31 23:15:28 +03:00
if ( precommitted & & ! ( fmt - > features & FMT_PRECOMMIT ) )
use_precommitted = 0 ;
2005-04-06 22:59:55 +04:00
2005-03-22 01:40:35 +03:00
/* create format instance with appropriate metadata area */
2006-04-13 01:23:04 +04:00
if ( ! ( fid = fmt - > ops - > create_instance ( fmt , vgname , vgid , NULL ) ) ) {
2005-03-22 01:40:35 +03:00
log_error ( " Failed to create format instance " ) ;
return NULL ;
}
/* Ensure contents of all metadata areas match - else recover */
2005-06-01 20:51:55 +04:00
list_iterate_items ( mda , & fid - > metadata_areas ) {
2005-10-31 23:15:28 +03:00
if ( ( use_precommitted & &
2005-04-06 22:59:55 +04:00
! ( vg = mda - > ops - > vg_read_precommit ( fid , vgname ,
mda ) ) ) | |
2005-10-31 23:15:28 +03:00
( ! use_precommitted & &
2005-04-06 22:59:55 +04:00
! ( vg = mda - > ops - > vg_read ( fid , vgname , mda ) ) ) ) {
2005-03-22 01:40:35 +03:00
inconsistent = 1 ;
continue ;
}
if ( ! correct_vg ) {
correct_vg = vg ;
2007-02-07 16:29:52 +03:00
if ( ! _update_pv_list ( & all_pvs , correct_vg ) )
return_NULL ;
2005-03-22 01:40:35 +03:00
continue ;
}
2007-02-07 16:29:52 +03:00
if ( strncmp ( ( char * ) vg - > id . uuid ,
( char * ) correct_vg - > id . uuid , ID_LEN ) ) {
inconsistent = 1 ;
inconsistent_vgid = 1 ;
}
2005-03-22 01:40:35 +03:00
/* FIXME Also ensure contents same - checksums same? */
if ( correct_vg - > seqno ! = vg - > seqno ) {
inconsistent = 1 ;
2007-02-07 16:29:52 +03:00
if ( vg - > seqno > correct_vg - > seqno ) {
if ( ! _update_pv_list ( & all_pvs , vg ) )
return_NULL ;
2005-03-22 01:40:35 +03:00
correct_vg = vg ;
2007-02-07 16:29:52 +03:00
}
2005-03-22 01:40:35 +03:00
}
}
/* Give up looking */
if ( ! correct_vg ) {
stack ;
return NULL ;
}
2002-05-13 16:38:54 +04:00
}
2003-07-05 02:34:56 +04:00
lvmcache_update_vg ( correct_vg ) ;
2002-11-18 17:04:08 +03:00
2002-04-24 22:20:51 +04:00
if ( inconsistent ) {
2005-10-31 23:15:28 +03:00
/* FIXME Test should be if we're *using* precommitted metadata not if we were searching for it */
if ( use_precommitted ) {
2005-04-06 22:59:55 +04:00
log_error ( " Inconsistent pre-commit metadata copies "
" for volume group %s " , vgname ) ;
return NULL ;
}
2002-11-18 17:04:08 +03:00
if ( ! * consistent )
return correct_vg ;
/* Don't touch partial volume group metadata */
/* Should be fixed manually with vgcfgbackup/restore etc. */
if ( ( correct_vg - > status & PARTIAL_VG ) ) {
log_error ( " Inconsistent metadata copies found for "
" partial volume group %s " , vgname ) ;
* consistent = 0 ;
return correct_vg ;
}
2007-02-07 16:29:52 +03:00
/* Don't touch if vgids didn't match */
if ( inconsistent_vgid ) {
log_error ( " Inconsistent metadata UUIDs found for "
" volume group %s " , vgname ) ;
* consistent = 0 ;
return correct_vg ;
}
log_print ( " Inconsistent metadata found for VG %s - updating "
" to use version %u " , vgname , correct_vg - > seqno ) ;
2002-04-24 22:20:51 +04:00
if ( ! vg_write ( correct_vg ) ) {
log_error ( " Automatic metadata correction failed " ) ;
return NULL ;
}
2007-02-07 16:29:52 +03:00
2005-01-17 21:24:28 +03:00
if ( ! vg_commit ( correct_vg ) ) {
log_error ( " Automatic metadata correction commit "
" failed " ) ;
return NULL ;
}
2007-02-07 16:29:52 +03:00
list_iterate_items ( pvl , & all_pvs ) {
list_iterate_items ( pvl2 , & correct_vg - > pvs ) {
if ( pvl - > pv - > dev = = pvl2 - > pv - > dev )
goto next_pv ;
}
if ( ! id_write_format ( & pvl - > pv - > id , uuid , sizeof ( uuid ) ) )
return_NULL ;
log_error ( " Removing PV %s (%s) that no longer belongs to VG %s " ,
dev_name ( pvl - > pv - > dev ) , uuid , correct_vg - > name ) ;
if ( ! pv_write_orphan ( cmd , pvl - > pv ) )
return_NULL ;
next_pv :
;
}
2002-04-24 22:20:51 +04:00
}
2003-05-06 16:06:02 +04:00
if ( ( correct_vg - > status & PVMOVE ) & & ! pvmove_mode ( ) ) {
2003-04-30 19:23:43 +04:00
log_error ( " WARNING: Interrupted pvmove detected in "
2003-07-15 05:26:24 +04:00
" volume group %s " , correct_vg - > name ) ;
2003-04-30 19:23:43 +04:00
log_error ( " Please restore the metadata by running "
" vgcfgrestore. " ) ;
return NULL ;
}
2002-04-24 22:20:51 +04:00
2003-04-30 19:23:43 +04:00
* consistent = 1 ;
2002-05-13 16:38:54 +04:00
return correct_vg ;
2002-04-24 22:20:51 +04:00
}
2005-04-06 22:59:55 +04:00
struct volume_group * vg_read ( struct cmd_context * cmd , const char * vgname ,
2006-04-13 01:23:04 +04:00
const char * vgid , int * consistent )
2005-04-06 22:59:55 +04:00
{
2005-06-14 21:54:48 +04:00
struct volume_group * vg ;
struct lv_list * lvl ;
2006-04-13 01:23:04 +04:00
if ( ! ( vg = _vg_read ( cmd , vgname , vgid , consistent , 0 ) ) )
2005-06-14 21:54:48 +04:00
return NULL ;
if ( ! check_pv_segments ( vg ) ) {
log_error ( " Internal error: PV segments corrupted in %s. " ,
vg - > name ) ;
return NULL ;
}
list_iterate_items ( lvl , & vg - > lvs ) {
2005-10-28 01:51:28 +04:00
if ( ! check_lv_segments ( lvl - > lv , 1 ) ) {
2005-06-14 21:54:48 +04:00
log_error ( " Internal error: LV segments corrupted in %s. " ,
lvl - > lv - > name ) ;
return NULL ;
}
}
return vg ;
2005-04-06 22:59:55 +04:00
}
2002-11-18 17:04:08 +03:00
/* This is only called by lv_from_lvid, which is only called from
* activate . c so we know the appropriate VG lock is already held and
* the vg_read is therefore safe .
*/
2005-10-31 23:15:28 +03:00
static struct volume_group * _vg_read_by_vgid ( struct cmd_context * cmd ,
const char * vgid ,
int precommitted )
2002-04-24 22:20:51 +04:00
{
2003-10-16 00:10:11 +04:00
const char * vgname ;
2005-06-01 20:51:55 +04:00
struct list * vgnames ;
2002-04-24 22:20:51 +04:00
struct volume_group * vg ;
2003-07-05 02:34:56 +04:00
struct lvmcache_vginfo * vginfo ;
2005-06-01 20:51:55 +04:00
struct str_list * strl ;
2002-11-18 17:04:08 +03:00
int consistent = 0 ;
/* Is corresponding vgname already cached? */
if ( ( vginfo = vginfo_from_vgid ( vgid ) ) & &
vginfo - > vgname & & * vginfo - > vgname ) {
2006-04-13 01:23:04 +04:00
if ( ( vg = _vg_read ( cmd , vginfo - > vgname , vgid ,
2005-10-31 23:15:28 +03:00
& consistent , precommitted ) ) & &
2006-05-10 01:23:51 +04:00
! strncmp ( ( char * ) vg - > id . uuid , vgid , ID_LEN ) ) {
2002-11-18 17:04:08 +03:00
if ( ! consistent ) {
log_error ( " Volume group %s metadata is "
" inconsistent " , vginfo - > vgname ) ;
2006-07-04 23:36:49 +04:00
if ( ! partial_mode ( ) )
return NULL ;
2002-11-18 17:04:08 +03:00
}
return vg ;
}
}
2002-04-24 22:20:51 +04:00
2003-10-16 00:10:11 +04:00
/* Mustn't scan if memory locked: ensure cache gets pre-populated! */
if ( memlock ( ) )
return NULL ;
2003-07-05 02:34:56 +04:00
/* FIXME Need a genuine read by ID here - don't vg_read by name! */
/* FIXME Disabled vgrenames while active for now because we aren't
* allowed to do a full scan here any more . */
// The slow way - full scan required to cope with vgrename
2005-03-08 16:46:17 +03:00
if ( ! ( vgnames = get_vgs ( cmd , 2 ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " vg_read_by_vgid: get_vgs failed " ) ;
return NULL ;
}
2005-06-01 20:51:55 +04:00
list_iterate_items ( strl , vgnames ) {
vgname = strl - > str ;
2002-11-18 17:04:08 +03:00
if ( ! vgname | | ! * vgname )
2003-07-05 02:34:56 +04:00
continue ; // FIXME Unnecessary?
2002-11-18 17:04:08 +03:00
consistent = 0 ;
2006-04-13 01:23:04 +04:00
if ( ( vg = _vg_read ( cmd , vgname , vgid , & consistent ,
2005-10-31 23:15:28 +03:00
precommitted ) ) & &
2006-05-10 01:23:51 +04:00
! strncmp ( ( char * ) vg - > id . uuid , vgid , ID_LEN ) ) {
2002-11-18 17:04:08 +03:00
if ( ! consistent ) {
log_error ( " Volume group %s metadata is "
" inconsistent " , vgname ) ;
return NULL ;
}
return vg ;
}
2002-04-24 22:20:51 +04:00
}
return NULL ;
}
2002-11-18 17:04:08 +03:00
/* Only called by activate.c */
2005-10-31 23:15:28 +03:00
struct logical_volume * lv_from_lvid ( struct cmd_context * cmd , const char * lvid_s ,
int precommitted )
2002-11-18 17:04:08 +03:00
{
struct lv_list * lvl ;
struct volume_group * vg ;
2002-12-20 02:25:55 +03:00
const union lvid * lvid ;
2002-11-18 17:04:08 +03:00
2002-12-20 02:25:55 +03:00
lvid = ( const union lvid * ) lvid_s ;
2002-11-18 17:04:08 +03:00
log_very_verbose ( " Finding volume group for uuid %s " , lvid_s ) ;
2006-05-10 01:23:51 +04:00
if ( ! ( vg = _vg_read_by_vgid ( cmd , ( char * ) lvid - > id [ 0 ] . uuid , precommitted ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " Volume group for uuid not found: %s " , lvid_s ) ;
return NULL ;
}
log_verbose ( " Found volume group \" %s \" " , vg - > name ) ;
if ( vg - > status & EXPORTED_VG ) {
log_error ( " Volume group \" %s \" is exported " , vg - > name ) ;
return NULL ;
}
if ( ! ( lvl = find_lv_in_vg_by_lvid ( vg , lvid ) ) ) {
log_very_verbose ( " Can't find logical volume id %s " , lvid_s ) ;
return NULL ;
}
return lvl - > lv ;
}
/* FIXME Use label functions instead of PV functions */
struct physical_volume * pv_read ( struct cmd_context * cmd , const char * pv_name ,
2004-06-19 23:27:00 +04:00
struct list * mdas , uint64_t * label_sector ,
int warnings )
2002-04-24 22:20:51 +04:00
{
struct physical_volume * pv ;
2002-11-18 17:04:08 +03:00
struct label * label ;
2003-07-05 02:34:56 +04:00
struct lvmcache_info * info ;
2002-11-18 17:04:08 +03:00
struct device * dev ;
2002-04-24 22:20:51 +04:00
2002-11-18 17:04:08 +03:00
if ( ! ( dev = dev_cache_get ( pv_name , cmd - > filter ) ) ) {
stack ;
2005-04-20 00:52:35 +04:00
return NULL ;
2002-04-24 22:20:51 +04:00
}
2002-11-18 17:04:08 +03:00
if ( ! ( label_read ( dev , & label ) ) ) {
2004-06-19 23:27:00 +04:00
if ( warnings )
log_error ( " No physical volume label read from %s " ,
pv_name ) ;
2005-04-20 00:52:35 +04:00
return NULL ;
2002-04-24 22:20:51 +04:00
}
2003-07-05 02:34:56 +04:00
info = ( struct lvmcache_info * ) label - > info ;
2002-11-18 17:04:08 +03:00
if ( label_sector & & * label_sector )
* label_sector = label - > sector ;
2005-10-17 03:03:59 +04:00
if ( ! ( pv = dm_pool_zalloc ( cmd - > mem , sizeof ( * pv ) ) ) ) {
2003-04-25 02:23:24 +04:00
log_error ( " pv allocation for '%s' failed " , pv_name ) ;
2005-04-20 00:52:35 +04:00
return NULL ;
2002-11-18 17:04:08 +03:00
}
2004-03-08 20:19:15 +03:00
list_init ( & pv - > tags ) ;
2005-04-20 00:52:35 +04:00
list_init ( & pv - > segments ) ;
2004-03-08 20:19:15 +03:00
2002-11-18 17:04:08 +03:00
/* FIXME Move more common code up here */
if ( ! ( info - > fmt - > ops - > pv_read ( info - > fmt , pv_name , pv , mdas ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " Failed to read existing physical volume '%s' " ,
pv_name ) ;
2005-04-20 00:52:35 +04:00
return NULL ;
2002-04-24 22:20:51 +04:00
}
if ( ! pv - > size )
return NULL ;
2005-04-20 00:52:35 +04:00
if ( ! alloc_pv_segment_whole_pv ( cmd - > mem , pv ) ) {
stack ;
return NULL ;
}
return pv ;
2002-04-24 22:20:51 +04:00
}
2002-11-18 17:04:08 +03:00
/* May return empty list */
struct list * get_vgs ( struct cmd_context * cmd , int full_scan )
2002-04-24 22:20:51 +04:00
{
2003-07-05 02:34:56 +04:00
return lvmcache_get_vgnames ( cmd , full_scan ) ;
2002-04-24 22:20:51 +04:00
}
2006-04-13 01:23:04 +04:00
struct list * get_vgids ( struct cmd_context * cmd , int full_scan )
{
return lvmcache_get_vgids ( cmd , full_scan ) ;
}
2002-04-24 22:20:51 +04:00
struct list * get_pvs ( struct cmd_context * cmd )
{
2005-06-01 20:51:55 +04:00
struct str_list * strl ;
2002-04-24 22:20:51 +04:00
struct list * results ;
2006-04-13 01:23:04 +04:00
const char * vgname , * vgid ;
2002-11-18 17:04:08 +03:00
struct list * pvh , * tmp ;
2006-04-13 01:23:04 +04:00
struct list * vgids ;
2002-11-18 17:04:08 +03:00
struct volume_group * vg ;
int consistent = 0 ;
2003-04-30 19:23:43 +04:00
int old_partial ;
int old_pvmove ;
2002-11-18 17:04:08 +03:00
2003-07-05 02:34:56 +04:00
lvmcache_label_scan ( cmd , 0 ) ;
2002-04-24 22:20:51 +04:00
2005-10-17 03:03:59 +04:00
if ( ! ( results = dm_pool_alloc ( cmd - > mem , sizeof ( * results ) ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " PV list allocation failed " ) ;
return NULL ;
}
list_init ( results ) ;
2002-11-18 17:04:08 +03:00
/* Get list of VGs */
2006-04-13 01:23:04 +04:00
if ( ! ( vgids = get_vgids ( cmd , 0 ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " get_pvs: get_vgs failed " ) ;
2002-04-24 22:20:51 +04:00
return NULL ;
}
2002-11-18 17:04:08 +03:00
/* Read every VG to ensure cache consistency */
/* Orphan VG is last on list */
2003-04-30 19:23:43 +04:00
old_partial = partial_mode ( ) ;
old_pvmove = pvmove_mode ( ) ;
2002-11-18 17:04:08 +03:00
init_partial ( 1 ) ;
2003-04-30 19:23:43 +04:00
init_pvmove ( 1 ) ;
2006-04-13 01:23:04 +04:00
list_iterate_items ( strl , vgids ) {
vgid = strl - > str ;
if ( ! vgid )
2002-11-18 17:04:08 +03:00
continue ; /* FIXME Unnecessary? */
consistent = 0 ;
2006-04-14 01:08:29 +04:00
if ( ! ( vgname = vgname_from_vgid ( NULL , vgid ) ) ) {
2006-04-13 01:23:04 +04:00
stack ;
continue ;
}
if ( ! ( vg = vg_read ( cmd , vgname , vgid , & consistent ) ) ) {
2002-11-18 17:04:08 +03:00
stack ;
continue ;
}
if ( ! consistent )
log_print ( " Warning: Volume Group %s is not consistent " ,
vgname ) ;
/* Move PVs onto results list */
list_iterate_safe ( pvh , tmp , & vg - > pvs ) {
list_add ( results , pvh ) ;
}
}
2003-04-30 19:23:43 +04:00
init_pvmove ( old_pvmove ) ;
init_partial ( old_partial ) ;
2002-11-18 17:04:08 +03:00
2002-04-24 22:20:51 +04:00
return results ;
}
2006-05-11 21:58:58 +04:00
int pv_write ( struct cmd_context * cmd __attribute ( ( unused ) ) , struct physical_volume * pv ,
2002-11-18 17:04:08 +03:00
struct list * mdas , int64_t label_sector )
2002-04-24 22:20:51 +04:00
{
2003-08-27 01:12:06 +04:00
if ( ! pv - > fmt - > ops - > pv_write ) {
log_error ( " Format does not support writing physical volumes " ) ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( * pv - > vg_name | | pv - > pe_alloc_count ) {
log_error ( " Assertion failed: can't _pv_write non-orphan PV "
" (in VG %s) " , pv - > vg_name ) ;
return 0 ;
2002-04-24 22:20:51 +04:00
}
2002-11-18 17:04:08 +03:00
if ( ! pv - > fmt - > ops - > pv_write ( pv - > fmt , pv , mdas , label_sector ) ) {
stack ;
return 0 ;
2002-04-24 22:20:51 +04:00
}
return 1 ;
}
2007-02-07 16:29:52 +03:00
int pv_write_orphan ( struct cmd_context * cmd , struct physical_volume * pv )
{
const char * old_vg_name = pv - > vg_name ;
pv - > vg_name = ORPHAN ;
pv - > status = ALLOCATABLE_PV ;
if ( ! dev_get_size ( pv - > dev , & pv - > size ) ) {
log_error ( " %s: Couldn't get size. " , dev_name ( pv - > dev ) ) ;
return 0 ;
}
if ( ! pv_write ( cmd , pv , NULL , INT64_C ( - 1 ) ) ) {
log_error ( " Failed to clear metadata from physical "
" volume \" %s \" after removal from \" %s \" " ,
dev_name ( pv - > dev ) , old_vg_name ) ;
return 0 ;
}
return 1 ;
}