2019-11-21 01:07:27 +03:00
/*
* Copyright ( C ) 2014 - 2015 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/metadata/metadata.h"
# include "lib/locking/locking.h"
# include "lib/misc/lvm-string.h"
# include "lib/commands/toolcontext.h"
# include "lib/display/display.h"
# include "lib/metadata/segtype.h"
# include "lib/activate/activate.h"
# include "lib/config/defaults.h"
# define DEFAULT_TAG_SIZE 4 /* bytes */
# define DEFAULT_MODE 'J'
# define DEFAULT_INTERNAL_HASH "crc32c"
# define DEFAULT_BLOCK_SIZE 512
# define ONE_MB_IN_BYTES 1048576
2020-06-30 20:52:27 +03:00
# define ONE_GB_IN_BYTES 1073741824
2019-11-21 01:07:27 +03:00
int lv_is_integrity_origin ( const struct logical_volume * lv )
{
struct seg_list * sl ;
dm_list_iterate_items ( sl , & lv - > segs_using_this_lv ) {
if ( ! sl - > seg | | ! sl - > seg - > lv | | ! sl - > seg - > origin )
continue ;
if ( lv_is_integrity ( sl - > seg - > lv ) & & ( sl - > seg - > origin = = lv ) )
return 1 ;
}
return 0 ;
}
/*
* Every 500 M of data needs 4 M of metadata .
* ( From trial and error testing . )
2020-06-30 20:52:27 +03:00
*
* plus some initial space for journals .
* ( again from trial and error testing . )
2019-11-21 01:07:27 +03:00
*/
static uint64_t _lv_size_bytes_to_integrity_meta_bytes ( uint64_t lv_size_bytes )
{
2020-06-30 20:52:27 +03:00
uint64_t meta_bytes ;
uint64_t initial_bytes ;
/* Every 500M of data needs 4M of metadata. */
meta_bytes = ( ( lv_size_bytes / ( 500 * ONE_MB_IN_BYTES ) ) + 1 ) * ( 4 * ONE_MB_IN_BYTES ) ;
/*
* initial space used for journals
* lv_size < = 512 M - > 4 M
* lv_size < = 1 G - > 8 M
* lv_size < = 4 G - > 32 M
* lv_size > 4 G - > 64 M
*/
if ( lv_size_bytes < = ( 512 * ONE_MB_IN_BYTES ) )
initial_bytes = 4 * ONE_MB_IN_BYTES ;
else if ( lv_size_bytes < = ONE_GB_IN_BYTES )
initial_bytes = 8 * ONE_MB_IN_BYTES ;
else if ( lv_size_bytes < = ( 4ULL * ONE_GB_IN_BYTES ) )
initial_bytes = 32 * ONE_MB_IN_BYTES ;
else if ( lv_size_bytes > ( 4ULL * ONE_GB_IN_BYTES ) )
initial_bytes = 64 * ONE_MB_IN_BYTES ;
return meta_bytes + initial_bytes ;
2019-11-21 01:07:27 +03:00
}
/*
* The user wants external metadata , but did not specify an existing
* LV to hold metadata , so create an LV for metadata .
*/
static int _lv_create_integrity_metadata ( struct cmd_context * cmd ,
struct volume_group * vg ,
struct lvcreate_params * lp ,
struct logical_volume * * meta_lv )
{
2021-09-15 15:07:19 +03:00
char metaname [ NAME_LEN ] = { 0 } ;
2019-11-21 01:07:27 +03:00
uint64_t lv_size_bytes , meta_bytes , meta_sectors ;
struct logical_volume * lv ;
struct lvcreate_params lp_meta = {
. activate = CHANGE_AN ,
. alloc = ALLOC_INHERIT ,
. major = - 1 ,
. minor = - 1 ,
. permission = LVM_READ | LVM_WRITE ,
. pvh = & vg - > pvs ,
. read_ahead = DM_READ_AHEAD_NONE ,
. stripes = 1 ,
. vg_name = vg - > name ,
2023-02-25 22:29:05 +03:00
. temporary = 1 ,
2019-11-21 01:07:27 +03:00
. zero = 0 ,
. wipe_signatures = 0 ,
. suppress_zero_warn = 1 ,
} ;
if ( lp - > lv_name & &
dm_snprintf ( metaname , NAME_LEN , " %s_imeta " , lp - > lv_name ) < 0 ) {
log_error ( " Failed to create metadata LV name. " ) ;
return 0 ;
}
lp_meta . lv_name = metaname ;
lp_meta . pvh = lp - > pvh ;
lv_size_bytes = ( uint64_t ) lp - > extents * ( uint64_t ) vg - > extent_size * 512 ;
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes ( lv_size_bytes ) ;
meta_sectors = meta_bytes / 512 ;
lp_meta . extents = meta_sectors / vg - > extent_size ;
2023-02-25 22:29:05 +03:00
log_verbose ( " Creating integrity metadata LV %s with size %s. " ,
metaname , display_size ( cmd , meta_sectors ) ) ;
2019-11-21 01:07:27 +03:00
dm_list_init ( & lp_meta . tags ) ;
if ( ! ( lp_meta . segtype = get_segtype_from_string ( vg - > cmd , SEG_TYPE_NAME_STRIPED ) ) )
return_0 ;
if ( ! ( lv = lv_create_single ( vg , & lp_meta ) ) ) {
log_error ( " Failed to create integrity metadata LV " ) ;
return 0 ;
}
if ( dm_list_size ( & lv - > segments ) > 1 ) {
log_error ( " Integrity metadata uses more than one segment. " ) ;
return 0 ;
}
* meta_lv = lv ;
return 1 ;
}
int lv_extend_integrity_in_raid ( struct logical_volume * lv , struct dm_list * pvh )
{
struct cmd_context * cmd = lv - > vg - > cmd ;
struct volume_group * vg = lv - > vg ;
const struct segment_type * segtype ;
struct lv_segment * seg_top , * seg_image ;
struct logical_volume * lv_image ;
struct logical_volume * lv_iorig ;
struct logical_volume * lv_imeta ;
struct dm_list allocatable_pvs ;
struct dm_list * use_pvh ;
uint64_t lv_size_bytes , meta_bytes , meta_sectors , prev_meta_sectors ;
uint32_t meta_extents , prev_meta_extents ;
uint32_t area_count , s ;
2020-09-02 20:40:45 +03:00
if ( ! lv_is_raid ( lv ) )
return_0 ;
2019-11-21 01:07:27 +03:00
seg_top = first_seg ( lv ) ;
if ( ! ( segtype = get_segtype_from_string ( cmd , SEG_TYPE_NAME_STRIPED ) ) )
return_0 ;
area_count = seg_top - > area_count ;
for ( s = 0 ; s < area_count ; s + + ) {
lv_image = seg_lv ( seg_top , s ) ;
seg_image = first_seg ( lv_image ) ;
if ( ! ( lv_imeta = seg_image - > integrity_meta_dev ) ) {
log_error ( " LV %s segment has no integrity metadata device. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! ( lv_iorig = seg_lv ( seg_image , 0 ) ) ) {
log_error ( " LV %s integrity segment has no origin " , display_lvname ( lv ) ) ;
return 0 ;
}
lv_size_bytes = lv_iorig - > size * 512 ;
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes ( lv_size_bytes ) ;
meta_sectors = meta_bytes / 512 ;
meta_extents = meta_sectors / vg - > extent_size ;
prev_meta_sectors = lv_imeta - > size ;
prev_meta_extents = prev_meta_sectors / vg - > extent_size ;
if ( meta_extents < = prev_meta_extents ) {
log_debug ( " extend not needed for imeta LV %s " , lv_imeta - > name ) ;
continue ;
}
/*
* We only allow lv_imeta to exist on a single PV ( for now ) ,
* so the allocatable_pvs is the one PV currently used by
* lv_imeta .
*/
dm_list_init ( & allocatable_pvs ) ;
if ( ! get_pv_list_for_lv ( cmd - > mem , lv_imeta , & allocatable_pvs ) ) {
log_error ( " Failed to build list of PVs for extending %s. " , display_lvname ( lv_imeta ) ) ;
return 0 ;
}
use_pvh = & allocatable_pvs ;
if ( ! lv_extend ( lv_imeta , segtype , 1 , 0 , 0 , 0 ,
meta_extents - prev_meta_extents ,
use_pvh , lv_imeta - > alloc , 0 ) ) {
log_error ( " Failed to extend integrity metadata LV %s " , lv_imeta - > name ) ;
return 0 ;
}
}
return 1 ;
}
int lv_remove_integrity_from_raid ( struct logical_volume * lv )
{
struct logical_volume * iorig_lvs [ DEFAULT_RAID_MAX_IMAGES ] ;
struct logical_volume * imeta_lvs [ DEFAULT_RAID_MAX_IMAGES ] ;
struct cmd_context * cmd = lv - > vg - > cmd ;
struct volume_group * vg = lv - > vg ;
struct lv_segment * seg_top , * seg_image ;
struct logical_volume * lv_image ;
struct logical_volume * lv_iorig ;
struct logical_volume * lv_imeta ;
uint32_t area_count , s ;
int is_active = lv_is_active ( lv ) ;
seg_top = first_seg ( lv ) ;
if ( ! seg_is_raid1 ( seg_top ) & & ! seg_is_raid4 ( seg_top ) & &
! seg_is_any_raid5 ( seg_top ) & & ! seg_is_any_raid6 ( seg_top ) & &
! seg_is_any_raid10 ( seg_top ) ) {
log_error ( " LV %s segment is unsupported raid for integrity. " , display_lvname ( lv ) ) ;
return 0 ;
}
area_count = seg_top - > area_count ;
for ( s = 0 ; s < area_count ; s + + ) {
lv_image = seg_lv ( seg_top , s ) ;
seg_image = first_seg ( lv_image ) ;
if ( ! ( lv_imeta = seg_image - > integrity_meta_dev ) ) {
log_error ( " LV %s segment has no integrity metadata device. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! ( lv_iorig = seg_lv ( seg_image , 0 ) ) ) {
log_error ( " LV %s integrity segment has no origin " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! remove_seg_from_segs_using_this_lv ( seg_image - > integrity_meta_dev , seg_image ) )
return_0 ;
iorig_lvs [ s ] = lv_iorig ;
imeta_lvs [ s ] = lv_imeta ;
lv_image - > status & = ~ INTEGRITY ;
seg_image - > integrity_meta_dev = NULL ;
seg_image - > integrity_data_sectors = 0 ;
memset ( & seg_image - > integrity_settings , 0 , sizeof ( seg_image - > integrity_settings ) ) ;
if ( ! remove_layer_from_lv ( lv_image , lv_iorig ) )
return_0 ;
}
if ( is_active ) {
/* vg_write(), suspend_lv(), vg_commit(), resume_lv() */
if ( ! lv_update_and_reload ( lv ) ) {
log_error ( " Failed to update and reload LV after integrity remove. " ) ;
return 0 ;
}
}
for ( s = 0 ; s < area_count ; s + + ) {
lv_iorig = iorig_lvs [ s ] ;
lv_imeta = imeta_lvs [ s ] ;
if ( is_active ) {
if ( ! deactivate_lv ( cmd , lv_iorig ) )
log_error ( " Failed to deactivate unused iorig LV %s. " , lv_iorig - > name ) ;
if ( ! deactivate_lv ( cmd , lv_imeta ) )
log_error ( " Failed to deactivate unused imeta LV %s. " , lv_imeta - > name ) ;
}
lv_imeta - > status & = ~ INTEGRITY_METADATA ;
lv_set_visible ( lv_imeta ) ;
if ( ! lv_remove ( lv_iorig ) )
log_error ( " Failed to remove unused iorig LV %s. " , lv_iorig - > name ) ;
if ( ! lv_remove ( lv_imeta ) )
log_error ( " Failed to remove unused imeta LV %s. " , lv_imeta - > name ) ;
}
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) )
return_0 ;
return 1 ;
}
2020-09-01 20:53:00 +03:00
int integrity_mode_set ( const char * mode , struct integrity_settings * settings )
{
if ( ! mode )
settings - > mode [ 0 ] = DEFAULT_MODE ;
else if ( ! strcmp ( mode , " bitmap " ) | | ! strcmp ( mode , " B " ) )
settings - > mode [ 0 ] = ' B ' ;
else if ( ! strcmp ( mode , " journal " ) | | ! strcmp ( mode , " J " ) )
settings - > mode [ 0 ] = ' J ' ;
else {
log_error ( " Invalid raid integrity mode (use \" bitmap \" or \" journal \" ) " ) ;
return 0 ;
}
return 1 ;
}
2020-06-16 19:59:59 +03:00
static int _set_integrity_block_size ( struct cmd_context * cmd , struct logical_volume * lv , int is_active ,
2019-11-21 01:07:27 +03:00
struct integrity_settings * settings ,
int lbs_4k , int lbs_512 , int pbs_4k , int pbs_512 )
{
char pathname [ PATH_MAX ] ;
uint32_t fs_block_size = 0 ;
int rv ;
if ( lbs_4k & & lbs_512 ) {
log_error ( " Integrity requires consistent logical block size for LV devices. " ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
if ( settings - > block_size & &
( settings - > block_size ! = 512 & & settings - > block_size ! = 1024 & &
settings - > block_size ! = 2048 & & settings - > block_size ! = 4096 ) ) {
log_error ( " Invalid integrity block size, possible values are 512, 1024, 2048, 4096 " ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
if ( lbs_4k & & settings - > block_size & & ( settings - > block_size < 4096 ) ) {
log_error ( " Integrity block size %u not allowed with device logical block size 4096. " ,
settings - > block_size ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
if ( ! strcmp ( cmd - > name , " lvcreate " ) ) {
if ( lbs_4k ) {
settings - > block_size = 4096 ;
} else if ( lbs_512 & & pbs_4k & & ! pbs_512 ) {
settings - > block_size = 4096 ;
} else if ( lbs_512 ) {
if ( ! settings - > block_size )
settings - > block_size = 512 ;
} else if ( ! lbs_4k & & ! lbs_512 ) {
if ( ! settings - > block_size )
settings - > block_size = 512 ;
log_print ( " Using integrity block size %u with unknown device logical block size. " ,
settings - > block_size ) ;
} else {
goto_bad ;
}
} else if ( ! strcmp ( cmd - > name , " lvconvert " ) ) {
if ( dm_snprintf ( pathname , sizeof ( pathname ) , " %s%s/%s " , cmd - > dev_dir ,
lv - > vg - > name , lv - > name ) < 0 ) {
log_error ( " Path name too long to get LV block size %s " , display_lvname ( lv ) ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
/*
2022-09-09 23:16:56 +03:00
* fs_block_size_and_type ( ) returns the libblkid BLOCK_SIZE value ,
2019-11-21 01:07:27 +03:00
* where libblkid has fs - specific code to set BLOCK_SIZE to the
* value we need here .
*
* The term " block size " here may not equate directly to what the fs
* calls the block size , e . g . xfs calls this the sector size ( and
* something different the block size ) ; while ext4 does call this
* value the block size , but it ' s possible values are not the same
* as xfs ' s , and do not seem to relate directly to the device LBS .
*/
2022-09-09 23:16:56 +03:00
rv = fs_block_size_and_type ( pathname , & fs_block_size , NULL , NULL ) ;
2019-11-21 01:07:27 +03:00
if ( ! rv | | ! fs_block_size ) {
int use_bs ;
if ( lbs_4k & & pbs_4k ) {
use_bs = 4096 ;
} else if ( lbs_512 & & pbs_512 ) {
use_bs = 512 ;
} else if ( lbs_512 & & pbs_4k ) {
if ( settings - > block_size = = 4096 )
use_bs = 4096 ;
else
use_bs = 512 ;
} else {
use_bs = 512 ;
}
if ( settings - > block_size & & ( settings - > block_size ! = use_bs ) ) {
log_error ( " Cannot use integrity block size %u with unknown file system block size, logical block size %u, physical block size %u. " ,
settings - > block_size , lbs_4k ? 4096 : 512 , pbs_4k ? 4096 : 512 ) ;
goto bad ;
}
settings - > block_size = use_bs ;
log_print ( " Using integrity block size %u for unknown file system block size, logical block size %u, physical block size %u. " ,
settings - > block_size , lbs_4k ? 4096 : 512 , pbs_4k ? 4096 : 512 ) ;
goto out ;
}
if ( ! settings - > block_size ) {
2020-06-16 19:59:59 +03:00
if ( is_active & & lbs_512 ) {
/* increasing the lbs from 512 to 4k under an active LV could cause problems
for an application that expects a given io size / alignment is possible . */
settings - > block_size = 512 ;
if ( fs_block_size > 512 )
log_print ( " Limiting integrity block size to 512 because the LV is active. " ) ;
} else if ( fs_block_size < = 4096 )
2019-11-21 01:07:27 +03:00
settings - > block_size = fs_block_size ;
else
settings - > block_size = 4096 ; /* dm-integrity max is 4096 */
log_print ( " Using integrity block size %u for file system block size %u. " ,
settings - > block_size , fs_block_size ) ;
} else {
/* let user specify integrity block size that is less than fs block size */
if ( settings - > block_size > fs_block_size ) {
log_error ( " Integrity block size %u cannot be larger than file system block size %u. " ,
settings - > block_size , fs_block_size ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
log_print ( " Using integrity block size %u for file system block size %u. " ,
settings - > block_size , fs_block_size ) ;
}
}
out :
return 1 ;
bad :
return 0 ;
}
/*
* Add integrity to each raid image .
*
* for each rimage_N :
* . create and allocate a new linear LV rimage_N_imeta
* . move the segments from rimage_N to a new rimage_N_iorig
* . add an integrity segment to rimage_N with
* origin = rimage_N_iorig , meta_dev = rimage_N_imeta
*
* Before :
* rimage_0
* segment1 : striped : pv0 : A
* rimage_1
* segment1 : striped : pv1 : B
*
* After :
* rimage_0
* segment1 : integrity : rimage_0_iorig , rimage_0_imeta
* rimage_1
* segment1 : integrity : rimage_1_iorig , rimage_1_imeta
* rimage_0_iorig
* segment1 : striped : pv0 : A
* rimage_1_iorig
* segment1 : striped : pv1 : B
* rimage_0_imeta
* segment1 : striped : pv2 : A
* rimage_1_imeta
* segment1 : striped : pv2 : B
*
*/
int lv_add_integrity_to_raid ( struct logical_volume * lv , struct integrity_settings * settings ,
struct dm_list * pvh , struct logical_volume * lv_imeta_0 )
{
char imeta_name [ NAME_LEN ] ;
char * imeta_name_dup ;
struct lvcreate_params lp ;
struct dm_list allocatable_pvs ;
struct logical_volume * imeta_lvs [ DEFAULT_RAID_MAX_IMAGES ] ;
struct cmd_context * cmd = lv - > vg - > cmd ;
struct volume_group * vg = lv - > vg ;
struct logical_volume * lv_image , * lv_imeta , * lv_iorig ;
struct lv_segment * seg_top , * seg_image ;
struct pv_list * pvl ;
const struct segment_type * segtype ;
struct integrity_settings * set = NULL ;
struct dm_list * use_pvh = NULL ;
uint32_t area_count , s ;
uint32_t revert_meta_lvs = 0 ;
int lbs_4k = 0 , lbs_512 = 0 , lbs_unknown = 0 ;
int pbs_4k = 0 , pbs_512 = 0 , pbs_unknown = 0 ;
int is_active ;
memset ( imeta_lvs , 0 , sizeof ( imeta_lvs ) ) ;
is_active = lv_is_active ( lv ) ;
if ( dm_list_size ( & lv - > segments ) ! = 1 )
return_0 ;
if ( ! dm_list_empty ( & lv - > segs_using_this_lv ) ) {
log_error ( " Integrity can only be added to top level raid LV. " ) ;
return 0 ;
}
if ( lv_is_origin ( lv ) ) {
log_error ( " Integrity cannot be added to snapshot origins. " ) ;
return 0 ;
}
seg_top = first_seg ( lv ) ;
area_count = seg_top - > area_count ;
if ( ! seg_is_raid1 ( seg_top ) & & ! seg_is_raid4 ( seg_top ) & &
! seg_is_any_raid5 ( seg_top ) & & ! seg_is_any_raid6 ( seg_top ) & &
! seg_is_any_raid10 ( seg_top ) ) {
log_error ( " Integrity can only be added to raid1,4,5,6,10. " ) ;
return 0 ;
}
/*
* For each rimage , create an _imeta LV for integrity metadata .
* Each needs to be zeroed .
*/
for ( s = 0 ; s < area_count ; s + + ) {
struct logical_volume * meta_lv ;
2020-09-15 17:38:50 +03:00
struct wipe_params wipe = { . do_zero = 1 } ;
2019-11-21 01:07:27 +03:00
if ( s > = DEFAULT_RAID_MAX_IMAGES )
goto_bad ;
lv_image = seg_lv ( seg_top , s ) ;
/*
* This function is used to add integrity to new images added
* to the raid , in which case old images will already be
* integrity .
*/
if ( seg_is_integrity ( first_seg ( lv_image ) ) )
continue ;
if ( ! seg_is_striped ( first_seg ( lv_image ) ) ) {
log_error ( " raid image must be linear to add integrity " ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
/*
* Use an existing lv_imeta from previous linear + integrity LV .
* FIXME : is it guaranteed that lv_image_0 is the existing ?
*/
if ( ! s & & lv_imeta_0 ) {
if ( dm_snprintf ( imeta_name , sizeof ( imeta_name ) , " %s_imeta " , lv_image - > name ) > 0 ) {
if ( ( imeta_name_dup = dm_pool_strdup ( vg - > vgmem , imeta_name ) ) )
lv_imeta_0 - > name = imeta_name_dup ;
}
imeta_lvs [ 0 ] = lv_imeta_0 ;
continue ;
}
dm_list_init ( & allocatable_pvs ) ;
if ( ! get_pv_list_for_lv ( cmd - > mem , lv_image , & allocatable_pvs ) ) {
log_error ( " Failed to build list of PVs for %s. " , display_lvname ( lv_image ) ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
dm_list_iterate_items ( pvl , & allocatable_pvs ) {
unsigned int pbs = 0 ;
unsigned int lbs = 0 ;
if ( ! dev_get_direct_block_sizes ( pvl - > pv - > dev , & pbs , & lbs ) ) {
lbs_unknown + + ;
pbs_unknown + + ;
continue ;
}
if ( lbs = = 4096 )
lbs_4k + + ;
else if ( lbs = = 512 )
lbs_512 + + ;
else
lbs_unknown + + ;
if ( pbs = = 4096 )
pbs_4k + + ;
else if ( pbs = = 512 )
pbs_512 + + ;
else
pbs_unknown + + ;
}
use_pvh = & allocatable_pvs ;
/*
* allocate a new linear LV NAME_rimage_N_imeta
*/
memset ( & lp , 0 , sizeof ( lp ) ) ;
lp . lv_name = lv_image - > name ;
lp . pvh = use_pvh ;
lp . extents = lv_image - > size / vg - > extent_size ;
if ( ! _lv_create_integrity_metadata ( cmd , vg , & lp , & meta_lv ) )
goto_bad ;
revert_meta_lvs + + ;
/* Used below to set up the new integrity segment. */
imeta_lvs [ s ] = meta_lv ;
/*
* dm - integrity requires the metadata LV header to be zeroed .
*/
if ( ! activate_lv ( cmd , meta_lv ) ) {
log_error ( " Failed to activate LV %s to zero " , display_lvname ( meta_lv ) ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
if ( ! wipe_lv ( meta_lv , wipe ) ) {
log_error ( " Failed to zero LV for integrity metadata %s " , display_lvname ( meta_lv ) ) ;
if ( deactivate_lv ( cmd , meta_lv ) )
log_error ( " Failed to deactivate LV %s after zero " , display_lvname ( meta_lv ) ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
if ( ! deactivate_lv ( cmd , meta_lv ) ) {
log_error ( " Failed to deactivate LV %s after zero " , display_lvname ( meta_lv ) ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
}
2020-06-11 20:43:52 +03:00
if ( ! is_active ) {
/* checking block size of fs on the lv requires the lv to be active */
if ( ! activate_lv ( cmd , lv ) ) {
log_error ( " Failed to activate LV to check block size %s " , display_lvname ( lv ) ) ;
goto bad ;
}
if ( ! sync_local_dev_names ( cmd ) )
stack ;
}
2019-11-21 01:07:27 +03:00
/*
* Set settings - > block_size which will be copied to segment settings below .
* integrity block size chosen based on device logical block size and
* file system block size .
*/
2020-06-16 19:59:59 +03:00
if ( ! _set_integrity_block_size ( cmd , lv , is_active , settings , lbs_4k , lbs_512 , pbs_4k , pbs_512 ) ) {
2020-06-11 20:43:52 +03:00
if ( ! is_active & & ! deactivate_lv ( cmd , lv ) )
stack ;
2019-11-21 01:07:27 +03:00
goto_bad ;
2020-06-11 20:43:52 +03:00
}
if ( ! is_active ) {
if ( ! deactivate_lv ( cmd , lv ) ) {
log_error ( " Failed to deactivate LV after checking block size %s " , display_lvname ( lv ) ) ;
goto bad ;
}
}
2019-11-21 01:07:27 +03:00
/*
* For each rimage , move its segments to a new rimage_iorig and give
* the rimage a new integrity segment .
*/
for ( s = 0 ; s < area_count ; s + + ) {
lv_image = seg_lv ( seg_top , s ) ;
/* Not adding integrity to this image. */
if ( ! imeta_lvs [ s ] )
continue ;
if ( ! ( segtype = get_segtype_from_string ( cmd , SEG_TYPE_NAME_INTEGRITY ) ) )
goto_bad ;
log_debug ( " Adding integrity to raid image %s " , lv_image - > name ) ;
/*
* " lv_iorig " is a new LV with new id , but with the segments
* from " lv_image " . " lv_image " keeps the existing name and id ,
* but gets a new integrity segment , in place of the segments
* that were moved to lv_iorig .
*/
if ( ! ( lv_iorig = insert_layer_for_lv ( cmd , lv_image , INTEGRITY , " _iorig " ) ) )
goto_bad ;
lv_image - > status | = INTEGRITY ;
/*
* Set up the new first segment of lv_image as integrity .
*/
seg_image = first_seg ( lv_image ) ;
seg_image - > segtype = segtype ;
lv_imeta = imeta_lvs [ s ] ;
lv_imeta - > status | = INTEGRITY_METADATA ;
lv_set_hidden ( lv_imeta ) ;
seg_image - > integrity_data_sectors = lv_image - > size ;
seg_image - > integrity_meta_dev = lv_imeta ;
seg_image - > integrity_recalculate = 1 ;
memcpy ( & seg_image - > integrity_settings , settings , sizeof ( struct integrity_settings ) ) ;
set = & seg_image - > integrity_settings ;
if ( ! set - > mode [ 0 ] )
set - > mode [ 0 ] = DEFAULT_MODE ;
if ( ! set - > tag_size )
set - > tag_size = DEFAULT_TAG_SIZE ;
if ( ! set - > block_size )
set - > block_size = DEFAULT_BLOCK_SIZE ;
if ( ! set - > internal_hash )
set - > internal_hash = DEFAULT_INTERNAL_HASH ;
}
if ( is_active ) {
log_debug ( " Writing VG and updating LV with new integrity LV %s " , lv - > name ) ;
/* vg_write(), suspend_lv(), vg_commit(), resume_lv() */
if ( ! lv_update_and_reload ( lv ) ) {
log_error ( " LV update and reload failed " ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
revert_meta_lvs = 0 ;
} else {
log_debug ( " Writing VG with new integrity LV %s " , lv - > name ) ;
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) )
goto_bad ;
revert_meta_lvs = 0 ;
/*
* This first activation includes " recalculate " which starts the
* kernel ' s recalculating ( initialization ) process .
*/
log_debug ( " Activating to start integrity initialization for LV %s " , lv - > name ) ;
if ( ! activate_lv ( cmd , lv ) ) {
log_error ( " Failed to activate integrity LV to initialize. " ) ;
2021-03-09 13:42:29 +03:00
goto bad ;
2019-11-21 01:07:27 +03:00
}
}
/*
* Now that the device is being initialized , update the VG to clear
* integrity_recalculate so that subsequent activations will not
* include " recalculate " and restart initialization .
*/
log_debug ( " Writing VG with initialized integrity LV %s " , lv - > name ) ;
for ( s = 0 ; s < area_count ; s + + ) {
lv_image = seg_lv ( seg_top , s ) ;
seg_image = first_seg ( lv_image ) ;
seg_image - > integrity_recalculate = 0 ;
}
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) )
goto_bad ;
return 1 ;
bad :
log_error ( " Failed to add integrity. " ) ;
2021-01-13 22:33:19 +03:00
if ( revert_meta_lvs ) {
for ( s = 0 ; s < DEFAULT_RAID_MAX_IMAGES ; s + + ) {
if ( ! imeta_lvs [ s ] )
continue ;
if ( ! lv_remove ( imeta_lvs [ s ] ) )
log_error ( " New integrity metadata LV may require manual removal. " ) ;
}
2019-11-21 01:07:27 +03:00
}
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) )
log_error ( " New integrity metadata LV may require manual removal. " ) ;
return 0 ;
}
/*
* This should rarely if ever be used . A command that adds integrity
* to an LV will activate and then clear the flag . If it fails before
* clearing the flag , then this function will be used by a subsequent
* activation to clear the flag .
*/
void lv_clear_integrity_recalculate_metadata ( struct logical_volume * lv )
{
struct volume_group * vg = lv - > vg ;
struct logical_volume * lv_image ;
struct lv_segment * seg , * seg_image ;
uint32_t s ;
2020-09-02 20:40:45 +03:00
if ( ! lv_is_raid ( lv ) & & ! lv_is_integrity ( lv ) ) {
log_error ( " Invalid LV type for clearing integrity " ) ;
return ;
}
2019-11-21 01:07:27 +03:00
seg = first_seg ( lv ) ;
if ( seg_is_raid ( seg ) ) {
for ( s = 0 ; s < seg - > area_count ; s + + ) {
lv_image = seg_lv ( seg , s ) ;
seg_image = first_seg ( lv_image ) ;
seg_image - > integrity_recalculate = 0 ;
}
} else if ( seg_is_integrity ( seg ) ) {
seg - > integrity_recalculate = 0 ;
}
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) ) {
log_warn ( " WARNING: failed to clear integrity recalculate flag for %s " ,
display_lvname ( lv ) ) ;
}
}
int lv_has_integrity_recalculate_metadata ( struct logical_volume * lv )
{
struct logical_volume * lv_image ;
struct lv_segment * seg , * seg_image ;
uint32_t s ;
int ret = 0 ;
2020-09-02 20:40:45 +03:00
if ( ! lv_is_raid ( lv ) & & ! lv_is_integrity ( lv ) )
2020-09-11 00:33:46 +03:00
return 0 ;
2020-09-02 20:40:45 +03:00
2019-11-21 01:07:27 +03:00
seg = first_seg ( lv ) ;
if ( seg_is_raid ( seg ) ) {
for ( s = 0 ; s < seg - > area_count ; s + + ) {
lv_image = seg_lv ( seg , s ) ;
seg_image = first_seg ( lv_image ) ;
if ( ! seg_is_integrity ( seg_image ) )
continue ;
if ( seg_image - > integrity_recalculate )
ret = 1 ;
}
} else if ( seg_is_integrity ( seg ) ) {
ret = seg - > integrity_recalculate ;
}
return ret ;
}
int lv_raid_has_integrity ( struct logical_volume * lv )
{
struct logical_volume * lv_image ;
struct lv_segment * seg , * seg_image ;
uint32_t s ;
2020-09-02 20:40:45 +03:00
if ( ! lv_is_raid ( lv ) )
2020-09-11 00:33:46 +03:00
return 0 ;
2019-11-21 01:07:27 +03:00
2020-09-02 20:40:45 +03:00
seg = first_seg ( lv ) ;
2019-11-21 01:07:27 +03:00
2020-09-02 20:40:45 +03:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
lv_image = seg_lv ( seg , s ) ;
seg_image = first_seg ( lv_image ) ;
if ( seg_is_integrity ( seg_image ) )
return 1 ;
2019-11-21 01:07:27 +03:00
}
return 0 ;
}
int lv_get_raid_integrity_settings ( struct logical_volume * lv , struct integrity_settings * * isettings )
{
struct logical_volume * lv_image ;
struct lv_segment * seg , * seg_image ;
uint32_t s ;
2020-09-02 20:40:45 +03:00
if ( ! lv_is_raid ( lv ) )
return_0 ;
2019-11-21 01:07:27 +03:00
seg = first_seg ( lv ) ;
2020-09-02 20:40:45 +03:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
lv_image = seg_lv ( seg , s ) ;
seg_image = first_seg ( lv_image ) ;
2019-11-21 01:07:27 +03:00
2020-09-02 20:40:45 +03:00
if ( seg_is_integrity ( seg_image ) ) {
* isettings = & seg_image - > integrity_settings ;
return 1 ;
2019-11-21 01:07:27 +03:00
}
}
return 0 ;
}
2020-11-12 00:10:15 +03:00
int lv_raid_integrity_total_mismatches ( struct cmd_context * cmd ,
const struct logical_volume * lv ,
uint64_t * mismatches )
{
struct logical_volume * lv_image ;
struct lv_segment * seg , * seg_image ;
uint32_t s ;
uint64_t mismatches_image ;
uint64_t total = 0 ;
int errors = 0 ;
if ( ! lv_is_raid ( lv ) )
return 0 ;
seg = first_seg ( lv ) ;
for ( s = 0 ; s < seg - > area_count ; s + + ) {
lv_image = seg_lv ( seg , s ) ;
seg_image = first_seg ( lv_image ) ;
if ( ! seg_is_integrity ( seg_image ) )
continue ;
mismatches_image = 0 ;
if ( ! lv_integrity_mismatches ( cmd , lv_image , & mismatches_image ) )
errors + + ;
total + = mismatches_image ;
}
* mismatches = total ;
if ( errors )
return 0 ;
return 1 ;
}
2020-09-02 00:15:31 +03:00
int lv_integrity_mismatches ( struct cmd_context * cmd ,
const struct logical_volume * lv ,
uint64_t * mismatches )
{
2023-03-01 23:05:47 +03:00
struct lv_with_info_and_seg_status status = {
. seg_status . type = SEG_STATUS_NONE ,
} ;
2020-09-02 00:15:31 +03:00
2020-11-12 00:10:15 +03:00
if ( lv_is_raid ( lv ) & & lv_raid_has_integrity ( ( struct logical_volume * ) lv ) )
return lv_raid_integrity_total_mismatches ( cmd , lv , mismatches ) ;
2020-09-02 20:40:45 +03:00
if ( ! lv_is_integrity ( lv ) )
return_0 ;
2020-09-02 00:15:31 +03:00
status . seg_status . seg = first_seg ( lv ) ;
/* FIXME: why reporter_pool? */
if ( ! ( status . seg_status . mem = dm_pool_create ( " reporter_pool " , 1024 ) ) ) {
log_error ( " Failed to get mem for LV status. " ) ;
return 0 ;
}
if ( ! lv_info_with_seg_status ( cmd , first_seg ( lv ) , & status , 1 , 1 ) ) {
log_error ( " Failed to get device mapper status for %s " , display_lvname ( lv ) ) ;
goto fail ;
}
if ( ! status . info . exists )
goto fail ;
if ( status . seg_status . type ! = SEG_STATUS_INTEGRITY ) {
log_error ( " Invalid device mapper status type (%d) for %s " ,
( uint32_t ) status . seg_status . type , display_lvname ( lv ) ) ;
goto fail ;
}
* mismatches = status . seg_status . integrity - > number_of_mismatches ;
dm_pool_destroy ( status . seg_status . mem ) ;
return 1 ;
fail :
dm_pool_destroy ( status . seg_status . mem ) ;
return 0 ;
}