2001-09-25 16:49:28 +04:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2009-02-09 12:45:49 +03:00
* Copyright ( C ) 2004 - 2009 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
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser 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-09-25 16:49:28 +04:00
*/
2001-10-06 01:39:30 +04:00
# include "tools.h"
2002-01-01 00:27:39 +03:00
# include <sys/stat.h>
2006-04-19 19:33:07 +04:00
const char * command_name ( struct cmd_context * cmd )
{
return cmd - > command - > name ;
}
2006-08-26 03:02:33 +04:00
/*
* Strip dev_dir if present
*/
2011-02-18 17:47:28 +03:00
const char * skip_dev_dir ( struct cmd_context * cmd , const char * vg_name ,
2007-03-09 23:47:41 +03:00
unsigned * dev_dir_found )
2006-08-26 03:02:33 +04:00
{
2007-03-09 23:47:41 +03:00
const char * dmdir = dm_dir ( ) ;
size_t dmdir_len = strlen ( dmdir ) , vglv_sz ;
char * vgname , * lvname , * layer , * vglv ;
2006-08-26 03:02:33 +04:00
2007-03-09 23:47:41 +03:00
/* FIXME Do this properly */
2006-08-26 03:02:33 +04:00
if ( * vg_name = = ' / ' ) {
while ( * vg_name = = ' / ' )
vg_name + + ;
vg_name - - ;
}
2007-03-09 23:47:41 +03:00
/* Reformat string if /dev/mapper found */
if ( ! strncmp ( vg_name , dmdir , dmdir_len ) & & vg_name [ dmdir_len ] = = ' / ' ) {
if ( dev_dir_found )
* dev_dir_found = 1 ;
vg_name + = dmdir_len ;
while ( * vg_name = = ' / ' )
vg_name + + ;
if ( ! dm_split_lvm_name ( cmd - > mem , vg_name , & vgname , & lvname , & layer ) | |
* layer ) {
log_error ( " skip_dev_dir: Couldn't split up device name %s " ,
vg_name ) ;
2011-02-18 17:47:28 +03:00
return vg_name ;
2007-03-09 23:47:41 +03:00
}
vglv_sz = strlen ( vgname ) + strlen ( lvname ) + 2 ;
if ( ! ( vglv = dm_pool_alloc ( cmd - > mem , vglv_sz ) ) | |
dm_snprintf ( vglv , vglv_sz , " %s%s%s " , vgname ,
* lvname ? " / " : " " ,
lvname ) < 0 ) {
log_error ( " vg/lv string alloc failed " ) ;
2011-02-18 17:47:28 +03:00
return vg_name ;
2007-03-09 23:47:41 +03:00
}
return vglv ;
}
2006-08-26 03:02:33 +04:00
if ( ! strncmp ( vg_name , cmd - > dev_dir , strlen ( cmd - > dev_dir ) ) ) {
2007-03-09 23:47:41 +03:00
if ( dev_dir_found )
* dev_dir_found = 1 ;
2006-08-26 03:02:33 +04:00
vg_name + = strlen ( cmd - > dev_dir ) ;
while ( * vg_name = = ' / ' )
vg_name + + ;
2007-03-09 23:47:41 +03:00
} else if ( dev_dir_found )
* dev_dir_found = 0 ;
2006-08-26 03:02:33 +04:00
2011-02-18 17:47:28 +03:00
return vg_name ;
2006-08-26 03:02:33 +04:00
}
2005-01-19 20:31:51 +03:00
/*
* Metadata iteration functions
*/
2007-08-07 13:06:05 +04:00
int process_each_lv_in_vg ( struct cmd_context * cmd ,
2010-03-23 17:24:04 +03:00
struct volume_group * vg ,
2008-11-04 01:14:30 +03:00
const struct dm_list * arg_lvnames ,
const struct dm_list * tags ,
2010-05-21 16:21:51 +04:00
struct dm_list * failed_lvnames ,
2002-11-18 17:04:08 +03:00
void * handle ,
2010-04-14 03:57:41 +04:00
process_single_lv_fn_t process_single_lv )
2001-11-19 18:20:50 +03:00
{
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2001-11-19 18:20:50 +03:00
int ret = 0 ;
2006-05-10 01:23:51 +04:00
unsigned process_all = 0 ;
unsigned process_lv = 0 ;
unsigned tags_supplied = 0 ;
unsigned lvargs_supplied = 0 ;
unsigned lvargs_matched = 0 ;
2010-05-21 16:21:51 +04:00
char * lv_name ;
2003-10-16 00:02:46 +04:00
struct lv_list * lvl ;
2001-11-19 18:20:50 +03:00
2007-06-19 08:36:12 +04:00
if ( ! vg_check_status ( vg , EXPORTED_VG ) )
2001-11-19 18:20:50 +03:00
return ECMD_FAILED ;
2002-01-29 20:23:33 +03:00
2008-11-04 01:14:30 +03:00
if ( tags & & ! dm_list_empty ( tags ) )
2004-03-08 20:19:15 +03:00
tags_supplied = 1 ;
2008-11-04 01:14:30 +03:00
if ( arg_lvnames & & ! dm_list_empty ( arg_lvnames ) )
2004-03-08 20:19:15 +03:00
lvargs_supplied = 1 ;
/* Process all LVs in this VG if no restrictions given */
if ( ! tags_supplied & & ! lvargs_supplied )
process_all = 1 ;
/* Or if VG tags match */
if ( ! process_lv & & tags_supplied & &
2010-11-09 15:34:40 +03:00
str_list_match_list ( tags , & vg - > tags , NULL ) ) {
2004-03-08 20:19:15 +03:00
process_all = 1 ;
2004-05-11 22:45:11 +04:00
}
2004-03-08 20:19:15 +03:00
2012-03-04 19:58:31 +04:00
/*
* FIXME : In case of remove it goes through deleted entries ,
* but it works since entries are allocated from vg mem pool .
*/
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( lvl , & vg - > lvs ) {
2005-04-07 16:39:44 +04:00
if ( lvl - > lv - > status & SNAPSHOT )
continue ;
2011-11-18 23:19:22 +04:00
/* Skip availability change for non-virt snaps when processing all LVs */
/* FIXME: pass process_all to process_single_lv() */
2012-06-27 15:48:31 +04:00
if ( process_all & & arg_count ( cmd , activate_ARG ) & &
2011-11-18 23:19:22 +04:00
lv_is_cow ( lvl - > lv ) & & ! lv_is_virtual_origin ( origin_from_cow ( lvl - > lv ) ) )
continue ;
2009-05-27 22:19:21 +04:00
if ( lv_is_virtual_origin ( lvl - > lv ) & & ! arg_count ( cmd , all_ARG ) )
2009-05-27 17:23:41 +04:00
continue ;
2010-04-14 06:19:49 +04:00
/*
* Only let hidden LVs through it - - all was used or the LVs
* were specifically named on the command line .
*/
if ( ! lvargs_supplied & & ! lv_is_visible ( lvl - > lv ) & & ! arg_count ( cmd , all_ARG ) )
continue ;
2004-03-08 20:19:15 +03:00
/* Should we process this LV? */
if ( process_all )
process_lv = 1 ;
else
process_lv = 0 ;
/* LV tag match? */
if ( ! process_lv & & tags_supplied & &
2010-11-09 15:34:40 +03:00
str_list_match_list ( tags , & lvl - > lv - > tags , NULL ) ) {
2004-03-08 20:19:15 +03:00
process_lv = 1 ;
2004-05-11 22:45:11 +04:00
}
2004-03-08 20:19:15 +03:00
/* LV name match? */
2004-05-05 22:31:38 +04:00
if ( lvargs_supplied & &
str_list_match_item ( arg_lvnames , lvl - > lv - > name ) ) {
2004-03-08 20:19:15 +03:00
process_lv = 1 ;
2004-05-05 22:31:38 +04:00
lvargs_matched + + ;
}
2004-03-08 20:19:15 +03:00
if ( ! process_lv )
continue ;
2010-05-21 18:15:39 +04:00
lvl - > lv - > vg - > cmd_missing_vgs = 0 ;
2010-04-14 03:57:41 +04:00
ret = process_single_lv ( cmd , lvl - > lv , handle ) ;
2010-05-21 16:21:51 +04:00
if ( ret ! = ECMD_PROCESSED & & failed_lvnames ) {
lv_name = dm_pool_strdup ( cmd - > mem , lvl - > lv - > name ) ;
if ( ! lv_name | |
! str_list_add ( cmd - > mem , failed_lvnames , lv_name ) ) {
log_error ( " Allocation failed for str_list. " ) ;
return ECMD_FAILED ;
}
2010-05-21 18:15:39 +04:00
if ( lvl - > lv - > vg - > cmd_missing_vgs )
ret = ECMD_PROCESSED ;
2010-05-21 16:21:51 +04:00
}
2001-11-19 18:20:50 +03:00
if ( ret > ret_max )
ret_max = ret ;
2011-02-18 18:02:25 +03:00
if ( sigint_caught ( ) ) {
stack ;
2007-06-15 14:11:14 +04:00
return ret_max ;
2011-02-18 18:02:25 +03:00
}
2001-11-19 18:20:50 +03:00
}
2008-11-04 01:14:30 +03:00
if ( lvargs_supplied & & lvargs_matched ! = dm_list_size ( arg_lvnames ) ) {
2012-03-04 19:58:31 +04:00
/*
* FIXME : lvm supports removal of LV with all its dependencies
* this leads to miscalculation that depends on the order of args .
*/
2004-05-05 22:31:38 +04:00
log_error ( " One or more specified logical volume(s) not found. " ) ;
if ( ret_max < ECMD_FAILED )
ret_max = ECMD_FAILED ;
}
2001-11-19 18:20:50 +03:00
return ret_max ;
}
2002-02-11 23:50:53 +03:00
int process_each_lv ( struct cmd_context * cmd , int argc , char * * argv ,
2009-07-01 21:00:50 +04:00
uint32_t flags , void * handle ,
2010-04-14 03:57:41 +04:00
process_single_lv_fn_t process_single_lv )
2001-11-14 21:38:07 +03:00
{
int opt = 0 ;
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2001-11-14 21:38:07 +03:00
int ret = 0 ;
2008-11-04 01:14:30 +03:00
struct dm_list * tags_arg ;
struct dm_list * vgnames ; /* VGs to process */
2005-06-01 20:51:55 +04:00
struct str_list * sll , * strl ;
2010-05-21 18:15:39 +04:00
struct cmd_vg * cvl_vg ;
struct dm_list cmd_vgs ;
struct dm_list failed_lvnames ;
2008-11-04 01:14:30 +03:00
struct dm_list tags , lvnames ;
struct dm_list arg_lvnames ; /* Cmdline vgname or vgname/lvname */
2010-11-23 23:39:13 +03:00
struct dm_list arg_vgnames ;
2004-03-08 20:19:15 +03:00
char * vglv ;
size_t vglv_sz ;
2001-11-14 21:38:07 +03:00
2002-12-20 02:25:55 +03:00
const char * vgname ;
2001-11-14 21:38:07 +03:00
2008-11-04 01:14:30 +03:00
dm_list_init ( & tags ) ;
dm_list_init ( & arg_lvnames ) ;
2010-05-21 18:15:39 +04:00
dm_list_init ( & failed_lvnames ) ;
2004-03-08 20:19:15 +03:00
2001-11-14 21:38:07 +03:00
if ( argc ) {
log_verbose ( " Using logical volume(s) on command line " ) ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & arg_vgnames ) ;
2004-03-08 20:19:15 +03:00
2001-11-14 21:38:07 +03:00
for ( ; opt < argc ; opt + + ) {
2004-03-08 20:19:15 +03:00
const char * lv_name = argv [ opt ] ;
2010-12-01 13:36:25 +03:00
const char * tmp_lv_name ;
2003-07-05 02:34:56 +04:00
char * vgname_def ;
2007-03-09 23:47:41 +03:00
unsigned dev_dir_found = 0 ;
2002-11-18 17:04:08 +03:00
2004-03-08 20:19:15 +03:00
/* Do we have a tag or vgname or lvname? */
2002-11-18 17:04:08 +03:00
vgname = lv_name ;
2004-03-08 20:19:15 +03:00
if ( * vgname = = ' @ ' ) {
2010-11-17 13:19:29 +03:00
if ( ! validate_tag ( vgname + 1 ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " Skipping invalid tag %s " ,
vgname ) ;
continue ;
}
if ( ! str_list_add ( cmd - > mem , & tags ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem ,
2004-03-08 20:19:15 +03:00
vgname + 1 ) ) ) {
log_error ( " strlist allocation failed " ) ;
return ECMD_FAILED ;
}
continue ;
}
/* FIXME Jumbled parsing */
2007-03-09 23:47:41 +03:00
vgname = skip_dev_dir ( cmd , vgname , & dev_dir_found ) ;
2003-07-05 02:34:56 +04:00
if ( * vgname = = ' / ' ) {
log_error ( " \" %s \" : Invalid path for Logical "
2004-03-08 20:19:15 +03:00
" Volume " , argv [ opt ] ) ;
2003-07-05 02:34:56 +04:00
if ( ret_max < ECMD_FAILED )
ret_max = ECMD_FAILED ;
continue ;
}
2004-03-08 20:19:15 +03:00
lv_name = vgname ;
2010-12-01 13:36:25 +03:00
if ( ( tmp_lv_name = strchr ( vgname , ' / ' ) ) ) {
2002-11-18 17:04:08 +03:00
/* Must be an LV */
2010-12-01 13:36:25 +03:00
lv_name = tmp_lv_name ;
2004-03-08 20:19:15 +03:00
while ( * lv_name = = ' / ' )
lv_name + + ;
if ( ! ( vgname = extract_vgname ( cmd , vgname ) ) ) {
2011-02-18 18:02:25 +03:00
if ( ret_max < ECMD_FAILED ) {
stack ;
2002-11-18 17:04:08 +03:00
ret_max = ECMD_FAILED ;
2011-02-18 18:02:25 +03:00
}
2002-11-18 17:04:08 +03:00
continue ;
}
2004-03-08 20:19:15 +03:00
} else if ( ! dev_dir_found & &
2004-05-11 22:45:11 +04:00
( vgname_def = default_vgname ( cmd ) ) ) {
2003-07-05 02:34:56 +04:00
vgname = vgname_def ;
2004-03-08 20:19:15 +03:00
} else
lv_name = NULL ;
2003-07-05 02:34:56 +04:00
2004-03-08 20:19:15 +03:00
if ( ! str_list_add ( cmd - > mem , & arg_vgnames ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem , vgname ) ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " strlist allocation failed " ) ;
2002-01-29 20:23:33 +03:00
return ECMD_FAILED ;
}
2004-03-08 20:19:15 +03:00
if ( ! lv_name ) {
if ( ! str_list_add ( cmd - > mem , & arg_lvnames ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem ,
2004-05-11 22:45:11 +04:00
vgname ) ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " strlist allocation failed " ) ;
return ECMD_FAILED ;
}
} else {
vglv_sz = strlen ( vgname ) + strlen ( lv_name ) + 2 ;
2005-10-17 03:03:59 +04:00
if ( ! ( vglv = dm_pool_alloc ( cmd - > mem , vglv_sz ) ) | |
2006-08-21 16:54:53 +04:00
dm_snprintf ( vglv , vglv_sz , " %s/%s " , vgname ,
2004-03-08 20:19:15 +03:00
lv_name ) < 0 ) {
log_error ( " vg/lv string alloc failed " ) ;
return ECMD_FAILED ;
}
2004-06-15 21:23:49 +04:00
if ( ! str_list_add ( cmd - > mem , & arg_lvnames , vglv ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " strlist allocation failed " ) ;
return ECMD_FAILED ;
}
2001-11-14 21:38:07 +03:00
}
}
2004-03-08 20:19:15 +03:00
vgnames = & arg_vgnames ;
2004-05-11 22:45:11 +04:00
}
2004-03-08 20:19:15 +03:00
2008-11-04 01:14:30 +03:00
if ( ! argc | | ! dm_list_empty ( & tags ) ) {
2002-01-16 17:43:27 +03:00
log_verbose ( " Finding all logical volumes " ) ;
2012-02-28 22:08:08 +04:00
if ( ! lvmetad_vg_list_to_lvmcache ( cmd ) )
stack ;
2010-03-16 19:57:03 +03:00
if ( ! ( vgnames = get_vgnames ( cmd , 0 ) ) | | dm_list_empty ( vgnames ) ) {
2001-11-14 21:38:07 +03:00
log_error ( " No volume groups found " ) ;
2004-05-20 20:18:58 +04:00
return ret_max ;
2001-11-14 21:38:07 +03:00
}
2004-03-08 20:19:15 +03:00
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( strl , vgnames ) {
2005-06-01 20:51:55 +04:00
vgname = strl - > str ;
2010-05-21 18:15:39 +04:00
dm_list_init ( & cmd_vgs ) ;
if ( ! ( cvl_vg = cmd_vg_add ( cmd - > mem , & cmd_vgs ,
vgname , NULL , flags ) ) ) {
stack ;
return ECMD_FAILED ;
}
2006-09-02 05:18:17 +04:00
2010-05-21 18:15:39 +04:00
if ( ! cmd_vg_read ( cmd , & cmd_vgs ) ) {
2010-12-08 23:50:48 +03:00
free_cmd_vgs ( & cmd_vgs ) ;
2009-07-01 21:00:50 +04:00
if ( ret_max < ECMD_FAILED ) {
log_error ( " Skipping volume group %s " , vgname ) ;
2006-09-02 05:18:17 +04:00
ret_max = ECMD_FAILED ;
2009-09-15 02:47:49 +04:00
} else
stack ;
2006-09-02 05:18:17 +04:00
continue ;
}
2004-03-08 20:19:15 +03:00
tags_arg = & tags ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & lvnames ) ; /* LVs to be processed in this VG */
dm_list_iterate_items ( sll , & arg_lvnames ) {
2004-03-08 20:19:15 +03:00
const char * vg_name = sll - > str ;
const char * lv_name = strchr ( vg_name , ' / ' ) ;
if ( ( ! lv_name & & ! strcmp ( vg_name , vgname ) ) ) {
/* Process all LVs in this VG */
tags_arg = NULL ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & lvnames ) ;
2004-03-08 20:19:15 +03:00
break ;
2010-12-01 15:22:49 +03:00
} else if ( ! strncmp ( vg_name , vgname , strlen ( vgname ) ) & & lv_name & &
2007-08-22 18:38:18 +04:00
strlen ( vgname ) = = ( size_t ) ( lv_name - vg_name ) ) {
2004-03-08 20:19:15 +03:00
if ( ! str_list_add ( cmd - > mem , & lvnames ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem ,
2010-04-28 16:23:11 +04:00
lv_name + 1 ) ) ) {
2004-05-11 22:45:11 +04:00
log_error ( " strlist allocation failed " ) ;
2010-12-08 23:50:48 +03:00
free_cmd_vgs ( & cmd_vgs ) ;
2004-05-11 22:45:11 +04:00
return ECMD_FAILED ;
2002-11-18 17:04:08 +03:00
}
2001-11-14 21:38:07 +03:00
}
}
2004-03-08 20:19:15 +03:00
2010-05-21 18:15:39 +04:00
while ( ! sigint_caught ( ) ) {
ret = process_each_lv_in_vg ( cmd , cvl_vg - > vg , & lvnames ,
tags_arg , & failed_lvnames ,
handle , process_single_lv ) ;
2011-05-19 17:59:22 +04:00
if ( ret ! = ECMD_PROCESSED ) {
2011-02-18 18:02:25 +03:00
stack ;
2010-05-21 18:15:39 +04:00
break ;
2011-02-18 18:02:25 +03:00
}
2010-05-21 18:15:39 +04:00
2011-05-19 17:59:22 +04:00
if ( dm_list_empty ( & failed_lvnames ) )
break ;
2010-05-21 18:15:39 +04:00
/* Try again with failed LVs in this VG */
dm_list_init ( & lvnames ) ;
dm_list_splice ( & lvnames , & failed_lvnames ) ;
2010-12-08 23:50:48 +03:00
free_cmd_vgs ( & cmd_vgs ) ;
2010-05-21 18:15:39 +04:00
if ( ! cmd_vg_read ( cmd , & cmd_vgs ) ) {
2011-02-18 18:02:25 +03:00
stack ;
2010-05-21 18:15:39 +04:00
ret = ECMD_FAILED ; /* break */
break ;
}
}
2004-03-08 20:19:15 +03:00
if ( ret > ret_max )
ret_max = ret ;
2010-05-21 18:15:39 +04:00
2010-12-08 23:50:48 +03:00
free_cmd_vgs ( & cmd_vgs ) ;
2010-05-21 16:19:22 +04:00
/* FIXME: logic for breaking command is not consistent */
2011-02-18 18:02:25 +03:00
if ( sigint_caught ( ) ) {
stack ;
2010-05-21 16:19:22 +04:00
return ECMD_FAILED ;
2011-02-18 18:02:25 +03:00
}
2001-11-14 21:38:07 +03:00
}
return ret_max ;
}
2005-04-20 00:58:25 +04:00
int process_each_segment_in_pv ( struct cmd_context * cmd ,
struct volume_group * vg ,
struct physical_volume * pv ,
void * handle ,
2010-04-14 03:57:41 +04:00
process_single_pvseg_fn_t process_single_pvseg )
2005-04-20 00:58:25 +04:00
{
struct pv_segment * pvseg ;
2009-04-07 14:22:14 +04:00
struct pv_list * pvl ;
2007-11-14 21:41:05 +03:00
const char * vg_name = NULL ;
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2005-04-20 00:58:25 +04:00
int ret ;
2009-04-10 14:01:38 +04:00
struct volume_group * old_vg = vg ;
2009-04-21 16:59:18 +04:00
struct pv_segment _free_pv_segment = { . pv = pv } ;
2007-11-14 21:41:05 +03:00
2009-04-21 16:57:31 +04:00
if ( is_pv ( pv ) & & ! vg & & ! is_orphan ( pv ) ) {
2007-11-14 21:41:05 +03:00
vg_name = pv_vg_name ( pv ) ;
2009-07-01 21:00:50 +04:00
vg = vg_read ( cmd , vg_name , NULL , 0 ) ;
if ( vg_read_error ( vg ) ) {
2011-08-11 00:25:29 +04:00
release_vg ( vg ) ;
2007-11-15 05:20:03 +03:00
log_error ( " Skipping volume group %s " , vg_name ) ;
return ECMD_FAILED ;
2007-11-14 21:41:05 +03:00
}
2009-04-07 14:22:14 +04:00
/*
* Replace possibly incomplete PV structure with new one
* allocated in vg_read_internal ( ) path .
*/
if ( ! ( pvl = find_pv_in_vg ( vg , pv_dev_name ( pv ) ) ) ) {
log_error ( " Unable to find %s in volume group %s " ,
pv_dev_name ( pv ) , vg_name ) ;
2011-08-11 00:25:29 +04:00
unlock_and_release_vg ( cmd , vg , vg_name ) ;
2009-12-11 16:11:56 +03:00
return ECMD_FAILED ;
2009-04-07 14:22:14 +04:00
}
pv = pvl - > pv ;
2007-11-14 21:41:05 +03:00
}
2005-04-20 00:58:25 +04:00
2009-04-21 16:59:18 +04:00
if ( dm_list_empty ( & pv - > segments ) ) {
2010-04-14 03:57:41 +04:00
ret = process_single_pvseg ( cmd , NULL , & _free_pv_segment , handle ) ;
2005-04-20 00:58:25 +04:00
if ( ret > ret_max )
ret_max = ret ;
2009-04-21 16:59:18 +04:00
} else
dm_list_iterate_items ( pvseg , & pv - > segments ) {
2010-04-14 03:57:41 +04:00
ret = process_single_pvseg ( cmd , vg , pvseg , handle ) ;
2009-04-21 16:59:18 +04:00
if ( ret > ret_max )
ret_max = ret ;
if ( sigint_caught ( ) )
break ;
}
2005-04-20 00:58:25 +04:00
2007-11-14 21:41:05 +03:00
if ( vg_name )
unlock_vg ( cmd , vg_name ) ;
2009-04-10 14:01:38 +04:00
if ( ! old_vg )
2011-08-11 00:25:29 +04:00
release_vg ( vg ) ;
2007-11-14 21:41:05 +03:00
2005-04-20 00:58:25 +04:00
return ret_max ;
}
2002-12-12 23:55:49 +03:00
int process_each_segment_in_lv ( struct cmd_context * cmd ,
struct logical_volume * lv ,
void * handle ,
2010-04-14 03:57:41 +04:00
process_single_seg_fn_t process_single_seg )
2002-12-12 23:55:49 +03:00
{
struct lv_segment * seg ;
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2002-12-12 23:55:49 +03:00
int ret ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( seg , & lv - > segments ) {
2010-04-14 03:57:41 +04:00
ret = process_single_seg ( cmd , seg , handle ) ;
2002-12-12 23:55:49 +03:00
if ( ret > ret_max )
ret_max = ret ;
2010-05-21 16:19:22 +04:00
/* FIXME: logic for breaking command is not consistent */
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2010-05-21 16:19:22 +04:00
return ECMD_FAILED ;
2002-12-12 23:55:49 +03:00
}
return ret_max ;
}
2004-03-08 20:19:15 +03:00
static int _process_one_vg ( struct cmd_context * cmd , const char * vg_name ,
2006-04-13 01:23:04 +04:00
const char * vgid ,
2008-11-04 01:14:30 +03:00
struct dm_list * tags , struct dm_list * arg_vgnames ,
2009-07-01 21:00:50 +04:00
uint32_t flags , void * handle , int ret_max ,
2010-04-14 03:57:41 +04:00
process_single_vg_fn_t process_single_vg )
2004-03-08 20:19:15 +03:00
{
2010-05-21 18:11:13 +04:00
struct dm_list cmd_vgs ;
struct cmd_vg * cvl_vg ;
2004-03-08 20:19:15 +03:00
int ret = 0 ;
log_verbose ( " Finding volume group \" %s \" " , vg_name ) ;
2010-05-21 18:11:13 +04:00
dm_list_init ( & cmd_vgs ) ;
if ( ! ( cvl_vg = cmd_vg_add ( cmd - > mem , & cmd_vgs , vg_name , vgid , flags ) ) )
return_0 ;
for ( ; ; ) {
/* FIXME: consistent handling of command break */
if ( sigint_caught ( ) ) {
2011-01-24 16:38:31 +03:00
ret = ECMD_FAILED ;
2010-05-21 18:11:13 +04:00
break ;
}
if ( ! cmd_vg_read ( cmd , & cmd_vgs ) )
/* Allow FAILED_INCONSISTENT through only for vgcfgrestore */
if ( vg_read_error ( cvl_vg - > vg ) & &
( ! ( ( flags & READ_ALLOW_INCONSISTENT ) & &
( vg_read_error ( cvl_vg - > vg ) = = FAILED_INCONSISTENT ) ) ) ) {
ret = ECMD_FAILED ;
break ;
}
2006-09-02 05:18:17 +04:00
2010-05-21 18:11:13 +04:00
if ( ! dm_list_empty ( tags ) & &
/* Only process if a tag matches or it's on arg_vgnames */
! str_list_match_item ( arg_vgnames , vg_name ) & &
2010-11-09 15:34:40 +03:00
! str_list_match_list ( tags , & cvl_vg - > vg - > tags , NULL ) )
2010-05-21 18:11:13 +04:00
break ;
ret = process_single_vg ( cmd , vg_name , cvl_vg - > vg , handle ) ;
if ( vg_read_error ( cvl_vg - > vg ) ) /* FAILED_INCONSISTENT */
break ;
if ( ! cvl_vg - > vg - > cmd_missing_vgs )
break ;
2010-12-08 23:50:48 +03:00
free_cmd_vgs ( & cmd_vgs ) ;
2009-06-06 00:00:52 +04:00
}
2010-12-08 23:50:48 +03:00
free_cmd_vgs ( & cmd_vgs ) ;
2004-03-08 20:19:15 +03:00
2010-05-21 18:11:13 +04:00
return ( ret > ret_max ) ? ret : ret_max ;
2004-03-08 20:19:15 +03:00
}
2002-02-12 00:00:35 +03:00
int process_each_vg ( struct cmd_context * cmd , int argc , char * * argv ,
2009-07-01 21:00:50 +04:00
uint32_t flags , void * handle ,
2010-04-14 03:57:41 +04:00
process_single_vg_fn_t process_single_vg )
2001-10-06 01:39:30 +04:00
{
int opt = 0 ;
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2001-10-06 01:39:30 +04:00
2004-03-08 20:19:15 +03:00
struct str_list * sl ;
2008-11-04 01:14:30 +03:00
struct dm_list * vgnames , * vgids ;
struct dm_list arg_vgnames , tags ;
2001-10-06 01:39:30 +04:00
2006-04-13 01:23:04 +04:00
const char * vg_name , * vgid ;
2002-02-11 18:42:34 +03:00
2008-11-04 01:14:30 +03:00
dm_list_init ( & tags ) ;
dm_list_init ( & arg_vgnames ) ;
2004-03-08 20:19:15 +03:00
2001-10-06 01:39:30 +04:00
if ( argc ) {
log_verbose ( " Using volume group(s) on command line " ) ;
2004-03-08 20:19:15 +03:00
2002-02-11 18:42:34 +03:00
for ( ; opt < argc ; opt + + ) {
vg_name = argv [ opt ] ;
2004-03-08 20:19:15 +03:00
if ( * vg_name = = ' @ ' ) {
2010-11-17 13:19:29 +03:00
if ( ! validate_tag ( vg_name + 1 ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " Skipping invalid tag %s " ,
vg_name ) ;
2009-07-21 15:10:49 +04:00
if ( ret_max < EINVALID_CMD_LINE )
ret_max = EINVALID_CMD_LINE ;
2004-03-08 20:19:15 +03:00
continue ;
}
if ( ! str_list_add ( cmd - > mem , & tags ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem ,
2004-03-08 20:19:15 +03:00
vg_name + 1 ) ) ) {
log_error ( " strlist allocation failed " ) ;
return ECMD_FAILED ;
}
continue ;
}
2007-03-09 23:47:41 +03:00
vg_name = skip_dev_dir ( cmd , vg_name , NULL ) ;
2002-04-24 22:20:51 +04:00
if ( strchr ( vg_name , ' / ' ) ) {
log_error ( " Invalid volume group name: %s " ,
vg_name ) ;
2009-07-21 15:10:49 +04:00
if ( ret_max < EINVALID_CMD_LINE )
ret_max = EINVALID_CMD_LINE ;
2002-04-24 22:20:51 +04:00
continue ;
}
2004-03-08 20:19:15 +03:00
if ( ! str_list_add ( cmd - > mem , & arg_vgnames ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem , vg_name ) ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " strlist allocation failed " ) ;
return ECMD_FAILED ;
2002-02-11 18:42:34 +03:00
}
}
2004-03-08 20:19:15 +03:00
vgnames = & arg_vgnames ;
}
2008-11-04 01:14:30 +03:00
if ( ! argc | | ! dm_list_empty ( & tags ) ) {
2002-01-16 17:43:27 +03:00
log_verbose ( " Finding all volume groups " ) ;
2012-02-28 22:08:08 +04:00
if ( ! lvmetad_vg_list_to_lvmcache ( cmd ) )
stack ;
2010-03-16 19:57:03 +03:00
if ( ! ( vgids = get_vgids ( cmd , 0 ) ) | | dm_list_empty ( vgids ) ) {
2001-10-06 01:39:30 +04:00
log_error ( " No volume groups found " ) ;
2004-05-20 20:18:58 +04:00
return ret_max ;
2001-10-06 01:39:30 +04:00
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( sl , vgids ) {
2006-04-13 01:23:04 +04:00
vgid = sl - > str ;
2012-02-10 05:28:27 +04:00
if ( ! ( vgid ) | | ! ( vg_name = lvmcache_vgname_from_vgid ( cmd - > mem , vgid ) ) )
2006-04-13 01:23:04 +04:00
continue ;
ret_max = _process_one_vg ( cmd , vg_name , vgid , & tags ,
& arg_vgnames ,
2009-07-01 21:00:50 +04:00
flags , handle ,
2010-04-14 03:57:41 +04:00
ret_max , process_single_vg ) ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
return ret_max ;
2006-04-13 01:23:04 +04:00
}
} else {
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( sl , vgnames ) {
2006-04-13 01:23:04 +04:00
vg_name = sl - > str ;
2007-11-02 23:40:05 +03:00
if ( is_orphan_vg ( vg_name ) )
2006-04-13 01:23:04 +04:00
continue ; /* FIXME Unnecessary? */
ret_max = _process_one_vg ( cmd , vg_name , NULL , & tags ,
& arg_vgnames ,
2009-07-01 21:00:50 +04:00
flags , handle ,
2010-04-14 03:57:41 +04:00
ret_max , process_single_vg ) ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
return ret_max ;
2006-04-13 01:23:04 +04:00
}
2001-10-06 01:39:30 +04:00
}
return ret_max ;
}
2001-10-08 22:44:22 +04:00
2002-02-11 23:50:53 +03:00
int process_each_pv_in_vg ( struct cmd_context * cmd , struct volume_group * vg ,
2008-11-04 01:14:30 +03:00
const struct dm_list * tags , void * handle ,
2010-04-14 03:57:41 +04:00
process_single_pv_fn_t process_single_pv )
2001-11-19 18:20:50 +03:00
{
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2001-11-19 18:20:50 +03:00
int ret = 0 ;
2003-10-16 00:02:46 +04:00
struct pv_list * pvl ;
2001-11-19 18:20:50 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl , & vg - > pvs ) {
if ( tags & & ! dm_list_empty ( tags ) & &
2010-11-09 15:34:40 +03:00
! str_list_match_list ( tags , & pvl - > pv - > tags , NULL ) ) {
2004-03-08 20:19:15 +03:00
continue ;
2004-05-11 22:45:11 +04:00
}
2010-04-14 03:57:41 +04:00
if ( ( ret = process_single_pv ( cmd , vg , pvl - > pv , handle ) ) > ret_max )
2002-01-21 19:05:23 +03:00
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
return ret_max ;
2004-03-08 20:19:15 +03:00
}
2002-01-21 19:05:23 +03:00
return ret_max ;
2001-11-19 18:20:50 +03:00
}
2004-06-19 23:27:00 +04:00
static int _process_all_devs ( struct cmd_context * cmd , void * handle ,
2010-04-14 03:57:41 +04:00
process_single_pv_fn_t process_single_pv )
2004-06-19 23:27:00 +04:00
{
struct physical_volume * pv ;
struct physical_volume pv_dummy ;
struct dev_iter * iter ;
struct device * dev ;
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2004-06-19 23:27:00 +04:00
int ret = 0 ;
2010-11-23 04:55:53 +03:00
if ( ! scan_vgs_for_pvs ( cmd , 1 ) ) {
2008-02-06 19:09:51 +03:00
stack ;
return ECMD_FAILED ;
}
2005-03-08 16:46:17 +03:00
if ( ! ( iter = dev_iter_create ( cmd - > filter , 1 ) ) ) {
2004-06-19 23:27:00 +04:00
log_error ( " dev_iter creation failed " ) ;
return ECMD_FAILED ;
}
while ( ( dev = dev_iter_get ( iter ) ) ) {
2011-06-01 23:29:31 +04:00
if ( ! ( pv = pv_read ( cmd , dev_name ( dev ) , 0 , 0 ) ) ) {
2004-06-19 23:27:00 +04:00
memset ( & pv_dummy , 0 , sizeof ( pv_dummy ) ) ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & pv_dummy . tags ) ;
dm_list_init ( & pv_dummy . segments ) ;
2004-06-19 23:27:00 +04:00
pv_dummy . dev = dev ;
pv = & pv_dummy ;
}
2010-04-14 03:57:41 +04:00
ret = process_single_pv ( cmd , NULL , pv , handle ) ;
2011-03-11 17:56:56 +03:00
free_pv_fid ( pv ) ;
2004-06-19 23:27:00 +04:00
if ( ret > ret_max )
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2007-07-10 21:51:26 +04:00
break ;
2004-06-19 23:27:00 +04:00
}
dev_iter_destroy ( iter ) ;
return ret_max ;
}
2009-04-08 16:53:20 +04:00
/*
* If the lock_type is LCK_VG_READ ( used only in reporting commands ) ,
* we lock VG_GLOBAL to enable use of metadata cache .
* This can pause alongide pvscan or vgscan process for a while .
*/
2002-02-12 00:00:35 +03:00
int process_each_pv ( struct cmd_context * cmd , int argc , char * * argv ,
2009-07-15 09:50:22 +04:00
struct volume_group * vg , uint32_t flags ,
2009-02-09 12:45:49 +03:00
int scan_label_only , void * handle ,
2010-04-14 03:57:41 +04:00
process_single_pv_fn_t process_single_pv )
2001-10-12 01:35:55 +04:00
{
int opt = 0 ;
2008-06-11 00:07:04 +04:00
int ret_max = ECMD_PROCESSED ;
2001-10-12 01:35:55 +04:00
int ret = 0 ;
2012-12-12 17:39:52 +04:00
int lock_global = ! ( flags & READ_WITHOUT_LOCK ) & & ! ( flags & READ_FOR_UPDATE ) & & ! lvmetad_active ( ) ;
2001-10-12 01:35:55 +04:00
2002-01-21 17:28:12 +03:00
struct pv_list * pvl ;
2002-12-12 23:55:49 +03:00
struct physical_volume * pv ;
2008-11-04 01:14:30 +03:00
struct dm_list * pvslist , * vgnames ;
struct dm_list tags ;
2004-03-08 20:19:15 +03:00
struct str_list * sll ;
2010-09-23 16:02:33 +04:00
char * at_sign , * tagname ;
2008-01-16 21:15:26 +03:00
int scanned = 0 ;
2004-03-08 20:19:15 +03:00
2008-11-04 01:14:30 +03:00
dm_list_init ( & tags ) ;
2001-10-12 01:35:55 +04:00
2013-03-18 00:29:58 +04:00
if ( lock_global & & ! lock_vol ( cmd , VG_GLOBAL , LCK_VG_READ , NULL ) ) {
2009-04-08 16:53:20 +04:00
log_error ( " Unable to obtain global lock. " ) ;
return ECMD_FAILED ;
}
2001-10-12 01:35:55 +04:00
if ( argc ) {
log_verbose ( " Using physical volume(s) on command line " ) ;
for ( ; opt < argc ; opt + + ) {
2011-08-30 18:55:15 +04:00
dm_unescape_colons_and_at_signs ( argv [ opt ] , NULL , & at_sign ) ;
2010-09-23 16:02:33 +04:00
if ( at_sign & & ( at_sign = = argv [ opt ] ) ) {
tagname = at_sign + 1 ;
2004-03-08 20:19:15 +03:00
2010-11-17 13:19:29 +03:00
if ( ! validate_tag ( tagname ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " Skipping invalid tag %s " ,
tagname ) ;
2004-05-05 22:31:38 +04:00
if ( ret_max < EINVALID_CMD_LINE )
ret_max = EINVALID_CMD_LINE ;
2004-03-08 20:19:15 +03:00
continue ;
}
if ( ! str_list_add ( cmd - > mem , & tags ,
2005-10-17 03:03:59 +04:00
dm_pool_strdup ( cmd - > mem ,
2004-03-08 20:19:15 +03:00
tagname ) ) ) {
log_error ( " strlist allocation failed " ) ;
2009-04-08 16:53:20 +04:00
goto bad ;
2004-03-08 20:19:15 +03:00
}
continue ;
}
2002-12-12 23:55:49 +03:00
if ( vg ) {
if ( ! ( pvl = find_pv_in_vg ( vg , argv [ opt ] ) ) ) {
log_error ( " Physical Volume \" %s \" not "
" found in Volume Group "
" \" %s \" " , argv [ opt ] ,
vg - > name ) ;
2003-10-22 02:06:07 +04:00
ret_max = ECMD_FAILED ;
2002-12-12 23:55:49 +03:00
continue ;
}
pv = pvl - > pv ;
} else {
2011-06-01 23:29:31 +04:00
if ( ! ( pv = pv_read ( cmd , argv [ opt ] ,
2011-02-21 15:15:59 +03:00
1 , scan_label_only ) ) ) {
2002-12-12 23:55:49 +03:00
log_error ( " Failed to read physical "
" volume \" %s \" " , argv [ opt ] ) ;
2003-10-22 02:06:07 +04:00
ret_max = ECMD_FAILED ;
2002-12-12 23:55:49 +03:00
continue ;
}
2008-01-16 21:15:26 +03:00
2008-01-30 17:00:02 +03:00
/*
* If a PV has no MDAs it may appear to be an
2008-01-16 21:15:26 +03:00
* orphan until the metadata is read off
* another PV in the same VG . Detecting this
* means checking every VG by scanning every
* PV on the system .
2008-01-30 17:00:02 +03:00
*/
2010-03-18 20:29:12 +03:00
if ( ! scanned & & is_orphan ( pv ) & &
2012-06-30 00:22:09 +04:00
! dm_list_size ( & pv - > fid - > metadata_areas_in_use ) ) {
2009-02-09 12:45:49 +03:00
if ( ! scan_label_only & &
2010-11-23 04:55:53 +03:00
! scan_vgs_for_pvs ( cmd , 1 ) ) {
2008-01-16 21:15:26 +03:00
stack ;
ret_max = ECMD_FAILED ;
continue ;
}
scanned = 1 ;
2011-03-11 17:56:56 +03:00
free_pv_fid ( pv ) ;
2008-01-16 21:15:26 +03:00
if ( ! ( pv = pv_read ( cmd , argv [ opt ] ,
2011-06-01 23:29:31 +04:00
1 ,
2009-02-26 02:29:06 +03:00
scan_label_only ) ) ) {
2008-01-16 21:15:26 +03:00
log_error ( " Failed to read "
" physical volume "
" \" %s \" " , argv [ opt ] ) ;
ret_max = ECMD_FAILED ;
continue ;
}
}
2001-10-12 01:35:55 +04:00
}
2002-12-12 23:55:49 +03:00
2010-04-14 03:57:41 +04:00
ret = process_single_pv ( cmd , vg , pv , handle ) ;
2011-03-11 17:56:56 +03:00
/*
* Free PV only if we called pv_read before ,
* otherwise the PV structure is part of the VG .
*/
if ( ! vg )
free_pv_fid ( pv ) ;
2001-10-12 01:35:55 +04:00
if ( ret > ret_max )
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2009-04-08 16:53:20 +04:00
goto out ;
2001-10-12 01:35:55 +04:00
}
2010-03-16 19:57:03 +03:00
if ( ! dm_list_empty ( & tags ) & & ( vgnames = get_vgnames ( cmd , 1 ) ) & &
2008-11-04 01:14:30 +03:00
! dm_list_empty ( vgnames ) ) {
dm_list_iterate_items ( sll , vgnames ) {
2009-07-15 09:50:22 +04:00
vg = vg_read ( cmd , sll - > str , NULL , flags ) ;
if ( vg_read_error ( vg ) ) {
2009-04-10 13:54:36 +04:00
ret_max = ECMD_FAILED ;
2011-08-11 00:25:29 +04:00
release_vg ( vg ) ;
2009-09-15 02:47:49 +04:00
stack ;
2007-11-14 21:41:05 +03:00
continue ;
}
2006-09-02 05:18:17 +04:00
2004-03-08 20:19:15 +03:00
ret = process_each_pv_in_vg ( cmd , vg , & tags ,
handle ,
2010-04-14 03:57:41 +04:00
process_single_pv ) ;
2007-11-14 21:41:05 +03:00
2011-08-11 00:25:29 +04:00
unlock_and_release_vg ( cmd , vg , sll - > str ) ;
2007-11-14 21:41:05 +03:00
2004-03-08 20:19:15 +03:00
if ( ret > ret_max )
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2009-04-08 16:53:20 +04:00
goto out ;
2004-03-08 20:19:15 +03:00
}
}
2001-10-12 01:35:55 +04:00
} else {
2002-12-12 23:55:49 +03:00
if ( vg ) {
log_verbose ( " Using all physical volume(s) in "
" volume group " ) ;
2004-03-08 20:19:15 +03:00
ret = process_each_pv_in_vg ( cmd , vg , NULL , handle ,
2010-04-14 03:57:41 +04:00
process_single_pv ) ;
2003-10-22 02:06:07 +04:00
if ( ret > ret_max )
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2009-04-08 16:53:20 +04:00
goto out ;
2004-06-19 23:27:00 +04:00
} else if ( arg_count ( cmd , all_ARG ) ) {
2010-04-14 03:57:41 +04:00
ret = _process_all_devs ( cmd , handle , process_single_pv ) ;
2004-06-19 23:27:00 +04:00
if ( ret > ret_max )
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2009-04-08 16:53:20 +04:00
goto out ;
2002-12-12 23:55:49 +03:00
} else {
log_verbose ( " Scanning for physical volume names " ) ;
2009-04-08 16:53:20 +04:00
2012-02-23 17:11:07 +04:00
lvmcache_seed_infos_from_lvmetad ( cmd ) ;
2002-12-20 02:25:55 +03:00
if ( ! ( pvslist = get_pvs ( cmd ) ) )
2009-04-08 16:53:20 +04:00
goto bad ;
2002-12-12 23:55:49 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl , pvslist ) {
2010-04-14 03:57:41 +04:00
ret = process_single_pv ( cmd , NULL , pvl - > pv ,
2003-10-16 00:02:46 +04:00
handle ) ;
2011-03-11 17:56:56 +03:00
free_pv_fid ( pvl - > pv ) ;
2002-12-12 23:55:49 +03:00
if ( ret > ret_max )
ret_max = ret ;
2007-06-15 14:11:14 +04:00
if ( sigint_caught ( ) )
2009-04-08 16:53:20 +04:00
goto out ;
2002-12-12 23:55:49 +03:00
}
}
2001-10-12 01:35:55 +04:00
}
2009-04-08 16:53:20 +04:00
out :
if ( lock_global )
unlock_vg ( cmd , VG_GLOBAL ) ;
2001-10-12 01:35:55 +04:00
return ret_max ;
2009-04-08 16:53:20 +04:00
bad :
if ( lock_global )
unlock_vg ( cmd , VG_GLOBAL ) ;
return ECMD_FAILED ;
2001-10-12 01:35:55 +04:00
}
2005-01-19 20:31:51 +03:00
/*
* Determine volume group name from a logical volume name
*/
2002-12-20 02:25:55 +03:00
const char * extract_vgname ( struct cmd_context * cmd , const char * lv_name )
2001-11-14 21:38:07 +03:00
{
2002-12-20 02:25:55 +03:00
const char * vg_name = lv_name ;
2001-11-06 22:02:26 +03:00
char * st ;
2002-04-24 22:20:51 +04:00
char * dev_dir = cmd - > dev_dir ;
2001-10-29 16:52:23 +03:00
/* Path supplied? */
2001-11-06 22:02:26 +03:00
if ( vg_name & & strchr ( vg_name , ' / ' ) ) {
2001-11-15 20:27:45 +03:00
/* Strip dev_dir (optional) */
2003-07-05 02:34:56 +04:00
if ( * vg_name = = ' / ' ) {
while ( * vg_name = = ' / ' )
vg_name + + ;
vg_name - - ;
}
if ( ! strncmp ( vg_name , dev_dir , strlen ( dev_dir ) ) ) {
2001-11-12 18:10:01 +03:00
vg_name + = strlen ( dev_dir ) ;
2003-07-05 02:34:56 +04:00
while ( * vg_name = = ' / ' )
vg_name + + ;
}
if ( * vg_name = = ' / ' ) {
log_error ( " \" %s \" : Invalid path for Logical "
" Volume " , lv_name ) ;
return 0 ;
}
2004-03-08 20:19:15 +03:00
2003-07-05 02:34:56 +04:00
/* Require exactly one set of consecutive slashes */
if ( ( st = strchr ( vg_name , ' / ' ) ) )
while ( * st = = ' / ' )
st + + ;
2001-10-29 16:52:23 +03:00
2010-10-25 16:08:15 +04:00
if ( ! st | | strchr ( st , ' / ' ) ) {
2002-01-30 18:04:48 +03:00
log_error ( " \" %s \" : Invalid path for Logical Volume " ,
2001-11-14 21:38:07 +03:00
lv_name ) ;
2001-10-29 16:52:23 +03:00
return 0 ;
}
2005-10-17 03:03:59 +04:00
vg_name = dm_pool_strdup ( cmd - > mem , vg_name ) ;
2001-10-29 16:52:23 +03:00
if ( ! vg_name ) {
log_error ( " Allocation of vg_name failed " ) ;
return 0 ;
}
* strchr ( vg_name , ' / ' ) = ' \0 ' ;
return vg_name ;
}
2001-11-06 22:02:26 +03:00
2002-04-24 22:20:51 +04:00
if ( ! ( vg_name = default_vgname ( cmd ) ) ) {
2001-11-06 22:02:26 +03:00
if ( lv_name )
2002-01-30 18:04:48 +03:00
log_error ( " Path required for Logical Volume \" %s \" " ,
2001-11-14 21:38:07 +03:00
lv_name ) ;
2001-11-06 22:02:26 +03:00
return 0 ;
}
2001-11-14 21:38:07 +03:00
2001-11-06 22:02:26 +03:00
return vg_name ;
}
2005-01-19 20:31:51 +03:00
/*
* Extract default volume group name from environment
*/
2002-04-24 22:20:51 +04:00
char * default_vgname ( struct cmd_context * cmd )
2001-11-06 22:02:26 +03:00
{
2011-02-18 17:47:28 +03:00
const char * vg_path ;
2001-11-06 22:02:26 +03:00
2001-10-29 16:52:23 +03:00
/* Take default VG from environment? */
2001-11-14 21:38:07 +03:00
vg_path = getenv ( " LVM_VG_NAME " ) ;
2001-11-06 22:02:26 +03:00
if ( ! vg_path )
2001-10-29 16:52:23 +03:00
return 0 ;
2007-03-09 23:47:41 +03:00
vg_path = skip_dev_dir ( cmd , vg_path , NULL ) ;
2001-10-29 16:52:23 +03:00
if ( strchr ( vg_path , ' / ' ) ) {
2002-01-30 18:04:48 +03:00
log_error ( " Environment Volume Group in LVM_VG_NAME invalid: "
" \" %s \" " , vg_path ) ;
2001-10-29 16:52:23 +03:00
return 0 ;
}
2005-10-17 03:03:59 +04:00
return dm_pool_strdup ( cmd - > mem , vg_path ) ;
2001-10-29 16:52:23 +03:00
}
2002-01-21 19:05:23 +03:00
2005-01-19 20:31:51 +03:00
/*
* Process physical extent range specifiers
*/
2006-10-22 03:18:43 +04:00
static int _add_pe_range ( struct dm_pool * mem , const char * pvname ,
2008-11-04 01:14:30 +03:00
struct dm_list * pe_ranges , uint32_t start , uint32_t count )
2003-04-25 02:23:24 +04:00
{
2004-08-18 01:55:23 +04:00
struct pe_range * per ;
2003-04-25 02:23:24 +04:00
2006-10-22 03:18:43 +04:00
log_debug ( " Adding PE range: start PE % " PRIu32 " length % " PRIu32
" on %s " , start , count , pvname ) ;
2003-04-25 02:23:24 +04:00
/* Ensure no overlap with existing areas */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( per , pe_ranges ) {
2004-08-18 01:55:23 +04:00
if ( ( ( start < per - > start ) & & ( start + count - 1 > = per - > start ) )
| | ( ( start > = per - > start ) & &
( per - > start + per - > count - 1 ) > = start ) ) {
2006-10-22 03:18:43 +04:00
log_error ( " Overlapping PE ranges specified (% " PRIu32
" -% " PRIu32 " , % " PRIu32 " -% " PRIu32 " ) "
" on %s " ,
2004-08-18 01:55:23 +04:00
start , start + count - 1 , per - > start ,
2006-10-22 03:18:43 +04:00
per - > start + per - > count - 1 , pvname ) ;
2003-04-25 02:23:24 +04:00
return 0 ;
}
}
2005-10-17 03:03:59 +04:00
if ( ! ( per = dm_pool_alloc ( mem , sizeof ( * per ) ) ) ) {
2003-04-25 02:23:24 +04:00
log_error ( " Allocation of list failed " ) ;
return 0 ;
}
2004-08-18 01:55:23 +04:00
per - > start = start ;
per - > count = count ;
2008-11-04 01:14:30 +03:00
dm_list_add ( pe_ranges , & per - > list ) ;
2003-04-25 02:23:24 +04:00
return 1 ;
}
2007-09-12 00:12:54 +04:00
static int xstrtouint32 ( const char * s , char * * p , int base , uint32_t * result )
{
unsigned long ul ;
errno = 0 ;
ul = strtoul ( s , p , base ) ;
2012-01-20 01:28:06 +04:00
if ( errno | | * p = = s | | ul > UINT32_MAX )
return 0 ;
2007-09-12 00:12:54 +04:00
* result = ul ;
2012-01-20 01:28:06 +04:00
return 1 ;
2007-09-12 00:12:54 +04:00
}
2008-11-04 01:14:30 +03:00
static int _parse_pes ( struct dm_pool * mem , char * c , struct dm_list * pe_ranges ,
2006-10-22 03:18:43 +04:00
const char * pvname , uint32_t size )
2003-04-25 02:23:24 +04:00
{
char * endptr ;
uint32_t start , end ;
/* Default to whole PV */
if ( ! c ) {
2008-01-30 16:19:47 +03:00
if ( ! _add_pe_range ( mem , pvname , pe_ranges , UINT32_C ( 0 ) , size ) )
return_0 ;
2003-04-25 02:23:24 +04:00
return 1 ;
}
while ( * c ) {
if ( * c ! = ' : ' )
goto error ;
c + + ;
/* Disallow :: and :\0 */
if ( * c = = ' : ' | | ! * c )
goto error ;
/* Default to whole range */
start = UINT32_C ( 0 ) ;
end = size - 1 ;
/* Start extent given? */
if ( isdigit ( * c ) ) {
2012-01-20 01:28:06 +04:00
if ( ! xstrtouint32 ( c , & endptr , 10 , & start ) )
2003-04-25 02:23:24 +04:00
goto error ;
c = endptr ;
/* Just one number given? */
if ( ! * c | | * c = = ' : ' )
end = start ;
}
/* Range? */
if ( * c = = ' - ' ) {
c + + ;
if ( isdigit ( * c ) ) {
2012-01-20 01:28:06 +04:00
if ( ! xstrtouint32 ( c , & endptr , 10 , & end ) )
2003-04-25 02:23:24 +04:00
goto error ;
c = endptr ;
}
}
if ( * c & & * c ! = ' : ' )
goto error ;
if ( ( start > end ) | | ( end > size - 1 ) ) {
log_error ( " PE range error: start extent % " PRIu32 " to "
" end extent % " PRIu32 , start , end ) ;
return 0 ;
}
2008-01-30 16:19:47 +03:00
if ( ! _add_pe_range ( mem , pvname , pe_ranges , start , end - start + 1 ) )
return_0 ;
2003-04-25 02:23:24 +04:00
}
return 1 ;
error :
log_error ( " Physical extent parsing error at %s " , c ) ;
return 0 ;
}
2006-10-22 03:18:43 +04:00
static int _create_pv_entry ( struct dm_pool * mem , struct pv_list * pvl ,
2008-11-04 01:14:30 +03:00
char * colon , int allocatable_only , struct dm_list * r )
2004-03-08 20:19:15 +03:00
{
const char * pvname ;
2006-10-22 03:18:43 +04:00
struct pv_list * new_pvl = NULL , * pvl2 ;
2008-11-04 01:14:30 +03:00
struct dm_list * pe_ranges ;
2004-03-08 20:19:15 +03:00
2007-10-12 18:29:32 +04:00
pvname = pv_dev_name ( pvl - > pv ) ;
2004-08-18 01:55:23 +04:00
if ( allocatable_only & & ! ( pvl - > pv - > status & ALLOCATABLE_PV ) ) {
2012-10-16 12:14:41 +04:00
log_warn ( " Physical volume %s not allocatable. " , pvname ) ;
2006-10-22 03:18:43 +04:00
return 1 ;
2004-03-08 20:19:15 +03:00
}
2010-03-16 17:37:38 +03:00
if ( allocatable_only & & is_missing_pv ( pvl - > pv ) ) {
2012-10-16 12:14:41 +04:00
log_warn ( " Physical volume %s is missing. " , pvname ) ;
2009-04-23 20:45:30 +04:00
return 1 ;
}
2004-08-18 01:55:23 +04:00
if ( allocatable_only & &
( pvl - > pv - > pe_count = = pvl - > pv - > pe_alloc_count ) ) {
2012-10-16 12:14:41 +04:00
log_warn ( " No free extents on physical volume \" %s \" . " , pvname ) ;
2006-10-22 03:18:43 +04:00
return 1 ;
2004-03-08 20:19:15 +03:00
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl2 , r )
2006-10-22 03:18:43 +04:00
if ( pvl - > pv - > dev = = pvl2 - > pv - > dev ) {
new_pvl = pvl2 ;
break ;
}
2009-10-06 20:00:38 +04:00
2006-10-22 03:18:43 +04:00
if ( ! new_pvl ) {
if ( ! ( new_pvl = dm_pool_alloc ( mem , sizeof ( * new_pvl ) ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Unable to allocate physical volume list. " ) ;
2006-10-22 03:18:43 +04:00
return 0 ;
}
2004-03-08 20:19:15 +03:00
2006-10-22 03:18:43 +04:00
memcpy ( new_pvl , pvl , sizeof ( * new_pvl ) ) ;
2004-03-08 20:19:15 +03:00
2006-10-22 03:18:43 +04:00
if ( ! ( pe_ranges = dm_pool_alloc ( mem , sizeof ( * pe_ranges ) ) ) ) {
log_error ( " Allocation of pe_ranges list failed " ) ;
return 0 ;
}
2008-11-04 01:14:30 +03:00
dm_list_init ( pe_ranges ) ;
2006-10-22 03:18:43 +04:00
new_pvl - > pe_ranges = pe_ranges ;
2008-11-04 01:14:30 +03:00
dm_list_add ( r , & new_pvl - > list ) ;
2004-03-08 20:19:15 +03:00
}
2004-08-18 01:55:23 +04:00
/* Determine selected physical extents */
2007-10-12 18:29:32 +04:00
if ( ! _parse_pes ( mem , colon , new_pvl - > pe_ranges , pv_dev_name ( pvl - > pv ) ,
2008-01-30 16:19:47 +03:00
pvl - > pv - > pe_count ) )
return_0 ;
2004-03-08 20:19:15 +03:00
2006-10-22 03:18:43 +04:00
return 1 ;
2004-03-08 20:19:15 +03:00
}
2008-11-04 01:14:30 +03:00
struct dm_list * create_pv_list ( struct dm_pool * mem , struct volume_group * vg , int argc ,
2004-08-18 01:55:23 +04:00
char * * argv , int allocatable_only )
2002-01-21 19:05:23 +03:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * r ;
2004-03-08 20:19:15 +03:00
struct pv_list * pvl ;
2008-11-04 01:14:30 +03:00
struct dm_list tags , arg_pvnames ;
2010-09-23 16:02:33 +04:00
char * pvname = NULL ;
char * colon , * at_sign , * tagname ;
2002-01-21 19:05:23 +03:00
int i ;
/* Build up list of PVs */
2005-10-17 03:03:59 +04:00
if ( ! ( r = dm_pool_alloc ( mem , sizeof ( * r ) ) ) ) {
2002-01-21 19:05:23 +03:00
log_error ( " Allocation of list failed " ) ;
return NULL ;
}
2008-11-04 01:14:30 +03:00
dm_list_init ( r ) ;
2002-01-21 19:05:23 +03:00
2008-11-04 01:14:30 +03:00
dm_list_init ( & tags ) ;
dm_list_init ( & arg_pvnames ) ;
2004-03-08 20:19:15 +03:00
2002-01-21 19:05:23 +03:00
for ( i = 0 ; i < argc ; i + + ) {
2011-08-30 18:55:15 +04:00
dm_unescape_colons_and_at_signs ( argv [ i ] , & colon , & at_sign ) ;
2010-09-23 16:02:33 +04:00
if ( at_sign & & ( at_sign = = argv [ i ] ) ) {
tagname = at_sign + 1 ;
2010-11-17 13:19:29 +03:00
if ( ! validate_tag ( tagname ) ) {
2004-03-08 20:19:15 +03:00
log_error ( " Skipping invalid tag %s " , tagname ) ;
continue ;
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl , & vg - > pvs ) {
2004-03-08 20:19:15 +03:00
if ( str_list_match_item ( & pvl - > pv - > tags ,
2004-05-19 01:55:55 +04:00
tagname ) ) {
2006-10-22 03:18:43 +04:00
if ( ! _create_pv_entry ( mem , pvl , NULL ,
allocatable_only ,
2008-01-30 16:19:47 +03:00
r ) )
return_NULL ;
2004-03-08 20:19:15 +03:00
}
2003-04-25 02:23:24 +04:00
}
2002-01-21 19:05:23 +03:00
continue ;
}
2004-03-08 20:19:15 +03:00
pvname = argv [ i ] ;
2003-04-25 02:23:24 +04:00
2010-09-23 16:02:33 +04:00
if ( colon & & ! ( pvname = dm_pool_strndup ( mem , pvname ,
( unsigned ) ( colon - pvname ) ) ) ) {
log_error ( " Failed to clone PV name " ) ;
return NULL ;
2003-04-25 02:23:24 +04:00
}
2004-05-11 22:45:11 +04:00
if ( ! ( pvl = find_pv_in_vg ( vg , pvname ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Physical Volume \" %s \" not found in "
" Volume Group \" %s \" " , pvname , vg - > name ) ;
2004-05-11 22:45:11 +04:00
return NULL ;
}
2008-01-30 16:19:47 +03:00
if ( ! _create_pv_entry ( mem , pvl , colon , allocatable_only , r ) )
return_NULL ;
2002-01-21 19:05:23 +03:00
}
2008-11-04 01:14:30 +03:00
if ( dm_list_empty ( r ) )
2004-03-08 20:19:15 +03:00
log_error ( " No specified PVs have space available " ) ;
2008-11-04 01:14:30 +03:00
return dm_list_empty ( r ) ? NULL : r ;
2002-01-21 19:05:23 +03:00
}
2003-04-25 02:23:24 +04:00
2008-11-04 01:14:30 +03:00
struct dm_list * clone_pv_list ( struct dm_pool * mem , struct dm_list * pvsl )
2003-04-25 02:23:24 +04:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * r ;
2003-04-25 02:23:24 +04:00
struct pv_list * pvl , * new_pvl ;
/* Build up list of PVs */
2005-10-17 03:03:59 +04:00
if ( ! ( r = dm_pool_alloc ( mem , sizeof ( * r ) ) ) ) {
2003-04-25 02:23:24 +04:00
log_error ( " Allocation of list failed " ) ;
return NULL ;
}
2008-11-04 01:14:30 +03:00
dm_list_init ( r ) ;
2003-04-25 02:23:24 +04:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl , pvsl ) {
2005-10-17 03:03:59 +04:00
if ( ! ( new_pvl = dm_pool_zalloc ( mem , sizeof ( * new_pvl ) ) ) ) {
2003-04-25 02:23:24 +04:00
log_error ( " Unable to allocate physical volume list. " ) ;
return NULL ;
}
memcpy ( new_pvl , pvl , sizeof ( * new_pvl ) ) ;
2008-11-04 01:14:30 +03:00
dm_list_add ( r , & new_pvl - > list ) ;
2003-04-25 02:23:24 +04:00
}
return r ;
}
2004-06-15 21:23:49 +04:00
2009-11-01 23:02:32 +03:00
void vgcreate_params_set_defaults ( struct vgcreate_params * vp_def ,
struct volume_group * vg )
{
if ( vg ) {
vp_def - > vg_name = NULL ;
vp_def - > extent_size = vg - > extent_size ;
vp_def - > max_pv = vg - > max_pv ;
vp_def - > max_lv = vg - > max_lv ;
vp_def - > alloc = vg - > alloc ;
vp_def - > clustered = vg_is_clustered ( vg ) ;
2010-07-01 00:03:52 +04:00
vp_def - > vgmetadatacopies = vg - > mda_copies ;
2009-11-01 23:02:32 +03:00
} else {
vp_def - > vg_name = NULL ;
vp_def - > extent_size = DEFAULT_EXTENT_SIZE * 2 ;
vp_def - > max_pv = DEFAULT_MAX_PV ;
vp_def - > max_lv = DEFAULT_MAX_LV ;
vp_def - > alloc = DEFAULT_ALLOC_POLICY ;
vp_def - > clustered = DEFAULT_CLUSTERED ;
2010-07-01 00:03:52 +04:00
vp_def - > vgmetadatacopies = DEFAULT_VGMETADATACOPIES ;
2009-11-01 23:02:32 +03:00
}
}
2008-01-15 00:07:58 +03:00
/*
2009-11-01 23:03:24 +03:00
* Set members of struct vgcreate_params from cmdline arguments .
2008-01-15 00:07:58 +03:00
* Do preliminary validation with arg_ * ( ) interface .
* Further , more generic validation is done in validate_vgcreate_params ( ) .
2008-01-16 22:54:39 +03:00
* This function is to remain in tools directory .
2008-01-15 00:07:58 +03:00
*/
2009-11-01 23:03:24 +03:00
int vgcreate_params_set_from_args ( struct cmd_context * cmd ,
struct vgcreate_params * vp_new ,
struct vgcreate_params * vp_def )
2008-01-15 00:07:58 +03:00
{
2009-11-01 23:03:24 +03:00
vp_new - > vg_name = skip_dev_dir ( cmd , vp_def - > vg_name , NULL ) ;
2008-01-16 01:56:30 +03:00
vp_new - > max_lv = arg_uint_value ( cmd , maxlogicalvolumes_ARG ,
vp_def - > max_lv ) ;
vp_new - > max_pv = arg_uint_value ( cmd , maxphysicalvolumes_ARG ,
vp_def - > max_pv ) ;
2012-03-02 01:14:43 +04:00
vp_new - > alloc = ( alloc_policy_t ) arg_uint_value ( cmd , alloc_ARG , vp_def - > alloc ) ;
2008-01-15 00:07:58 +03:00
/* Units of 512-byte sectors */
2008-01-16 01:56:30 +03:00
vp_new - > extent_size =
arg_uint_value ( cmd , physicalextentsize_ARG , vp_def - > extent_size ) ;
2008-01-15 00:07:58 +03:00
if ( arg_count ( cmd , clustered_ARG ) )
2008-01-30 17:00:02 +03:00
vp_new - > clustered =
2008-01-16 01:56:30 +03:00
! strcmp ( arg_str_value ( cmd , clustered_ARG ,
vp_def - > clustered ? " y " : " n " ) , " y " ) ;
2008-01-15 00:07:58 +03:00
else
/* Default depends on current locking type */
2008-01-16 01:56:30 +03:00
vp_new - > clustered = locking_is_clustered ( ) ;
2008-01-15 00:07:58 +03:00
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , physicalextentsize_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2008-01-15 00:07:58 +03:00
log_error ( " Physical extent size may not be negative " ) ;
2012-10-16 12:07:27 +04:00
return 0 ;
2008-01-15 00:07:58 +03:00
}
2011-03-02 23:00:09 +03:00
if ( arg_uint64_value ( cmd , physicalextentsize_ARG , 0 ) > MAX_EXTENT_SIZE ) {
log_error ( " Physical extent size cannot be larger than %s " ,
display_size ( cmd , ( uint64_t ) MAX_EXTENT_SIZE ) ) ;
2012-10-16 12:07:27 +04:00
return 0 ;
2011-03-02 23:00:09 +03:00
}
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , maxlogicalvolumes_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2008-01-15 00:07:58 +03:00
log_error ( " Max Logical Volumes may not be negative " ) ;
2012-10-16 12:07:27 +04:00
return 0 ;
2008-01-15 00:07:58 +03:00
}
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , maxphysicalvolumes_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2008-01-15 00:07:58 +03:00
log_error ( " Max Physical Volumes may not be negative " ) ;
2012-10-16 12:07:27 +04:00
return 0 ;
2008-01-15 00:07:58 +03:00
}
2010-06-29 00:38:23 +04:00
if ( arg_count ( cmd , metadatacopies_ARG ) ) {
2010-07-01 00:03:52 +04:00
vp_new - > vgmetadatacopies = arg_int_value ( cmd , metadatacopies_ARG ,
2010-06-29 00:38:23 +04:00
DEFAULT_VGMETADATACOPIES ) ;
} else if ( arg_count ( cmd , vgmetadatacopies_ARG ) ) {
2010-07-01 00:03:52 +04:00
vp_new - > vgmetadatacopies = arg_int_value ( cmd , vgmetadatacopies_ARG ,
2010-06-29 00:38:23 +04:00
DEFAULT_VGMETADATACOPIES ) ;
} else {
2013-03-05 20:00:43 +04:00
vp_new - > vgmetadatacopies = find_config_tree_int ( cmd , metadata_vgmetadatacopies_CFG ) ;
2010-06-29 00:38:23 +04:00
}
2012-10-16 12:07:27 +04:00
return 1 ;
2008-01-15 00:07:58 +03:00
}
2008-12-19 17:22:48 +03:00
2013-04-11 15:51:08 +04:00
/* Shared code for changing activation state for vgchange/lvchange */
int lv_change_activate ( struct cmd_context * cmd , struct logical_volume * lv ,
activation_change_t activate )
{
2013-04-29 16:04:38 +04:00
if ( ! lv_active_change ( cmd , lv , activate ) )
return_0 ;
2013-04-11 15:51:08 +04:00
if ( background_polling ( ) & &
2013-04-29 16:04:38 +04:00
( activate ! = CHANGE_AN ) & &
( activate ! = CHANGE_ALN ) & &
2013-04-11 15:51:08 +04:00
( lv - > status & ( PVMOVE | CONVERTING | MERGING ) ) )
lv_spawn_background_polling ( cmd , lv ) ;
return 1 ;
}
2008-12-19 17:22:48 +03:00
int lv_refresh ( struct cmd_context * cmd , struct logical_volume * lv )
{
2010-01-06 00:07:31 +03:00
int r = 0 ;
2010-06-03 16:45:05 +04:00
if ( ! cmd - > partial_activation & & ( lv - > status & PARTIAL_LV ) ) {
log_error ( " Refusing refresh of partial LV %s. Use --partial to override. " ,
lv - > name ) ;
goto out ;
}
2010-01-06 00:07:31 +03:00
r = suspend_lv ( cmd , lv ) ;
if ( ! r )
goto_out ;
r = resume_lv ( cmd , lv ) ;
if ( ! r )
goto_out ;
2010-01-13 04:50:34 +03:00
/*
* check if snapshot merge should be polled
* - unfortunately : even though the dev_manager will clear
* the lv ' s merge attributes if a merge is not possible ;
* it is clearing a different instance of the lv ( as
* retrieved with lv_from_lvid )
* - fortunately : polldaemon will immediately shutdown if the
* origin doesn ' t have a status with a snapshot percentage
*/
2010-01-13 04:55:43 +03:00
if ( background_polling ( ) & & lv_is_origin ( lv ) & & lv_is_merging_origin ( lv ) )
2010-01-13 04:50:34 +03:00
lv_spawn_background_polling ( cmd , lv ) ;
2010-01-06 00:07:31 +03:00
out :
return r ;
2008-12-19 17:22:48 +03:00
}
2008-12-22 12:00:51 +03:00
int vg_refresh_visible ( struct cmd_context * cmd , struct volume_group * vg )
{
struct lv_list * lvl ;
int r = 1 ;
2009-10-06 20:00:38 +04:00
2011-09-07 12:41:47 +04:00
sigint_allow ( ) ;
dm_list_iterate_items ( lvl , & vg - > lvs ) {
if ( sigint_caught ( ) )
return_0 ;
2008-12-22 12:00:51 +03:00
if ( lv_is_visible ( lvl - > lv ) )
if ( ! lv_refresh ( cmd , lvl - > lv ) )
r = 0 ;
2011-09-07 12:41:47 +04:00
}
sigint_restore ( ) ;
2009-10-06 20:00:38 +04:00
2008-12-22 12:00:51 +03:00
return r ;
}
2009-09-30 00:22:35 +04:00
void lv_spawn_background_polling ( struct cmd_context * cmd ,
struct logical_volume * lv )
{
const char * pvname ;
2009-09-30 00:33:49 +04:00
if ( ( lv - > status & PVMOVE ) & &
( pvname = get_pvmove_pvname_from_lv_mirr ( lv ) ) ) {
log_verbose ( " Spawning background pvmove process for %s " ,
pvname ) ;
pvmove_poll ( cmd , pvname , 1 ) ;
} else if ( ( lv - > status & LOCKED ) & &
2009-09-30 00:22:35 +04:00
( pvname = get_pvmove_pvname_from_lv ( lv ) ) ) {
log_verbose ( " Spawning background pvmove process for %s " ,
pvname ) ;
pvmove_poll ( cmd , pvname , 1 ) ;
}
2010-01-13 04:56:18 +03:00
if ( lv - > status & ( CONVERTING | MERGING ) ) {
2009-09-30 00:22:35 +04:00
log_verbose ( " Spawning background lvconvert process for %s " ,
lv - > name ) ;
lvconvert_poll ( cmd , lv , 1 ) ;
}
}
2009-10-06 00:03:54 +04:00
/*
* Intial sanity checking of non - recovery related command - line arguments .
*
* Output arguments :
* pp : structure allocated by caller , fields written / validated here
*/
2009-11-01 22:51:54 +03:00
int pvcreate_params_validate ( struct cmd_context * cmd ,
2009-10-06 00:03:54 +04:00
int argc , char * * argv ,
struct pvcreate_params * pp )
{
if ( ! argc ) {
log_error ( " Please enter a physical volume path " ) ;
return 0 ;
}
pp - > yes = arg_count ( cmd , yes_ARG ) ;
2012-03-02 01:14:43 +04:00
pp - > force = ( force_t ) arg_count ( cmd , force_ARG ) ;
2009-10-06 00:03:54 +04:00
if ( arg_int_value ( cmd , labelsector_ARG , 0 ) > = LABEL_SCAN_SECTORS ) {
log_error ( " labelsector must be less than %lu " ,
LABEL_SCAN_SECTORS ) ;
return 0 ;
} else {
pp - > labelsector = arg_int64_value ( cmd , labelsector_ARG ,
DEFAULT_LABELSECTOR ) ;
}
if ( ! ( cmd - > fmt - > features & FMT_MDAS ) & &
2009-10-06 00:55:56 +04:00
( arg_count ( cmd , pvmetadatacopies_ARG ) | |
2009-10-06 00:03:54 +04:00
arg_count ( cmd , metadatasize_ARG ) | |
arg_count ( cmd , dataalignment_ARG ) | |
arg_count ( cmd , dataalignmentoffset_ARG ) ) ) {
log_error ( " Metadata and data alignment parameters only "
" apply to text format. " ) ;
return 0 ;
}
2013-05-28 14:37:22 +04:00
if ( ! ( cmd - > fmt - > features & FMT_BAS ) & &
arg_count ( cmd , bootloaderareasize_ARG ) ) {
log_error ( " Bootloader area parameters only "
2013-02-15 14:14:26 +04:00
" apply to text format. " ) ;
return 0 ;
}
2009-10-06 00:55:56 +04:00
if ( arg_count ( cmd , pvmetadatacopies_ARG ) & &
arg_int_value ( cmd , pvmetadatacopies_ARG , - 1 ) > 2 ) {
2009-10-06 00:03:54 +04:00
log_error ( " Metadatacopies may only be 0, 1 or 2 " ) ;
return 0 ;
}
2013-03-05 20:00:43 +04:00
if ( arg_count ( cmd , metadataignore_ARG ) )
pp - > metadataignore = arg_int_value ( cmd , metadataignore_ARG ,
DEFAULT_PVMETADATAIGNORE ) ;
else
pp - > metadataignore = find_config_tree_bool ( cmd , metadata_pvmetadataignore_CFG ) ;
2010-06-30 16:17:24 +04:00
if ( arg_count ( cmd , pvmetadatacopies_ARG ) & &
! arg_int_value ( cmd , pvmetadatacopies_ARG , - 1 ) & &
2010-07-01 00:03:52 +04:00
pp - > metadataignore ) {
2010-06-30 16:17:24 +04:00
log_error ( " metadataignore only applies to metadatacopies > 0 " ) ;
return 0 ;
}
2009-10-06 00:03:54 +04:00
if ( arg_count ( cmd , zero_ARG ) )
pp - > zero = strcmp ( arg_str_value ( cmd , zero_ARG , " y " ) , " n " ) ;
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , dataalignment_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2009-10-06 00:03:54 +04:00
log_error ( " Physical volume data alignment may not be negative " ) ;
return 0 ;
}
pp - > data_alignment = arg_uint64_value ( cmd , dataalignment_ARG , UINT64_C ( 0 ) ) ;
2012-01-26 01:52:53 +04:00
if ( pp - > data_alignment > UINT32_MAX ) {
2009-10-06 00:03:54 +04:00
log_error ( " Physical volume data alignment is too big. " ) ;
return 0 ;
}
2013-02-18 15:47:31 +04:00
if ( pp - > data_alignment & & pp - > rp . pe_start ! = PV_PE_START_CALC ) {
2013-02-18 13:51:32 +04:00
if ( pp - > rp . pe_start % pp - > data_alignment )
2009-10-06 00:03:54 +04:00
log_warn ( " WARNING: Ignoring data alignment % " PRIu64
" incompatible with --restorefile value (% "
2013-02-18 13:51:32 +04:00
PRIu64 " ). " , pp - > data_alignment , pp - > rp . pe_start ) ;
2009-10-06 00:03:54 +04:00
pp - > data_alignment = 0 ;
}
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , dataalignmentoffset_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2009-10-06 00:03:54 +04:00
log_error ( " Physical volume data alignment offset may not be negative " ) ;
return 0 ;
}
pp - > data_alignment_offset = arg_uint64_value ( cmd , dataalignmentoffset_ARG , UINT64_C ( 0 ) ) ;
2012-01-26 01:52:53 +04:00
if ( pp - > data_alignment_offset > UINT32_MAX ) {
2009-10-06 00:03:54 +04:00
log_error ( " Physical volume data alignment offset is too big. " ) ;
return 0 ;
}
2013-02-18 15:47:31 +04:00
if ( pp - > data_alignment_offset & & pp - > rp . pe_start ! = PV_PE_START_CALC ) {
2009-10-06 00:03:54 +04:00
log_warn ( " WARNING: Ignoring data alignment offset % " PRIu64
" incompatible with --restorefile value (% "
2013-02-18 13:51:32 +04:00
PRIu64 " ). " , pp - > data_alignment_offset , pp - > rp . pe_start ) ;
2009-10-06 00:03:54 +04:00
pp - > data_alignment_offset = 0 ;
}
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , metadatasize_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2009-10-06 00:03:54 +04:00
log_error ( " Metadata size may not be negative " ) ;
return 0 ;
}
2013-05-28 14:37:22 +04:00
if ( arg_sign_value ( cmd , bootloaderareasize_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
log_error ( " Bootloader area size may not be negative " ) ;
2013-02-15 14:14:26 +04:00
return 0 ;
}
2009-10-06 00:03:54 +04:00
pp - > pvmetadatasize = arg_uint64_value ( cmd , metadatasize_ARG , UINT64_C ( 0 ) ) ;
if ( ! pp - > pvmetadatasize )
2013-03-05 20:00:43 +04:00
pp - > pvmetadatasize = find_config_tree_int ( cmd , metadata_pvmetadatasize_CFG ) ;
2009-10-06 00:03:54 +04:00
2009-10-06 00:55:56 +04:00
pp - > pvmetadatacopies = arg_int_value ( cmd , pvmetadatacopies_ARG , - 1 ) ;
2009-10-06 00:03:54 +04:00
if ( pp - > pvmetadatacopies < 0 )
2013-03-05 20:00:43 +04:00
pp - > pvmetadatacopies = find_config_tree_int ( cmd , metadata_pvmetadatacopies_CFG ) ;
2009-10-06 00:03:54 +04:00
2013-05-28 14:37:22 +04:00
pp - > rp . ba_size = arg_uint64_value ( cmd , bootloaderareasize_ARG , pp - > rp . ba_size ) ;
2013-02-15 14:14:26 +04:00
2009-10-06 00:03:54 +04:00
return 1 ;
}
2010-03-24 01:30:18 +03:00
int get_activation_monitoring_mode ( struct cmd_context * cmd ,
int * monitoring_mode )
{
* monitoring_mode = DEFAULT_DMEVENTD_MONITOR ;
if ( arg_count ( cmd , monitor_ARG ) & &
2010-05-06 15:15:55 +04:00
( arg_count ( cmd , ignoremonitoring_ARG ) | |
arg_count ( cmd , sysinit_ARG ) ) ) {
log_error ( " --ignoremonitoring or --sysinit option not allowed with --monitor option " ) ;
2010-03-24 01:30:18 +03:00
return 0 ;
}
if ( arg_count ( cmd , monitor_ARG ) )
* monitoring_mode = arg_int_value ( cmd , monitor_ARG ,
DEFAULT_DMEVENTD_MONITOR ) ;
else if ( is_static ( ) | | arg_count ( cmd , ignoremonitoring_ARG ) | |
2010-05-06 15:15:55 +04:00
arg_count ( cmd , sysinit_ARG ) | |
2013-03-05 20:00:43 +04:00
! find_config_tree_bool ( cmd , activation_monitoring_CFG ) )
2010-03-24 01:30:18 +03:00
* monitoring_mode = DMEVENTD_MONITOR_IGNORE ;
2010-03-29 20:09:40 +04:00
2010-03-24 01:30:18 +03:00
return 1 ;
}
2010-04-13 05:54:32 +04:00
2013-03-06 14:58:09 +04:00
int get_pool_params ( struct cmd_context * cmd , int * passed_args ,
2012-11-15 17:48:32 +04:00
uint32_t * chunk_size ,
thin_discards_t * discards ,
uint64_t * pool_metadata_size ,
int * zero )
{
2012-11-26 14:20:13 +04:00
const char * dstr ;
2012-11-15 17:48:32 +04:00
2013-03-06 14:58:09 +04:00
* passed_args = 0 ;
2012-11-26 14:20:13 +04:00
if ( arg_count ( cmd , zero_ARG ) ) {
2013-03-06 14:58:09 +04:00
* passed_args | = PASS_ARG_ZERO ;
2012-11-26 14:20:13 +04:00
* zero = strcmp ( arg_str_value ( cmd , zero_ARG , " y " ) , " n " ) ;
log_very_verbose ( " Setting pool zeroing: %u " , * zero ) ;
} else
2013-03-05 20:00:43 +04:00
* zero = find_config_tree_bool ( cmd , allocation_thin_pool_zero_CFG ) ;
2012-11-26 14:20:13 +04:00
if ( arg_count ( cmd , discards_ARG ) ) {
2013-03-06 14:58:09 +04:00
* passed_args | = PASS_ARG_DISCARDS ;
2012-11-26 14:20:13 +04:00
* discards = ( thin_discards_t ) arg_uint_value ( cmd , discards_ARG , 0 ) ;
log_very_verbose ( " Setting pool discards: %s " ,
get_pool_discards_name ( * discards ) ) ;
} else {
2013-03-05 20:00:43 +04:00
dstr = find_config_tree_str ( cmd , allocation_thin_pool_discards_CFG ) ;
2012-11-26 14:20:13 +04:00
if ( ! get_pool_discards ( dstr , discards ) )
return_0 ;
2012-11-15 17:48:32 +04:00
}
2012-11-26 14:20:13 +04:00
if ( arg_count ( cmd , chunksize_ARG ) ) {
if ( arg_sign_value ( cmd , chunksize_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
log_error ( " Negative chunk size is invalid. " ) ;
return 0 ;
}
2013-03-06 14:58:09 +04:00
* passed_args | = PASS_ARG_CHUNK_SIZE ;
2012-11-26 14:20:13 +04:00
* chunk_size = arg_uint_value ( cmd , chunksize_ARG ,
DM_THIN_MIN_DATA_BLOCK_SIZE ) ;
log_very_verbose ( " Setting pool chunk size: %s " ,
display_size ( cmd , * chunk_size ) ) ;
} else
2013-03-05 20:00:43 +04:00
* chunk_size = find_config_tree_int ( cmd , allocation_thin_pool_chunk_size_CFG ) * 2 ;
2012-11-15 17:48:32 +04:00
if ( ( * chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE ) | |
( * chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE ) ) {
log_error ( " Chunk size must be in the range %s to %s. " ,
display_size ( cmd , DM_THIN_MIN_DATA_BLOCK_SIZE ) ,
display_size ( cmd , DM_THIN_MAX_DATA_BLOCK_SIZE ) ) ;
return 0 ;
}
2013-03-06 14:58:09 +04:00
if ( arg_count ( cmd , poolmetadatasize_ARG ) ) {
if ( arg_sign_value ( cmd , poolmetadatasize_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
log_error ( " Negative pool metadata size is invalid. " ) ;
return 0 ;
}
* passed_args | = PASS_ARG_POOL_METADATA_SIZE ;
2012-11-15 17:48:32 +04:00
}
* pool_metadata_size = arg_uint64_value ( cmd , poolmetadatasize_ARG , UINT64_C ( 0 ) ) ;
return 1 ;
}
2012-11-19 16:37:36 +04:00
struct logical_volume * alloc_pool_metadata ( struct logical_volume * pool_lv ,
alloc_policy_t alloc ,
const char * name ,
struct dm_list * pvh ,
uint32_t read_ahead ,
uint32_t stripes ,
uint32_t stripe_size ,
uint64_t size )
{
struct logical_volume * metadata_lv ;
struct lvcreate_params lvc ;
/* FIXME: Make lvm2api usable */
memset ( & lvc , 0 , sizeof ( lvc ) ) ;
if ( ! ( lvc . extents = extents_from_size ( pool_lv - > vg - > cmd , size ,
pool_lv - > vg - > extent_size ) ) )
return_0 ;
if ( ! ( lvc . segtype = get_segtype_from_string ( pool_lv - > vg - > cmd , " striped " ) ) )
return_0 ;
dm_list_init ( & lvc . tags ) ;
/* FIXME: allocate properly space for metadata_lv */
lvc . activate = CHANGE_ALY ;
lvc . alloc = alloc ;
lvc . lv_name = name ;
lvc . major = - 1 ;
lvc . minor = - 1 ;
lvc . permission = LVM_READ | LVM_WRITE ;
lvc . pvh = pvh ;
lvc . read_ahead = read_ahead ;
lvc . stripe_size = stripe_size ;
lvc . stripes = stripes ;
lvc . vg_name = pool_lv - > vg - > name ;
lvc . zero = 1 ;
if ( ! ( metadata_lv = lv_create_single ( pool_lv - > vg , & lvc ) ) )
return_0 ;
return metadata_lv ;
}
2010-04-13 05:54:32 +04:00
/*
* Generic stripe parameter checks .
*/
static int _validate_stripe_params ( struct cmd_context * cmd , uint32_t * stripes ,
uint32_t * stripe_size )
{
if ( * stripes = = 1 & & * stripe_size ) {
config: add silent mode
Accept -q as the short form of --quiet.
Suppress non-essential standard output if -q is given twice.
Treat log/silent in lvm.conf as equivalent to -qq.
Review all log_print messages and change some to
log_print_unless_silent.
When silent, the following commands still produce output:
dumpconfig, lvdisplay, lvmdiskscan, lvs, pvck, pvdisplay,
pvs, version, vgcfgrestore -l, vgdisplay, vgs.
[Needs checking.]
Non-essential messages are shifted from log level 4 to log level 5
for syslog and lvm2_log_fn purposes.
2012-08-25 23:35:48 +04:00
log_print_unless_silent ( " Ignoring stripesize argument with single stripe " ) ;
2010-04-13 05:54:32 +04:00
* stripe_size = 0 ;
}
if ( * stripes > 1 & & ! * stripe_size ) {
2013-03-05 20:00:43 +04:00
* stripe_size = find_config_tree_int ( cmd , metadata_stripesize_CFG ) * 2 ;
config: add silent mode
Accept -q as the short form of --quiet.
Suppress non-essential standard output if -q is given twice.
Treat log/silent in lvm.conf as equivalent to -qq.
Review all log_print messages and change some to
log_print_unless_silent.
When silent, the following commands still produce output:
dumpconfig, lvdisplay, lvmdiskscan, lvs, pvck, pvdisplay,
pvs, version, vgcfgrestore -l, vgdisplay, vgs.
[Needs checking.]
Non-essential messages are shifted from log level 4 to log level 5
for syslog and lvm2_log_fn purposes.
2012-08-25 23:35:48 +04:00
log_print_unless_silent ( " Using default stripesize %s " ,
2010-04-13 05:54:32 +04:00
display_size ( cmd , ( uint64_t ) * stripe_size ) ) ;
}
if ( * stripes < 1 | | * stripes > MAX_STRIPES ) {
log_error ( " Number of stripes (%d) must be between %d and %d " ,
* stripes , 1 , MAX_STRIPES ) ;
return 0 ;
}
if ( * stripes > 1 & & ( * stripe_size < STRIPE_SIZE_MIN | |
* stripe_size & ( * stripe_size - 1 ) ) ) {
log_error ( " Invalid stripe size %s " ,
display_size ( cmd , ( uint64_t ) * stripe_size ) ) ;
return 0 ;
}
return 1 ;
}
/*
* The stripe size is limited by the size of a uint32_t , but since the
* value given by the user is doubled , and the final result must be a
* power of 2 , we must divide UINT_MAX by four and add 1 ( to round it
* up to the power of 2 )
*/
int get_stripe_params ( struct cmd_context * cmd , uint32_t * stripes , uint32_t * stripe_size )
{
/* stripes_long_ARG takes precedence (for lvconvert) */
2011-01-24 16:38:31 +03:00
* stripes = arg_uint_value ( cmd , arg_count ( cmd , stripes_long_ARG ) ? stripes_long_ARG : stripes_ARG , 1 ) ;
2010-04-13 05:54:32 +04:00
* stripe_size = arg_uint_value ( cmd , stripesize_ARG , 0 ) ;
if ( * stripe_size ) {
2012-02-28 18:24:57 +04:00
if ( arg_sign_value ( cmd , stripesize_ARG , SIGN_NONE ) = = SIGN_MINUS ) {
2010-04-13 05:54:32 +04:00
log_error ( " Negative stripesize is invalid " ) ;
return 0 ;
}
2011-03-02 19:56:06 +03:00
if ( arg_uint64_value ( cmd , stripesize_ARG , 0 ) > STRIPE_SIZE_LIMIT * 2 ) {
2010-04-13 05:54:32 +04:00
log_error ( " Stripe size cannot be larger than %s " ,
display_size ( cmd , ( uint64_t ) STRIPE_SIZE_LIMIT ) ) ;
return 0 ;
}
}
return _validate_stripe_params ( cmd , stripes , stripe_size ) ;
}
2011-01-24 16:38:31 +03:00
/* FIXME move to lib */
static int _pv_change_tag ( struct physical_volume * pv , const char * tag , int addtag )
{
if ( addtag ) {
if ( ! str_list_add ( pv - > fmt - > cmd - > mem , & pv - > tags , tag ) ) {
log_error ( " Failed to add tag %s to physical volume %s " ,
tag , pv_dev_name ( pv ) ) ;
return 0 ;
}
2012-02-08 16:52:58 +04:00
} else
str_list_del ( & pv - > tags , tag ) ;
2011-01-24 16:38:31 +03:00
return 1 ;
}
/* Set exactly one of VG, LV or PV */
int change_tag ( struct cmd_context * cmd , struct volume_group * vg ,
struct logical_volume * lv , struct physical_volume * pv , int arg )
{
const char * tag ;
struct arg_value_group_list * current_group ;
dm_list_iterate_items ( current_group , & cmd - > arg_value_groups ) {
if ( ! grouped_arg_is_set ( current_group - > arg_values , arg ) )
continue ;
if ( ! ( tag = grouped_arg_str_value ( current_group - > arg_values , arg , NULL ) ) ) {
log_error ( " Failed to get tag " ) ;
return 0 ;
}
if ( vg & & ! vg_change_tag ( vg , tag , arg = = addtag_ARG ) )
return_0 ;
else if ( lv & & ! lv_change_tag ( lv , tag , arg = = addtag_ARG ) )
return_0 ;
else if ( pv & & ! _pv_change_tag ( pv , tag , arg = = addtag_ARG ) )
return_0 ;
}
return 1 ;
}
2011-09-15 19:26:40 +04:00
2012-01-05 19:38:18 +04:00
/* Return percents of extents and avoid overflow, with optional roundup */
uint32_t percent_of_extents ( uint32_t percents , uint32_t count , int roundup )
2011-09-15 19:26:40 +04:00
{
2012-01-05 19:38:18 +04:00
return ( uint32_t ) ( ( ( uint64_t ) percents * ( uint64_t ) count +
( ( roundup ) ? 99 : 0 ) ) / 100 ) ;
2011-09-15 19:26:40 +04:00
}