2001-11-13 17:17:50 +03:00
/*
* Copyright ( C ) 2001 Sistina Software
*
* LVM is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* LVM is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with LVM ; see the file COPYING . If not , write to
* the Free Software Foundation , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*
*/
# 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 ;
uint32_t extents = 0 ;
uint32_t size = 0 ;
2001-11-27 16:42:37 +03:00
uint32_t stripes = 0 , stripesize = 0 ;
uint32_t seg_stripes = 0 , seg_stripesize = 0 , seg_size = 0 ;
uint32_t size_rest ;
2001-11-13 17:17:50 +03:00
sign_t sign = SIGN_NONE ;
char * lv_name , * vg_name ;
char * st ;
char * dummy ;
const char * cmd_name ;
2002-01-21 17:28:12 +03:00
struct list * pvh , * segh ;
struct lv_list * lvl ;
2001-11-13 17:17:50 +03:00
int opt = 0 ;
2002-01-26 01:58:01 +03:00
int active ;
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 ) ) {
extents = arg_int_value ( cmd , extents_ARG , 0 ) ;
sign = arg_sign_value ( cmd , extents_ARG , SIGN_NONE ) ;
2001-11-13 17:17:50 +03:00
}
2002-02-12 00:00:35 +03:00
if ( arg_count ( cmd , size_ARG ) ) {
size = arg_int_value ( cmd , size_ARG , 0 ) ;
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 ;
}
2002-02-12 00:00:35 +03:00
if ( arg_count ( cmd , stripes_ARG ) ) {
2002-01-26 00:14:43 +03:00
log_print ( " Varied striping not yet supported. Ignoring. " ) ;
2002-02-11 23:50:53 +03:00
/* FUTURE stripes = arg_int_value(cmd,stripes_ARG, 1); */
2001-11-27 20:02:24 +03:00
}
2002-02-12 00:00:35 +03:00
if ( arg_count ( cmd , stripesize_ARG ) ) {
2002-01-26 00:14:43 +03:00
log_print ( " Varied stripesize not yet supported. Ignoring. " ) ;
2002-02-11 23:50:53 +03:00
/* FUTURE stripesize = 2 * arg_int_value(cmd,stripesize_ARG, 0); */
2002-01-26 00:14:43 +03:00
}
2001-11-27 16:42:37 +03:00
2001-11-13 17:17:50 +03:00
if ( ! argc ) {
log_error ( " Please provide the logical volume name " ) ;
return EINVALID_CMD_LINE ;
}
lv_name = argv [ 0 ] ;
argv + + ;
argc - - ;
2002-02-11 23:50:53 +03:00
if ( ! ( vg_name = extract_vgname ( cmd - > fid , 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-11 18:42:34 +03:00
if ( ! lock_vol ( vg_name , LCK_VG | LCK_WRITE ) ) {
log_error ( " Can't get lock for %s " , vg_name ) ;
return ECMD_FAILED ;
}
2002-02-11 23:50:53 +03:00
if ( ! ( vg = cmd - > fid - > ops - > vg_read ( cmd - > fid , vg_name ) ) ) {
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-01-21 19:49:32 +03:00
lv = lvl - > lv ;
2001-11-13 17:17:50 +03:00
if ( size ) {
/* No of 512-byte sectors */
extents = size * 2 ;
if ( extents % vg - > extent_size ) {
char * s1 ;
if ( sign = = SIGN_MINUS )
extents - = extents % vg - > extent_size ;
else
extents + = vg - > extent_size -
( extents % vg - > extent_size ) ;
log_print ( " Rounding up size to full physical extent %s " ,
( s1 = display_size ( extents / 2 , SIZE_SHORT ) ) ) ;
dbg_free ( s1 ) ;
}
extents / = vg - > extent_size ;
}
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
}
2001-11-27 16:42:37 +03:00
/* If extending, find stripes, stripesize & size of last segment */
2002-01-21 19:05:23 +03:00
if ( extents > lv - > le_count & &
2001-12-03 19:27:16 +03:00
! ( stripes = = 1 | | ( stripes > 1 & & stripesize ) ) ) {
2001-11-28 16:45:50 +03:00
list_iterate ( segh , & lv - > segments ) {
struct stripe_segment * seg ;
uint32_t sz , str ;
2002-02-12 00:00:35 +03:00
seg = list_item ( segh , struct stripe_segment ) ;
2001-11-28 16:45:50 +03:00
sz = seg - > stripe_size ;
str = seg - > stripes ;
2001-11-27 16:42:37 +03:00
2001-11-27 20:02:24 +03:00
if ( ( seg_stripesize & & seg_stripesize ! = sz
2001-11-27 16:42:37 +03:00
& & ! stripesize ) | |
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-01-30 15:17:40 +03:00
if ( ! stripesize & & stripes > 1 )
stripesize = seg_stripesize ;
2001-11-27 16:42:37 +03:00
seg_size = extents - lv - > le_count ;
}
/* If reducing, find stripes, stripesize & size of last segment */
if ( extents < lv - > le_count ) {
uint32_t extents_used = 0 ;
if ( stripes | | stripesize )
log_error ( " Ignoring stripes and stripesize arguments "
" when reducing " ) ;
2001-11-28 16:45:50 +03:00
list_iterate ( segh , & lv - > segments ) {
struct stripe_segment * seg ;
uint32_t seg_extents ;
seg = list_item ( segh , struct stripe_segment ) ;
seg_extents = seg - > len ;
seg_stripesize = seg - > stripe_size ;
seg_stripes = seg - > stripes ;
2001-11-27 16:42:37 +03:00
if ( extents < = extents_used + seg_extents )
break ;
extents_used + = seg_extents ;
}
seg_size = extents - extents_used ;
stripesize = seg_stripesize ;
stripes = seg_stripes ;
}
2001-11-28 19:16:44 +03:00
if ( ( size_rest = seg_size % stripes ) ) {
2001-11-27 20:02:24 +03:00
log_print ( " Rounding size (%d extents) down to stripe boundary "
" size of last segment (%d extents) " , extents ,
extents - size_rest ) ;
extents = extents - size_rest ;
}
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-01-18 19:43:19 +03:00
if ( lv_active ( lv ) > 0 ) {
2001-11-13 17:17:50 +03:00
dummy =
2002-02-15 04:26:16 +03:00
display_size ( ( unsigned long long )
extents * ( vg - > extent_size / 2 ) ,
2001-11-13 17:17:50 +03:00
SIZE_SHORT ) ;
log_print ( " WARNING: Reducing active%s logical volume "
" to %s " , lv_open_count ( lv ) ? " and open " : " " ,
dummy ) ;
log_print ( " THIS MAY DESTROY YOUR DATA "
" (filesystem etc.) " ) ;
dbg_free ( dummy ) ;
}
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-02-11 23:50:53 +03:00
if ( ! lv_reduce ( cmd - > fid , lv , lv - > le_count - extents ) )
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
2002-01-21 19:05:23 +03:00
if ( ( resize = = LV_EXTEND & & argc ) & &
2002-02-12 00:00:35 +03:00
! ( pvh = create_pv_list ( cmd - > mem , vg , argc - opt , argv + opt ) ) ) {
2002-01-21 19:05:23 +03:00
stack ;
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
if ( resize = = LV_EXTEND ) {
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
2001-11-13 17:17:50 +03:00
if ( ! argc ) {
/* Use full list from VG */
pvh = & vg - > pvs ;
}
2002-02-15 04:26:16 +03:00
dummy = display_size ( ( unsigned long long )
extents * ( vg - > extent_size / 2 ) ,
SIZE_SHORT ) ;
2001-11-13 17:17:50 +03:00
log_print ( " Extending logical volume %s to %s " , lv_name , dummy ) ;
dbg_free ( dummy ) ;
2002-02-11 23:50:53 +03:00
if ( ! lv_extend ( cmd - > fid , lv , stripes , stripesize ,
2002-01-24 20:15:24 +03:00
extents - lv - > le_count , pvh ) )
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
}
2002-01-26 01:58:01 +03:00
active = lv_active ( lv ) ;
2001-11-13 17:17:50 +03:00
/********* FIXME Suspend lv ***********/
/* store vg on disk(s) */
2002-02-11 23:50:53 +03:00
if ( ! cmd - > fid - > ops - > vg_write ( cmd - > fid , vg ) )
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
backup ( vg ) ;
2002-01-01 00:27:39 +03:00
2002-01-26 01:58:01 +03:00
if ( active & & ! lv_reactivate ( lv ) )
2002-02-11 18:42:34 +03:00
goto error ;
2001-11-13 17:17:50 +03:00
/********* FIXME Resume *********/
2002-02-11 18:42:34 +03:00
lock_vol ( vg_name , LCK_VG | LCK_NONE ) ;
2001-11-13 17:17:50 +03:00
log_print ( " Logical volume %s successfully resized " , lv_name ) ;
return 0 ;
2002-02-11 18:42:34 +03:00
error :
lock_vol ( vg_name , LCK_VG | LCK_NONE ) ;
return ECMD_FAILED ;
error_cmdline :
lock_vol ( vg_name , LCK_VG | LCK_NONE ) ;
return EINVALID_CMD_LINE ;
2001-11-13 17:17:50 +03:00
}