2020-01-14 23:12:20 +03:00
/*
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 - 2017 Red Hat , Inc . All rights reserved .
*
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include "lib/misc/lib.h"
# include "lib/misc/lvm-string.h"
# include "lib/datastruct/str_list.h"
# include "lib/device/device.h"
# include "lib/metadata/metadata.h"
/*
* Process physical extent range specifiers
*/
static int _add_pe_range ( struct dm_pool * mem , const char * pvname ,
struct dm_list * pe_ranges , uint32_t start , uint32_t count )
{
struct pe_range * per ;
log_debug ( " Adding PE range: start PE " FMTu32 " length " FMTu32 " on %s. " ,
start , count , pvname ) ;
/* Ensure no overlap with existing areas */
dm_list_iterate_items ( per , pe_ranges ) {
if ( ( ( start < per - > start ) & & ( start + count - 1 > = per - > start ) ) | |
( ( start > = per - > start ) & &
( per - > start + per - > count - 1 ) > = start ) ) {
log_error ( " Overlapping PE ranges specified ( " FMTu32
" - " FMTu32 " , " FMTu32 " - " FMTu32 " ) on %s. " ,
start , start + count - 1 , per - > start ,
per - > start + per - > count - 1 , pvname ) ;
return 0 ;
}
}
if ( ! ( per = dm_pool_alloc ( mem , sizeof ( * per ) ) ) ) {
log_error ( " Allocation of list failed. " ) ;
return 0 ;
}
per - > start = start ;
per - > count = count ;
dm_list_add ( pe_ranges , & per - > list ) ;
return 1 ;
}
static int _xstrtouint32 ( const char * s , char * * p , int base , uint32_t * result )
{
unsigned long ul ;
errno = 0 ;
ul = strtoul ( s , p , base ) ;
if ( errno | | * p = = s | | ul > UINT32_MAX )
return 0 ;
* result = ul ;
return 1 ;
}
static int _parse_pes ( struct dm_pool * mem , char * c , struct dm_list * pe_ranges ,
const char * pvname , uint32_t size )
{
char * endptr ;
uint32_t start , end , len ;
/* Default to whole PV */
if ( ! c ) {
if ( ! _add_pe_range ( mem , pvname , pe_ranges , UINT32_C ( 0 ) , size ) )
return_0 ;
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 ) ) {
if ( ! _xstrtouint32 ( c , & endptr , 10 , & start ) )
goto error ;
c = endptr ;
/* Just one number given? */
if ( ! * c | | * c = = ' : ' )
end = start ;
}
/* Range? */
if ( * c = = ' - ' ) {
c + + ;
if ( isdigit ( * c ) ) {
if ( ! _xstrtouint32 ( c , & endptr , 10 , & end ) )
goto error ;
c = endptr ;
}
} else if ( * c = = ' + ' ) { /* Length? */
c + + ;
if ( isdigit ( * c ) ) {
if ( ! _xstrtouint32 ( c , & endptr , 10 , & len ) )
goto error ;
c = endptr ;
end = start + ( len ? ( len - 1 ) : 0 ) ;
}
}
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 ;
}
if ( ! _add_pe_range ( mem , pvname , pe_ranges , start , end - start + 1 ) )
return_0 ;
}
return 1 ;
error :
log_error ( " Physical extent parsing error at %s. " , c ) ;
return 0 ;
}
static int _create_pv_entry ( struct dm_pool * mem , struct pv_list * pvl ,
char * colon , int allocatable_only , struct dm_list * r )
{
const char * pvname ;
struct pv_list * new_pvl = NULL , * pvl2 ;
struct dm_list * pe_ranges ;
pvname = pv_dev_name ( pvl - > pv ) ;
if ( allocatable_only & & ! ( pvl - > pv - > status & ALLOCATABLE_PV ) ) {
log_warn ( " WARNING: Physical volume %s not allocatable. " , pvname ) ;
return 1 ;
}
if ( allocatable_only & & is_missing_pv ( pvl - > pv ) ) {
log_warn ( " WARNING: Physical volume %s is missing. " , pvname ) ;
return 1 ;
}
if ( allocatable_only & &
( pvl - > pv - > pe_count = = pvl - > pv - > pe_alloc_count ) ) {
log_warn ( " WARNING: No free extents on physical volume \" %s \" . " , pvname ) ;
return 1 ;
}
dm_list_iterate_items ( pvl2 , r )
if ( pvl - > pv - > dev = = pvl2 - > pv - > dev ) {
new_pvl = pvl2 ;
break ;
}
if ( ! new_pvl ) {
if ( ! ( new_pvl = dm_pool_alloc ( mem , sizeof ( * new_pvl ) ) ) ) {
log_error ( " Unable to allocate physical volume list. " ) ;
return 0 ;
}
memcpy ( new_pvl , pvl , sizeof ( * new_pvl ) ) ;
if ( ! ( pe_ranges = dm_pool_alloc ( mem , sizeof ( * pe_ranges ) ) ) ) {
log_error ( " Allocation of pe_ranges list failed. " ) ;
return 0 ;
}
dm_list_init ( pe_ranges ) ;
new_pvl - > pe_ranges = pe_ranges ;
dm_list_add ( r , & new_pvl - > list ) ;
}
/* Determine selected physical extents */
if ( ! _parse_pes ( mem , colon , new_pvl - > pe_ranges , pv_dev_name ( pvl - > pv ) ,
pvl - > pv - > pe_count ) )
return_0 ;
return 1 ;
}
struct dm_list * create_pv_list ( struct dm_pool * mem , struct volume_group * vg , int argc ,
char * * argv , int allocatable_only )
{
struct dm_list * r ;
struct pv_list * pvl ;
struct dm_list tagsl , arg_pvnames ;
char * pvname = NULL ;
char * colon , * at_sign , * tagname ;
int i ;
/* Build up list of PVs */
if ( ! ( r = dm_pool_alloc ( mem , sizeof ( * r ) ) ) ) {
log_error ( " Allocation of list failed. " ) ;
return NULL ;
}
dm_list_init ( r ) ;
dm_list_init ( & tagsl ) ;
dm_list_init ( & arg_pvnames ) ;
for ( i = 0 ; i < argc ; i + + ) {
dm_unescape_colons_and_at_signs ( argv [ i ] , & colon , & at_sign ) ;
if ( at_sign & & ( at_sign = = argv [ i ] ) ) {
tagname = at_sign + 1 ;
if ( ! validate_tag ( tagname ) ) {
log_error ( " Skipping invalid tag %s. " , tagname ) ;
continue ;
}
dm_list_iterate_items ( pvl , & vg - > pvs ) {
if ( str_list_match_item ( & pvl - > pv - > tags ,
tagname ) ) {
if ( ! _create_pv_entry ( mem , pvl , NULL ,
allocatable_only ,
r ) )
return_NULL ;
}
}
continue ;
}
pvname = argv [ i ] ;
if ( colon & & ! ( pvname = dm_pool_strndup ( mem , pvname ,
( unsigned ) ( colon - pvname ) ) ) ) {
log_error ( " Failed to clone PV name. " ) ;
return NULL ;
}
if ( ! ( pvl = find_pv_in_vg ( vg , pvname ) ) ) {
log_error ( " Physical Volume \" %s \" not found in "
" Volume Group \" %s \" . " , pvname , vg - > name ) ;
return NULL ;
}
if ( ! _create_pv_entry ( mem , pvl , colon , allocatable_only , r ) )
return_NULL ;
}
2021-03-09 13:42:29 +03:00
if ( dm_list_empty ( r ) ) {
2020-01-14 23:12:20 +03:00
log_error ( " No specified PVs have space available. " ) ;
2021-03-09 13:42:29 +03:00
return NULL ;
}
2020-01-14 23:12:20 +03:00
2021-03-09 13:42:29 +03:00
return r ;
2020-01-14 23:12:20 +03:00
}
struct dm_list * clone_pv_list ( struct dm_pool * mem , struct dm_list * pvsl )
{
struct dm_list * r ;
struct pv_list * pvl , * new_pvl ;
/* Build up list of PVs */
if ( ! ( r = dm_pool_alloc ( mem , sizeof ( * r ) ) ) ) {
log_error ( " Allocation of list failed. " ) ;
return NULL ;
}
dm_list_init ( r ) ;
dm_list_iterate_items ( pvl , pvsl ) {
2021-03-09 13:42:29 +03:00
if ( ! ( new_pvl = dm_pool_alloc ( mem , sizeof ( * new_pvl ) ) ) ) {
2020-01-14 23:12:20 +03:00
log_error ( " Unable to allocate physical volume list. " ) ;
return NULL ;
}
memcpy ( new_pvl , pvl , sizeof ( * new_pvl ) ) ;
dm_list_add ( r , & new_pvl - > list ) ;
}
return r ;
}