2007-03-31 01:00:26 +04:00
/*
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2007 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
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2007-03-31 01:00:26 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2007-03-31 01:00:26 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2007-03-31 01:00:26 +04:00
*/
2018-06-08 15:40:53 +03:00
# include "base/memory/zalloc.h"
2007-03-31 01:00:26 +04:00
# include "tools.h"
2019-05-22 22:25:08 +03:00
# include "lib/format_text/format-text.h"
2019-05-31 22:10:44 +03:00
# include "lib/format_text/layout.h"
# include "lib/mm/xlate.h"
# include "lib/misc/crc.h"
2019-10-18 01:24:22 +03:00
# define PRINT_CURRENT 1
# define PRINT_ALL 2
2019-05-31 22:10:44 +03:00
static char * _chars_to_str ( void * in , void * out , int num , int max , const char * field )
{
char * i = in ;
char * o = out ;
int n ;
memset ( out , 0 , max ) ;
if ( num > max - 1 ) {
log_print ( " CHECK: abbreviating output for %s " , field ) ;
num = max - 1 ;
}
for ( n = 0 ; n < num ; n + + ) {
if ( isprint ( ( int ) * i ) )
* o = * i ;
else
* o = ' ? ' ;
i + + ;
o + + ;
}
return out ;
}
/*
* This is used to print mda_header . magic as a series of hex values
* since it only contains some printable chars .
*/
static char * _chars_to_hexstr ( void * in , void * out , int num , int max , const char * field )
{
char * tmp ;
char * i = in ;
int n ;
int off = 0 ;
int ret ;
if ( ! ( tmp = zalloc ( max ) ) ) {
log_print ( " CHECK: no mem for printing %s " , field ) ;
return out ;
}
memset ( out , 0 , max ) ;
memset ( tmp , 0 , max ) ;
if ( num > max - 1 ) {
log_print ( " CHECK: abbreviating output for %s " , field ) ;
num = max - 1 ;
}
for ( n = 0 ; n < num ; n + + ) {
ret = sprintf ( tmp + off , " %x " , * i & 0xFF ) ;
off + = ret ;
i + + ;
}
memcpy ( out , tmp , 256 ) ;
free ( tmp ) ;
return out ;
}
2019-10-18 01:24:22 +03:00
static int _check_vgname_start ( char * buf , int * len )
{
int chars = 0 ;
int space = 0 ;
int i ;
char c ;
/*
* Valid metadata begins : ' vgname { '
*/
for ( i = 0 ; i < = NAME_LEN + 2 ; i + + ) {
c = buf [ i ] ;
if ( isalnum ( c ) | | c = = ' . ' | | c = = ' _ ' | | c = = ' - ' | | c = = ' + ' ) {
if ( space )
return 0 ;
chars + + ;
continue ;
}
if ( c = = ' ' ) {
if ( ! chars | | space )
return 0 ;
space + + ;
continue ;
}
if ( c = = ' { ' ) {
if ( chars & & space ) {
* len = chars ;
return 1 ;
}
return 0 ;
}
return 0 ;
}
return 0 ;
}
static void _copy_out_metadata ( char * buf , uint32_t start , uint32_t first_start , uint64_t mda_size , char * * meta_buf , uint64_t * meta_size , int * bad_end )
{
char * new_buf ;
uint64_t i ;
uint64_t new_len ;
uint64_t len_a = 0 , len_b = 0 ;
uint32_t stop ;
int found_end ;
/*
* If we wrap around the buffer searching for the
* end of some metadata , either stop when we reach
* where we began ( start ) , or stop where we found
* the first copy of metadata ( first_start ) .
*/
if ( ! first_start )
stop = start ;
else
stop = first_start ;
found_end = 0 ;
for ( i = start ; i < mda_size ; i + + ) {
if ( buf [ i ] = = ' \0 ' ) {
found_end = 1 ;
break ;
}
}
if ( found_end ) {
new_len = i - start ;
} else {
len_a = i - start ;
found_end = 0 ;
for ( i = 512 ; i < stop ; i + + ) {
if ( buf [ i ] = = ' \0 ' ) {
found_end = 1 ;
break ;
}
}
if ( ! found_end )
return ;
len_b = i - 512 ;
new_len = len_a + len_b ;
}
if ( new_len < 256 ) {
log_print ( " skip invalid metadata with len %llu at %llu " ,
( unsigned long long ) new_len , ( unsigned long long ) start ) ;
return ;
}
/* terminating 0 byte */
new_len + + ;
if ( ! ( new_buf = malloc ( new_len ) ) )
return ;
memset ( new_buf , 0 , new_len ) ;
if ( len_a ) {
memcpy ( new_buf , buf + start , len_a ) ;
memcpy ( new_buf + len_a , buf + 512 , len_b ) ;
} else {
memcpy ( new_buf , buf + start , new_len ) ;
}
/* \0 should be preceded by \n\n (0x0a0a) */
if ( new_buf [ new_len - 1 ] ! = 0 | | new_buf [ new_len - 2 ] ! = 0x0a | | new_buf [ new_len - 3 ] ! = 0x0a )
* bad_end = 1 ;
* meta_buf = new_buf ;
* meta_size = new_len ;
}
static int _text_buf_parsable ( char * text_buf , uint64_t text_size )
{
struct dm_config_tree * cft ;
if ( ! ( cft = config_open ( CONFIG_FILE_SPECIAL , NULL , 0 ) ) ) {
return 0 ;
}
if ( ! dm_config_parse ( cft , text_buf , text_buf + text_size ) ) {
config_destroy ( cft ) ;
return 0 ;
}
config_destroy ( cft ) ;
return 1 ;
}
# define MAX_LINE_CHECK 128
# define ID_STR_SIZE 48
static void _copy_line ( char * in , char * out , int * len )
{
int i ;
* len = 0 ;
for ( i = 0 ; i < MAX_LINE_CHECK ; i + + ) {
if ( ( in [ i ] = = ' \n ' ) | | ( in [ i ] = = ' \0 ' ) )
break ;
out [ i ] = in [ i ] ;
}
* len = i + 1 ;
}
static int _dump_all_text ( struct cmd_context * cmd , const char * tofile , struct device * dev ,
int mda_num , uint64_t mda_offset , uint64_t mda_size , char * buf )
{
FILE * fp = NULL ;
char line [ MAX_LINE_CHECK ] ;
char vgname [ NAME_LEN + 1 ] ;
char id_str [ ID_STR_SIZE ] ;
char id_first [ ID_STR_SIZE ] ;
char * text_buf ;
char * p ;
uint32_t buf_off ; /* offset with buf which begins with mda_header */
uint32_t buf_off_first = 0 ;
uint32_t seqno ;
uint32_t crc ;
uint64_t text_size ;
uint64_t meta_size ;
int multiple_vgs = 0 ;
int bad_end ;
int vgnamelen ;
int count ;
int len ;
if ( tofile ) {
if ( ! ( fp = fopen ( tofile , " wx " ) ) ) {
log_error ( " Failed to create file %s " , tofile ) ;
return 0 ;
}
}
/*
* If metadata has not wrapped , and the metadata area beginning
* has not been damaged , the text area will begin with vgname { .
* Wrapping or damage would mean we find no metadata starting at
* the start of the area .
*
* Try looking at each 512 byte offset within the area for the start
* of another copy of metadata . Metadata copies have begun at 512
* aligned offsets since very early lvm2 code in 2002.
*
* ( We could also search for something definitive like
* " # Generated by LVM2 " in the area , and then work backward to find
* a likely beginning . )
*
* N . B . This relies on VG metadata first having the id = " ... " field
* followed by the " seqno = N " field .
*/
memset ( id_first , 0 , sizeof ( id_str ) ) ;
/*
* A count of 512 byte chunks within the metadata area .
*/
count = 0 ;
meta_size = mda_size - 512 ;
/*
* Search 512 byte boundaries for the start of new metadata copies .
*/
while ( count < ( meta_size / 512 ) ) {
memset ( vgname , 0 , sizeof ( vgname ) ) ;
memset ( id_str , 0 , sizeof ( id_str ) ) ;
seqno = 0 ;
vgnamelen = 0 ;
text_size = 0 ;
bad_end = 0 ;
/*
* Check for a new metadata copy at each 512 offset
* ( after skipping 512 bytes for mda_header at the
* start of the buf ) .
*
* If a line looks like it begins with a vgname
* it could be a new copy of metadata , but it could
* also be a random bit of metadata that looks like
* a vgname , so confirm it ' s the start of metadata
* by looking for id and seqno lines following the
* possible vgname .
*/
buf_off = 512 + ( count * 512 ) ;
p = buf + buf_off ;
/*
* copy line of possible metadata to check for vgname
*/
memset ( line , 0 , sizeof ( line ) ) ;
_copy_line ( p , line , & len ) ;
p + = len ;
if ( ! _check_vgname_start ( line , & vgnamelen ) ) {
count + + ;
continue ;
}
memcpy ( vgname , line , vgnamelen ) ;
/*
* copy next line of metadata , which should contain id
*/
memset ( line , 0 , sizeof ( line ) ) ;
_copy_line ( p , line , & len ) ;
p + = len ;
if ( strncmp ( line , " id = " , 5 ) ) {
count + + ;
continue ;
}
memcpy ( id_str , line + 6 , 38 ) ;
/*
* copy next line of metadata , which should contain seqno
*/
memset ( line , 0 , sizeof ( line ) ) ;
_copy_line ( p , line , & len ) ;
p + = len ;
if ( strncmp ( line , " seqno = " , 8 ) ) {
count + + ;
continue ;
}
2019-11-14 21:31:27 +03:00
sscanf ( line , " seqno = %u " , & seqno ) ;
2019-10-18 01:24:22 +03:00
/*
* The first three lines look like metadata with
* vgname / id / seqno , so copy out the full metadata .
*
* If this reaches the end of buf without reaching the
* end marker of metadata , it will wrap around to the
* start of buf and continue copying until it reaches
* a NL or until it reaches buf_off_first ( which is
* where we ' ve already taken text from . )
*/
_copy_out_metadata ( buf , buf_off , buf_off_first , mda_size , & text_buf , & text_size , & bad_end ) ;
if ( ! text_buf ) {
log_warn ( " Failed to extract full metadata text at %llu, skipping. " ,
( unsigned long long ) ( mda_offset + buf_off ) ) ;
count + + ;
continue ;
}
/*
* check if it ' s finding metadata from different vgs
*/
if ( ! id_first [ 0 ] )
memcpy ( id_first , id_str , sizeof ( id_first ) ) ;
else if ( memcmp ( id_first , id_str , sizeof ( id_first ) ) )
multiple_vgs = 1 ;
crc = calc_crc ( INITIAL_CRC , ( uint8_t * ) text_buf , text_size ) ;
log_print ( " metadata at %llu length %llu crc %08x vg %s seqno %u id %s " ,
( unsigned long long ) ( mda_offset + buf_off ) ,
( unsigned long long ) text_size ,
crc , vgname , seqno , id_str ) ;
/*
* save the location of the first metadata we ' ve found so
* we know where to stop after wrapping buf .
*/
if ( ! buf_off_first )
buf_off_first = buf_off ;
/*
* check if the full metadata is parsable
*/
if ( ! _text_buf_parsable ( text_buf , text_size ) )
log_warn ( " WARNING: parse error for metadata at %llu " , ( unsigned long long ) ( mda_offset + buf_off ) ) ;
if ( bad_end )
log_warn ( " WARNING: bad terminating bytes for metadata at %llu " , ( unsigned long long ) ( mda_offset + buf_off ) ) ;
if ( arg_is_set ( cmd , verbose_ARG ) ) {
char * str1 , * str2 ;
if ( ( str1 = strstr ( text_buf , " description = " ) ) ) {
memset ( line , 0 , sizeof ( line ) ) ;
_copy_line ( str1 , line , & len ) ;
log_print ( " %s " , line ) ;
2019-11-14 21:31:28 +03:00
}
if ( ( str2 = strstr ( str1 , " creation_time = " ) ) ) {
memset ( line , 0 , sizeof ( line ) ) ;
_copy_line ( str2 , line , & len ) ;
log_print ( " %s \n " , line ) ;
2019-10-18 01:24:22 +03:00
}
}
if ( fp ) {
fprintf ( fp , " %s " , text_buf ) ;
fprintf ( fp , " \n -- \n " ) ;
}
free ( text_buf ) ;
text_buf = NULL ;
if ( text_size < 512 )
count + + ;
else if ( ! ( text_size % 512 ) )
count + = ( text_size / 512 ) ;
else
count + = ( ( text_size / 512 ) + 1 ) ;
}
if ( multiple_vgs )
log_warn ( " WARNING: metadata from multiple VGs was found. " ) ;
if ( fp ) {
if ( fflush ( fp ) )
stack ;
if ( fclose ( fp ) )
stack ;
}
return 1 ;
}
2019-06-06 00:23:23 +03:00
static int _check_label_header ( struct label_header * lh , uint64_t labelsector ,
int * found_label )
2019-05-31 22:10:44 +03:00
{
uint32_t crc ;
2019-06-06 00:23:23 +03:00
int good_id = 1 , good_type = 1 ;
2019-05-31 22:10:44 +03:00
int bad = 0 ;
if ( memcmp ( lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ) {
log_print ( " CHECK: label_header.id expected %s " , LABEL_ID ) ;
2019-06-06 00:23:23 +03:00
good_id = 0 ;
2019-05-31 22:10:44 +03:00
bad + + ;
}
if ( xlate64 ( lh - > sector_xl ) ! = labelsector ) {
log_print ( " CHECK: label_header.sector expected %d " , ( int ) labelsector ) ;
bad + + ;
}
crc = calc_crc ( INITIAL_CRC , ( uint8_t * ) & lh - > offset_xl ,
LABEL_SIZE - ( ( uint8_t * ) & lh - > offset_xl - ( uint8_t * ) lh ) ) ;
if ( crc ! = xlate32 ( lh - > crc_xl ) ) {
log_print ( " CHECK: label_header.crc expected 0x%x " , crc ) ;
bad + + ;
}
if ( xlate32 ( lh - > offset_xl ) ! = 32 ) {
log_print ( " CHECK: label_header.offset expected 32 " ) ;
bad + + ;
}
if ( memcmp ( lh - > type , LVM2_LABEL , sizeof ( lh - > type ) ) ) {
log_print ( " CHECK: label_header.type expected %s " , LVM2_LABEL ) ;
2019-06-06 00:23:23 +03:00
good_type = 0 ;
2019-05-31 22:10:44 +03:00
bad + + ;
}
2019-06-06 00:23:23 +03:00
/* Report a label is found if at least id and type are correct. */
if ( found_label & & good_id & & good_type )
* found_label = 1 ;
2019-05-31 22:10:44 +03:00
if ( bad )
return 0 ;
return 1 ;
}
static int _check_pv_header ( struct pv_header * ph )
{
struct id id ;
int bad = 0 ;
if ( ! id_read_format_try ( & id , ( char * ) & ph - > pv_uuid ) ) {
log_print ( " CHECK: pv_header.pv_uuid invalid format " ) ;
bad + + ;
}
if ( bad )
return 0 ;
return 1 ;
}
/*
* mda_offset / mda_size are from the pv_header / disk_locn and could
* be incorrect .
*/
2019-06-06 00:23:23 +03:00
static int _check_mda_header ( struct mda_header * mh , int mda_num , uint64_t mda_offset , uint64_t mda_size , int * found_header )
2019-05-31 22:10:44 +03:00
{
char str [ 256 ] ;
uint32_t crc ;
2019-06-06 00:23:23 +03:00
int good_magic = 1 ;
2019-05-31 22:10:44 +03:00
int bad = 0 ;
crc = calc_crc ( INITIAL_CRC , ( uint8_t * ) mh - > magic ,
MDA_HEADER_SIZE - sizeof ( mh - > checksum_xl ) ) ;
if ( crc ! = xlate32 ( mh - > checksum_xl ) ) {
log_print ( " CHECK: mda_header_%d.checksum expected 0x%x " , mda_num , crc ) ;
bad + + ;
}
if ( memcmp ( mh - > magic , FMTT_MAGIC , sizeof ( mh - > magic ) ) ) {
log_print ( " CHECK: mda_header_%d.magic expected 0x%s " , mda_num , _chars_to_hexstr ( ( void * ) & FMTT_MAGIC , str , 16 , 256 , " mda_header.magic " ) ) ;
2019-06-06 00:23:23 +03:00
good_magic = 0 ;
2019-05-31 22:10:44 +03:00
bad + + ;
}
if ( xlate32 ( mh - > version ) ! = FMTT_VERSION ) {
log_print ( " CHECK: mda_header_%d.version expected %u " , mda_num , FMTT_VERSION ) ;
bad + + ;
}
if ( xlate64 ( mh - > start ) ! = mda_offset ) {
log_print ( " CHECK: mda_header_%d.start does not match pv_header.disk_locn.offset %llu " , mda_num , ( unsigned long long ) mda_offset ) ;
bad + + ;
}
if ( xlate64 ( mh - > size ) ! = mda_size ) {
log_print ( " CHECK: mda_header_%d.size does not match pv_header.disk_locn.size %llu " , mda_num , ( unsigned long long ) mda_size ) ;
bad + + ;
}
2019-06-06 00:23:23 +03:00
/* Report a header is found if at least magic is correct. */
if ( found_header & & good_magic )
* found_header = 1 ;
2019-05-31 22:10:44 +03:00
if ( bad )
return 0 ;
return 1 ;
}
2019-05-22 22:25:08 +03:00
/*
*
2019-05-31 22:10:44 +03:00
* mda_offset , mda_size are from pv_header . disk_locn
* ( the location of the metadata area . )
*
* meta_offset , meta_size , meta_checksum are from mda_header . raw_locn
* ( the location of the metadata text in the metadata area . )
2019-05-22 22:25:08 +03:00
*/
2019-05-31 22:10:44 +03:00
static int _dump_raw_locn ( struct device * dev , int print_fields ,
struct raw_locn * rlocn , int rlocn_index , uint64_t rlocn_offset ,
int mda_num , uint64_t mda_offset , uint64_t mda_size ,
uint64_t * meta_offset_ret ,
uint64_t * meta_size_ret ,
uint32_t * meta_checksum_ret )
{
uint64_t meta_offset , meta_size ;
uint32_t meta_checksum ;
uint32_t meta_flags ;
int bad = 0 ;
int mn = mda_num ; /* 1 or 2 */
int ri = rlocn_index ; /* 0 or 1 */
int wrapped = 0 ;
meta_offset = xlate64 ( rlocn - > offset ) ;
meta_size = xlate64 ( rlocn - > size ) ;
meta_checksum = xlate32 ( rlocn - > checksum ) ;
meta_flags = xlate32 ( rlocn - > flags ) ;
if ( meta_offset + meta_size > mda_size )
wrapped = 1 ;
if ( print_fields ) {
log_print ( " mda_header_%d.raw_locn[%d] at %llu # %s%s " , mn , ri , ( unsigned long long ) rlocn_offset , ( ri = = 0 ) ? " commit " : " precommit " , wrapped ? " wrapped " : " " ) ;
log_print ( " mda_header_%d.raw_locn[%d].offset %llu " , mn , ri , ( unsigned long long ) meta_offset ) ;
log_print ( " mda_header_%d.raw_locn[%d].size %llu " , mn , ri , ( unsigned long long ) meta_size ) ;
log_print ( " mda_header_%d.raw_locn[%d].checksum 0x%x " , mn , ri , meta_checksum ) ;
if ( meta_flags & RAW_LOCN_IGNORED )
log_print ( " mda_header_%d.raw_locn[%d].flags 0x%x # RAW_LOCN_IGNORED " , mn , ri , meta_flags ) ;
else
log_print ( " mda_header_%d.raw_locn[%d].flags 0x%x " , mn , ri , meta_flags ) ;
}
/* The precommit pointer will usually be empty. */
if ( ( rlocn_index = = 1 ) & & meta_offset )
log_print ( " CHECK: mda_header_%d.raw_locn[%d] for precommit not empty " , mn , ri ) ;
/* This metadata area is not being used to hold text metadata. */
/* Old, out of date text metadata may exist if the area was once used. */
if ( meta_flags & RAW_LOCN_IGNORED )
return 1 ;
/*
* A valid meta_size can be no larger than the metadata area size minus
* the 512 bytes used by the mda_header sector .
*/
if ( meta_size > ( mda_size - 512 ) ) {
log_print ( " CHECK: mda_header_%d.raw_locn[%d].size larger than metadata area size " , mn , ri ) ;
/* If meta_size is bad, try to continue using a reasonable value */
meta_size = ( mda_size - 512 ) ;
}
if ( meta_offset_ret )
* meta_offset_ret = meta_offset ;
if ( meta_size_ret )
* meta_size_ret = meta_size ;
if ( meta_checksum_ret )
* meta_checksum_ret = meta_checksum ;
/* No text metadata exists in this metadata area. */
if ( ! meta_offset )
return 1 ;
if ( bad )
return 0 ;
return 1 ;
}
static int _dump_meta_area ( struct device * dev , const char * tofile ,
uint64_t mda_offset , uint64_t mda_size )
{
FILE * fp ;
char * meta_buf ;
if ( ! tofile )
return_0 ;
2019-11-14 21:31:31 +03:00
if ( ! ( meta_buf = malloc ( mda_size ) ) )
2019-05-31 22:10:44 +03:00
return_0 ;
2019-11-14 21:31:31 +03:00
memset ( meta_buf , 0 , mda_size ) ;
2019-05-31 22:10:44 +03:00
if ( ! dev_read_bytes ( dev , mda_offset , mda_size , meta_buf ) ) {
log_print ( " CHECK: failed to read metadata area at offset %llu size %llu " ,
( unsigned long long ) mda_offset , ( unsigned long long ) mda_size ) ;
return 0 ;
}
if ( ! ( fp = fopen ( tofile , " wx " ) ) ) {
2019-11-14 21:31:29 +03:00
log_error ( " Failed to create file %s " , tofile ) ;
2019-05-31 22:10:44 +03:00
return 0 ;
}
2019-11-14 21:31:29 +03:00
fwrite ( meta_buf , mda_size - 512 , 1 , fp ) ;
2019-05-31 22:10:44 +03:00
if ( fflush ( fp ) )
stack ;
if ( fclose ( fp ) )
stack ;
return 1 ;
}
2019-06-05 19:10:37 +03:00
/*
* Search for any instance of id_str [ ] in the metadata area ,
* where the id_str indicates the start of a metadata copy
* ( which could be complete or a fragment . )
* id_str is an open brace followed by id = < uuid > .
*
* { \ n
* id = " lL7Mnk-oCGn-Bde2-9B6S-44Z7-VrHa-wvfC3v "
*
* 1 \ 23456789012345678901234567890123456789012345678
* 10 20 30 40
*/
2019-10-18 01:24:22 +03:00
static int _dump_current_text ( struct device * dev ,
int print_fields , int print_metadata , const char * tofile ,
int mda_num , int rlocn_index ,
uint64_t mda_offset , uint64_t mda_size ,
uint64_t meta_offset , uint64_t meta_size ,
uint32_t meta_checksum )
2019-05-31 22:10:44 +03:00
{
char * meta_buf ;
struct dm_config_tree * cft ;
const char * vgname = NULL ;
uint32_t crc ;
2019-06-05 19:10:37 +03:00
uint32_t seqno = 0 ;
2019-05-31 22:10:44 +03:00
int mn = mda_num ; /* 1 or 2 */
int ri = rlocn_index ; /* 0 or 1 */
int bad = 0 ;
2019-11-14 21:31:31 +03:00
if ( ! ( meta_buf = malloc ( meta_size ) ) ) {
2019-05-31 22:10:44 +03:00
log_print ( " CHECK: mda_header_%d.raw_locn[%d] no mem for metadata text size %llu " , mn , ri ,
( unsigned long long ) meta_size ) ;
return 0 ;
}
2019-11-14 21:31:31 +03:00
memset ( meta_buf , 0 , meta_size ) ;
2019-05-31 22:10:44 +03:00
/*
* Read the metadata text specified by the raw_locn so we can
* check the raw_locn values .
*
* meta_offset is the offset from the start of the mda_header ,
* so the text location from the start of the disk is
* mda_offset + meta_offset .
*/
if ( meta_offset + meta_size > mda_size ) {
2019-11-14 21:31:29 +03:00
/* text metadata wraps to start of text metadata area */
2019-05-31 22:10:44 +03:00
uint32_t wrap = ( uint32_t ) ( ( meta_offset + meta_size ) - mda_size ) ;
off_t offset_a = mda_offset + meta_offset ;
uint32_t size_a = meta_size - wrap ;
off_t offset_b = mda_offset + 512 ; /* continues after mda_header sector */
uint32_t size_b = wrap ;
if ( ! dev_read_bytes ( dev , offset_a , size_a , meta_buf ) ) {
log_print ( " CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu part_a %llu %llu " , mn , ri ,
( unsigned long long ) meta_offset , ( unsigned long long ) meta_size ,
( unsigned long long ) offset_a , ( unsigned long long ) size_a ) ;
return 0 ;
}
if ( ! dev_read_bytes ( dev , offset_b , size_b , meta_buf + size_a ) ) {
log_print ( " CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu part_b %llu %llu " , mn , ri ,
( unsigned long long ) meta_offset , ( unsigned long long ) meta_size ,
( unsigned long long ) offset_b , ( unsigned long long ) size_b ) ;
return 0 ;
}
} else {
if ( ! dev_read_bytes ( dev , mda_offset + meta_offset , meta_size , meta_buf ) ) {
log_print ( " CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu " , mn , ri ,
( unsigned long long ) meta_offset , ( unsigned long long ) meta_size ) ;
return 0 ;
}
}
crc = calc_crc ( INITIAL_CRC , ( uint8_t * ) meta_buf , meta_size ) ;
if ( crc ! = meta_checksum ) {
log_print ( " CHECK: metadata text at %llu crc does not match mda_header_%d.raw_locn[%d].checksum " ,
( unsigned long long ) ( mda_offset + meta_offset ) , mn , ri ) ;
bad + + ;
}
if ( ! ( cft = config_open ( CONFIG_FILE_SPECIAL , NULL , 0 ) ) ) {
log_print ( " CHECK: failed to set up metadata parsing " ) ;
bad + + ;
} else {
if ( ! dm_config_parse ( cft , meta_buf , meta_buf + meta_size ) ) {
log_print ( " CHECK: failed to parse metadata text at %llu size %llu " ,
( unsigned long long ) ( mda_offset + meta_offset ) ,
( unsigned long long ) meta_size ) ;
bad + + ;
} else {
2019-06-05 19:10:37 +03:00
if ( cft - > root & & cft - > root - > key )
vgname = strdup ( cft - > root - > key ) ;
if ( cft - > root & & cft - > root - > child )
dm_config_get_uint32 ( cft - > root - > child , " seqno " , & seqno ) ;
2019-05-31 22:10:44 +03:00
}
config_destroy ( cft ) ;
}
2019-06-06 00:23:23 +03:00
if ( print_fields | | print_metadata )
log_print ( " metadata text at %llu crc 0x%x # vgname %s seqno %u " ,
( unsigned long long ) ( mda_offset + meta_offset ) , crc ,
vgname ? vgname : " ? " , seqno ) ;
2019-05-31 22:10:44 +03:00
if ( ! print_metadata )
goto out ;
if ( ! tofile ) {
log_print ( " --- " ) ;
printf ( " %s \n " , meta_buf ) ;
log_print ( " --- " ) ;
} else {
FILE * fp ;
if ( ! ( fp = fopen ( tofile , " wx " ) ) ) {
log_error ( " Failed to create file %s " , tofile ) ;
goto out ;
}
fprintf ( fp , " %s " , meta_buf ) ;
if ( fflush ( fp ) )
stack ;
if ( fclose ( fp ) )
stack ;
}
out :
2019-11-14 21:31:29 +03:00
if ( bad )
return 0 ;
return 1 ;
2019-05-31 22:10:44 +03:00
}
2019-10-22 00:39:46 +03:00
static int _dump_label_and_pv_header ( struct cmd_context * cmd , uint64_t labelsector , struct device * dev ,
int print_fields ,
2019-06-06 00:23:23 +03:00
int * found_label ,
2019-05-31 22:10:44 +03:00
uint64_t * mda1_offset , uint64_t * mda1_size ,
2019-10-01 00:20:17 +03:00
uint64_t * mda2_offset , uint64_t * mda2_size ,
int * mda_count_out )
2019-05-31 22:10:44 +03:00
{
char str [ 256 ] ;
struct label_header * lh ;
struct pv_header * pvh ;
struct pv_header_extension * pvhe ;
struct disk_locn * dlocn ;
uint64_t lh_offset ;
uint64_t pvh_offset ;
uint64_t pvhe_offset ;
uint64_t dlocn_offset ;
char * buf ;
uint64_t tmp ;
int mda_count = 0 ;
int bad = 0 ;
int di ;
lh_offset = labelsector * 512 ; /* from start of disk */
if ( ! ( buf = zalloc ( 512 ) ) )
return_0 ;
if ( ! dev_read_bytes ( dev , lh_offset , 512 , buf ) ) {
log_print ( " CHECK: failed to read label_header at %llu " ,
( unsigned long long ) lh_offset ) ;
return 0 ;
}
lh = ( struct label_header * ) buf ;
if ( print_fields ) {
log_print ( " label_header at %llu " , ( unsigned long long ) lh_offset ) ;
log_print ( " label_header.id %s " , _chars_to_str ( lh - > id , str , 8 , 256 , " label_header.id " ) ) ;
log_print ( " label_header.sector %llu " , ( unsigned long long ) xlate64 ( lh - > sector_xl ) ) ;
log_print ( " label_header.crc 0x%x " , xlate32 ( lh - > crc_xl ) ) ;
log_print ( " label_header.offset %u " , xlate32 ( lh - > offset_xl ) ) ;
log_print ( " label_header.type %s " , _chars_to_str ( lh - > type , str , 8 , 256 , " label_header.type " ) ) ;
}
2019-06-06 00:23:23 +03:00
if ( ! _check_label_header ( lh , labelsector , found_label ) )
2019-05-31 22:10:44 +03:00
bad + + ;
/*
* The label_header is 32 bytes in size ( size of struct label_header ) .
* The pv_header should begin immediately after the label_header .
* The label_header . offset gives the offset of pv_header from the
* start of the label_header , which should always be 32.
*
* If label_header . offset is corrupted , then we should print a
* warning about the bad value , and read the pv_header from the
* correct location instead of the bogus location .
*/
pvh = ( struct pv_header * ) ( buf + 32 ) ;
pvh_offset = lh_offset + 32 ; /* from start of disk */
/* sanity check */
if ( ( void * ) pvh ! = ( void * ) ( buf + pvh_offset - lh_offset ) )
log_print ( " CHECK: problem with pv_header offset calculation " ) ;
if ( print_fields ) {
log_print ( " pv_header at %llu " , ( unsigned long long ) pvh_offset ) ;
log_print ( " pv_header.pv_uuid %s " , _chars_to_str ( pvh - > pv_uuid , str , ID_LEN , 256 , " pv_header.pv_uuid " ) ) ;
log_print ( " pv_header.device_size %llu " , ( unsigned long long ) xlate64 ( pvh - > device_size_xl ) ) ;
}
if ( ! _check_pv_header ( pvh ) )
bad + + ;
/*
* The pv_header is 40 bytes , excluding disk_locn ' s .
* disk_locn structs immediately follow the pv_header .
* Each disk_locn is 16 bytes .
*/
di = 0 ;
dlocn = pvh - > disk_areas_xl ;
dlocn_offset = pvh_offset + 40 ; /* from start of disk */
/* sanity check */
if ( ( void * ) dlocn ! = ( void * ) ( buf + dlocn_offset - lh_offset ) )
log_print ( " CHECK: problem with pv_header.disk_locn[%d] offset calculation " , di ) ;
while ( ( tmp = xlate64 ( dlocn - > offset ) ) ) {
if ( print_fields ) {
log_print ( " pv_header.disk_locn[%d] at %llu # location of data area " , di ,
( unsigned long long ) dlocn_offset ) ;
log_print ( " pv_header.disk_locn[%d].offset %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > offset ) ) ;
log_print ( " pv_header.disk_locn[%d].size %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > size ) ) ;
}
di + + ;
dlocn + + ;
dlocn_offset + = 16 ;
}
/* all-zero dlocn struct is area list end */
if ( print_fields ) {
log_print ( " pv_header.disk_locn[%d] at %llu # location list end " , di ,
( unsigned long long ) dlocn_offset ) ;
log_print ( " pv_header.disk_locn[%d].offset %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > offset ) ) ;
log_print ( " pv_header.disk_locn[%d].size %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > size ) ) ;
}
/* advance past the all-zero dlocn struct */
di + + ;
dlocn + + ;
dlocn_offset + = 16 ;
/* sanity check */
if ( ( void * ) dlocn ! = ( void * ) ( buf + dlocn_offset - lh_offset ) )
log_print ( " CHECK: problem with pv_header.disk_locn[%d] offset calculation " , di ) ;
while ( ( tmp = xlate64 ( dlocn - > offset ) ) ) {
if ( print_fields ) {
log_print ( " pv_header.disk_locn[%d] at %llu # location of metadata area " , di ,
( unsigned long long ) dlocn_offset ) ;
log_print ( " pv_header.disk_locn[%d].offset %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > offset ) ) ;
log_print ( " pv_header.disk_locn[%d].size %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > size ) ) ;
}
if ( ! mda_count ) {
* mda1_offset = xlate64 ( dlocn - > offset ) ;
* mda1_size = xlate64 ( dlocn - > size ) ;
if ( * mda1_offset ! = 4096 ) {
log_print ( " CHECK: pv_header.disk_locn[%d].offset expected 4096 # for first mda " , di ) ;
bad + + ;
}
} else {
* mda2_offset = xlate64 ( dlocn - > offset ) ;
* mda2_size = xlate64 ( dlocn - > size ) ;
/*
* No fixed location for second mda , so we have to look for
* mda_header at this offset to see if it ' s correct .
*/
}
di + + ;
dlocn + + ;
dlocn_offset + = 16 ;
mda_count + + ;
}
2019-10-01 00:20:17 +03:00
* mda_count_out = mda_count ;
2019-05-31 22:10:44 +03:00
/* all-zero dlocn struct is area list end */
if ( print_fields ) {
log_print ( " pv_header.disk_locn[%d] at %llu # location list end " , di ,
( unsigned long long ) dlocn_offset ) ;
log_print ( " pv_header.disk_locn[%d].offset %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > offset ) ) ;
log_print ( " pv_header.disk_locn[%d].size %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > size ) ) ;
}
/* advance past the all-zero dlocn struct */
di + + ;
dlocn + + ;
dlocn_offset + = 16 ;
/*
* pv_header_extension follows the last disk_locn
* terminating struct , so it ' s not always at the
* same location .
*/
pvhe = ( struct pv_header_extension * ) dlocn ;
pvhe_offset = dlocn_offset ; /* from start of disk */
/* sanity check */
if ( ( void * ) pvhe ! = ( void * ) ( buf + pvhe_offset - lh_offset ) )
log_print ( " CHECK: problem with pv_header_extension offset calculation " ) ;
if ( print_fields ) {
log_print ( " pv_header_extension at %llu " , ( unsigned long long ) pvhe_offset ) ;
log_print ( " pv_header_extension.version %u " , xlate32 ( pvhe - > version ) ) ;
log_print ( " pv_header_extension.flags %u " , xlate32 ( pvhe - > flags ) ) ;
}
/*
* The pv_header_extension is 8 bytes , excluding disk_locn ' s .
* disk_locn structs immediately follow the pv_header_extension .
* Each disk_locn is 16 bytes .
*/
di = 0 ;
dlocn = pvhe - > bootloader_areas_xl ;
dlocn_offset = pvhe_offset + 8 ;
while ( ( tmp = xlate64 ( dlocn - > offset ) ) ) {
if ( print_fields ) {
log_print ( " pv_header_extension.disk_locn[%d] at %llu # bootloader area " , di ,
( unsigned long long ) dlocn_offset ) ;
log_print ( " pv_header_extension.disk_locn[%d].offset %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > offset ) ) ;
log_print ( " pv_header_extension.disk_locn[%d].size %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > size ) ) ;
}
di + + ;
dlocn + + ;
dlocn_offset + = 16 ;
}
/* all-zero dlocn struct is area list end */
if ( print_fields ) {
log_print ( " pv_header_extension.disk_locn[%d] at %llu # location list end " , di ,
( unsigned long long ) dlocn_offset ) ;
log_print ( " pv_header_extension.disk_locn[%d].offset %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > offset ) ) ;
log_print ( " pv_header_extension.disk_locn[%d].size %llu " , di ,
( unsigned long long ) xlate64 ( dlocn - > size ) ) ;
}
2019-11-14 21:31:29 +03:00
if ( bad )
return 0 ;
return 1 ;
2019-05-31 22:10:44 +03:00
}
/*
* mda_offset and mda_size are the location / size of the metadata area ,
* which starts with the mda_header and continues through the circular
* buffer of text .
*
* mda_offset and mda_size values come from the pv_header / disk_locn ,
* which could be incorrect .
*
* We know that the first mda_offset will always be 4096 , so we use
* that value regardless of what the first mda_offset value in the
* pv_header is .
*/
static int _dump_mda_header ( struct cmd_context * cmd ,
int print_fields , int print_metadata , int print_area ,
const char * tofile ,
struct device * dev ,
uint64_t mda_offset , uint64_t mda_size ,
2019-06-06 00:23:23 +03:00
uint32_t * checksum0_ret ,
int * found_header )
2019-05-31 22:10:44 +03:00
{
char str [ 256 ] ;
char * buf ;
struct mda_header * mh ;
struct raw_locn * rlocn0 , * rlocn1 ;
uint64_t rlocn0_offset , rlocn1_offset ;
uint64_t meta_offset = 0 ;
uint64_t meta_size = 0 ;
uint32_t meta_checksum = 0 ;
int mda_num = ( mda_offset = = 4096 ) ? 1 : 2 ;
int bad = 0 ;
* checksum0_ret = 0 ; /* checksum from raw_locn[0] */
if ( ! ( buf = zalloc ( 512 ) ) )
return_0 ;
/*
* The first mda_header is 4096 bytes from the start
* of the device . Each mda_header is 512 bytes .
*
* The start / size values in the mda_header should
* match the mda_offset / mda_size values that came
* from the pv_header / disk_locn .
*
* ( Why was mda_header magic made only partially printable ? )
*/
if ( ! dev_read_bytes ( dev , mda_offset , 512 , buf ) ) {
log_print ( " CHECK: failed to read mda_header at %llu " , ( unsigned long long ) mda_offset ) ;
return 0 ;
}
mh = ( struct mda_header * ) buf ;
if ( print_fields ) {
log_print ( " mda_header_%d at %llu # metadata area " , mda_num , ( unsigned long long ) mda_offset ) ;
log_print ( " mda_header_%d.checksum 0x%x " , mda_num , xlate32 ( mh - > checksum_xl ) ) ;
log_print ( " mda_header_%d.magic 0x%s " , mda_num , _chars_to_hexstr ( mh - > magic , str , 16 , 256 , " mda_header.magic " ) ) ;
log_print ( " mda_header_%d.version %u " , mda_num , xlate32 ( mh - > version ) ) ;
log_print ( " mda_header_%d.start %llu " , mda_num , ( unsigned long long ) xlate64 ( mh - > start ) ) ;
log_print ( " mda_header_%d.size %llu " , mda_num , ( unsigned long long ) xlate64 ( mh - > size ) ) ;
}
2019-06-06 00:23:23 +03:00
if ( ! _check_mda_header ( mh , mda_num , mda_offset , mda_size , found_header ) )
2019-05-31 22:10:44 +03:00
bad + + ;
if ( print_area ) {
if ( ! _dump_meta_area ( dev , tofile , mda_offset , mda_size ) )
bad + + ;
goto out ;
}
/*
* mda_header is 40 bytes , the raw_locn structs
* follow immediately after , each raw_locn struct
* is 24 bytes .
*/
rlocn0 = mh - > raw_locns ;
rlocn0_offset = mda_offset + 40 ; /* from start of disk */
/* sanity check */
if ( ( void * ) rlocn0 ! = ( void * ) ( buf + rlocn0_offset - mda_offset ) )
log_print ( " CHECK: problem with rlocn0 offset calculation " ) ;
meta_offset = 0 ;
meta_size = 0 ;
meta_checksum = 0 ;
if ( ! _dump_raw_locn ( dev , print_fields , rlocn0 , 0 , rlocn0_offset , mda_num , mda_offset , mda_size ,
& meta_offset , & meta_size , & meta_checksum ) )
bad + + ;
* checksum0_ret = meta_checksum ;
rlocn1 = ( struct raw_locn * ) ( ( char * ) mh - > raw_locns + 24 ) ;
rlocn1_offset = rlocn0_offset + 24 ;
/* sanity check */
if ( ( void * ) rlocn1 ! = ( void * ) ( buf + rlocn1_offset - mda_offset ) )
log_print ( " CHECK: problem with rlocn1 offset calculation " ) ;
if ( ! _dump_raw_locn ( dev , print_fields , rlocn1 , 1 , rlocn1_offset , mda_num , mda_offset , mda_size ,
NULL , NULL , NULL ) )
bad + + ;
if ( ! meta_offset )
goto out ;
2019-06-05 19:10:37 +03:00
/*
* looking at the current copy of metadata referenced by raw_locn
*/
2019-10-18 01:24:22 +03:00
if ( print_metadata < = PRINT_CURRENT ) {
if ( ! _dump_current_text ( dev , print_fields , print_metadata , tofile , mda_num , 0 , mda_offset , mda_size , meta_offset , meta_size , meta_checksum ) )
2019-06-05 19:10:37 +03:00
bad + + ;
}
/*
* looking at all copies of the metadata in the area
*/
2019-10-18 01:24:22 +03:00
if ( print_metadata = = PRINT_ALL ) {
free ( buf ) ;
if ( ! ( buf = malloc ( mda_size ) ) )
goto_out ;
memset ( buf , 0 , mda_size ) ;
2019-06-05 19:10:37 +03:00
2019-10-18 01:24:22 +03:00
if ( ! dev_read_bytes ( dev , mda_offset , mda_size , buf ) ) {
log_print ( " CHECK: failed to read metadata area at offset %llu size %llu " ,
( unsigned long long ) mda_offset , ( unsigned long long ) mda_size ) ;
2019-06-05 19:10:37 +03:00
bad + + ;
2019-10-18 01:24:22 +03:00
goto out ;
}
_dump_all_text ( cmd , tofile , dev , mda_num , mda_offset , mda_size , buf ) ;
2019-06-05 19:10:37 +03:00
}
2019-05-31 22:10:44 +03:00
/* Should we also check text metadata if it exists in rlocn1? */
out :
2019-11-14 21:31:29 +03:00
if ( buf )
free ( buf ) ;
if ( bad )
return 0 ;
return 1 ;
2019-05-31 22:10:44 +03:00
}
2019-10-22 00:39:46 +03:00
static int _dump_headers ( struct cmd_context * cmd , uint64_t labelsector , struct device * dev ,
2019-05-31 22:10:44 +03:00
int argc , char * * argv )
{
uint64_t mda1_offset = 0 , mda1_size = 0 , mda2_offset = 0 , mda2_size = 0 ;
uint32_t mda1_checksum , mda2_checksum ;
2019-10-01 00:20:17 +03:00
int mda_count = 0 ;
2019-05-31 22:10:44 +03:00
int bad = 0 ;
2019-10-22 00:39:46 +03:00
if ( ! _dump_label_and_pv_header ( cmd , labelsector , dev , 1 , NULL ,
2019-10-01 00:20:17 +03:00
& mda1_offset , & mda1_size , & mda2_offset , & mda2_size , & mda_count ) )
2019-05-31 22:10:44 +03:00
bad + + ;
2019-10-01 00:20:17 +03:00
if ( ! mda_count ) {
log_print ( " zero metadata copies " ) ;
return ECMD_PROCESSED ;
}
2019-05-31 22:10:44 +03:00
/* N.B. mda1_size and mda2_size may be different */
/*
* The first mda is always 4096 bytes from the start of the device .
*
* TODO : A second mda may not exist . If the pv_header says there
* is no second mda , we may still want to check for a second mda
* in case it ' s the pv_header that is wrong . Try looking for
* an mda_header at 1 MB prior to the end of the device , if
* mda2_offset is 0 or if we don ' t find an mda_header at mda2_offset
* which may have been corrupted .
*/
2019-06-06 00:23:23 +03:00
if ( ! _dump_mda_header ( cmd , 1 , 0 , 0 , NULL , dev , 4096 , mda1_size , & mda1_checksum , NULL ) )
2019-05-31 22:10:44 +03:00
bad + + ;
/*
* mda2_offset may be incorrect . Probe for a valid mda_header at
* mda2_offset and at other possible / expected locations , e . g .
* 1 MB before end of device . Call dump_mda_header with a different
* offset than mda2_offset if there ' s no valid header at mda2_offset
* but there is a valid header elsewhere .
*/
if ( mda2_offset ) {
2019-06-06 00:23:23 +03:00
if ( ! _dump_mda_header ( cmd , 1 , 0 , 0 , NULL , dev , mda2_offset , mda2_size , & mda2_checksum , NULL ) )
2019-05-31 22:10:44 +03:00
bad + + ;
/* This probably indicates that one was committed and the other not. */
if ( mda1_checksum & & mda2_checksum & & ( mda1_checksum ! = mda2_checksum ) )
log_print ( " CHECK: mdas have different raw_locn[0].checksum values " ) ;
}
if ( bad ) {
log_error ( " Found bad header or metadata values. " ) ;
return ECMD_FAILED ;
}
return ECMD_PROCESSED ;
}
2019-10-22 00:39:46 +03:00
static int _dump_metadata ( struct cmd_context * cmd , uint64_t labelsector , struct device * dev ,
2019-06-05 19:10:37 +03:00
int argc , char * * argv ,
int print_metadata , int print_area )
2019-05-22 22:25:08 +03:00
{
const char * tofile = NULL ;
2019-05-31 22:10:44 +03:00
uint64_t mda1_offset = 0 , mda1_size = 0 , mda2_offset = 0 , mda2_size = 0 ;
uint32_t mda1_checksum , mda2_checksum ;
2019-10-01 00:20:17 +03:00
int mda_count = 0 ;
2019-05-22 22:25:08 +03:00
int mda_num = 1 ;
2019-05-31 22:10:44 +03:00
int bad = 0 ;
2019-05-22 22:25:08 +03:00
if ( arg_is_set ( cmd , file_ARG ) ) {
if ( ! ( tofile = arg_str_value ( cmd , file_ARG , NULL ) ) )
return ECMD_FAILED ;
}
/* 1: dump metadata from first mda, 2: dump metadata from second mda */
if ( arg_is_set ( cmd , pvmetadatacopies_ARG ) )
mda_num = arg_int_value ( cmd , pvmetadatacopies_ARG , 1 ) ;
2019-10-22 00:39:46 +03:00
if ( ! _dump_label_and_pv_header ( cmd , labelsector , dev , 0 , NULL ,
2019-10-01 00:20:17 +03:00
& mda1_offset , & mda1_size , & mda2_offset , & mda2_size , & mda_count ) )
2019-05-31 22:10:44 +03:00
bad + + ;
2019-05-22 22:25:08 +03:00
2019-10-01 00:20:17 +03:00
if ( ! mda_count ) {
log_print ( " zero metadata copies " ) ;
return ECMD_PROCESSED ;
}
2019-05-22 22:25:08 +03:00
2019-05-31 22:10:44 +03:00
/*
* The first mda is always 4096 bytes from the start of the device .
*
* TODO : A second mda may not exist . If the pv_header says there
* is no second mda , we may still want to check for a second mda
* in case it ' s the pv_header that is wrong . Try looking for
* an mda_header at 1 MB prior to the end of the device , if
* mda2_offset is 0 or if we don ' t find an mda_header at mda2_offset
* which may have been corrupted .
*
* mda2_offset may be incorrect . Probe for a valid mda_header at
* mda2_offset and at other possible / expected locations , e . g .
* 1 MB before end of device . Call dump_mda_header with a different
* offset than mda2_offset if there ' s no valid header at mda2_offset
* but there is a valid header elsewhere .
*/
2019-05-22 22:25:08 +03:00
2019-05-31 22:10:44 +03:00
if ( mda_num = = 1 ) {
2019-06-06 00:23:23 +03:00
if ( ! _dump_mda_header ( cmd , 0 , print_metadata , print_area , tofile , dev , 4096 , mda1_size , & mda1_checksum , NULL ) )
2019-05-31 22:10:44 +03:00
bad + + ;
} else if ( mda_num = = 2 ) {
if ( ! mda2_offset ) {
log_print ( " CHECK: second mda not found " ) ;
bad + + ;
} else {
2019-06-06 00:23:23 +03:00
if ( ! _dump_mda_header ( cmd , 0 , print_metadata , print_area , tofile , dev , mda2_offset , mda2_size , & mda2_checksum , NULL ) )
2019-05-31 22:10:44 +03:00
bad + + ;
}
2019-05-22 22:25:08 +03:00
}
2019-05-31 22:10:44 +03:00
if ( bad ) {
log_error ( " Found bad header or metadata values. " ) ;
2019-05-22 22:25:08 +03:00
return ECMD_FAILED ;
2019-05-31 22:10:44 +03:00
}
2019-05-22 22:25:08 +03:00
return ECMD_PROCESSED ;
}
2007-03-31 01:00:26 +04:00
2019-10-22 00:39:46 +03:00
static int _dump_found ( struct cmd_context * cmd , uint64_t labelsector , struct device * dev )
2019-06-06 00:23:23 +03:00
{
uint64_t mda1_offset = 0 , mda1_size = 0 , mda2_offset = 0 , mda2_size = 0 ;
uint32_t mda1_checksum = 0 , mda2_checksum = 0 ;
int found_label = 0 , found_header1 = 0 , found_header2 = 0 ;
2019-10-01 00:20:17 +03:00
int mda_count = 0 ;
2019-06-06 00:23:23 +03:00
int bad = 0 ;
2019-10-22 00:39:46 +03:00
if ( ! _dump_label_and_pv_header ( cmd , labelsector , dev , 0 , & found_label ,
2019-10-01 00:20:17 +03:00
& mda1_offset , & mda1_size , & mda2_offset , & mda2_size , & mda_count ) )
2019-06-06 00:23:23 +03:00
bad + + ;
if ( found_label & & mda1_offset ) {
if ( ! _dump_mda_header ( cmd , 0 , 0 , 0 , NULL , dev , 4096 , mda1_size , & mda1_checksum , & found_header1 ) )
bad + + ;
}
if ( found_label & & mda2_offset ) {
if ( ! _dump_mda_header ( cmd , 0 , 0 , 0 , NULL , dev , mda2_offset , mda2_size , & mda2_checksum , & found_header2 ) )
bad + + ;
}
if ( found_label )
log_print ( " Found label on %s, sector %llu, type=LVM2 001 " ,
dev_name ( dev ) , ( unsigned long long ) labelsector ) ;
else {
log_error ( " Could not find LVM label on %s " , dev_name ( dev ) ) ;
return 0 ;
}
if ( found_header1 )
log_print ( " Found text metadata area: offset=%llu, size=%llu " ,
( unsigned long long ) mda1_offset ,
( unsigned long long ) mda1_size ) ;
if ( found_header2 )
log_print ( " Found text metadata area: offset=%llu, size=%llu " ,
( unsigned long long ) mda2_offset ,
( unsigned long long ) mda2_size ) ;
if ( bad )
return 0 ;
return 1 ;
}
2019-10-18 01:24:22 +03:00
# define ONE_MB_IN_BYTES 1048576
/*
* Look for metadata text in common locations , without using any headers
* ( pv_header / mda_header ) to find the location , since the headers may be
* zeroed / damaged .
*/
2019-10-22 00:39:46 +03:00
static int _dump_search ( struct cmd_context * cmd , uint64_t labelsector , struct device * dev ,
2019-10-18 01:24:22 +03:00
int argc , char * * argv )
{
char str [ 256 ] ;
const char * tofile = NULL ;
char * buf ;
struct mda_header * mh ;
uint64_t mda1_offset = 0 , mda1_size = 0 , mda2_offset = 0 , mda2_size = 0 ;
uint64_t mda_offset , mda_size ;
int found_header = 0 ;
int mda_count = 0 ;
int mda_num = 1 ;
if ( arg_is_set ( cmd , file_ARG ) ) {
if ( ! ( tofile = arg_str_value ( cmd , file_ARG , NULL ) ) )
return ECMD_FAILED ;
}
/* 1: dump metadata from first mda, 2: dump metadata from second mda */
if ( arg_is_set ( cmd , pvmetadatacopies_ARG ) )
mda_num = arg_int_value ( cmd , pvmetadatacopies_ARG , 1 ) ;
2019-10-22 00:39:46 +03:00
_dump_label_and_pv_header ( cmd , labelsector , dev , 0 , NULL ,
2019-10-18 01:24:22 +03:00
& mda1_offset , & mda1_size , & mda2_offset , & mda2_size , & mda_count ) ;
/*
* TODO : allow mda_offset and mda_size to be specified on the
* command line .
*
* For mda1 , mda_offset is always 4096 bytes from the start of
* device , and mda_size is the space between mda_offset and
* the first PE which is usually at 1 MB .
*
* For mda2 , take the dev_size , reduce that to be a 1 MB
* multiple . The mda_offset is then 1 MB prior to that ,
* and mda_size is the amount of space between that offset
* and the end of the device .
*
* The second mda is generally 4 K larger ( at least ) than the
* first mda because the first mda begins at a 4 K offset from
* the start of the device and ends on a 1 MB boundary .
* The second mda begins on a 1 MB boundary ( no 4 K offset like
* mda1 ) , then goes to the end of the device . Extra space
* at the end of device ( mod 1 MB extra ) can make mda2 even
* larger .
*/
if ( mda_num = = 1 ) {
mda_offset = 4096 ;
mda_size = ONE_MB_IN_BYTES - 4096 ;
} else if ( mda_num = = 2 ) {
uint64_t dev_sectors = 0 ;
uint64_t dev_bytes ;
uint64_t extra_bytes ;
2019-11-14 21:31:30 +03:00
dev_get_size ( dev , & dev_sectors ) ;
2019-10-18 01:24:22 +03:00
dev_bytes = dev_sectors * 512 ;
extra_bytes = dev_bytes % ONE_MB_IN_BYTES ;
if ( dev_bytes < ( 2 * ONE_MB_IN_BYTES ) )
return ECMD_FAILED ;
mda_offset = dev_bytes - extra_bytes - ONE_MB_IN_BYTES ;
mda_size = dev_bytes - mda_offset ;
}
if ( ( mda_num = = 1 ) & & ( mda1_offset ! = mda_offset ) ) {
log_print ( " Ignoring mda1_offset %llu mda1_size %llu from pv_header. " ,
( unsigned long long ) mda1_offset ,
( unsigned long long ) mda1_size ) ;
}
if ( ( mda_num = = 2 ) & & ( mda2_offset ! = mda_offset ) ) {
log_print ( " Ignoring mda2_size %llu mda2_offset %llu from pv_header. " ,
( unsigned long long ) mda2_offset ,
( unsigned long long ) mda2_size ) ;
}
log_print ( " Searching for metadata in mda%d at offset %llu size %llu " , mda_num ,
( unsigned long long ) mda_offset , ( unsigned long long ) mda_size ) ;
2019-11-14 21:31:31 +03:00
if ( ! ( buf = malloc ( mda_size ) ) )
2019-10-18 01:24:22 +03:00
return ECMD_FAILED ;
2019-11-14 21:31:31 +03:00
memset ( buf , 0 , mda_size ) ;
2019-10-18 01:24:22 +03:00
if ( ! dev_read_bytes ( dev , mda_offset , mda_size , buf ) ) {
log_print ( " CHECK: failed to read metadata area at offset %llu size %llu " ,
( unsigned long long ) mda_offset , ( unsigned long long ) mda_size ) ;
free ( buf ) ;
return ECMD_FAILED ;
}
mh = ( struct mda_header * ) buf ;
/* Can be useful to know if there's a valid mda_header at this location. */
log_print ( " mda_header_%d at %llu # metadata area " , mda_num , ( unsigned long long ) mda_offset ) ;
log_print ( " mda_header_%d.checksum 0x%x " , mda_num , xlate32 ( mh - > checksum_xl ) ) ;
log_print ( " mda_header_%d.magic 0x%s " , mda_num , _chars_to_hexstr ( mh - > magic , str , 16 , 256 , " mda_header.magic " ) ) ;
log_print ( " mda_header_%d.version %u " , mda_num , xlate32 ( mh - > version ) ) ;
log_print ( " mda_header_%d.start %llu " , mda_num , ( unsigned long long ) xlate64 ( mh - > start ) ) ;
log_print ( " mda_header_%d.size %llu " , mda_num , ( unsigned long long ) xlate64 ( mh - > size ) ) ;
_check_mda_header ( mh , mda_num , mda_offset , mda_size , & found_header ) ;
log_print ( " searching for metadata text " ) ;
_dump_all_text ( cmd , tofile , dev , mda_num , mda_offset , mda_size , buf ) ;
free ( buf ) ;
return ECMD_PROCESSED ;
}
2007-03-31 01:00:26 +04:00
int pvck ( struct cmd_context * cmd , int argc , char * * argv )
{
2018-05-04 01:12:07 +03:00
struct device * dev ;
2019-05-22 22:25:08 +03:00
const char * dump ;
2018-05-04 01:12:07 +03:00
const char * pv_name ;
2019-06-06 00:23:23 +03:00
uint64_t labelsector = 1 ;
int bad = 0 ;
2007-04-26 00:03:16 +04:00
int i ;
2019-10-22 00:39:46 +03:00
/*
* By default LVM skips the first sector ( sector 0 ) , and writes
* the label_header in the second sector ( sector 1 ) .
* ( sector size 512 bytes )
*/
if ( arg_is_set ( cmd , labelsector_ARG ) )
labelsector = arg_uint64_value ( cmd , labelsector_ARG , UINT64_C ( 0 ) ) ;
2019-05-22 22:25:08 +03:00
if ( arg_is_set ( cmd , dump_ARG ) ) {
2019-10-22 00:39:46 +03:00
pv_name = argv [ 0 ] ;
if ( ! ( dev = dev_cache_get ( cmd , pv_name , cmd - > filter ) ) ) {
log_error ( " No device found for %s %s. " , pv_name , dev_cache_filtered_reason ( pv_name ) ) ;
return ECMD_FAILED ;
}
}
label_scan_setup_bcache ( ) ;
if ( arg_is_set ( cmd , dump_ARG ) ) {
cmd - > use_hints = 0 ;
2019-05-22 22:25:08 +03:00
dump = arg_str_value ( cmd , dump_ARG , NULL ) ;
if ( ! strcmp ( dump , " metadata " ) )
2019-10-22 00:39:46 +03:00
return _dump_metadata ( cmd , labelsector , dev , argc , argv , PRINT_CURRENT , 0 ) ;
2019-06-05 19:10:37 +03:00
if ( ! strcmp ( dump , " metadata_all " ) )
2019-10-22 00:39:46 +03:00
return _dump_metadata ( cmd , labelsector , dev , argc , argv , PRINT_ALL , 0 ) ;
2019-05-22 22:25:08 +03:00
if ( ! strcmp ( dump , " metadata_area " ) )
2019-10-22 00:39:46 +03:00
return _dump_metadata ( cmd , labelsector , dev , argc , argv , 0 , 1 ) ;
2019-05-22 22:25:08 +03:00
2019-10-18 01:24:22 +03:00
if ( ! strcmp ( dump , " metadata_search " ) )
2019-10-22 00:39:46 +03:00
return _dump_search ( cmd , labelsector , dev , argc , argv ) ;
2019-10-18 01:24:22 +03:00
2019-05-31 22:10:44 +03:00
if ( ! strcmp ( dump , " headers " ) )
2019-10-22 00:39:46 +03:00
return _dump_headers ( cmd , labelsector , dev , argc , argv ) ;
2019-05-31 22:10:44 +03:00
2019-05-22 22:25:08 +03:00
log_error ( " Unknown dump value. " ) ;
return ECMD_FAILED ;
}
2019-06-06 00:23:23 +03:00
/*
* The old / original form of pvck , which did not do much ,
* but this is here to preserve the historical output .
*/
2018-02-15 20:06:50 +03:00
2019-06-06 00:23:23 +03:00
for ( i = 0 ; i < argc ; i + + ) {
2018-05-04 01:12:07 +03:00
pv_name = argv [ i ] ;
2019-06-06 00:23:23 +03:00
if ( ! ( dev = dev_cache_get ( cmd , argv [ i ] , cmd - > filter ) ) ) {
2018-05-04 01:12:07 +03:00
log_error ( " Device %s %s. " , pv_name , dev_cache_filtered_reason ( pv_name ) ) ;
continue ;
}
2019-10-22 00:39:46 +03:00
if ( ! _dump_found ( cmd , labelsector , dev ) )
2019-06-06 00:23:23 +03:00
bad + + ;
2007-04-26 00:03:16 +04:00
}
2019-06-06 00:23:23 +03:00
if ( bad )
return ECMD_FAILED ;
return ECMD_PROCESSED ;
2007-03-31 01:00:26 +04:00
}