2001-11-13 17:17:50 +03:00
/*
2004-05-11 20:01:58 +04:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
2001-11-13 17:17:50 +03:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
2001-11-13 17:17:50 +03:00
*
2004-03-30 23:35:44 +04:00
* 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 .
2001-11-13 17:17:50 +03:00
*
* You should have received a copy of the GNU General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-11-13 17:17:50 +03:00
*/
# include "tools.h"
2002-02-11 23:50:53 +03:00
int lvresize ( struct cmd_context * cmd , int argc , char * * argv )
2001-11-13 17:17:50 +03:00
{
struct volume_group * vg ;
struct logical_volume * lv ;
2003-07-05 02:34:56 +04:00
struct snapshot * snap ;
2003-01-09 01:44:07 +03:00
struct lvinfo info ;
2001-11-13 17:17:50 +03:00
uint32_t extents = 0 ;
2004-05-11 20:01:58 +04:00
uint64_t size = 0 ;
2002-12-20 02:25:55 +03:00
uint32_t stripes = 0 , ssize = 0 , stripesize_extents = 0 ;
2001-11-27 16:42:37 +03:00
uint32_t seg_stripes = 0 , seg_stripesize = 0 , seg_size = 0 ;
2002-04-24 22:20:51 +04:00
uint32_t extents_used = 0 ;
2001-11-27 16:42:37 +03:00
uint32_t size_rest ;
2001-11-13 17:17:50 +03:00
sign_t sign = SIGN_NONE ;
2002-12-20 02:25:55 +03:00
char * lv_name ;
const char * vg_name ;
2003-07-05 02:34:56 +04:00
char * st , * lock_lvid ;
2001-11-13 17:17:50 +03:00
const char * cmd_name ;
2003-10-16 00:02:46 +04:00
struct list * pvh ;
2002-01-21 17:28:12 +03:00
struct lv_list * lvl ;
2001-11-13 17:17:50 +03:00
int opt = 0 ;
2002-11-18 17:04:08 +03:00
int consistent = 1 ;
2003-10-16 00:02:46 +04:00
struct lv_segment * seg ;
uint32_t seg_extents ;
uint32_t sz , str ;
2004-05-11 20:01:58 +04:00
struct segment_type * segtype = NULL ;
2001-11-13 17:17:50 +03:00
enum {
LV_ANY = 0 ,
LV_REDUCE = 1 ,
LV_EXTEND = 2
} resize = LV_ANY ;
2002-02-11 23:50:53 +03:00
cmd_name = command_name ( cmd ) ;
2001-11-13 17:17:50 +03:00
if ( ! strcmp ( cmd_name , " lvreduce " ) )
resize = LV_REDUCE ;
if ( ! strcmp ( cmd_name , " lvextend " ) )
resize = LV_EXTEND ;
2002-02-12 00:00:35 +03:00
if ( arg_count ( cmd , extents_ARG ) + arg_count ( cmd , size_ARG ) ! = 1 ) {
2001-11-13 17:17:50 +03:00
log_error ( " Please specify either size or extents (not both) " ) ;
return EINVALID_CMD_LINE ;
}
2002-02-12 00:00:35 +03:00
if ( arg_count ( cmd , extents_ARG ) ) {
2002-12-20 02:25:55 +03:00
extents = arg_uint_value ( cmd , extents_ARG , 0 ) ;
2002-02-12 00:00:35 +03:00
sign = arg_sign_value ( cmd , extents_ARG , SIGN_NONE ) ;
2001-11-13 17:17:50 +03:00
}
2004-05-11 20:01:58 +04:00
/* Size returned in kilobyte units; held in sectors */
2002-02-12 00:00:35 +03:00
if ( arg_count ( cmd , size_ARG ) ) {
2004-05-11 20:01:58 +04:00
size = arg_uint64_value ( cmd , size_ARG , UINT64_C ( 0 ) ) * 2 ;
2002-02-12 00:00:35 +03:00
sign = arg_sign_value ( cmd , size_ARG , SIGN_NONE ) ;
2001-11-13 17:17:50 +03:00
}
if ( resize = = LV_EXTEND & & sign = = SIGN_MINUS ) {
log_error ( " Negative argument not permitted - use lvreduce " ) ;
return EINVALID_CMD_LINE ;
}
if ( resize = = LV_REDUCE & & sign = = SIGN_PLUS ) {
log_error ( " Positive sign not permitted - use lvextend " ) ;
return EINVALID_CMD_LINE ;
}
if ( ! argc ) {
log_error ( " Please provide the logical volume name " ) ;
return EINVALID_CMD_LINE ;
}
lv_name = argv [ 0 ] ;
argv + + ;
argc - - ;
2002-04-24 22:20:51 +04:00
if ( ! ( vg_name = extract_vgname ( cmd , lv_name ) ) ) {
2001-11-13 17:17:50 +03:00
log_error ( " Please provide a volume group name " ) ;
return EINVALID_CMD_LINE ;
}
if ( ( st = strrchr ( lv_name , ' / ' ) ) )
lv_name = st + 1 ;
/* does VG exist? */
log_verbose ( " Finding volume group %s " , vg_name ) ;
2002-02-27 15:26:41 +03:00
if ( ! lock_vol ( cmd , vg_name , LCK_VG_WRITE ) ) {
2002-02-11 18:42:34 +03:00
log_error ( " Can't get lock for %s " , vg_name ) ;
return ECMD_FAILED ;
}
2002-11-18 17:04:08 +03:00
if ( ! ( vg = vg_read ( cmd , vg_name , & consistent ) ) ) {
2001-11-13 17:17:50 +03:00
log_error ( " Volume group %s doesn't exist " , vg_name ) ;
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
2002-02-12 00:00:35 +03:00
if ( vg - > status & EXPORTED_VG ) {
log_error ( " Volume group %s is exported " , vg - > name ) ;
goto error ;
}
2002-01-29 20:23:33 +03:00
2002-02-12 00:00:35 +03:00
if ( ! ( vg - > status & LVM_WRITE ) ) {
log_error ( " Volume group %s is read-only " , vg_name ) ;
goto error ;
}
2002-01-29 20:23:33 +03:00
2001-11-13 17:17:50 +03:00
/* does LV exist? */
2002-01-21 17:28:12 +03:00
if ( ! ( lvl = find_lv_in_vg ( vg , lv_name ) ) ) {
2001-11-13 17:17:50 +03:00
log_error ( " Logical volume %s not found in volume group %s " ,
lv_name , vg_name ) ;
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
2002-04-24 22:20:51 +04:00
if ( arg_count ( cmd , stripes_ARG ) ) {
if ( vg - > fid - > fmt - > features & FMT_SEGMENTS )
2002-12-20 02:25:55 +03:00
stripes = arg_uint_value ( cmd , stripes_ARG , 1 ) ;
2002-04-24 22:20:51 +04:00
else
log_print ( " Varied striping not supported. Ignoring. " ) ;
}
if ( arg_count ( cmd , stripesize_ARG ) ) {
2003-09-15 19:04:39 +04:00
if ( arg_sign_value ( cmd , stripesize_ARG , 0 ) = = SIGN_MINUS ) {
log_error ( " Stripesize may not be negative. " ) ;
goto error ;
}
2002-04-24 22:20:51 +04:00
if ( vg - > fid - > fmt - > features & FMT_SEGMENTS )
2002-12-20 02:25:55 +03:00
ssize = 2 * arg_uint_value ( cmd , stripesize_ARG , 0 ) ;
2002-04-24 22:20:51 +04:00
else
log_print ( " Varied stripesize not supported. Ignoring. " ) ;
}
2002-01-21 19:49:32 +03:00
lv = lvl - > lv ;
2001-11-13 17:17:50 +03:00
2003-05-06 16:10:18 +04:00
if ( lv - > status & LOCKED ) {
log_error ( " Can't resize locked LV %s " , lv - > name ) ;
goto error ;
}
2001-11-13 17:17:50 +03:00
if ( size ) {
2004-05-11 20:01:58 +04:00
if ( size % vg - > extent_size ) {
2001-11-13 17:17:50 +03:00
if ( sign = = SIGN_MINUS )
2004-05-11 20:01:58 +04:00
size - = size % vg - > extent_size ;
2001-11-13 17:17:50 +03:00
else
2004-05-11 20:01:58 +04:00
size + = vg - > extent_size -
( size % vg - > extent_size ) ;
2001-11-13 17:17:50 +03:00
log_print ( " Rounding up size to full physical extent %s " ,
2004-05-11 20:01:58 +04:00
display_size ( cmd , ( uint64_t ) size / 2 ,
2002-12-20 02:25:55 +03:00
SIZE_SHORT ) ) ;
2001-11-13 17:17:50 +03:00
}
2004-05-11 20:01:58 +04:00
extents = size / vg - > extent_size ;
2001-11-13 17:17:50 +03:00
}
if ( sign = = SIGN_PLUS )
extents + = lv - > le_count ;
if ( sign = = SIGN_MINUS ) {
if ( extents > = lv - > le_count ) {
log_error ( " Unable to reduce %s below 1 extent " ,
lv_name ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-13 17:17:50 +03:00
}
extents = lv - > le_count - extents ;
}
if ( ! extents ) {
log_error ( " New size of 0 not permitted " ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-13 17:17:50 +03:00
}
if ( extents = = lv - > le_count ) {
log_error ( " New size (%d extents) matches existing size "
" (%d extents) " , extents , lv - > le_count ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-13 17:17:50 +03:00
}
2002-04-24 22:20:51 +04:00
seg_size = extents - lv - > le_count ;
2004-05-11 20:01:58 +04:00
/* Use segment type of last segment */
list_iterate_items ( seg , & lv - > segments ) {
segtype = seg - > segtype ;
}
/* FIXME Support LVs with mixed segment types */
if ( segtype ! = ( struct segment_type * ) arg_ptr_value ( cmd , type_ARG ,
segtype ) ) {
log_error ( " VolumeType does not match (%s) " , segtype - > name ) ;
goto error_cmdline ;
}
2001-11-27 16:42:37 +03:00
/* If extending, find stripes, stripesize & size of last segment */
2002-12-20 02:25:55 +03:00
if ( extents > lv - > le_count & & ! ( stripes = = 1 | | ( stripes > 1 & & ssize ) ) ) {
2003-10-16 00:02:46 +04:00
list_iterate_items ( seg , & lv - > segments ) {
2004-05-11 20:01:58 +04:00
if ( ! ( seg - > segtype - > flags & SEG_AREAS_STRIPED ) )
continue ;
2003-04-25 02:23:24 +04:00
2001-11-28 16:45:50 +03:00
sz = seg - > stripe_size ;
2003-04-25 02:23:24 +04:00
str = seg - > area_count ;
2001-11-27 16:42:37 +03:00
2001-11-27 20:02:24 +03:00
if ( ( seg_stripesize & & seg_stripesize ! = sz
2002-12-20 02:25:55 +03:00
& & ! ssize ) | |
2001-11-27 20:02:24 +03:00
( seg_stripes & & seg_stripes ! = str & & ! stripes ) ) {
2001-11-27 16:42:37 +03:00
log_error ( " Please specify number of "
" stripes (-i) and stripesize (-I) " ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-27 16:42:37 +03:00
}
seg_stripesize = sz ;
seg_stripes = str ;
}
if ( ! stripes )
stripes = seg_stripes ;
2002-12-20 02:25:55 +03:00
if ( ! ssize & & stripes > 1 ) {
2002-04-24 22:20:51 +04:00
if ( seg_stripesize ) {
log_print ( " Using stripesize of last segment "
" %dKB " , seg_stripesize / 2 ) ;
2002-12-20 02:25:55 +03:00
ssize = seg_stripesize ;
2002-04-24 22:20:51 +04:00
} else {
2004-03-08 21:28:45 +03:00
ssize = find_config_int ( cmd - > cft - > root ,
2002-12-20 02:25:55 +03:00
" metadata/stripesize " ,
DEFAULT_STRIPESIZE ) * 2 ;
2002-04-24 22:20:51 +04:00
log_print ( " Using default stripesize %dKB " ,
2002-12-20 02:25:55 +03:00
ssize / 2 ) ;
2002-04-24 22:20:51 +04:00
}
}
2001-11-27 16:42:37 +03:00
}
/* If reducing, find stripes, stripesize & size of last segment */
if ( extents < lv - > le_count ) {
2002-04-24 22:20:51 +04:00
extents_used = 0 ;
2001-11-27 16:42:37 +03:00
2002-12-20 02:25:55 +03:00
if ( stripes | | ssize )
2001-11-27 16:42:37 +03:00
log_error ( " Ignoring stripes and stripesize arguments "
" when reducing " ) ;
2003-10-16 00:02:46 +04:00
list_iterate_items ( seg , & lv - > segments ) {
2001-11-28 16:45:50 +03:00
seg_extents = seg - > len ;
2004-05-05 01:25:57 +04:00
if ( seg - > segtype - > flags & SEG_AREAS_STRIPED ) {
2003-04-25 02:23:24 +04:00
seg_stripesize = seg - > stripe_size ;
seg_stripes = seg - > area_count ;
}
2001-11-27 16:42:37 +03:00
if ( extents < = extents_used + seg_extents )
break ;
extents_used + = seg_extents ;
}
seg_size = extents - extents_used ;
2002-12-20 02:25:55 +03:00
ssize = seg_stripesize ;
2001-11-27 16:42:37 +03:00
stripes = seg_stripes ;
}
2002-12-20 02:25:55 +03:00
if ( stripes > 1 & & ! ssize ) {
2002-04-24 22:20:51 +04:00
log_error ( " Stripesize for striped segment should not be 0! " ) ;
2002-05-31 23:29:43 +04:00
goto error_cmdline ;
}
2002-11-18 17:04:08 +03:00
2002-05-31 23:29:43 +04:00
if ( ( stripes > 1 ) ) {
2002-12-20 02:25:55 +03:00
if ( ! ( stripesize_extents = ssize / vg - > extent_size ) )
2002-05-31 23:29:43 +04:00
stripesize_extents = 1 ;
if ( ( size_rest = seg_size % ( stripes * stripesize_extents ) ) ) {
log_print ( " Rounding size (%d extents) down to stripe "
" boundary size for segment (%d extents) " ,
extents , extents - size_rest ) ;
extents = extents - size_rest ;
}
2001-11-27 20:02:24 +03:00
}
2001-11-27 16:42:37 +03:00
if ( extents = = lv - > le_count ) {
log_error ( " New size (%d extents) matches existing size "
" (%d extents) " , extents , lv - > le_count ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-27 16:42:37 +03:00
}
2001-11-13 17:17:50 +03:00
if ( extents < lv - > le_count ) {
if ( resize = = LV_EXTEND ) {
log_error ( " New size given (%d extents) not larger "
" than existing size (%d extents) " ,
extents , lv - > le_count ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-13 17:17:50 +03:00
} else
resize = LV_REDUCE ;
}
if ( extents > lv - > le_count ) {
if ( resize = = LV_REDUCE ) {
log_error ( " New size given (%d extents) not less than "
" existing size (%d extents) " , extents ,
lv - > le_count ) ;
2002-02-11 18:42:34 +03:00
goto error_cmdline ;
2001-11-13 17:17:50 +03:00
} else
resize = LV_EXTEND ;
}
if ( resize = = LV_REDUCE ) {
if ( argc )
log_print ( " Ignoring PVs on command line when reducing " ) ;
2002-04-24 22:20:51 +04:00
memset ( & info , 0 , sizeof ( info ) ) ;
if ( ! lv_info ( lv , & info ) & & driver_version ( NULL , 0 ) ) {
log_error ( " lv_info failed: aborting " ) ;
2002-03-11 22:02:28 +03:00
goto error ;
}
if ( info . exists ) {
2001-11-13 17:17:50 +03:00
log_print ( " WARNING: Reducing active%s logical volume "
2002-03-11 22:02:28 +03:00
" to %s " , info . open_count ? " and open " : " " ,
2002-12-12 23:55:49 +03:00
display_size ( cmd , ( uint64_t )
extents * ( vg - > extent_size / 2 ) ,
SIZE_SHORT ) ) ;
2001-11-13 17:17:50 +03:00
log_print ( " THIS MAY DESTROY YOUR DATA "
" (filesystem etc.) " ) ;
}
2002-02-12 00:00:35 +03:00
if ( ! arg_count ( cmd , force_ARG ) ) {
2001-11-13 17:17:50 +03:00
if ( yes_no_prompt ( " Do you really want to reduce %s? "
" [y/n]: " , lv_name ) = = ' n ' ) {
log_print ( " Logical volume %s NOT reduced " ,
lv_name ) ;
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
}
2002-01-09 16:17:14 +03:00
if ( ! archive ( vg ) )
2002-02-11 18:42:34 +03:00
goto error ;
2002-01-09 16:17:14 +03:00
2002-04-24 22:20:51 +04:00
if ( ! lv_reduce ( vg - > fid , lv , lv - > le_count - extents ) )
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
if ( resize = = LV_EXTEND ) {
2002-12-20 02:25:55 +03:00
if ( ! ( pvh = argc ? create_pv_list ( cmd - > mem , vg , argc - opt ,
argv + opt ) : & vg - > pvs ) ) {
stack ;
goto error ;
}
2002-01-09 16:17:14 +03:00
if ( ! archive ( vg ) )
2002-02-11 18:42:34 +03:00
goto error ;
2002-01-09 16:17:14 +03:00
2002-12-12 23:55:49 +03:00
log_print ( " Extending logical volume %s to %s " , lv_name ,
display_size ( cmd , ( uint64_t )
extents * ( vg - > extent_size / 2 ) ,
SIZE_SHORT ) ) ;
2001-11-13 17:17:50 +03:00
2004-05-05 01:25:57 +04:00
if ( ! lv_extend ( vg - > fid , lv , segtype , stripes , ssize , 0u ,
2004-05-05 22:49:21 +04:00
extents - lv - > le_count , NULL , 0u , 0u , pvh ,
lv - > alloc ) )
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
/* store vg on disk(s) */
2002-04-24 22:20:51 +04:00
if ( ! vg_write ( vg ) ) {
2003-07-05 02:34:56 +04:00
stack ;
2002-02-11 18:42:34 +03:00
goto error ;
2002-02-21 00:30:27 +03:00
}
2001-11-13 17:17:50 +03:00
2002-02-12 00:00:35 +03:00
backup ( vg ) ;
2002-01-01 00:27:39 +03:00
2003-07-05 02:34:56 +04:00
/* If snapshot, must suspend all associated devices */
if ( ( snap = find_cow ( lv ) ) )
lock_lvid = snap - > origin - > lvid . s ;
else
lock_lvid = lv - > lvid . s ;
2004-05-05 16:03:07 +04:00
if ( ! suspend_lv ( cmd , lock_lvid ) ) {
log_error ( " Failed to suspend %s " , lv_name ) ;
2003-07-05 02:34:56 +04:00
vg_revert ( vg ) ;
goto error ;
}
if ( ! vg_commit ( vg ) ) {
stack ;
2004-05-05 16:03:07 +04:00
resume_lv ( cmd , lock_lvid ) ;
2003-07-05 02:34:56 +04:00
goto error ;
}
2004-05-05 16:03:07 +04:00
if ( ! resume_lv ( cmd , lock_lvid ) ) {
2002-02-21 00:30:27 +03:00
log_error ( " Problem reactivating %s " , lv_name ) ;
2002-02-11 18:42:34 +03:00
goto error ;
2002-02-21 00:30:27 +03:00
}
2001-11-13 17:17:50 +03:00
2002-03-11 18:08:39 +03:00
unlock_vg ( cmd , vg_name ) ;
2002-02-11 18:42:34 +03:00
2001-11-13 17:17:50 +03:00
log_print ( " Logical volume %s successfully resized " , lv_name ) ;
2003-10-22 02:06:07 +04:00
return ECMD_PROCESSED ;
2002-02-11 18:42:34 +03:00
error :
2002-03-11 18:08:39 +03:00
unlock_vg ( cmd , vg_name ) ;
2002-02-11 18:42:34 +03:00
return ECMD_FAILED ;
error_cmdline :
2002-03-11 18:08:39 +03:00
unlock_vg ( cmd , vg_name ) ;
2002-02-11 18:42:34 +03:00
return EINVALID_CMD_LINE ;
2001-11-13 17:17:50 +03:00
}