2001-09-25 16:49:28 +04:00
/*
2003-07-05 02:34:56 +04:00
* Copyright ( C ) 2001 - 2003 Sistina Software ( UK ) Limited .
2001-09-25 16:49:28 +04:00
*
2001-10-31 15:47:01 +03:00
* This file is released under the LGPL .
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 "pool.h"
# 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"
2001-09-25 16:49:28 +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 ;
2002-11-18 17:04:08 +03:00
struct pool * mem = fid - > fmt - > cmd - > mem ;
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
2003-04-25 02:23:24 +04:00
if ( ! ( pvl = 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 ) ;
if ( ! ( pv = pv_read ( fid - > fmt - > cmd , pv_name , & mdas , NULL ) ) ) {
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 ;
}
2001-11-12 15:16:57 +03:00
if ( ! ( pv - > vg_name = 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 ;
}
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 */
if ( pv - > pe_start < PE_ALIGN )
pv - > pe_start = PE_ALIGN ;
2001-10-12 18:25:53 +04:00
/*
* The next two fields should be corrected
2002-11-18 17:04:08 +03:00
* by fid - > pv_setup .
2001-10-12 18:25:53 +04:00
*/
2002-11-18 17:04:08 +03:00
pv - > pe_count = ( pv - > size - pv - > pe_start ) / vg - > extent_size ;
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 ;
}
2002-01-21 19:05:23 +03:00
pvl - > pv = pv ;
2001-10-15 22:39:40 +04:00
2001-10-31 15:47:01 +03:00
list_add ( & vg - > pvs , & pvl - > list ) ;
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 ;
}
2002-12-20 02:25:55 +03:00
int vg_rename ( struct cmd_context * cmd , struct volume_group * vg ,
const char * new_name )
{
struct pool * mem = cmd - > mem ;
struct physical_volume * pv ;
struct list * pvh ;
if ( ! ( vg - > name = pool_strdup ( mem , new_name ) ) ) {
log_error ( " vg->name allocation failed for '%s' " , new_name ) ;
return 0 ;
}
list_iterate ( pvh , & vg - > pvs ) {
pv = list_item ( pvh , struct pv_list ) - > pv ;
if ( ! ( pv - > vg_name = pool_strdup ( mem , new_name ) ) ) {
log_error ( " pv->vg_name allocation failed for '%s' " ,
dev_name ( pv - > dev ) ) ;
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 ,
uint32_t max_lv , int pv_count , char * * pv_names )
2001-10-12 18:25:53 +04:00
{
struct volume_group * vg ;
2002-04-24 22:20:51 +04:00
struct 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
2002-04-24 22:20:51 +04:00
if ( ! ( vg = 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 ) ;
2002-11-18 17:04:08 +03:00
if ( vg_read ( cmd , vg_name , & 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
2001-11-12 15:16:57 +03:00
if ( ! ( vg - > name = 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 ) ;
2002-01-30 15:47:29 +03:00
vg - > system_id = pool_alloc ( mem , NAME_LEN ) ;
* 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
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 ;
list_init ( & vg - > snapshots ) ;
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 ,
NULL ) ) ) {
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 :
2001-11-12 15:16:57 +03:00
pool_free ( mem , vg ) ;
2001-10-12 18:25:53 +04:00
return NULL ;
}
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
{
2002-11-18 17:04:08 +03:00
struct pool * mem = fmt - > cmd - > mem ;
2002-04-24 22:20:51 +04:00
struct physical_volume * pv = pool_alloc ( mem , sizeof ( * pv ) ) ;
2001-10-12 14:32:06 +04:00
if ( ! pv ) {
stack ;
return NULL ;
}
2002-01-16 21:10:08 +03:00
if ( ! id )
id_create ( & pv - > id ) ;
else
memcpy ( & pv - > id , id , sizeof ( * id ) ) ;
2002-11-18 17:04:08 +03:00
pv - > dev = dev ;
2001-10-12 14:32:06 +04:00
2002-12-20 02:25:55 +03:00
if ( ! ( pv - > vg_name = 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 ) ;
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 :
2001-11-12 15:16:57 +03:00
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
{
2001-10-31 15:47:01 +03:00
struct list * pvh ;
2002-01-21 17:28:12 +03:00
struct pv_list * pvl ;
2001-10-25 18:04:18 +04:00
2001-10-31 15:47:01 +03:00
list_iterate ( pvh , & vg - > pvs ) {
2002-01-21 17:28:12 +03:00
pvl = list_item ( pvh , struct pv_list ) ;
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-10-15 22:39:40 +04:00
}
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 )
{
struct list * pvh ;
list_iterate ( pvh , & vg - > pvs ) {
if ( pv = = list_item ( pvh , struct pv_list ) - > pv )
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 list * pvh ;
struct pv_list * pvl ;
2001-10-12 14:32:06 +04:00
2002-11-18 17:04:08 +03:00
list_iterate ( pvh , & vg - > pvs ) {
pvl = list_item ( pvh , struct pv_list ) ;
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
{
2001-10-31 15:47:01 +03:00
struct list * lvh ;
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
2002-01-21 17:28:12 +03:00
list_iterate ( lvh , & vg - > lvs ) {
lvl = list_item ( lvh , struct lv_list ) ;
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 list * lvh ;
struct lv_list * lvl ;
list_iterate ( lvh , & vg - > lvs ) {
lvl = list_item ( lvh , struct lv_list ) ;
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
{
2001-10-31 15:47:01 +03:00
struct list * pvh ;
2001-11-10 01:01:04 +03:00
struct physical_volume * pv ;
list_iterate ( pvh , & vg - > pvs ) {
2002-01-21 19:05:23 +03:00
pv = list_item ( pvh , struct pv_list ) - > pv ;
2001-11-10 01:01:04 +03:00
if ( dev = = pv - > dev )
return pv ;
}
return NULL ;
2001-10-29 16:52:23 +03:00
}
2002-02-25 15:56:16 +03:00
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 list * segh ;
struct lv_segment * seg ;
list_iterate ( segh , & lv - > segments ) {
seg = list_item ( segh , struct lv_segment ) ;
if ( le > = seg - > le & & le < seg - > le + seg - > len )
return seg ;
}
return NULL ;
}
2002-04-24 22:20:51 +04:00
int vg_remove ( struct volume_group * vg )
{
struct list * mdah ;
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 */
list_iterate ( mdah , & vg - > fid - > metadata_areas ) {
2002-11-18 17:04:08 +03:00
mda = list_item ( mdah , struct metadata_area ) ;
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 ;
}
2003-07-05 02:34:56 +04:00
/*
* After vg_write ( ) returns success ,
* caller MUST call either vg_commit ( ) or vg_revert ( )
*/
2002-04-24 22:20:51 +04:00
int vg_write ( struct volume_group * vg )
{
2003-07-05 02:34:56 +04:00
struct list * mdah , * mdah2 ;
2002-11-18 17:04:08 +03:00
struct metadata_area * mda ;
2002-04-24 22:20:51 +04:00
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 */
list_iterate ( mdah , & vg - > fid - > metadata_areas ) {
2002-11-18 17:04:08 +03:00
mda = list_item ( mdah , struct metadata_area ) ;
2003-08-27 01:12:06 +04:00
if ( ! mda - > ops - > vg_write ) {
log_error ( " Format does not support writing volume "
" group metadata areas " ) ;
/* Revert */
list_uniterate ( mdah2 , & vg - > fid - > metadata_areas , mdah ) {
mda = list_item ( mdah2 , struct metadata_area ) ;
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 */
list_uniterate ( mdah2 , & vg - > fid - > metadata_areas , mdah ) {
mda = list_item ( mdah2 , struct metadata_area ) ;
if ( mda - > ops - > vg_revert & &
! mda - > ops - > vg_revert ( vg - > fid , vg , mda ) ) {
stack ;
}
}
2002-04-24 22:20:51 +04:00
return 0 ;
}
}
2003-07-05 02:34:56 +04:00
return 1 ;
}
/* Commit pending changes */
int vg_commit ( struct volume_group * vg )
{
struct list * mdah ;
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 */
list_iterate ( mdah , & vg - > fid - > metadata_areas ) {
2002-11-18 17:04:08 +03:00
mda = list_item ( mdah , struct metadata_area ) ;
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 list * mdah ;
struct metadata_area * mda ;
list_iterate ( mdah , & vg - > fid - > metadata_areas ) {
mda = list_item ( mdah , struct metadata_area ) ;
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 ;
2002-11-18 17:04:08 +03:00
struct list * ih ;
struct device * dev ;
struct pv_list * pvl ;
struct volume_group * vg ;
struct physical_volume * pv ;
if ( ! ( vginfo = vginfo_from_vgname ( ORPHAN ) ) ) {
stack ;
return NULL ;
}
if ( ! ( vg = pool_zalloc ( cmd - > mem , sizeof ( * vg ) ) ) ) {
log_error ( " vg allocation failed " ) ;
return NULL ;
}
list_init ( & vg - > pvs ) ;
list_init ( & vg - > lvs ) ;
list_init ( & vg - > snapshots ) ;
2004-03-08 20:19:15 +03:00
list_init ( & vg - > tags ) ;
2002-11-18 17:04:08 +03:00
vg - > cmd = cmd ;
if ( ! ( vg - > name = pool_strdup ( cmd - > mem , ORPHAN ) ) ) {
log_error ( " vg name allocation failed " ) ;
return NULL ;
}
list_iterate ( ih , & vginfo - > infos ) {
2003-07-05 02:34:56 +04:00
dev = list_item ( ih , struct lvmcache_info ) - > dev ;
2002-11-18 17:04:08 +03:00
if ( ! ( pv = pv_read ( cmd , dev_name ( dev ) , NULL , NULL ) ) ) {
continue ;
}
if ( ! ( pvl = pool_zalloc ( cmd - > mem , sizeof ( * pvl ) ) ) ) {
log_error ( " pv_list allocation failed " ) ;
return NULL ;
}
pvl - > pv = pv ;
list_add ( & vg - > pvs , & pvl - > list ) ;
vg - > pv_count + + ;
}
return vg ;
}
/* 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 ) .
*/
struct volume_group * vg_read ( struct cmd_context * cmd , const char * vgname ,
int * consistent )
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 list * mdah ;
struct metadata_area * mda ;
2002-12-20 02:25:55 +03:00
int inconsistent = 0 ;
2002-04-24 22:20:51 +04:00
2002-11-18 17:04:08 +03:00
if ( ! * vgname ) {
* 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 */
if ( ! ( fmt = fmt_from_vgname ( vgname ) ) ) {
2003-07-05 02:34:56 +04:00
lvmcache_label_scan ( cmd , 0 ) ;
2002-11-18 17:04:08 +03:00
if ( ! ( fmt = fmt_from_vgname ( vgname ) ) ) {
2003-07-05 02:34:56 +04:00
if ( memlock ( ) ) {
stack ;
return NULL ;
}
lvmcache_label_scan ( cmd , 1 ) ;
2002-11-18 17:04:08 +03:00
if ( ! ( fmt = fmt_from_vgname ( vgname ) ) ) {
stack ;
return NULL ;
}
2002-04-24 22:20:51 +04:00
}
}
2002-11-18 17:04:08 +03:00
/* create format instance with appropriate metadata area */
if ( ! ( fid = fmt - > ops - > create_instance ( fmt , vgname , 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 */
list_iterate ( mdah , & fid - > metadata_areas ) {
2002-11-18 17:04:08 +03:00
mda = list_item ( mdah , struct metadata_area ) ;
if ( ! ( vg = mda - > ops - > vg_read ( fid , vgname , mda ) ) ) {
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 ;
}
}
2002-05-13 16:38:54 +04:00
/* Failed to find VG */
2002-12-20 02:25:55 +03:00
if ( ! correct_vg ) {
2002-05-13 16:38:54 +04:00
stack ;
return NULL ;
}
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 ) {
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 ;
}
2002-04-24 22:20:51 +04:00
log_print ( " Inconsistent metadata copies found - updating "
" to use version %u " , correct_vg - > seqno ) ;
if ( ! vg_write ( correct_vg ) ) {
log_error ( " Automatic metadata correction failed " ) ;
return NULL ;
}
}
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
}
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 .
*/
2002-04-24 22:20:51 +04:00
struct volume_group * vg_read_by_vgid ( struct cmd_context * cmd , const char * vgid )
{
2003-10-16 00:10:11 +04:00
const char * vgname ;
struct list * vgnames , * slh ;
2002-04-24 22:20:51 +04:00
struct volume_group * vg ;
2003-07-05 02:34:56 +04:00
struct lvmcache_vginfo * vginfo ;
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 ) {
if ( ( vg = vg_read ( cmd , vginfo - > vgname , & consistent ) ) & &
! strncmp ( vg - > id . uuid , vgid , ID_LEN ) ) {
if ( ! consistent ) {
log_error ( " Volume group %s metadata is "
" inconsistent " , vginfo - > vgname ) ;
return NULL ;
}
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
2002-11-18 17:04:08 +03:00
if ( ! ( vgnames = get_vgs ( cmd , 1 ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " vg_read_by_vgid: get_vgs failed " ) ;
return NULL ;
}
2002-11-18 17:04:08 +03:00
list_iterate ( slh , vgnames ) {
vgname = list_item ( slh , struct str_list ) - > str ;
if ( ! vgname | | ! * vgname )
2003-07-05 02:34:56 +04:00
continue ; // FIXME Unnecessary?
2002-11-18 17:04:08 +03:00
consistent = 0 ;
if ( ( vg = vg_read ( cmd , vgname , & consistent ) ) & &
! strncmp ( vg - > id . uuid , vgid , ID_LEN ) ) {
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 */
struct logical_volume * lv_from_lvid ( struct cmd_context * cmd , const char * lvid_s )
{
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 ) ;
if ( ! ( vg = vg_read_by_vgid ( cmd , lvid - > id [ 0 ] . uuid ) ) ) {
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 ,
struct list * mdas , uint64_t * label_sector )
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 ;
2002-04-24 22:20:51 +04:00
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( ! ( label_read ( dev , & label ) ) ) {
2003-04-22 20:09:11 +04:00
log_error ( " No physical volume label read from %s " , pv_name ) ;
2002-04-24 22:20:51 +04:00
return 0 ;
}
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 ;
if ( ! ( pv = pool_zalloc ( cmd - > mem , sizeof ( * pv ) ) ) ) {
2003-04-25 02:23:24 +04:00
log_error ( " pv allocation for '%s' failed " , pv_name ) ;
2002-11-18 17:04:08 +03:00
return 0 ;
}
2004-03-08 20:19:15 +03:00
list_init ( & pv - > tags ) ;
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 ) ;
return 0 ;
}
if ( ! pv - > size )
return NULL ;
else
return pv ;
}
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
}
struct list * get_pvs ( struct cmd_context * cmd )
{
struct list * results ;
2003-07-05 02:34:56 +04:00
const char * vgname ;
2002-11-18 17:04:08 +03:00
struct list * pvh , * tmp ;
struct list * vgnames , * slh ;
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
if ( ! ( results = pool_alloc ( cmd - > mem , sizeof ( * results ) ) ) ) {
log_error ( " PV list allocation failed " ) ;
return NULL ;
}
list_init ( results ) ;
2002-11-18 17:04:08 +03:00
/* Get list of VGs */
if ( ! ( vgnames = get_vgs ( cmd , 0 ) ) ) {
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 ) ;
2002-11-18 17:04:08 +03:00
list_iterate ( slh , vgnames ) {
vgname = list_item ( slh , struct str_list ) - > str ;
if ( ! vgname )
continue ; /* FIXME Unnecessary? */
consistent = 0 ;
if ( ! ( vg = vg_read ( cmd , vgname , & consistent ) ) ) {
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 ;
}
2002-11-18 17:04:08 +03:00
int pv_write ( struct cmd_context * cmd , struct physical_volume * pv ,
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 ;
}