v2_03_07 annotated tag has been created
by Marian Csontos
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=bd00599af4b57112904...
Commit: bd00599af4b57112904de27ee43dfa50cac72c18
Parent: 0000000000000000000000000000000000000000
Author: Marian Csontos <mcsontos(a)redhat.com>
AuthorDate: 2019-11-30 13:54 +0000
Committer: Marian Csontos <mcsontos(a)redhat.com>
CommitterDate: 2019-11-30 13:54 +0000
annotated tag: v2_03_07 has been created
at bd00599af4b57112904de27ee43dfa50cac72c18 (tag)
tagging 3d7f755674c651a012f7dc6c10e67bdd330a33ae (commit)
replaces v2_03_06
Release 2.03.07
- Subcommand in vgck for repairing headers and metadata.
- Fix resize of thin-pool with data and metadata of different segtype.
- Fix activation order when removing merged snapshot.
- Experimental VDO support for lvmdbusd.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)
iQIcBAABAgAGBQJd4nUbAAoJELkRJDHlCQOfUCsQAOPSm1zwahQMMmzyviFUGDBo
x6D/yZOC4P/jV+mTvyaKykrZIwWDuM1m0O2E8C+/6EIbJYUMktEffGL6zzaIM43A
vV8kyRrp+MnuqySKc/sqes37NM0RqUgg3fjzYV36s8AST5xvvcWfGehKTz4dO8sq
ZuOIuGl55O2NdeLAKKMoDVwzBky7gp1ouNX6vcAPgdASFHzBC5SbKlZpmQwweOgy
f9KDkrWTPv6j76nCqkwC7nx6yE0/3UAilnFp0PvFgAMXYcFsSuV9rtsw0yUS5iQP
i6hUdPQnjGYrpWVY0l9D6QTGd/iHyE3CC/cCESkGVvLchJcsGUcF2SVOo2TyOj50
lSknU8fpz/fnDxBGdGdMe6IQL16RfKrYSISSzy1qKCWFfODlD8GJClYlwyxUg1D8
xc6taMPKo6bOR32RfrrS+GgMwd9tTxYrvoza9V4Baoh7ZqC+ptD+jynMYwGlCrBw
1E0YNFS6rfplCCtMsH9Ngl9i/bjIUcmQ+TSN9HoCKZYM19zJhjVJaKdqYbm2RF6c
bTN51W+/Zk0eLV/HPgqJNMdhX3jNTZDZyVWXvFatzGNO8V/kWe4eF+aQcneJNFWV
T5orqbusNXCY1UVgVueFC+G2hRcqA+LRVwOTJZXZgv8PHmMEJzdttrd0h/C0z0FM
GzIb/AlWVENSvkWg7g1s
=EDW7
-----END PGP SIGNATURE-----
David Teigland (27):
man lvmthin: change wording about mounting xfs
tests: lvmlockd-lv-types handle new cpool renaming
lvmlockd: fix cachevol locking
Revert "debug: enhance debug messages"
Revert "hints: rewrite function"
lvs: disable scanning optimization
tests: update to md dev name
writecache: modprobe dm-writecache
pvcreate,pvremove: fix reacquiring global lock after prompt
bcache: add invalidate_bytes function
scanning: optimize by checking text offset and checksum
Revert "lvs: disable scanning optimization"
tests hints: update check for io count
scanning: use bool type for _scan_text_mismatch
Revert "cov: check for retvalue"
Revert "cov: avoid passing NULL to strstr function"
Revert "cov: fix mem leaking buffer"
Revert "pvck: check result of dev_get_size"
Revert "cov: use zalloc"
metadata: add vg_from_config_tree
scan: add simple scan to find a pvid
pvck: move some arg processing
pvck: allow disk locations to be specified
pvck: dump show most recent metadata
pvck: dump functions cleanup args and return vals
pvck: repair headers and metadata
pvck: use zalloc in more places
Heinz Mauelshagen (2):
man: adjust 'disks' to 'devices' as used throughout
lvcreate: ensure striped raid region size is at least stripe size
Heming Zhao (1):
fix dev_unset_last_byte after write error
Joe Thornber (6):
[bcache] add bcache_abort()
[label] Use bcache_abort_fd() to ensure blocks are no longer in the cache.
[bcache] add unit test
[bcache] pass up the error from io_submit rather than using generic -EIO
[bcache] reverse earlier patch.
[bcache] bcache_invalidate_fd, only remove prefixes on success.
Marian Csontos (7):
post-release
test: Fix metadata-zero-space with long VG names
test: Fix handling leftovers from previous tests
Partial revert "tests: update to md dev name"
tests: Find md name using lsblk
build: make generate
pre-release
Tony Asleson (34):
lvmdbusd: Remove duplicate error handling code
lvmdbusd: Remove use of tmp variables
lvmdbusd: Add d and D to type map for VolumeType
lvmdbustest.py: Add blurb about scan_lvs = 1
lvmdbusd: Add check for reserved name '_vdata'
man: Include '_vdata' as reserved name
man lvmvdo: Correct spellings
lvmdbustest.py: Remove duplicate setup code
lvmdbusdtest.py: Use common function for tag add
lvmdbustest.py: Add function for lv path check
lvmdbustest.py: Add func. _all_pv_object_paths
lvmdbustest.py: Add func. _create_thin_lv
lvmdbustest.py: Use existing _create_lv
lvmdbustest.py: Add func. _pv_scan
lvmdbustest.py: Remove 2 TODOs
lvmdbusd: Add cfg.vdo_support
lvmdbusd: Add VgVdo class & assoc. interface
lvmdbustest.py: Add cache LV rename test
lvmdbusd: Prevent running --nojson with VDO support
lvmdbustest.py: WS corrections
lvmdbustest.py: Add nested helper function major_minor
lvmdbustest.py: Create common func. _create_cache_lv
lvmdbusd: VDO Pool LV representation
testlib.py: Correct dbus signature verification
lvmdbustest.py: Add basic vdo test
testlib.py: WS corrections
testlib.py: Add interface instance vars.
lvmdbusd: Bug fix for activate/deactivate
lvmdbusd: Fix model inconsistency when LV loses interface
lvmdbusd: Debug msg. improvements.
lvmdbustest.py: Add tests for LV interface
lvmdbustest.py: Improve concurrent test handling
lvmdbustest.py: Use local data instead of fetching
Experimental VDO lvmdbusd support
Zdenek Kabelac (55):
snapshot: correctly check device id of merged thin
snapshot: fix checking of merged thin volume
snapshot: use single merging sequence
tests: explicit testing of thin snapshot
WHATS_NEW
mirror: fix leg splitting
mirror: directly activate updated mirror
mirror: remove unused code
manip: optimize lvs_using_lv
raid: drop internal error
tests: reduce space requirements
activation: drop removed declaration
WHATS_NEW: update
tests: avoid checking command result in cluster
tests: conversion only of exclusive lv
tests: slowdown delay of raid
tests: skip test if scsi_debug is not available
tests: be happy with less then 90 percent
gcc: avoid declaration shadowing
cov: missing checks of syscalls
cov: ensure read_ahead is available
daemon: better error path handling for shutdown
daemons: check for non-zero thread_id
tests: skip unneeded status check
tests: add extra settle
lvextend: fix resizing volumes of different segtype
tests: add test of resize of different segtypes
WHATS_NEW: update
cov: fix memory leak
devtype: simplify code
cov: check for NULL
cov: fix memleak for duplicate device
cov: check for retvalue
cov: avoid passing NULL to strstr function
cov: fix mem leaking buffer
pvck: check result of dev_get_size
hints: rewrite function
hints: allocate hint only when needed
hints: validate sscanf results
hints: validate allocation result
hints: fix mem leaking buffers
hints: check for _touch_hints
cov: inline _build_desc_write
dmeventd: vdo plugin link lvm library
cov: more checks for failing syscalls
cov: remove unused headers
cov: add explicit ret value ignoring
cov: use zalloc
hints: no need to check for NULL before free
hints: drop unneeded memset
debug: enhance debug messages
tests: enusure lib is initilized
revert "dmeventd: vdo plugin link lvm library"
tests: reduce amount of written date
tests: add more tracing info
4 years
master - post-release
by Marian Csontos
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=91f91b80f1c0879b48b...
Commit: 91f91b80f1c0879b48b683cc762512c0c980cb7c
Parent: 3d7f755674c651a012f7dc6c10e67bdd330a33ae
Author: Marian Csontos <mcsontos(a)redhat.com>
AuthorDate: Sat Nov 30 14:46:56 2019 +0100
Committer: Marian Csontos <mcsontos(a)redhat.com>
CommitterDate: Sat Nov 30 14:46:56 2019 +0100
post-release
---
VERSION | 2 +-
VERSION_DM | 2 +-
WHATS_NEW | 3 +++
WHATS_NEW_DM | 3 +++
4 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 717c4cf..51f8e69 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.03.07(2) (2019-11-30)
+2.03.08(2)-git (2019-11-30)
diff --git a/VERSION_DM b/VERSION_DM
index a230ed0..933e2d3 100644
--- a/VERSION_DM
+++ b/VERSION_DM
@@ -1 +1 @@
-1.02.167 (2019-11-30)
+1.02.169-git (2019-11-30)
diff --git a/WHATS_NEW b/WHATS_NEW
index 4adcfb3..ec41e54 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,3 +1,6 @@
+Version 2.03.08 -
+====================================
+
Version 2.03.07 - 30th November 2019
====================================
Subcommand in vgck for repairing headers and metadata.
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 663f461..b64a9a2 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,3 +1,6 @@
+Version 1.02.169 -
+=====================================
+
Version 1.02.167 - 30th November 2019
=====================================
4 years
master - pre-release
by Marian Csontos
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=3d7f755674c651a012f...
Commit: 3d7f755674c651a012f7dc6c10e67bdd330a33ae
Parent: 0a7495e6808473ee60385d3a4b6f48ac2240da72
Author: Marian Csontos <mcsontos(a)redhat.com>
AuthorDate: Sat Nov 30 14:45:51 2019 +0100
Committer: Marian Csontos <mcsontos(a)redhat.com>
CommitterDate: Sat Nov 30 14:45:51 2019 +0100
pre-release
---
VERSION | 2 +-
VERSION_DM | 2 +-
WHATS_NEW | 6 ++++--
WHATS_NEW_DM | 4 ++--
4 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/VERSION b/VERSION
index 720a8c8..717c4cf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.03.07(2)-git (2019-10-23)
+2.03.07(2) (2019-11-30)
diff --git a/VERSION_DM b/VERSION_DM
index 3045b1d..a230ed0 100644
--- a/VERSION_DM
+++ b/VERSION_DM
@@ -1 +1 @@
-1.02.167-git (2019-10-23)
+1.02.167 (2019-11-30)
diff --git a/WHATS_NEW b/WHATS_NEW
index 41d0e56..4adcfb3 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,8 +1,10 @@
-Version 2.03.07 -
-===================================
+Version 2.03.07 - 30th November 2019
+====================================
+ Subcommand in vgck for repairing headers and metadata.
Ensure minimum required region size on striped RaidLV creation.
Fix resize of thin-pool with data and metadata of different segtype.
Improve mirror type leg splitting.
+ Improve error path handling in daemons on shutdown.
Fix activation order when removing merged snapshot.
Experimental VDO support for lvmdbusd.
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 6fc70ca..663f461 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,5 @@
-Version 1.02.167 -
-====================================
+Version 1.02.167 - 30th November 2019
+=====================================
Version 1.02.165 - 23rd October 2019
====================================
4 years
master - build: make generate
by Marian Csontos
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=0a7495e6808473ee603...
Commit: 0a7495e6808473ee60385d3a4b6f48ac2240da72
Parent: 5a88b2ce7fcdffec909f7f236d9bcfe0a615b6b9
Author: Marian Csontos <mcsontos(a)redhat.com>
AuthorDate: Sat Nov 30 14:24:22 2019 +0100
Committer: Marian Csontos <mcsontos(a)redhat.com>
CommitterDate: Sat Nov 30 14:24:22 2019 +0100
build: make generate
---
man/pvck.8_pregen | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 233 insertions(+), 12 deletions(-)
diff --git a/man/pvck.8_pregen b/man/pvck.8_pregen
index 6cdfe42..ea9d17d 100644
--- a/man/pvck.8_pregen
+++ b/man/pvck.8_pregen
@@ -7,14 +7,173 @@ pvck - Check metadata on physical volumes
.br
[ \fIoption_args\fP ]
.br
+.P
+.ad l
+ \fB--commandprofile\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB--config\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB-d\fP|\fB--debug\fP
+.ad b
+.br
+.ad l
+ \fB--driverloaded\fP \fBy\fP|\fBn\fP
+.ad b
+.br
+.ad l
+ \fB--dump\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB-f\fP|\fB--file\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB-h\fP|\fB--help\fP
+.ad b
+.br
+.ad l
+ \fB--labelsector\fP \fINumber\fP
+.ad b
+.br
+.ad l
+ \fB--lockopt\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB--longhelp\fP
+.ad b
+.br
+.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
+ \fB--profile\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB--[pv]metadatacopies\fP \fB0\fP|\fB1\fP|\fB2\fP
+.ad b
+.br
+.ad l
+ \fB-q\fP|\fB--quiet\fP
+.ad b
+.br
+.ad l
+ \fB--repair\fP
+.ad b
+.br
+.ad l
+ \fB--repairtype\fP \fBpv_header\fP|\fBmetadata\fP|\fBlabel_header\fP
+.ad b
+.br
+.ad l
+ \fB--settings\fP \fIString\fP
+.ad b
+.br
+.ad l
+ \fB-t\fP|\fB--test\fP
+.ad b
+.br
+.ad l
+ \fB-v\fP|\fB--verbose\fP
+.ad b
+.br
+.ad l
+ \fB--version\fP
+.ad b
+.br
+.ad l
+ \fB-y\fP|\fB--yes\fP
+.ad b
.SH DESCRIPTION
-pvck checks LVM metadata on PVs.
+pvck checks and repairs LVM metadata on PVs.
-Use the --dump option to extract metadata from PVs for debugging.
-With dump, set --pvmetadatacopies 2 to extract metadata from a
-second metadata area at the end of the device. Use the --file
-option to save the raw metadata to a specified file. (The raw
-metadata is not usable with vgcfgbackup and vgcfgrestore.)
+.SS Dump
+
+.B headers
+.br
+Print header values and warn if any values are incorrect. Checks the
+label_header, pv_header, mda_header(s), and metadata text.
+
+.B metadata
+.br
+Print or save the current metadata text, using headers to locate the
+metadata. If headers are damaged, the metadata may not be found. Use
+--settings "mda_num=2" to look in mda2 (the second mda at the end of the
+device (if used). The metadata text is printed to stdout. With --file,
+the metadata text is saved to a file.
+
+.B metadata_all
+.br
+List or save all versions of metadata found in the metadata area, using
+headers to locate the metadata. If headers are damaged, the metadata may
+not be found. Use --settings "mda_num=2" as above. All metadata versions
+are listed (add -v to include descriptions and dates in the listing.)
+With -file, all versions are written to a file.
+
+.B metadata_search
+.br
+Search for all versions of metadata in the common locations. This does
+not use headers, so it can find metadata even when headers are damaged.
+Use --settings "mda_num=2" as above. All metadata versions are listed
+(add -v to include descriptions and dates in the listing.) With --file,
+all versions are written to a file. To save one copy of metadata, use
+--settings "metadata_offset=<offset>", where the offset is taken from the
+dump listing.
+
+.B metadata_area
+.br
+Save the entire text metadata area to a file without processing.
+
+.SS Repair
+
+.B --repair
+.br
+Repair headers and metadata on a PV. This uses a metadata input file that
+was extracted by --dump, or a backup file (from /etc/lvm/backup). When
+possible, use metadata saved by --dump from another PV in the same VG (or
+from a second metadata area on the PV).
+
+There are cases where the PV UUID needs to be specified for the PV being
+repaired. It is specified using --settings "pv_uuid=<UUID>". In
+particular, if the device name for the PV being repaired does not match
+the previous device name of the PV, then LVM may not be able to determine
+the correct PV UUID. When headers are damaged on more than one PV in a
+VG, it is important for the user to determine the correct PV UUID and
+specify it in --settings. Otherwise, the wrong PV UUID could be used if
+device names have been swapped since the metadata was last written.
+
+If a PV had no metadata areas and the pv_header is damaged, then the
+repair will not know to create no metadata areas during repair. It will
+by default repair metadata in mda1. To repair with no metadata areas, use
+--settings "mda_offset=0 mda_size=0".
+
+There are cases where repair should be run on all PVs in the VG (using the
+same metadata file): if all PVs in the VG are damaged, if using an old
+metadata version, or if a backup file is used instead of raw metadata.
+
+Using --repair is equivalent to running --repairtype pv_header followed by
+--repairtype metadata.
+
+.B --repairtype pv_header
+.br
+Repairs the header sector, containing the pv_header and label_header.
+
+.B --repairtype metadata
+.br
+Repairs the mda_header and metadata text. It requires the headers to be
+correct (having been undamaged or already repaired).
+
+.B --repairtype label_header
+.br
+Repairs label_header fields, leaving the pv_header (in the same sector)
+unchanged. (repairtype pv_header should usually be used instead.)
.SH USAGE
Check for metadata on a device
@@ -26,8 +185,9 @@ Check for metadata on a device
[ COMMON_OPTIONS ]
.RE
.br
+-
-Print metadata from a device
+Check and print LVM headers and metadata on a device
.br
.P
\fBpvck\fP \fB--dump\fP \fIString\fP \fIPV\fP
@@ -38,12 +198,51 @@ Print metadata from a device
.ad b
.br
.ad l
+[ \fB--settings\fP \fIString\fP ]
+.ad b
+.br
+.ad l
[ \fB--[pv]metadatacopies\fP \fB0\fP|\fB1\fP|\fB2\fP ]
.ad b
.br
[ COMMON_OPTIONS ]
.RE
.br
+-
+
+Repair LVM headers or metadata on a device
+.br
+.P
+\fBpvck\fP \fB--repairtype\fP \fBpv_header\fP|\fBmetadata\fP|\fBlabel_header\fP \fIPV\fP
+.br
+.RS 4
+.ad l
+[ \fB-f\fP|\fB--file\fP \fIString\fP ]
+.ad b
+.br
+.ad l
+[ \fB--settings\fP \fIString\fP ]
+.ad b
+.br
+[ COMMON_OPTIONS ]
+.RE
+.br
+-
+
+Repair LVM headers and metadata on a device
+.br
+.P
+\fBpvck\fP \fB--repair\fP \fB-f\fP|\fB--file\fP \fIString\fP \fIPV\fP
+.br
+.RS 4
+.ad l
+[ \fB--settings\fP \fIString\fP ]
+.ad b
+.br
+[ COMMON_OPTIONS ]
+.RE
+.br
+-
Common options for command:
.
@@ -147,16 +346,19 @@ For testing and debugging.
.ad l
\fB--dump\fP \fIString\fP
.br
-Dump metadata from a PV. Option values include \fBmetadata\fP
-to print or save the current text metadata, \fBmetadata_area\fP
-to save the entire text metadata area to a file, \fBmetadata_all\fP
-to save the current and any previous complete versions of metadata
-to a file, and \fBheaders\fP to print and check LVM headers.
+Dump headers and metadata from a PV for debugging and repair.
+Option values include: \fBheaders\fP to print and check LVM headers,
+\fBmetadata\fP to print or save the current text metadata,
+\fBmetadata_all\fP to list or save all versions of metadata,
+\fBmetadata_search\fP to list or save all versions of metadata,
+searching standard locations in case of damaged headers,
+\fBmetadata_area\fP to save an entire text metadata area to a file.
.ad b
.HP
.ad l
\fB-f\fP|\fB--file\fP \fIString\fP
.br
+Metadata file to read or write.
.ad b
.HP
.ad l
@@ -220,6 +422,25 @@ Repeat once to also suppress any prompts with answer 'no'.
.ad b
.HP
.ad l
+\fB--repair\fP
+.br
+Repair headers and metadata on a PV.
+.ad b
+.HP
+.ad l
+\fB--repairtype\fP \fBpv_header\fP|\fBmetadata\fP|\fBlabel_header\fP
+.br
+Repair headers and metadata on a PV. See command description.
+.ad b
+.HP
+.ad l
+\fB--settings\fP \fIString\fP
+.br
+Specifies command specific settings in "Key = Value" form.
+Repeat this option to specify multiple values.
+.ad b
+.HP
+.ad l
\fB-t\fP|\fB--test\fP
.br
Run in test mode. Commands will not update metadata.
4 years
master - pvck: repair headers and metadata
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=3145a855836c4cf50d4...
Commit: 3145a855836c4cf50d4b6064b3e6f1ce4a366aad
Parent: 2e0f2730081554460328b3ac7e28080bd902535c
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Oct 29 16:08:43 2019 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Nov 27 11:13:47 2019 -0600
pvck: repair headers and metadata
To write a new/repaired pv_header and label_header:
pvck --repairtype pv_header --file <file> <device>
This uses the metadata input file to find the PV UUID,
device size, and data offset.
To write new/repaired metadata text and mda_header:
pvck --repairtype metadata --file <file> <device>
This requires a good pv_header which points to one or two
metadata areas. Any metadata areas referenced by the
pv_header are updated with the specified metadata and
a new mda_header. "--settings mda_num=1|2" can be used
to select one mda to repair.
To combine all header and metadata repairs:
pvck --repair --file <file> <device>
It's best to use a raw metadata file as input, that was
extracted from another PV in the same VG (or from another
metadata area on the same PV.) pvck will also accept a
metadata backup file, but that will produce metadata that
is not identical to other metadata copies on other PVs
and other areas. So, when using a backup file, consider
using it to update metadata on all PVs/areas.
To get a raw metadata file to use for the repair, see
pvck --dump metadata|metadata_search.
List all instances of metadata from the metadata area:
pvck --dump metadata_search <device>
Save one instance of metadata at the given offset to
the specified file (this file can be used for repair):
pvck --dump metadata_search --file <file>
--settings "metadata_offset=<off>" <device>
---
man/pvck.8_des | 87 +++-
test/shell/pvck-repair.sh | 452 ++++++++++++++
tools/args.h | 22 +-
tools/command-lines.in | 15 +-
tools/command.c | 1 +
tools/lvmcmdline.c | 9 +
tools/pvck.c | 1461 ++++++++++++++++++++++++++++++++++++++++++---
tools/tools.h | 1 +
tools/vals.h | 1 +
9 files changed, 1946 insertions(+), 103 deletions(-)
diff --git a/man/pvck.8_des b/man/pvck.8_des
index fb826d3..2169d73 100644
--- a/man/pvck.8_des
+++ b/man/pvck.8_des
@@ -1,8 +1,83 @@
-pvck checks LVM metadata on PVs.
+pvck checks and repairs LVM metadata on PVs.
-Use the --dump option to extract metadata from PVs for debugging.
-With dump, set --pvmetadatacopies 2 to extract metadata from a
-second metadata area at the end of the device. Use the --file
-option to save the raw metadata to a specified file. (The raw
-metadata is not usable with vgcfgbackup and vgcfgrestore.)
+.SS Dump
+
+.B headers
+.br
+Print header values and warn if any values are incorrect. Checks the
+label_header, pv_header, mda_header(s), and metadata text.
+
+.B metadata
+.br
+Print or save the current metadata text, using headers to locate the
+metadata. If headers are damaged, the metadata may not be found. Use
+--settings "mda_num=2" to look in mda2 (the second mda at the end of the
+device (if used). The metadata text is printed to stdout. With --file,
+the metadata text is saved to a file.
+
+.B metadata_all
+.br
+List or save all versions of metadata found in the metadata area, using
+headers to locate the metadata. If headers are damaged, the metadata may
+not be found. Use --settings "mda_num=2" as above. All metadata versions
+are listed (add -v to include descriptions and dates in the listing.)
+With -file, all versions are written to a file.
+
+.B metadata_search
+.br
+Search for all versions of metadata in the common locations. This does
+not use headers, so it can find metadata even when headers are damaged.
+Use --settings "mda_num=2" as above. All metadata versions are listed
+(add -v to include descriptions and dates in the listing.) With --file,
+all versions are written to a file. To save one copy of metadata, use
+--settings "metadata_offset=<offset>", where the offset is taken from the
+dump listing.
+
+.B metadata_area
+.br
+Save the entire text metadata area to a file without processing.
+
+.SS Repair
+
+.B --repair
+.br
+Repair headers and metadata on a PV. This uses a metadata input file that
+was extracted by --dump, or a backup file (from /etc/lvm/backup). When
+possible, use metadata saved by --dump from another PV in the same VG (or
+from a second metadata area on the PV).
+
+There are cases where the PV UUID needs to be specified for the PV being
+repaired. It is specified using --settings "pv_uuid=<UUID>". In
+particular, if the device name for the PV being repaired does not match
+the previous device name of the PV, then LVM may not be able to determine
+the correct PV UUID. When headers are damaged on more than one PV in a
+VG, it is important for the user to determine the correct PV UUID and
+specify it in --settings. Otherwise, the wrong PV UUID could be used if
+device names have been swapped since the metadata was last written.
+
+If a PV had no metadata areas and the pv_header is damaged, then the
+repair will not know to create no metadata areas during repair. It will
+by default repair metadata in mda1. To repair with no metadata areas, use
+--settings "mda_offset=0 mda_size=0".
+
+There are cases where repair should be run on all PVs in the VG (using the
+same metadata file): if all PVs in the VG are damaged, if using an old
+metadata version, or if a backup file is used instead of raw metadata.
+
+Using --repair is equivalent to running --repairtype pv_header followed by
+--repairtype metadata.
+
+.B --repairtype pv_header
+.br
+Repairs the header sector, containing the pv_header and label_header.
+
+.B --repairtype metadata
+.br
+Repairs the mda_header and metadata text. It requires the headers to be
+correct (having been undamaged or already repaired).
+
+.B --repairtype label_header
+.br
+Repairs label_header fields, leaving the pv_header (in the same sector)
+unchanged. (repairtype pv_header should usually be used instead.)
diff --git a/test/shell/pvck-repair.sh b/test/shell/pvck-repair.sh
new file mode 100644
index 0000000..0e83848
--- /dev/null
+++ b/test/shell/pvck-repair.sh
@@ -0,0 +1,452 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2008-2013,2018 Red Hat, Inc. All rights reserved.
+#
+# 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 General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. lib/inittest
+
+aux prepare_devs 2
+get_devs
+
+# One PV, one mda, pv_header zeroed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate $vg "$dev1"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+pvck --dump headers "$dev1" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, one mda, mda_header zeroed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate $vg "$dev1"
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, one mda, pv_header and mda_header zeroed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate $vg "$dev1"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, one mda, metadata zeroed, use backup
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate $vg "$dev1"
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2 seek=9
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, one mda, mda_header and metadata zeroed, use backup
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate $vg "$dev1"
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, one mda, pv_header, mda_header and metadata zeroed, use backup
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate $vg "$dev1"
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, pv_header zeroed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+pvck --dump headers "$dev1" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, mda_header1 zeroed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata_search --settings mda_num=1 "$dev1" || true
+pvck --dump metadata_search --settings mda_num=2 "$dev1" || true
+pvck --dump metadata --settings mda_num=1 "$dev1" || true
+pvck --dump metadata --settings mda_num=2 "$dev1" || true
+pvck --dump metadata --settings mda_num=2 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, pv_header and mda_header1 zeroed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata --settings mda_num=2 "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --dump metadata_search --settings mda_num=2 "$dev1" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, metadata1 zeroed, use mda2
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+dd if=/dev/zero of="$dev1" bs=512 count=2 seek=9
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata --settings mda_num=2 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, mda_header1 and metadata1 zeroed, use mda2
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata --settings mda_num=2 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, pv_header, mda_header1 and metadata1 zeroed, use mda2
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata --settings mda_num=2 "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --dump metadata_search --settings mda_num=2 "$dev1" || true
+pvck --dump metadata_search --settings "mda_num=2 seqno=1" -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, pv_header, both mda_header, and both metadata zeroed, use backup
+# only writes mda1 since there's no evidence that mda2 existed
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=67584
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata --settings mda_num=2 "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --dump metadata_search --settings mda_num=2 "$dev1" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, two mdas, pv_header, both mda_header, and both metadata zeroed, use backup
+# writes mda1 and also mda2 because of the mda2 settings passed to repair
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+vgcreate --pvmetadatacopies 2 $vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=67584
+pvck --dump headers "$dev1" || true
+pvck --dump metadata "$dev1" || true
+pvck --dump metadata --settings mda_num=2 "$dev1" || true
+pvck --dump metadata_search "$dev1" || true
+pvck --dump metadata_search --settings mda_num=2 "$dev1" || true
+pvck --repair --settings "mda2_offset=34603008 mda2_size=1048576" -y -f etc/backup/$vg "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, pv_header and mda_header zeroed on each
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev2" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+dd if=/dev/zero of="$dev2" bs=512 count=1 seek=8
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --repair -y -f meta "$dev2"
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, metadata zeroed on each, use backup
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1" "$dev2"
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2 seek=9
+dd if=/dev/zero of="$dev2" bs=512 count=2 seek=9
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --repair -y -f etc/backup/$vg "$dev2"
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, pv_header, mda_header and metadata zeroed on each, use backup
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1" "$dev2"
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev2" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+dd if=/dev/zero of="$dev2" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --repair -y -f etc/backup/$vg "$dev2"
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, pv_header and mda_header zeroed on first
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --dump metadata -f meta "$dev2"
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, metadata zeroed on first
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2 seek=9
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --dump metadata -f meta "$dev2"
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, pv_header, mda_header and metadata zeroed on first
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --dump metadata -f meta "$dev2"
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda on first, no mda on second, zero header on first
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+pvcreate "$dev1"
+pvcreate --pvmetadatacopies 0 "$dev2"
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --dump headers "$dev1"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda on first, no mda on second, zero headers on both
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+pvcreate "$dev1"
+pvcreate --pvmetadatacopies 0 "$dev2"
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev2" bs=512 count=2
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+pvck --repair -y --settings "mda_offset=0 mda_size=0" -f meta "$dev2"
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda on first, no mda on second, zero all on first
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+pvcreate "$dev1"
+pvcreate --pvmetadatacopies 0 "$dev2"
+vgcreate $vg "$dev1" "$dev2"
+vgcfgbackup
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=3 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --repair -y -f etc/backup/$vg "$dev1"
+pvck --repair -y --settings "mda_offset=0 mda_size=0" -f etc/backup/$vg "$dev2"
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, two mda on each, pv_header and mda_header1 zeroed on both
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+pvcreate --pvmetadatacopies 2 "$dev1"
+pvcreate --pvmetadatacopies 2 "$dev2"
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev2" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+dd if=/dev/zero of="$dev2" bs=512 count=1 seek=8
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+pvck --dump metadata_search --settings "mda_num=2 seqno=1" -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+rm meta
+pvck --dump metadata_search --settings "mda_num=2 seqno=1" -f meta "$dev2" || true
+pvck --repair -y -f meta "$dev2"
+rm meta
+pvck --dump headers "$dev1"
+pvck --dump headers "$dev2"
+vgs $vg
+lvcreate -l1 -an $vg
+
+# Two PV, one mda each, pv_header and mda_header zeroed on each,
+# non-standard data_offset/mda_size on first
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+pvcreate --metadatasize 2048k --dataalignment 128k "$dev1"
+pvcreate "$dev2"
+vgcreate $vg "$dev1" "$dev2"
+dd if=/dev/zero of="$dev1" bs=512 count=2
+dd if=/dev/zero of="$dev1" bs=512 count=1 seek=8
+dd if=/dev/zero of="$dev2" bs=512 count=2
+dd if=/dev/zero of="$dev2" bs=512 count=1 seek=8
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+pvck --repair -y -f meta "$dev1"
+rm meta
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev2" || true
+pvck --repair -y -f meta "$dev2"
+rm meta
+pvck --dump headers "$dev1" || true
+pvck --dump headers "$dev2" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
+# One PV, one mda, pv_header zeroed, unmatching dev name requires specified uuid
+rm meta || true
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+vgcreate $vg "$dev1"
+pvck --dump headers "$dev1" || true
+UUID1=`pvck --dump headers "$dev1" | grep pv_header.pv_uuid | awk '{print $2}'`
+echo $UUID1
+dd if=/dev/zero of="$dev1" bs=512 count=2
+pvck --dump headers "$dev1" || true
+pvck --dump metadata_search --settings seqno=1 -f meta "$dev1" || true
+sed 's/\/dev\/mapper\/LVMTEST/\/dev\/mapper\/BADTEST/' meta > meta.bad
+grep device meta
+grep device meta.bad
+not pvck --repair -y -f meta.bad "$dev1"
+pvck --repair -y -f meta.bad --settings pv_uuid=$UUID1 "$dev1"
+pvck --dump headers "$dev1" || true
+vgs $vg
+lvcreate -l1 -an $vg
+
diff --git a/tools/args.h b/tools/args.h
index 2f84d2c..533cc54 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -214,11 +214,13 @@ arg(driverloaded_ARG, '\0', "driverloaded", bool_VAL, 0, 0,
"For testing and debugging.\n")
arg(dump_ARG, '\0', "dump", string_VAL, 0, 0,
- "Dump metadata from a PV. Option values include \\fBmetadata\\fP\n"
- "to print or save the current text metadata, \\fBmetadata_area\\fP\n"
- "to save the entire text metadata area to a file, \\fBmetadata_all\\fP\n"
- "to save the current and any previous complete versions of metadata\n"
- "to a file, and \\fBheaders\\fP to print and check LVM headers.\n")
+ "Dump headers and metadata from a PV for debugging and repair.\n"
+ "Option values include: \\fBheaders\\fP to print and check LVM headers,\n"
+ "\\fBmetadata\\fP to print or save the current text metadata,\n"
+ "\\fBmetadata_all\\fP to list or save all versions of metadata,\n"
+ "\\fBmetadata_search\\fP to list or save all versions of metadata,\n"
+ "searching standard locations in case of damaged headers,\n"
+ "\\fBmetadata_area\\fP to save an entire text metadata area to a file.\n")
arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
"Specifies thin pool behavior when data space is exhausted.\n"
@@ -544,9 +546,15 @@ arg(rebuild_ARG, '\0', "rebuild", pv_VAL, ARG_GROUPABLE, 0,
"See \\fBlvmraid\\fP(7) for more information.\n")
arg(repair_ARG, '\0', "repair", 0, 0, 0,
+ "#lvconvert\n"
"Replace failed PVs in a raid or mirror LV, or run a repair\n"
"utility on a thin pool. See \\fBlvmraid\\fP(7) and \\fBlvmthin\\fP(7)\n"
- "for more information.\n")
+ "for more information.\n"
+ "#pvck\n"
+ "Repair headers and metadata on a PV.\n")
+
+arg(repairtype_ARG, '\0', "repairtype", repairtype_VAL, 0, 0,
+ "Repair headers and metadata on a PV. See command description.\n")
arg(replace_ARG, '\0', "replace", pv_VAL, ARG_GROUPABLE, 0,
"Replace a specific PV in a raid LV with another PV.\n"
@@ -1001,6 +1009,8 @@ arg(exported_ARG, 'e', "exported", 0, 0, 0,
arg(physicalextent_ARG, 'E', "physicalextent", 0, 0, 0, NULL)
arg(file_ARG, 'f', "file", string_VAL, 0, 0,
+ "#pvck\n"
+ "Metadata file to read or write.\n"
"#lvmconfig\n"
"#dumpconfig\n"
"#config\n"
diff --git a/tools/command-lines.in b/tools/command-lines.in
index cbd64a8..10165ea 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1437,9 +1437,20 @@ ID: pvck_general
DESC: Check for metadata on a device
pvck --dump String PV
-OO: --settings String, --file String, --pvmetadatacopies MetadataCopiesPV, --labelsector Number
+OO: --settings String, --file String,
+--pvmetadatacopies MetadataCopiesPV, --labelsector Number
ID: pvck_dump
-DESC: Print metadata from a device
+DESC: Check and print LVM headers and metadata on a device
+
+pvck --repairtype RepairType PV
+OO: --settings String, --file String, --labelsector Number
+ID: pvck_repair_type
+DESC: Repair LVM headers or metadata on a device
+
+pvck --repair --file String PV
+OO: --settings String, --labelsector Number
+ID: pvck_repair
+DESC: Repair LVM headers and metadata on a device
---
diff --git a/tools/command.c b/tools/command.c
index 2e69eff..7bad98d 100644
--- a/tools/command.c
+++ b/tools/command.c
@@ -122,6 +122,7 @@ static inline int syncaction_arg(struct cmd_context *cmd __attribute__((unused))
static inline int reportformat_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
static inline int configreport_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
static inline int configtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
+static inline int repairtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
/* needed to include commands.h when building man page generator */
#define CACHE_VGMETADATA 0x00000001
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 860e6de..3d79e97 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -1077,6 +1077,15 @@ int configtype_arg(struct cmd_context *cmd, struct arg_values *av)
return 0;
}
+int repairtype_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+ if (!strcmp(av->value, "pv_header") ||
+ !strcmp(av->value, "metadata") ||
+ !strcmp(av->value, "label_header"))
+ return 1;
+ return 0;
+}
+
/*
* FIXME: there's been a confusing mixup among:
* resizeable, resizable, allocatable, allocation.
diff --git a/tools/pvck.c b/tools/pvck.c
index 8588906..cc23d9b 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -23,14 +23,45 @@
#define PRINT_CURRENT 1
#define PRINT_ALL 2
+#define ID_STR_SIZE 40 /* uuid formatted with dashes is 38 chars */
+
+/*
+ * command line input from --settings
+ */
struct settings {
- uint64_t metadata_offset; /* start of text metadata, from start of disk */
- uint64_t mda_offset; /* start of mda_header, from start of disk */
- uint64_t mda_size; /* size of metadata area (mda_header + text area) */
+ uint64_t metadata_offset; /* bytes, start of text metadata (from start of disk) */
+ uint64_t mda_offset; /* bytes, start of mda_header (from start of disk) */
+ uint64_t mda_size; /* bytes, size of metadata area (mda_header + text area) */
+ uint64_t mda2_offset; /* bytes */
+ uint64_t mda2_size; /* bytes */
+ uint64_t device_size; /* bytes */
+ uint64_t data_offset; /* bytes, start of data (pe_start) */
+ uint32_t seqno;
+ struct id pvid;
+
+ int mda_num; /* 1 or 2 for first or second mda */
+ char *backup_file;
unsigned metadata_offset_set:1;
unsigned mda_offset_set:1;
unsigned mda_size_set:1;
+ unsigned mda2_offset_set;
+ unsigned mda2_size_set;
+ unsigned device_size_set:1;
+ unsigned data_offset_set:1;
+ unsigned seqno_set:1;
+ unsigned pvid_set:1;
+};
+
+/*
+ * command line input from --file
+ */
+struct metadata_file {
+ const char *filename;
+ char *text_buf;
+ uint64_t text_size; /* bytes */
+ uint32_t text_crc;
+ char vgid_str[ID_STR_SIZE];
};
static char *_chars_to_str(void *in, void *out, int num, int max, const char *field)
@@ -136,6 +167,8 @@ static int _check_vgname_start(char *buf, int *len)
return 0;
}
+/* all sizes and offsets in bytes */
+
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;
@@ -213,10 +246,14 @@ static void _copy_out_metadata(char *buf, uint32_t start, uint32_t first_start,
*meta_size = new_len;
}
-static int _text_buf_parsable(char *text_buf, uint64_t text_size)
+/* all sizes and offsets in bytes */
+
+static int _text_buf_parse(char *text_buf, uint64_t text_size, struct dm_config_tree **cft_out)
{
struct dm_config_tree *cft;
+ *cft_out = NULL;
+
if (!(cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0))) {
return 0;
}
@@ -226,27 +263,41 @@ static int _text_buf_parsable(char *text_buf, uint64_t text_size)
return 0;
}
+ *cft_out = cft;
+ return 1;
+}
+
+/* all sizes and offsets in bytes */
+
+static int _text_buf_parsable(char *text_buf, uint64_t text_size)
+{
+ struct dm_config_tree *cft = NULL;
+
+ if (!_text_buf_parse(text_buf, text_size, &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)
+static void _copy_line(char *in, char *out, int *len, int linesize)
{
int i;
*len = 0;
- for (i = 0; i < MAX_LINE_CHECK; i++) {
+ for (i = 0; i < linesize; i++) {
+ out[i] = in[i];
if ((in[i] == '\n') || (in[i] == '\0'))
break;
- out[i] = in[i];
}
*len = i+1;
}
+/* all sizes and offsets in bytes */
+
static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const char *tofile, struct device *dev,
int mda_num, uint64_t mda_offset, uint64_t mda_size, char *buf)
{
@@ -255,19 +306,16 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
char vgname[NAME_LEN+1];
char id_str[ID_STR_SIZE];
char id_first[ID_STR_SIZE];
- char latest_vgname[NAME_LEN+1];
- char latest_id_str[ID_STR_SIZE];
char *text_buf;
char *p;
- uint32_t buf_off; /* offset with buf which begins with mda_header */
+ uint32_t buf_off; /* offset with buf which begins with mda_header, bytes */
uint32_t buf_off_first = 0;
uint32_t seqno;
- uint32_t latest_seqno;
uint32_t crc;
- uint64_t text_size;
- uint64_t meta_size;
- uint64_t latest_offset;
- int metadata_offset_found = 0;
+ uint64_t text_size; /* bytes */
+ uint64_t meta_size; /* bytes */
+ int print_count = 0;
+ int one_found = 0;
int multiple_vgs = 0;
int bad_end;
int vgnamelen;
@@ -281,11 +329,6 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
}
}
- memset(latest_vgname, 0, sizeof(latest_vgname));
- memset(latest_id_str, 0, sizeof(latest_id_str));
- latest_offset = 0;
- latest_seqno = 0;
-
/*
* If metadata has not wrapped, and the metadata area beginning
* has not been damaged, the text area will begin with vgname {.
@@ -324,6 +367,9 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
text_size = 0;
bad_end = 0;
+ if (one_found)
+ break;
+
/*
* Check for a new metadata copy at each 512 offset
* (after skipping 512 bytes for mda_header at the
@@ -346,17 +392,14 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
count++;
continue;
}
- if (set->metadata_offset_set) {
- if (metadata_offset_found)
- break;
- metadata_offset_found = 1;
- }
+ if (set->metadata_offset_set)
+ one_found = 1;
/*
* copy line of possible metadata to check for vgname
*/
memset(line, 0, sizeof(line));
- _copy_line(p, line, &len);
+ _copy_line(p, line, &len, sizeof(line));
p += len;
if (!_check_vgname_start(line, &vgnamelen)) {
@@ -370,7 +413,7 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
* copy next line of metadata, which should contain id
*/
memset(line, 0, sizeof(line));
- _copy_line(p, line, &len);
+ _copy_line(p, line, &len, sizeof(line));
p += len;
if (strncmp(line, "id = ", 5)) {
@@ -384,15 +427,29 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
* copy next line of metadata, which should contain seqno
*/
memset(line, 0, sizeof(line));
- _copy_line(p, line, &len);
+ _copy_line(p, line, &len, sizeof(line));
p += len;
if (strncmp(line, "seqno = ", 8)) {
count++;
continue;
}
+ if (sscanf(line, "seqno = %u", &seqno) != 1) {
+ count++;
+ continue;
+ }
- sscanf(line, "seqno = %u", &seqno);
+ /*
+ * user specified metadata with one seqno
+ * (this is not good practice since multiple old copies of metadata
+ * can have the same seqno; this is mostly to simplify testing)
+ */
+ if (set->seqno_set && (set->seqno != seqno)) {
+ count++;
+ continue;
+ }
+ if (set->seqno_set)
+ one_found = 1;
/*
* The first three lines look like metadata with
@@ -428,13 +485,6 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
(unsigned long long)text_size,
crc, vgname, seqno, id_str);
- if (!latest_seqno || (seqno > latest_seqno)) {
- latest_seqno = seqno;
- latest_offset = mda_offset + buf_off;
- memcpy(latest_vgname, vgname, NAME_LEN);
- memcpy(latest_id_str, id_str, ID_STR_SIZE);
- }
-
/*
* save the location of the first metadata we've found so
* we know where to stop after wrapping buf.
@@ -449,26 +499,30 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
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));
+ log_warn("WARNING: unexpected 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);
+ _copy_line(str1, line, &len, sizeof(line));
+ if ((p = strchr(line, '\n')))
+ *p = '\0';
log_print("%s", line);
}
if (str1 && (str2 = strstr(str1, "creation_time = "))) {
memset(line, 0, sizeof(line));
- _copy_line(str2, line, &len);
+ _copy_line(str2, line, &len, sizeof(line));
+ if ((p = strchr(line, '\n')))
+ *p = '\0';
log_print("%s\n", line);
}
}
if (fp) {
- fprintf(fp, "%s", text_buf);
- if (!set->metadata_offset_set)
+ if (print_count++)
fprintf(fp, "\n--\n");
+ fprintf(fp, "%s", text_buf);
}
free(text_buf);
@@ -485,12 +539,6 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
if (multiple_vgs)
log_warn("WARNING: metadata from multiple VGs was found.");
- if (!set->metadata_offset_set)
- log_print("Most recent metadata found at %llu seqno %u for vg %s id %s",
- (unsigned long long)latest_offset, latest_seqno,
- latest_vgname, latest_id_str);
-
-
if (fp) {
if (fflush(fp))
stack;
@@ -501,6 +549,8 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _check_label_header(struct label_header *lh, uint64_t labelsector,
int *found_label)
{
@@ -521,7 +571,7 @@ static int _check_label_header(struct label_header *lh, uint64_t labelsector,
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++;
@@ -563,6 +613,8 @@ static int _check_pv_header(struct pv_header *ph)
}
/*
+ * all sizes and offsets in bytes
+ *
* mda_offset/mda_size are from the pv_header/disk_locn and could
* be incorrect.
*/
@@ -612,6 +664,7 @@ static int _check_mda_header(struct mda_header *mh, int mda_num, uint64_t mda_of
}
/*
+ * all sizes and offsets in bytes
*
* mda_offset, mda_size are from pv_header.disk_locn
* (the location of the metadata area.)
@@ -706,16 +759,20 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
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);
+ free(meta_buf);
return 0;
}
if (!(fp = fopen(tofile, "wx"))) {
log_error("Failed to create file %s", tofile);
+ free(meta_buf);
return 0;
}
fwrite(meta_buf, mda_size - 512, 1, fp);
+ free(meta_buf);
+
if (fflush(fp))
stack;
if (fclose(fp))
@@ -723,6 +780,8 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _dump_current_text(struct device *dev,
int print_fields, int print_metadata, const char *tofile,
int mda_num, int rlocn_index,
@@ -739,12 +798,11 @@ static int _dump_current_text(struct device *dev,
int ri = rlocn_index; /* 0 or 1 */
int bad = 0;
- if (!(meta_buf = malloc(meta_size))) {
+ if (!(meta_buf = zalloc(meta_size))) {
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;
}
- memset(meta_buf, 0, meta_size);
/*
* Read the metadata text specified by the raw_locn so we can
@@ -755,7 +813,7 @@ static int _dump_current_text(struct device *dev,
* mda_offset + meta_offset.
*/
if (meta_offset + meta_size > mda_size) {
- /* text metadata wraps to start of text metadata area */
+ /* text metadata wraps to start of text metadata area */
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;
@@ -766,6 +824,7 @@ static int _dump_current_text(struct device *dev,
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);
+ free(meta_buf);
return 0;
}
@@ -773,12 +832,14 @@ static int _dump_current_text(struct device *dev,
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);
+ free(meta_buf);
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);
+ free(meta_buf);
return 0;
}
}
@@ -836,11 +897,14 @@ static int _dump_current_text(struct device *dev,
}
out:
+ free(meta_buf);
if (bad)
return 0;
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _dump_label_and_pv_header(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
int print_fields,
int *found_label,
@@ -848,16 +912,16 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, uint64_t labelsect
uint64_t *mda2_offset, uint64_t *mda2_size,
int *mda_count_out)
{
+ char buf[512];
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 lh_offset; /* bytes */
+ uint64_t pvh_offset; /* bytes */
+ uint64_t pvhe_offset; /* bytes */
+ uint64_t dlocn_offset; /* bytes */
uint64_t tmp;
int mda_count = 0;
int bad = 0;
@@ -865,9 +929,6 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, uint64_t labelsect
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);
@@ -1071,6 +1132,8 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, uint64_t labelsect
}
/*
+ * all sizes and offsets in bytes
+ *
* 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.
@@ -1091,22 +1154,20 @@ static int _dump_mda_header(struct cmd_context *cmd, struct settings *set,
uint32_t *checksum0_ret,
int *found_header)
{
+ char buf[512];
char str[256];
- char *buf;
+ char *mda_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;
+ uint64_t meta_offset = 0; /* bytes */
+ uint64_t meta_size = 0; /* bytes */
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.
@@ -1192,35 +1253,34 @@ static int _dump_mda_header(struct cmd_context *cmd, struct settings *set,
* looking at all copies of the metadata in the area
*/
if (print_metadata == PRINT_ALL) {
- free(buf);
-
- if (!(buf = malloc(mda_size)))
+ if (!(mda_buf = zalloc(mda_size)))
goto_out;
- memset(buf, 0, mda_size);
- if (!dev_read_bytes(dev, mda_offset, mda_size, buf)) {
+ if (!dev_read_bytes(dev, mda_offset, mda_size, mda_buf)) {
log_print("CHECK: failed to read metadata area at offset %llu size %llu",
(unsigned long long)mda_offset, (unsigned long long)mda_size);
bad++;
+ free(mda_buf);
goto out;
}
- _dump_all_text(cmd, set, tofile, dev, mda_num, mda_offset, mda_size, buf);
+ _dump_all_text(cmd, set, tofile, dev, mda_num, mda_offset, mda_size, mda_buf);
+ free(mda_buf);
}
/* Should we also check text metadata if it exists in rlocn1? */
out:
- if (buf)
- free(buf);
if (bad)
return 0;
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _dump_headers(struct cmd_context *cmd, const char *dump, struct settings *set,
uint64_t labelsector, struct device *dev)
{
- uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
uint32_t mda1_checksum, mda2_checksum;
int mda_count = 0;
int bad = 0;
@@ -1256,12 +1316,14 @@ static int _dump_headers(struct cmd_context *cmd, const char *dump, struct setti
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct settings *set,
uint64_t labelsector, struct device *dev,
int print_metadata, int print_area)
{
const char *tofile = NULL;
- uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
uint32_t mda1_checksum, mda2_checksum;
int mda_count = 0;
int mda_num = 1;
@@ -1309,9 +1371,11 @@ static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct sett
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _dump_found(struct cmd_context *cmd, struct settings *set, uint64_t labelsector, struct device *dev)
{
- uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
uint32_t mda1_checksum = 0, mda2_checksum = 0;
int found_label = 0, found_header1 = 0, found_header2 = 0;
int mda_count = 0;
@@ -1357,6 +1421,8 @@ static int _dump_found(struct cmd_context *cmd, struct settings *set, uint64_t l
#define ONE_MB_IN_BYTES 1048576
/*
+ * all sizes and offsets in bytes (except dev_sectors from dev_get_size)
+ *
* 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.
@@ -1367,8 +1433,8 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
{
const char *tofile = NULL;
char *buf;
- uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
- uint64_t mda_offset, mda_size;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
+ uint64_t mda_offset, mda_size; /* bytes */
int mda_count = 0;
int mda_num = 1;
@@ -1414,7 +1480,8 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
uint64_t dev_bytes;
uint64_t extra_bytes;
- dev_get_size(dev, &dev_sectors);
+ if (dev_get_size(dev, &dev_sectors))
+ stack;
dev_bytes = dev_sectors * 512;
extra_bytes = dev_bytes % ONE_MB_IN_BYTES;
@@ -1441,9 +1508,8 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
log_print("Searching for metadata at offset %llu size %llu",
(unsigned long long)mda_offset, (unsigned long long)mda_size);
- if (!(buf = malloc(mda_size)))
+ if (!(buf = zalloc(mda_size)))
return_0;
- memset(buf, 0, mda_size);
if (!dev_read_bytes(dev, mda_offset, mda_size, buf)) {
log_print("CHECK: failed to read metadata area at offset %llu size %llu",
@@ -1458,6 +1524,8 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
return 1;
}
+/* all sizes and offsets in bytes */
+
static int _get_one_setting(struct cmd_context *cmd, struct settings *set, char *key, char *val)
{
if (!strncmp(key, "metadata_offset", strlen("metadata_offset"))) {
@@ -1467,6 +1535,19 @@ static int _get_one_setting(struct cmd_context *cmd, struct settings *set, char
return 1;
}
+ if (!strncmp(key, "seqno", strlen("seqno"))) {
+ if (sscanf(val, "%u", &set->seqno) != 1)
+ goto_bad;
+ set->seqno_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "backup_file", strlen("backup_file"))) {
+ if ((set->backup_file = strdup(val)))
+ return 1;
+ return 0;
+ }
+
if (!strncmp(key, "mda_offset", strlen("mda_offset"))) {
if (sscanf(val, "%llu", (unsigned long long *)&set->mda_offset) != 1)
goto_bad;
@@ -1480,6 +1561,54 @@ static int _get_one_setting(struct cmd_context *cmd, struct settings *set, char
set->mda_size_set = 1;
return 1;
}
+
+ if (!strncmp(key, "mda2_offset", strlen("mda2_offset"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->mda2_offset) != 1)
+ goto_bad;
+ set->mda2_offset_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "mda2_size", strlen("mda2_size"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->mda2_size) != 1)
+ goto_bad;
+ set->mda2_size_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "device_size", strlen("device_size"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->device_size) != 1)
+ goto_bad;
+ set->device_size_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "data_offset", strlen("data_offset"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->data_offset) != 1)
+ goto_bad;
+ set->data_offset_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "pv_uuid", strlen("pv_uuid"))) {
+ if (strchr(val, '-') && (strlen(val) == 32)) {
+ memcpy(&set->pvid, val, 32);
+ set->pvid_set = 1;
+ return 1;
+ } else if (id_read_format_try(&set->pvid, val)) {
+ set->pvid_set = 1;
+ return 1;
+ } else {
+ log_error("Failed to parse UUID from pv_uuid setting.");
+ goto bad;
+ }
+ }
+
+ if (!strncmp(key, "mda_num", strlen("mda_num"))) {
+ if (sscanf(val, "%u", (int *)&set->mda_num) != 1)
+ goto_bad;
+ return 1;
+ }
bad:
log_error("Invalid setting: %s", key);
return 0;
@@ -1494,8 +1623,6 @@ static int _get_settings(struct cmd_context *cmd, struct settings *set)
int num;
int pos;
- memset(set, 0, sizeof(struct settings));
-
/*
* "grouped" means that multiple --settings options can be used.
* Each option is also allowed to contain multiple key = val pairs.
@@ -1531,16 +1658,1127 @@ static int _get_settings(struct cmd_context *cmd, struct settings *set)
return 1;
}
+/*
+ * pvck --repairtype label_header
+ *
+ * Writes new label_header without changing pv_header fields.
+ * All constant values except for recalculated crc.
+ *
+ * all sizes and offsets in bytes
+ */
+
+static int _repair_label_header(struct cmd_context *cmd, const char *repair,
+ struct settings *set, uint64_t labelsector, struct device *dev)
+{
+ char buf[512];
+ struct label_header *lh;
+ struct pv_header *pvh;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
+ uint64_t lh_offset; /* bytes */
+ uint64_t pvh_offset; /* bytes */
+ uint32_t crc;
+ int mda_count;
+ int found_label = 0;
+
+ lh_offset = labelsector * 512; /* from start of disk */
+
+ _dump_label_and_pv_header(cmd, labelsector, dev, 0, &found_label,
+ &mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count);
+
+ if (!found_label) {
+ log_warn("WARNING: No LVM label found on %s. It may not be an LVM device.", dev_name(dev));
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Write LVM header to device? ") == 'n')
+ return 0;
+ }
+
+ if (!dev_read_bytes(dev, lh_offset, 512, buf)) {
+ log_error("Failed to read label_header at %llu", (unsigned long long)lh_offset);
+ return 0;
+ }
+
+ lh = (struct label_header *)buf;
+ 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_error("Problem with pv_header offset calculation");
+ return 0;
+ }
+
+ memcpy(lh->id, LABEL_ID, sizeof(lh->id));
+ memcpy(lh->type, LVM2_LABEL, sizeof(lh->type));
+ lh->sector_xl = xlate64(labelsector);
+ lh->offset_xl = xlate32(32);
+
+ crc = calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl,
+ LABEL_SIZE - ((uint8_t *) &lh->offset_xl - (uint8_t *) lh));
+
+ lh->crc_xl = xlate32(crc);
+
+ log_print("Writing label_header.crc 0x%08x", crc);
+
+ if (arg_is_set(cmd, test_ARG)) {
+ log_warn("Skip writing in test mode.");
+ return 1;
+ }
+
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Write new LVM header to %s? ", dev_name(dev)) == 'n')
+ return 0;
+
+ if (!dev_write_bytes(dev, lh_offset, 512, buf)) {
+ log_error("Failed to write new header");
+ return 0;
+ }
+ return 1;
+}
+
+static int _get_pv_info_from_metadata(struct cmd_context *cmd, struct settings *set,
+ struct device *dev,
+ struct pv_header *pvh, int found_label,
+ char *text_buf, uint64_t text_size,
+ char *pvid,
+ uint64_t *device_size_sectors,
+ uint64_t *pe_start_sectors)
+{
+ int8_t pvid_cur[ID_LEN+1]; /* found in existing pv_header */
+ int8_t pvid_set[ID_LEN+1]; /* set by user in --settings */
+ int8_t pvid_use[ID_LEN+1]; /* the pvid chosen to use */
+ int pvid_cur_valid = 0; /* pvid_cur is valid */
+ int pvid_use_valid = 0; /* pvid_use is valid */
+ struct dm_config_tree *cft = NULL;
+ struct volume_group *vg = NULL;
+ struct pv_list *pvl;
+
+ memset(pvid_cur, 0, sizeof(pvid_cur));
+ memset(pvid_set, 0, sizeof(pvid_set));
+ memset(pvid_use, 0, sizeof(pvid_use));
+
+ /*
+ * Check if there's a valid existing PV UUID at the expected location.
+ */
+ if (!id_read_format_try((struct id *)&pvid_cur, (char *)&pvh->pv_uuid))
+ memset(&pvid_cur, 0, ID_LEN);
+ else {
+ memcpy(&pvid_use, &pvid_cur, ID_LEN);
+ pvid_use_valid = 1;
+ pvid_cur_valid = 1;
+ }
+
+ if (set->pvid_set) {
+ memcpy(&pvid_set, &set->pvid, ID_LEN);
+ memcpy(&pvid_use, &pvid_set, ID_LEN);
+ pvid_use_valid = 1;
+ }
+
+ if (pvid_cur_valid && set->pvid_set && memcmp(&pvid_cur, &pvid_set, ID_LEN)) {
+ log_warn("WARNING: existing PV UUID %s does not match pv_uuid setting %s.",
+ (char *)&pvid_cur, (char *)&pvid_set);
+
+ memcpy(&pvid_use, &pvid_set, ID_LEN);
+ pvid_use_valid = 1;
+ }
+
+ if (!_text_buf_parse(text_buf, text_size, &cft)) {
+ log_error("Invalid metadata file.");
+ return 0;
+ }
+
+ if (!(vg = vg_from_config_tree(cmd, cft))) {
+ config_destroy(cft);
+ log_error("Invalid metadata file.");
+ return 0;
+ }
+
+ config_destroy(cft);
+
+ /*
+ * If pvid_use is set, look for metadata PV section with matching PV UUID.
+ * Otherwise, look for metadata PV section with device name matching dev.
+ *
+ * pvid_use will be empty if there's no valid UUID in the existing
+ * pv_header, and the user did not specify a UUID in --settings.
+ *
+ * Choosing the PV UUID based only on a matching device name is somewhat
+ * weak since device names are dynamic, but we do scan devs to verify the
+ * chosen PV UUID is not in use elsewhere, which should avoid most of the
+ * risk of picking a wrong UUID.
+ */
+ if (!pvid_use_valid) {
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!strcmp(pvl->pv->device_hint, dev_name(dev)))
+ goto copy_pv;
+ }
+ } else {
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (id_equal(&pvl->pv->id, (struct id *)&pvid_use))
+ goto copy_pv;
+ }
+ }
+
+ release_vg(vg);
+
+ /*
+ * Don't know what PV UUID to use, possibly:
+ * . the user set a PV UUID that does not exist in the metadata file
+ * . the UUID in the existing pv_header does not exist in the metadata file
+ * . the metadata has no PV with a device name hint matching this device
+ */
+ if (set->pvid_set)
+ log_error("PV UUID %s not found in metadata file.", (char *)&pvid_set);
+ else if (pvid_cur_valid)
+ log_error("PV UUID %s in existing pv_header not found in metadata file.", (char *)&pvid_cur);
+ else if (!pvid_use_valid)
+ log_error("PV name %s not found in metadata file.", dev_name(dev));
+
+ log_error("No valid PV UUID, specify a PV UUID from metadata in --settings.");
+ return 0;
+
+ copy_pv:
+ *device_size_sectors = pvl->pv->size;
+ *pe_start_sectors = pvl->pv->pe_start;
+ memcpy(pvid, &pvl->pv->id, ID_LEN);
+
+ release_vg(vg);
+ return 1;
+}
+
+/*
+ * Checking for mda1 is simple because it's always at the same location,
+ * and when a PV is set to use zero metadata areas, this space is just
+ * unused. We could look for any surviving metadata text in mda1
+ * containing the VG UUID to confirm that this PV has been used for
+ * metadata, but if the start of the disk has been zeroed, then we
+ * may not find any.
+ */
+static int _check_for_mda1(struct cmd_context *cmd, struct device *dev)
+{
+ char buf[512];
+ struct mda_header *mh;
+
+ if (!dev_read_bytes(dev, 4096, 512, buf))
+ return_0;
+
+ mh = (struct mda_header *)buf;
+
+ if (!memcmp(mh->magic, FMTT_MAGIC, sizeof(mh->magic)))
+ return 1;
+ return 0;
+}
+
+/*
+ * Checking for mda2 is more complicated. Very often PVs will not use
+ * a second mda2, and the location is not quite as predictable. Also,
+ * if we mistakenly conclude that an mda2 belongs on the PV, we'd end
+ * up writing into the data area.
+ *
+ * all sizes and offsets in bytes
+ */
+static int _check_for_mda2(struct cmd_context *cmd, struct device *dev,
+ uint64_t device_size, struct metadata_file *mf,
+ uint64_t *mda2_offset, uint64_t *mda2_size)
+{
+ struct mda_header *mh;
+ char buf2[256];
+ char *buf;
+ uint64_t mda_offset, mda_size, extra_bytes; /* bytes */
+ int i, found = 0;
+
+ if (device_size < (2 * ONE_MB_IN_BYTES))
+ return_0;
+
+ extra_bytes = device_size % ONE_MB_IN_BYTES;
+ mda_offset = device_size - extra_bytes - ONE_MB_IN_BYTES;
+ mda_size = device_size - mda_offset;
+
+ if (!(buf = malloc(mda_size)))
+ return_0;
+
+ if (!dev_read_bytes(dev, mda_offset, mda_size, buf))
+ goto fail;
+
+ mh = (struct mda_header *)buf;
+
+ /*
+ * To be certain this is really an mda_header before writing it,
+ * require that magic, version and start are all correct.
+ */
+
+ if (memcmp(mh->magic, FMTT_MAGIC, sizeof(mh->magic)))
+ goto fail;
+
+ if (xlate32(mh->version) != FMTT_VERSION) {
+ log_print("Skipping mda2 (wrong mda_header.version)");
+ goto fail;
+ }
+
+ if (xlate64(mh->start) != mda_offset) {
+ log_print("Skipping mda2 (wrong mda_header.start)");
+ goto fail;
+ }
+
+ /*
+ * Search text area for an instance of current metadata before enabling
+ * mda2, in case this mda_header is from a previous generation PV and
+ * is not actually used by the current PV. An mda_header and metadata
+ * area from a previous PV (in a previous VG) that used mda2 might
+ * still exist, while the current PV does not use an mda2.
+ *
+ * Search for the vgid in the first 256 bytes at each 512 byte boundary
+ * in the first half of the metadata area.
+ */
+ for (i = 0; i < (mda_size / 1024); i++) {
+ memcpy(buf2, buf + 512 + (i * 512), sizeof(buf2));
+
+ if (strstr(buf2, mf->vgid_str)) {
+ log_print("Found mda2 header at offset %llu size %llu",
+ (unsigned long long)mda_offset, (unsigned long long)mda_size);
+ *mda2_offset = mda_offset;
+ *mda2_size = mda_size;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ log_print("Skipping mda2 (no matching VG UUID in metadata area)");
+ goto fail;
+ }
+
+ free(buf);
+ return 1;
+
+ fail:
+ free(buf);
+ *mda2_offset = 0;
+ *mda2_size = 0;
+ return 0;
+}
+
+/*
+ * pvck --repairtype pv_header --file input --settings
+ *
+ * Writes new pv_header and label_header.
+ *
+ * pv_header.pv_uuid
+ * If a uuid is given in --settings, that is used.
+ * Else if existing pv_header has a valid uuid, that is used.
+ * Else if the metadata file has a matching device name, that uuid is used.
+ *
+ * pv_header.device_size
+ * Use device size from metadata file.
+ *
+ * pv_header.disk_locn[0].offset (data area start)
+ * Use pe_start from metadata file.
+ *
+ * pv_header.disk_locn[2].offset/size (first metadata area)
+ * offset always 4096. size is pe_start - offset.
+ *
+ * pv_header.disk_locn[3].offset/size (second metadata area)
+ * Look for existing mda_header at expected offset, and if
+ * found use that value. Otherwise second mda is not used.
+ *
+ * The size/offset variables in sectors have a _sectors suffix,
+ * any other size/offset variables in bytes.
+ */
+
+static int _repair_pv_header(struct cmd_context *cmd, const char *repair,
+ struct settings *set, struct metadata_file *mf,
+ uint64_t labelsector, struct device *dev)
+{
+ char head_buf[512];
+ int8_t pvid[ID_LEN+1];
+ struct device *dev_with_pvid = NULL;
+ struct label_header *lh;
+ struct pv_header *pvh;
+ struct pv_header_extension *pvhe;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
+ uint64_t lh_offset; /* in bytes, from start of disk */
+ uint64_t device_size = 0; /* in bytes, as stored in pv_header */
+ uint64_t device_size_sectors = 0; /* in sectors, as stored in metadata */
+ uint64_t get_size_sectors = 0; /* in sectors, as dev_get_size returns */
+ uint64_t get_size = 0; /* in bytes */
+ uint64_t pe_start_sectors; /* in sectors, as stored in metadata */
+ uint64_t data_offset; /* in bytes, as stored in pv_header */
+ uint32_t head_crc;
+ int mda_count;
+ int found_label = 0;
+ int di;
+
+ memset(&pvid, 0, ID_LEN+1);
+
+ lh_offset = labelsector * 512; /* from start of disk */
+
+ if (!dev_get_size(dev, &get_size_sectors))
+ log_warn("WARNING: Cannot get device size.");
+ get_size = get_size_sectors << SECTOR_SHIFT;
+
+ _dump_label_and_pv_header(cmd, labelsector, dev, 0, &found_label,
+ &mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count);
+
+ /*
+ * The header sector may have been zeroed, or the user may have
+ * accidentally given the wrong device.
+ */
+ if (!found_label)
+ log_warn("WARNING: No LVM label found on %s. It may not be an LVM device.", dev_name(dev));
+
+ /*
+ * The PV may have had no metadata areas, or one, or two.
+ *
+ * Try to avoid writing new metadata areas where they didn't exist
+ * before. Writing mda1 when it didn't exist previously would not be
+ * terrible since the space is unused anyway, but wrongly writing mda2
+ * could end up in the data area.
+ *
+ * When the pv_header has no mda1 or mda2 locations, check for evidence
+ * of prior mda headers for mda1 and mda2.
+ *
+ * When the pv_header has an mda1 location and no mda2 location, just
+ * use mda1 and don't look for mda2 (unless requested by user setting)
+ * since it probably did not exist. (It's very unlikely that only the
+ * mda2 location was zeroed in the pv_header.)
+ */
+ if (!mda_count && !mda1_offset && !mda2_offset) {
+ if (_check_for_mda1(cmd, dev))
+ mda_count = 1;
+
+ if (_check_for_mda2(cmd, dev, get_size, mf, &mda2_offset, &mda2_size))
+ mda_count = 2;
+ }
+
+ /*
+ * The PV may have had zero metadata areas (not common), or the
+ * pv_header and the mda1 header at 4096 may have been zeroed
+ * (more likely). Ask the user if metadata in mda1 should be
+ * included; it would usually be yes. To repair a PV and use
+ * zero metadata areas, require the user to specify
+ * --settings "mda_offset=0 mda_size=0".
+ *
+ * NOTE: mda1 is not written by repair pv_header, this will only
+ * include a pointer to mda1 in the pv_header so that a subsequent
+ * repair metadata will use that to write an mda_header and metadata.
+ */
+ if (!mda_count && set->mda_offset_set && set->mda_size_set &&
+ !set->mda_offset && !set->mda_size) {
+ log_warn("WARNING: PV will have no metadata with zero metadata areas.");
+
+ } else if (!mda_count) {
+ log_warn("WARNING: no previous metadata areas found on device.");
+
+ if (arg_count(cmd, yes_ARG) ||
+ yes_no_prompt("Should a metadata area be included? ") == 'y') {
+ /* mda1_offset/mda1_size are set below */
+ mda_count = 1;
+ } else {
+ log_error("To repair with zero metadata areas, use --settings \"mda_offset=0 mda_size=0\".");
+ goto fail;
+ }
+ }
+
+ /*
+ * The user has provided offset or size for mda2. This would
+ * usually be done when these values do not exist on disk,
+ * but if mda2 *is* found on disk, ensure it agrees with the
+ * user's setting.
+ */
+ if (mda_count && (set->mda2_offset_set || set->mda2_size_set)) {
+ if (mda2_offset && (mda2_offset != set->mda2_offset)) {
+ log_error("mda2_offset setting %llu does not match mda2_offset found on disk %llu.",
+ (unsigned long long)set->mda2_offset, (unsigned long long)mda2_offset);
+ goto fail;
+ }
+ if (mda2_size && (mda2_size != set->mda2_size)) {
+ log_error("mda2_size setting %llu does not match mda2_size found on disk %llu.",
+ (unsigned long long)set->mda2_size, (unsigned long long)mda2_size);
+ goto fail;
+ }
+ mda2_offset = set->mda2_offset;
+ mda2_size = set->mda2_size;
+ mda_count = 2;
+ }
+
+ /*
+ * The header sector is read into this buffer.
+ * This same buffer is modified and written back.
+ */
+ if (!dev_read_bytes(dev, lh_offset, 512, head_buf)) {
+ log_error("Failed to read label_header at %llu", (unsigned long long)lh_offset);
+ goto fail;
+ }
+
+ lh = (struct label_header *)head_buf;
+ pvh = (struct pv_header *)(head_buf + 32);
+
+ /*
+ * Metadata file is not needed if user provides pvid/device_size/data_offset.
+ * All values in settings are in bytes.
+ */
+ if (set->device_size_set && set->pvid_set && set->data_offset_set && !mf->filename) {
+ device_size = set->device_size;
+ pe_start_sectors = set->data_offset >> SECTOR_SHIFT;
+ memcpy(&pvid, &set->pvid, ID_LEN);
+
+ if (get_size && (get_size != device_size)) {
+ log_warn("WARNING: device_size setting %llu bytes does not match device size %llu bytes.",
+ (unsigned long long)set->device_size, (unsigned long long)get_size);
+ }
+ goto scan;
+ }
+
+ if (!mf->filename) {
+ log_error("Metadata input file is needed for pv_header info.");
+ log_error("See pvck --dump to locate and create a metadata file.");
+ goto fail;
+ }
+
+ /*
+ * Look in the provided copy of VG metadata for info that determines
+ * pv_header fields.
+ *
+ * pv<N> {
+ * id = <uuid>
+ * device = <path> # device path hint, set when metadata was last written
+ * ...
+ * dev_size = <num> # in 512 sectors
+ * pe_start = <num> # in 512 sectors
+ * }
+ *
+ * Select the right pv entry by matching an existing pv uuid, or the
+ * current device name to the device path hint. Take the pv uuid,
+ * dev_size and pe_start from the metadata to use in the pv_header.
+ */
+ if (!_get_pv_info_from_metadata(cmd, set, dev, pvh, found_label,
+ mf->text_buf, mf->text_size, (char *)&pvid,
+ &device_size_sectors, &pe_start_sectors))
+ goto fail;
+
+ /*
+ * In pv_header, device_size is bytes, but in metadata dev_size is in sectors.
+ */
+ device_size = device_size_sectors << SECTOR_SHIFT;
+
+ scan:
+ /*
+ * Read all devs to verify the pvid that will be written does not exist
+ * on another device.
+ */
+ if (!label_scan_for_pvid(cmd, (char *)&pvid, &dev_with_pvid)) {
+ log_error("Failed to scan devices to check PV UUID.");
+ goto fail;
+ }
+
+ if (dev_with_pvid && (dev_with_pvid != dev)) {
+ log_error("Cannot use PV UUID %s which exists on %s", (char *)&pvid, dev_name(dev_with_pvid));
+ goto fail;
+ }
+
+ /*
+ * Set new label_header and pv_header fields.
+ */
+
+ /* set label_header (except crc) */
+ memcpy(lh->id, LABEL_ID, sizeof(lh->id));
+ memcpy(lh->type, LVM2_LABEL, sizeof(lh->type));
+ lh->sector_xl = xlate64(labelsector);
+ lh->offset_xl = xlate32(32);
+
+ /* set pv_header */
+ memcpy(pvh->pv_uuid, &pvid, ID_LEN);
+ pvh->device_size_xl = xlate64(device_size);
+
+ /* set data area location */
+ data_offset = (pe_start_sectors << SECTOR_SHIFT);
+ pvh->disk_areas_xl[0].offset = xlate64(data_offset);
+ pvh->disk_areas_xl[0].size = 0;
+
+ /* set end of data areas */
+ pvh->disk_areas_xl[1].offset = 0;
+ pvh->disk_areas_xl[1].size = 0;
+
+ di = 2;
+
+ /* set first metadata area location */
+ if (mda_count > 0) {
+ mda1_offset = 4096;
+ mda1_size = (pe_start_sectors << SECTOR_SHIFT) - 4096;
+ pvh->disk_areas_xl[di].offset = xlate64(mda1_offset);
+ pvh->disk_areas_xl[di].size = xlate64(mda1_size);
+ di++;
+ }
+
+ /* set second metadata area location */
+ if (mda_count > 1) {
+ pvh->disk_areas_xl[di].offset = xlate64(mda2_offset);
+ pvh->disk_areas_xl[di].size = xlate64(mda2_size);
+ di++;
+ }
+
+ /* set end of metadata areas */
+ pvh->disk_areas_xl[di].offset = 0;
+ pvh->disk_areas_xl[di].size = 0;
+ di++;
+
+ /* set pv_header_extension */
+ pvhe = (struct pv_header_extension *)((char *)pvh + sizeof(struct pv_header) + (di * sizeof(struct disk_locn)));
+ pvhe->version = xlate32(PV_HEADER_EXTENSION_VSN);
+ pvhe->flags = xlate32(PV_EXT_USED);
+ pvhe->bootloader_areas_xl[0].offset = 0;
+ pvhe->bootloader_areas_xl[0].size = 0;
+
+ head_crc = calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl,
+ LABEL_SIZE - ((uint8_t *) &lh->offset_xl - (uint8_t *) lh));
+
+ /* set label_header crc (last) */
+ lh->crc_xl = xlate32(head_crc);
+
+ /*
+ * Write the updated header sector.
+ */
+
+ log_print("Writing label_header.crc 0x%08x pv_header uuid %s device_size %llu",
+ head_crc, (char *)&pvid, (unsigned long long)device_size);
+
+ log_print("Writing data_offset %llu mda1_offset %llu mda1_size %llu mda2_offset %llu mda2_size %llu",
+ (unsigned long long)data_offset,
+ (unsigned long long)mda1_offset,
+ (unsigned long long)mda1_size,
+ (unsigned long long)mda2_offset,
+ (unsigned long long)mda2_size);
+
+ if (arg_is_set(cmd, test_ARG)) {
+ log_warn("Skip writing in test mode.");
+ return 1;
+ }
+
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Write new LVM header to %s? ", dev_name(dev)) == 'n')
+ goto fail;
+
+ if (!dev_write_bytes(dev, lh_offset, 512, head_buf)) {
+ log_error("Failed to write new header");
+ goto fail;
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+/* all sizes and offsets in bytes */
+
+static int _update_mda(struct cmd_context *cmd, struct metadata_file *mf, struct device *dev,
+ int mda_num, uint64_t mda_offset, uint64_t mda_size)
+{
+ char *buf[512];
+ struct mda_header *mh;
+ struct raw_locn *rlocn0, *rlocn1;
+ uint64_t max_size;
+ uint64_t text_offset;
+ uint32_t crc;
+
+ max_size = ((mda_size - 512) / 2) - 512;
+ if (mf->text_size > mda_size) {
+ log_error("Metadata text %llu too large for mda_size %llu max %llu",
+ (unsigned long long)mf->text_size,
+ (unsigned long long)mda_size,
+ (unsigned long long)max_size);
+ goto fail;
+ }
+
+ if (!dev_read_bytes(dev, mda_offset, 512, buf)) {
+ log_print("CHECK: failed to read mda_header_%d at %llu",
+ mda_num, (unsigned long long)mda_offset);
+ goto fail;
+ }
+
+ text_offset = mda_offset + 512;
+
+ mh = (struct mda_header *)buf;
+ memcpy(mh->magic, FMTT_MAGIC, sizeof(mh->magic));
+ mh->version = xlate32(FMTT_VERSION);
+ mh->start = xlate64(mda_offset);
+ mh->size = xlate64(mda_size);
+
+ rlocn0 = mh->raw_locns;
+ rlocn0->flags = 0;
+ rlocn0->offset = xlate64(512); /* text begins 512 from start of mda_header */
+ rlocn0->size = xlate64(mf->text_size);
+ rlocn0->checksum = xlate32(mf->text_crc);
+
+ rlocn1 = (struct raw_locn *)((char *)mh->raw_locns + 24);
+ rlocn1->flags = 0;
+ rlocn1->offset = 0;
+ rlocn1->size = 0;
+ rlocn1->checksum = 0;
+
+ crc = calc_crc(INITIAL_CRC, (uint8_t *)mh->magic,
+ MDA_HEADER_SIZE - sizeof(mh->checksum_xl));
+ mh->checksum_xl = xlate32(crc);
+
+ log_print("Writing metadata at %llu length %llu crc 0x%08x mda%d",
+ (unsigned long long)(mda_offset + 512),
+ (unsigned long long)mf->text_size, mf->text_crc, mda_num);
+
+ log_print("Writing mda_header at %llu mda%d",
+ (unsigned long long)mda_offset, mda_num);
+
+ if (arg_is_set(cmd, test_ARG)) {
+ log_warn("Skip writing in test mode.");
+ return 1;
+ }
+
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Write new LVM metadata to %s? ", dev_name(dev)) == 'n')
+ goto fail;
+
+ if (!dev_write_bytes(dev, text_offset, mf->text_size, mf->text_buf)) {
+ log_error("Failed to write new mda text");
+ goto fail;
+ }
+
+ if (!dev_write_bytes(dev, mda_offset, 512, buf)) {
+ log_error("Failed to write new mda header");
+ goto fail;
+ }
+
+ return 1;
+ fail:
+ return 0;
+}
+
+/*
+ * pvck --repairtype metadata --file input --settings
+ *
+ * Writes new metadata into the text area and writes new
+ * mda_header for it. Requires valid mda locations in pv_header.
+ * Metadata is written immediately after mda_header.
+ *
+ * all sizes and offsets in bytes
+ */
+
+static int _repair_metadata(struct cmd_context *cmd, const char *repair,
+ struct settings *set, struct metadata_file *mf,
+ uint64_t labelsector, struct device *dev)
+{
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0; /* bytes */
+ int found_label = 0;
+ int mda_count = 0;
+ int mda_num;
+ int bad = 0;
+
+ mda_num = set->mda_num;
+
+ if (!mf->filename) {
+ log_error("Metadata input file is required.");
+ return 0;
+ }
+
+ _dump_label_and_pv_header(cmd, labelsector, dev, 0, &found_label,
+ &mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count);
+
+ if (!found_label) {
+ log_error("No lvm label found on device.");
+ log_error("See --repairtype pv_header to repair headers.");
+ return 0;
+ }
+
+ if (!mda_count && set->mda_offset_set && set->mda_size_set &&
+ !set->mda_offset && !set->mda_size) {
+ log_print("No metadata areas on device to repair.");
+ return 1;
+ }
+
+ if (!mda_count) {
+ log_error("No metadata areas found on device.");
+ log_error("See --repairtype pv_header to repair headers.");
+ return 0;
+ }
+
+ if ((mda_num == 1) && !mda1_offset) {
+ log_error("No mda1 offset found.");
+ log_error("See --repairtype pv_header to repair headers.");
+ return 0;
+ }
+
+ if ((mda_num == 2) && !mda2_offset) {
+ log_error("No mda2 offset found.");
+ log_error("See --repairtype pv_header to repair headers.");
+ return 0;
+ }
+
+ if ((!mda_num || mda_num == 1) && mda1_offset) {
+ if (!_update_mda(cmd, mf, dev, 1, mda1_offset, mda1_size))
+ bad++;
+ }
+
+ if ((!mda_num || mda_num == 2) && mda2_offset) {
+ if (!_update_mda(cmd, mf, dev, 2, mda2_offset, mda2_size))
+ bad++;
+ }
+
+ if (bad)
+ return 0;
+
+ return 1;
+}
+
+static void _strip_backup_line(char *line1, int len1, char *line2, int *len2)
+{
+ int copying = 0;
+ int i, j = 0;
+
+ for (i = 0; i < len1; i++) {
+ if (line1[i] == '\0')
+ break;
+
+ if (line1[i] == '\n')
+ break;
+
+ /* omit tabs at start of line */
+ if (!copying && (line1[i] == '\t'))
+ continue;
+
+ /* omit tabs and comment at end of line (can tabs occur without comment?) */
+ if (copying && (line1[i] == '\t') && strchr(line1 + i, '#'))
+ break;
+
+ copying = 1;
+
+ line2[j++] = line1[i];
+ }
+
+ line2[j++] = '\n';
+ *len2 = j;
+}
+
+#define MAX_META_LINE 4096
+
+/* all sizes and offsets in bytes */
+
+static int _backup_file_to_raw_metadata(char *back_buf, uint64_t back_size,
+ char **text_buf_out, uint64_t *text_size_out)
+{
+ char line[MAX_META_LINE];
+ char line2[MAX_META_LINE];
+ char *p, *text_buf;
+ uint32_t text_pos, pre_len, back_pos, text_max;
+ int len, len2, vgnamelen;
+
+ text_max = back_size * 2;
+
+ if (!(text_buf = malloc(text_max)))
+ return_0;
+ memset(text_buf, 0, text_max);
+
+ p = back_buf;
+ text_pos = 0;
+ back_pos = 0;
+
+ while (1) {
+ if (back_pos >= back_size)
+ break;
+
+ memset(line, 0, sizeof(line));
+ len = 0;
+
+ _copy_line(p, line, &len, sizeof(line));
+ p += len;
+ back_pos += len;
+
+ if (len < 3)
+ continue;
+
+ if (_check_vgname_start(line, &vgnamelen)) {
+ /* vg name is first line of text_buf */
+ memcpy(text_buf, line, len);
+ text_pos = len;
+
+ pre_len = back_pos - len;
+ break;
+ }
+ }
+
+ while (1) {
+ if (back_pos >= back_size)
+ break;
+
+ memset(line, 0, sizeof(line));
+ memset(line2, 0, sizeof(line2));
+ len = 0;
+ len2 = 0;
+
+ _copy_line(p, line, &len, sizeof(line));
+
+ if (line[0] == '\0')
+ break;
+
+ p += len;
+ back_pos += len;
+
+ /* shouldn't happen */
+ if (text_pos + len > text_max)
+ return_0;
+
+ if (len == 1) {
+ text_buf[text_pos++] = '\n';
+ continue;
+ }
+
+ _strip_backup_line(line, len, line2, &len2);
+
+ memcpy(text_buf + text_pos, line2, len2);
+ text_pos += len2;
+ }
+
+ /* shouldn't happen */
+ if (text_pos + pre_len + 3 > text_max)
+ return_0;
+
+ /* copy first pre_len bytes of back_buf into text_buf */
+ memcpy(text_buf + text_pos, back_buf, pre_len);
+ text_pos += pre_len;
+
+ text_pos++; /* null termination */
+
+ *text_size_out = text_pos;
+ *text_buf_out = text_buf;
+
+ return 1;
+}
+
+static int _is_backup_file(struct cmd_context *cmd, char *text_buf, uint64_t text_size)
+{
+ if ((text_buf[0] == '#') && !strncmp(text_buf, "# Generated", 11))
+ return 1;
+ return 0;
+}
+
+/* all sizes and offsets in bytes */
+
+static int _dump_backup_to_raw(struct cmd_context *cmd, struct settings *set)
+{
+ const char *input = set->backup_file;
+ const char *tofile = NULL;
+ struct stat sb;
+ char *back_buf, *text_buf;
+ uint64_t back_size, text_size;
+ int fd, rv;
+
+ if (arg_is_set(cmd, file_ARG)) {
+ if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
+ return_0;
+ }
+
+ if (!input) {
+ log_error("Set backup file in --settings backup_file=path");
+ return 0;
+ }
+
+ if (!(fd = open(input, O_RDONLY))) {
+ log_error("Cannot open file: %s", input);
+ return 0;
+ }
+
+ if (fstat(fd, &sb)) {
+ log_error("Cannot access file: %s", input);
+ close(fd);
+ return 0;
+ }
+
+ if (!(back_size = (uint64_t)sb.st_size)) {
+ log_error("Empty file: %s", input);
+ close(fd);
+ return 0;
+ }
+
+ if (!(back_buf = zalloc(back_size))) {
+ close(fd);
+ return 0;
+ }
+
+ rv = read(fd, back_buf, back_size);
+ if (rv != back_size) {
+ log_error("Cannot read file: %s", input);
+ close(fd);
+ free(back_buf);
+ return 0;
+ }
+
+ close(fd);
+
+ if (!_is_backup_file(cmd, back_buf, back_size)) {
+ log_error("File does not appear to contain a metadata backup.");
+ free(back_buf);
+ return 0;
+ }
+
+ if (!_backup_file_to_raw_metadata(back_buf, back_size, &text_buf, &text_size)) {
+ free(back_buf);
+ return_0;
+ }
+
+ free(back_buf);
+
+ if (!tofile) {
+ log_print("---");
+ printf("%s\n", text_buf);
+ log_print("---");
+ } else {
+ FILE *fp;
+ if (!(fp = fopen(tofile, "wx"))) {
+ log_error("Failed to create file %s", tofile);
+ return 0;
+ }
+
+ fprintf(fp, "%s", text_buf);
+
+ if (fflush(fp))
+ stack;
+ if (fclose(fp))
+ stack;
+ }
+
+ return 1;
+}
+
+/* all sizes and offsets in bytes */
+
+static int _check_metadata_file(struct cmd_context *cmd, struct metadata_file *mf,
+ char *text_buf, int text_size)
+{
+ char *vgid;
+ int namelen;
+
+ if (text_size < NAME_LEN+1) {
+ log_error("Invalid raw text metadata in file. File size is too small.");
+ return 0;
+ }
+
+ /*
+ * Using pvck --dump metadata output redirected to file may be a common
+ * mistake, so check and warn about that specifically.
+ */
+ if (isspace(text_buf[0]) && isspace(text_buf[1]) && strstr(text_buf, "---")) {
+ log_error("Invalid raw text metadata in file.");
+ log_error("(pvck stdout is not valid input, see pvck -f.)");
+ return 0;
+ }
+
+ /*
+ * Using a metadata backup file may be another common mistake.
+ */
+ if ((text_buf[0] == '#') && !strncmp(text_buf, "# Generated", 11)) {
+ log_error("Invalid raw text metadata in file.");
+ log_error("(metadata backup file is not valid input.)");
+ return 0;
+ }
+
+ if (text_buf[text_size-1] != '\0' ||
+ text_buf[text_size-2] != '\n' ||
+ text_buf[text_size-3] != '\n')
+ log_warn("WARNING: unexpected final bytes of raw metadata, expected \\n\\n\\0.");
+
+ if (_check_vgname_start(text_buf, &namelen)) {
+ if (!(vgid = strstr(text_buf, "id = "))) {
+ log_error("Invalid raw text metadata in file. (No VG UUID found.)");
+ return 0;
+ }
+ memcpy(mf->vgid_str, vgid + 6, 38);
+ return 1;
+ }
+
+ log_warn("WARNING: file data does not begin with a VG name and may be invalid.");
+
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Write input file data to disk?") == 'n') {
+ log_error("Invalid raw text metadata in file.");
+ return 0;
+ }
+
+ return 1;
+}
+
+/* all sizes and offsets in bytes */
+
+static int _read_metadata_file(struct cmd_context *cmd, struct metadata_file *mf)
+{
+ struct stat sb;
+ char *text_buf;
+ uint64_t text_size;
+ uint32_t text_crc;
+ int fd, rv;
+
+ if (!(fd = open(mf->filename, O_RDONLY))) {
+ log_error("Cannot open file: %s", mf->filename);
+ return 0;
+ }
+
+ if (fstat(fd, &sb)) {
+ log_error("Cannot access file: %s", mf->filename);
+ close(fd);
+ return 0;
+ }
+
+ if (!(text_size = (uint64_t)sb.st_size)) {
+ log_error("Empty file: %s", mf->filename);
+ close(fd);
+ return 0;
+ }
+
+ if (!(text_buf = zalloc(text_size + 1))) {
+ close(fd);
+ return 0;
+ }
+
+ rv = read(fd, text_buf, text_size);
+ if (rv != text_size) {
+ log_error("Cannot read file: %s", mf->filename);
+ close(fd);
+ free(text_buf);
+ return 0;
+ }
+
+ text_size += 1; /* null terminating byte */
+
+ close(fd);
+
+ if (_is_backup_file(cmd, text_buf, text_size)) {
+ char *back_buf = text_buf;
+ uint64_t back_size = text_size;
+ text_buf = NULL;
+ text_size = 0;
+ if (!_backup_file_to_raw_metadata(back_buf, back_size, &text_buf, &text_size))
+ return_0;
+ }
+
+ if (!_check_metadata_file(cmd, mf, text_buf, text_size))
+ return_0;
+
+ text_crc = calc_crc(INITIAL_CRC, (uint8_t *)text_buf, text_size);
+
+ mf->text_size = text_size;
+ mf->text_buf = text_buf;
+ mf->text_crc = text_crc;
+ return 1;
+}
+
int pvck(struct cmd_context *cmd, int argc, char **argv)
{
struct settings set;
+ struct metadata_file mf;
struct device *dev;
- const char *dump;
+ const char *dump, *repair;
const char *pv_name;
uint64_t labelsector = 1;
int bad = 0;
+ int ret = 0;
int i;
+ memset(&set, 0, sizeof(set));
+ memset(&mf, 0, sizeof(mf));
+
/*
* By default LVM skips the first sector (sector 0), and writes
* the label_header in the second sector (sector 1).
@@ -1549,7 +2787,7 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
if (arg_is_set(cmd, labelsector_ARG))
labelsector = arg_uint64_value(cmd, labelsector_ARG, UINT64_C(0));
- if (arg_is_set(cmd, dump_ARG)) {
+ if (arg_is_set(cmd, dump_ARG) || arg_is_set(cmd, repairtype_ARG) || arg_is_set(cmd, repair_ARG)) {
pv_name = argv[0];
if (!(dev = dev_cache_get(cmd, pv_name, cmd->filter))) {
@@ -1561,6 +2799,14 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
if (!_get_settings(cmd, &set))
return ECMD_FAILED;
+ if (arg_is_set(cmd, file_ARG) && (arg_is_set(cmd, repairtype_ARG) || arg_is_set(cmd, repair_ARG))) {
+ if (!(mf.filename = arg_str_value(cmd, file_ARG, NULL)))
+ return ECMD_FAILED;
+
+ if (!_read_metadata_file(cmd, &mf))
+ return ECMD_FAILED;
+ }
+
label_scan_setup_bcache();
if (arg_is_set(cmd, dump_ARG)) {
@@ -1582,16 +2828,53 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
else if (!strcmp(dump, "headers"))
ret = _dump_headers(cmd, dump, &set, labelsector, dev);
- else {
+
+ else if (!strcmp(dump, "backup_to_raw")) {
+ ret = _dump_backup_to_raw(cmd, &set);
+
+ } else
log_error("Unknown dump value.");
- ret = 0;
- }
if (!ret)
return ECMD_FAILED;
return ECMD_PROCESSED;
}
+ if (arg_is_set(cmd, repairtype_ARG)) {
+ cmd->use_hints = 0;
+
+ repair = arg_str_value(cmd, repairtype_ARG, NULL);
+
+ if (!strcmp(repair, "label_header"))
+ ret = _repair_label_header(cmd, repair, &set, labelsector, dev);
+
+ else if (!strcmp(repair, "pv_header"))
+ ret = _repair_pv_header(cmd, repair, &set, &mf, labelsector, dev);
+
+ else if (!strcmp(repair, "metadata"))
+ ret = _repair_metadata(cmd, repair, &set, &mf, labelsector, dev);
+ else
+ log_error("Unknown repair value.");
+
+ if (!ret)
+ return ECMD_FAILED;
+ return ECMD_PROCESSED;
+ }
+
+ if (arg_is_set(cmd, repair_ARG)) {
+ cmd->use_hints = 0;
+
+ /* repair is a combination of repairtype pv_header+metadata */
+
+ if (!_repair_pv_header(cmd, "pv_header", &set, &mf, labelsector, dev))
+ return ECMD_FAILED;
+
+ if (!_repair_metadata(cmd, "metadata", &set, &mf, labelsector, dev))
+ return ECMD_FAILED;
+
+ return ECMD_PROCESSED;
+ }
+
/*
* The old/original form of pvck, which did not do much,
* but this is here to preserve the historical output.
diff --git a/tools/tools.h b/tools/tools.h
index b78c471..a2baaa5 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -184,6 +184,7 @@ int syncaction_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_v
int reportformat_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int configreport_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int configtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
+int repairtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
/* we use the enums to access the switches */
unsigned arg_count(const struct cmd_context *cmd, int a);
diff --git a/tools/vals.h b/tools/vals.h
index db8bae9..317e498 100644
--- a/tools/vals.h
+++ b/tools/vals.h
@@ -141,6 +141,7 @@ val(syncaction_VAL, syncaction_arg, "SyncAction", "check|repair")
val(reportformat_VAL, reportformat_arg, "ReportFmt", "basic|json")
val(configreport_VAL, configreport_arg, "ConfigReport", "log|vg|lv|pv|pvseg|seg")
val(configtype_VAL, configtype_arg, "ConfigType", "current|default|diff|full|list|missing|new|profilable|profilable-command|profilable-metadata")
+val(repairtype_VAL, repairtype_arg, "RepairType", "pv_header|metadata|label_header")
/* this should always be last */
val(VAL_COUNT, NULL, NULL, NULL)
4 years
master - pvck: use zalloc in more places
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=5a88b2ce7fcdffec909...
Commit: 5a88b2ce7fcdffec909f7f236d9bcfe0a615b6b9
Parent: 3145a855836c4cf50d4b6064b3e6f1ce4a366aad
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Nov 27 11:17:15 2019 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Nov 27 11:17:15 2019 -0600
pvck: use zalloc in more places
---
tools/pvck.c | 10 +++-------
1 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index cc23d9b..4eaf8b4 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -226,11 +226,9 @@ static void _copy_out_metadata(char *buf, uint32_t start, uint32_t first_start,
/* terminating 0 byte */
new_len++;
- if (!(new_buf = malloc(new_len)))
+ if (!(new_buf = zalloc(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);
@@ -752,9 +750,8 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
if (!tofile)
return_0;
- if (!(meta_buf = malloc(mda_size)))
+ if (!(meta_buf = zalloc(mda_size)))
return_0;
- memset(meta_buf, 0, mda_size);
if (!dev_read_bytes(dev, mda_offset, mda_size, meta_buf)) {
log_print("CHECK: failed to read metadata area at offset %llu size %llu",
@@ -2468,9 +2465,8 @@ static int _backup_file_to_raw_metadata(char *back_buf, uint64_t back_size,
text_max = back_size * 2;
- if (!(text_buf = malloc(text_max)))
+ if (!(text_buf = zalloc(text_max)))
return_0;
- memset(text_buf, 0, text_max);
p = back_buf;
text_pos = 0;
4 years
master - pvck: dump functions cleanup args and return vals
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=2e0f273008155446032...
Commit: 2e0f2730081554460328b3ac7e28080bd902535c
Parent: d051e899a5ab856da51b9482e3eaf822a73f274d
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Mon Nov 4 13:38:23 2019 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Nov 27 11:13:47 2019 -0600
pvck: dump functions cleanup args and return vals
---
tools/pvck.c | 68 ++++++++++++++++++++++++++++++---------------------------
1 files changed, 36 insertions(+), 32 deletions(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index 6e9b10d..8588906 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -1218,8 +1218,7 @@ static int _dump_mda_header(struct cmd_context *cmd, struct settings *set,
}
static int _dump_headers(struct cmd_context *cmd, const char *dump, struct settings *set,
- uint64_t labelsector, struct device *dev,
- int argc, char **argv)
+ uint64_t labelsector, struct device *dev)
{
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum, mda2_checksum;
@@ -1232,7 +1231,7 @@ static int _dump_headers(struct cmd_context *cmd, const char *dump, struct setti
if (!mda_count) {
log_print("zero metadata copies");
- return ECMD_PROCESSED;
+ return 1;
}
/*
@@ -1252,14 +1251,13 @@ static int _dump_headers(struct cmd_context *cmd, const char *dump, struct setti
if (bad) {
log_error("Found bad header or metadata values.");
- return ECMD_FAILED;
+ return 0;
}
- return ECMD_PROCESSED;
+ return 1;
}
static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct settings *set,
uint64_t labelsector, struct device *dev,
- int argc, char **argv,
int print_metadata, int print_area)
{
const char *tofile = NULL;
@@ -1271,11 +1269,12 @@ static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct sett
if (arg_is_set(cmd, file_ARG)) {
if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
- return ECMD_FAILED;
+ return 0;
}
- /* 1: dump metadata from first mda, 2: dump metadata from second mda */
- if (arg_is_set(cmd, pvmetadatacopies_ARG))
+ if (set->mda_num)
+ mda_num = set->mda_num;
+ else if (arg_is_set(cmd, pvmetadatacopies_ARG))
mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
if (!_dump_label_and_pv_header(cmd, labelsector, dev, 0, NULL,
@@ -1284,7 +1283,7 @@ static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct sett
if (!mda_count) {
log_print("zero metadata copies");
- return ECMD_PROCESSED;
+ return 1;
}
/*
@@ -1305,9 +1304,9 @@ static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct sett
if (bad) {
log_error("Found bad header or metadata values.");
- return ECMD_FAILED;
+ return 0;
}
- return ECMD_PROCESSED;
+ return 1;
}
static int _dump_found(struct cmd_context *cmd, struct settings *set, uint64_t labelsector, struct device *dev)
@@ -1364,8 +1363,7 @@ static int _dump_found(struct cmd_context *cmd, struct settings *set, uint64_t l
*/
static int _dump_search(struct cmd_context *cmd, const char *dump, struct settings *set,
- uint64_t labelsector, struct device *dev,
- int argc, char **argv)
+ uint64_t labelsector, struct device *dev)
{
const char *tofile = NULL;
char *buf;
@@ -1376,11 +1374,12 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
if (arg_is_set(cmd, file_ARG)) {
if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
- return ECMD_FAILED;
+ return_0;
}
- /* 1: dump metadata from first mda, 2: dump metadata from second mda */
- if (arg_is_set(cmd, pvmetadatacopies_ARG))
+ if (set->mda_num)
+ mda_num = set->mda_num;
+ else if (arg_is_set(cmd, pvmetadatacopies_ARG))
mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
_dump_label_and_pv_header(cmd, labelsector, dev, 0, NULL,
@@ -1421,7 +1420,7 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
extra_bytes = dev_bytes % ONE_MB_IN_BYTES;
if (dev_bytes < (2 * ONE_MB_IN_BYTES))
- return ECMD_FAILED;
+ return_0;
mda_offset = dev_bytes - extra_bytes - ONE_MB_IN_BYTES;
mda_size = dev_bytes - mda_offset;
@@ -1443,20 +1442,20 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
(unsigned long long)mda_offset, (unsigned long long)mda_size);
if (!(buf = malloc(mda_size)))
- return ECMD_FAILED;
+ return_0;
memset(buf, 0, mda_size);
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;
+ return 0;
}
_dump_all_text(cmd, set, tofile, dev, mda_num, mda_offset, mda_size, buf);
free(buf);
- return ECMD_PROCESSED;
+ return 1;
}
static int _get_one_setting(struct cmd_context *cmd, struct settings *set, char *key, char *val)
@@ -1570,22 +1569,27 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
dump = arg_str_value(cmd, dump_ARG, NULL);
if (!strcmp(dump, "metadata"))
- return _dump_metadata(cmd, dump, &set, labelsector, dev, argc, argv, PRINT_CURRENT, 0);
+ ret = _dump_metadata(cmd, dump, &set, labelsector, dev, PRINT_CURRENT, 0);
- if (!strcmp(dump, "metadata_all"))
- return _dump_metadata(cmd, dump, &set, labelsector, dev, argc, argv, PRINT_ALL, 0);
+ else if (!strcmp(dump, "metadata_all"))
+ ret = _dump_metadata(cmd, dump, &set, labelsector, dev, PRINT_ALL, 0);
- if (!strcmp(dump, "metadata_area"))
- return _dump_metadata(cmd, dump, &set, labelsector, dev, argc, argv, 0, 1);
+ else if (!strcmp(dump, "metadata_area"))
+ ret = _dump_metadata(cmd, dump, &set, labelsector, dev, 0, 1);
- if (!strcmp(dump, "metadata_search"))
- return _dump_search(cmd, dump, &set, labelsector, dev, argc, argv);
+ else if (!strcmp(dump, "metadata_search"))
+ ret = _dump_search(cmd, dump, &set, labelsector, dev);
- if (!strcmp(dump, "headers"))
- return _dump_headers(cmd, dump, &set, labelsector, dev, argc, argv);
+ else if (!strcmp(dump, "headers"))
+ ret = _dump_headers(cmd, dump, &set, labelsector, dev);
+ else {
+ log_error("Unknown dump value.");
+ ret = 0;
+ }
- log_error("Unknown dump value.");
- return ECMD_FAILED;
+ if (!ret)
+ return ECMD_FAILED;
+ return ECMD_PROCESSED;
}
/*
4 years
master - pvck: dump show most recent metadata
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=d051e899a5ab856da51...
Commit: d051e899a5ab856da51b9482e3eaf822a73f274d
Parent: 9cf08836ef42c32de6874e77e1d3003b7b476039
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Mon Nov 4 11:32:23 2019 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Nov 27 11:13:47 2019 -0600
pvck: dump show most recent metadata
---
tools/pvck.c | 24 +++++++++++++++++++++++-
1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index 483c49f..6e9b10d 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -255,14 +255,18 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
char vgname[NAME_LEN+1];
char id_str[ID_STR_SIZE];
char id_first[ID_STR_SIZE];
+ char latest_vgname[NAME_LEN+1];
+ char latest_id_str[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 latest_seqno;
uint32_t crc;
uint64_t text_size;
uint64_t meta_size;
+ uint64_t latest_offset;
int metadata_offset_found = 0;
int multiple_vgs = 0;
int bad_end;
@@ -277,6 +281,11 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
}
}
+ memset(latest_vgname, 0, sizeof(latest_vgname));
+ memset(latest_id_str, 0, sizeof(latest_id_str));
+ latest_offset = 0;
+ latest_seqno = 0;
+
/*
* If metadata has not wrapped, and the metadata area beginning
* has not been damaged, the text area will begin with vgname {.
@@ -419,6 +428,13 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
(unsigned long long)text_size,
crc, vgname, seqno, id_str);
+ if (!latest_seqno || (seqno > latest_seqno)) {
+ latest_seqno = seqno;
+ latest_offset = mda_offset + buf_off;
+ memcpy(latest_vgname, vgname, NAME_LEN);
+ memcpy(latest_id_str, id_str, ID_STR_SIZE);
+ }
+
/*
* save the location of the first metadata we've found so
* we know where to stop after wrapping buf.
@@ -442,7 +458,7 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
_copy_line(str1, line, &len);
log_print("%s", line);
}
- if ((str2 = strstr(str1, "creation_time = "))) {
+ if (str1 && (str2 = strstr(str1, "creation_time = "))) {
memset(line, 0, sizeof(line));
_copy_line(str2, line, &len);
log_print("%s\n", line);
@@ -469,6 +485,12 @@ static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const c
if (multiple_vgs)
log_warn("WARNING: metadata from multiple VGs was found.");
+ if (!set->metadata_offset_set)
+ log_print("Most recent metadata found at %llu seqno %u for vg %s id %s",
+ (unsigned long long)latest_offset, latest_seqno,
+ latest_vgname, latest_id_str);
+
+
if (fp) {
if (fflush(fp))
stack;
4 years
master - pvck: allow disk locations to be specified
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=9cf08836ef42c32de68...
Commit: 9cf08836ef42c32de6874e77e1d3003b7b476039
Parent: 53126ceada394f7c12ce95c4cd8d824132d418eb
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Oct 22 13:28:45 2019 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Nov 27 11:13:47 2019 -0600
pvck: allow disk locations to be specified
using --settings:
mda_offset=<offset> mda_size=<size> can be used
in place of the offset/size that normally come
from headers.
metadata_offset=<offset> prints/saves one instance
of metadata text at the given offset, in
metadata_all or metadata_search.
---
tools/args.h | 4 +
tools/command-lines.in | 2 +-
tools/pvck.c | 214 +++++++++++++++++++++++++++++-------------------
3 files changed, 134 insertions(+), 86 deletions(-)
diff --git a/tools/args.h b/tools/args.h
index bd07aa3..2f84d2c 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -459,6 +459,10 @@ arg(setphysicalvolumesize_ARG, '\0', "setphysicalvolumesize", sizemb_VAL, 0, 0,
"Overrides the automatically detected size of the PV.\n"
"Use with care, or prior to reducing the physical size of the device.\n")
+arg(settings_ARG, '\0', "settings", string_VAL, ARG_GROUPABLE, 0,
+ "Specifies command specific settings in \"Key = Value\" form.\n"
+ "Repeat this option to specify multiple values.\n")
+
arg(poll_ARG, '\0', "poll", bool_VAL, 0, 0,
"When yes, start the background transformation of an LV.\n"
"An incomplete transformation, e.g. pvmove or lvconvert interrupted\n"
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 7be4715..cbd64a8 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1437,7 +1437,7 @@ ID: pvck_general
DESC: Check for metadata on a device
pvck --dump String PV
-OO: --file String, --pvmetadatacopies MetadataCopiesPV, --labelsector Number
+OO: --settings String, --file String, --pvmetadatacopies MetadataCopiesPV, --labelsector Number
ID: pvck_dump
DESC: Print metadata from a device
diff --git a/tools/pvck.c b/tools/pvck.c
index d2c2f9d..483c49f 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -23,6 +23,16 @@
#define PRINT_CURRENT 1
#define PRINT_ALL 2
+struct settings {
+ uint64_t metadata_offset; /* start of text metadata, from start of disk */
+ uint64_t mda_offset; /* start of mda_header, from start of disk */
+ uint64_t mda_size; /* size of metadata area (mda_header + text area) */
+
+ unsigned metadata_offset_set:1;
+ unsigned mda_offset_set:1;
+ unsigned mda_size_set:1;
+};
+
static char *_chars_to_str(void *in, void *out, int num, int max, const char *field)
{
char *i = in;
@@ -237,7 +247,7 @@ static void _copy_line(char *in, char *out, int *len)
*len = i+1;
}
-static int _dump_all_text(struct cmd_context *cmd, const char *tofile, struct device *dev,
+static int _dump_all_text(struct cmd_context *cmd, struct settings *set, const char *tofile, struct device *dev,
int mda_num, uint64_t mda_offset, uint64_t mda_size, char *buf)
{
FILE *fp = NULL;
@@ -253,6 +263,7 @@ static int _dump_all_text(struct cmd_context *cmd, const char *tofile, struct de
uint32_t crc;
uint64_t text_size;
uint64_t meta_size;
+ int metadata_offset_found = 0;
int multiple_vgs = 0;
int bad_end;
int vgnamelen;
@@ -320,6 +331,19 @@ static int _dump_all_text(struct cmd_context *cmd, const char *tofile, struct de
p = buf + buf_off;
/*
+ * user specified metadata in one location
+ */
+ if (set->metadata_offset_set && (set->metadata_offset != (mda_offset + buf_off))) {
+ count++;
+ continue;
+ }
+ if (set->metadata_offset_set) {
+ if (metadata_offset_found)
+ break;
+ metadata_offset_found = 1;
+ }
+
+ /*
* copy line of possible metadata to check for vgname
*/
memset(line, 0, sizeof(line));
@@ -427,7 +451,8 @@ static int _dump_all_text(struct cmd_context *cmd, const char *tofile, struct de
if (fp) {
fprintf(fp, "%s", text_buf);
- fprintf(fp, "\n--\n");
+ if (!set->metadata_offset_set)
+ fprintf(fp, "\n--\n");
}
free(text_buf);
@@ -676,19 +701,6 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
return 1;
}
-/*
- * 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
- */
-
static int _dump_current_text(struct device *dev,
int print_fields, int print_metadata, const char *tofile,
int mda_num, int rlocn_index,
@@ -1049,7 +1061,7 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, uint64_t labelsect
* pv_header is.
*/
-static int _dump_mda_header(struct cmd_context *cmd,
+static int _dump_mda_header(struct cmd_context *cmd, struct settings *set,
int print_fields, int print_metadata, int print_area,
const char *tofile,
struct device *dev,
@@ -1171,7 +1183,7 @@ static int _dump_mda_header(struct cmd_context *cmd,
goto out;
}
- _dump_all_text(cmd, tofile, dev, mda_num, mda_offset, mda_size, buf);
+ _dump_all_text(cmd, set, tofile, dev, mda_num, mda_offset, mda_size, buf);
}
/* Should we also check text metadata if it exists in rlocn1? */
@@ -1183,7 +1195,8 @@ static int _dump_mda_header(struct cmd_context *cmd,
return 1;
}
-static int _dump_headers(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
+static int _dump_headers(struct cmd_context *cmd, const char *dump, struct settings *set,
+ uint64_t labelsector, struct device *dev,
int argc, char **argv)
{
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
@@ -1200,31 +1213,14 @@ static int _dump_headers(struct cmd_context *cmd, uint64_t labelsector, struct d
return ECMD_PROCESSED;
}
- /* 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 1MB 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.
*/
-
- if (!_dump_mda_header(cmd, 1, 0, 0, NULL, dev, 4096, mda1_size, &mda1_checksum, NULL))
+ if (!_dump_mda_header(cmd, set, 1, 0, 0, NULL, dev, 4096, mda1_size, &mda1_checksum, NULL))
bad++;
- /*
- * mda2_offset may be incorrect. Probe for a valid mda_header at
- * mda2_offset and at other possible/expected locations, e.g.
- * 1MB 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) {
- if (!_dump_mda_header(cmd, 1, 0, 0, NULL, dev, mda2_offset, mda2_size, &mda2_checksum, NULL))
+ if (!_dump_mda_header(cmd, set, 1, 0, 0, NULL, dev, mda2_offset, mda2_size, &mda2_checksum, NULL))
bad++;
/* This probably indicates that one was committed and the other not. */
@@ -1239,7 +1235,8 @@ static int _dump_headers(struct cmd_context *cmd, uint64_t labelsector, struct d
return ECMD_PROCESSED;
}
-static int _dump_metadata(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
+static int _dump_metadata(struct cmd_context *cmd, const char *dump, struct settings *set,
+ uint64_t labelsector, struct device *dev,
int argc, char **argv,
int print_metadata, int print_area)
{
@@ -1270,30 +1267,16 @@ static int _dump_metadata(struct cmd_context *cmd, uint64_t labelsector, struct
/*
* 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 1MB 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.
- * 1MB 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 (mda_num == 1) {
- if (!_dump_mda_header(cmd, 0, print_metadata, print_area, tofile, dev, 4096, mda1_size, &mda1_checksum, NULL))
+ if (!_dump_mda_header(cmd, set, 0, print_metadata, print_area, tofile, dev, 4096, mda1_size, &mda1_checksum, NULL))
bad++;
} else if (mda_num == 2) {
if (!mda2_offset) {
log_print("CHECK: second mda not found");
bad++;
} else {
- if (!_dump_mda_header(cmd, 0, print_metadata, print_area, tofile, dev, mda2_offset, mda2_size, &mda2_checksum, NULL))
+ if (!_dump_mda_header(cmd, set, 0, print_metadata, print_area, tofile, dev, mda2_offset, mda2_size, &mda2_checksum, NULL))
bad++;
}
}
@@ -1305,7 +1288,7 @@ static int _dump_metadata(struct cmd_context *cmd, uint64_t labelsector, struct
return ECMD_PROCESSED;
}
-static int _dump_found(struct cmd_context *cmd, uint64_t labelsector, struct device *dev)
+static int _dump_found(struct cmd_context *cmd, struct settings *set, uint64_t labelsector, struct device *dev)
{
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum = 0, mda2_checksum = 0;
@@ -1318,12 +1301,12 @@ static int _dump_found(struct cmd_context *cmd, uint64_t labelsector, struct dev
bad++;
if (found_label && mda1_offset) {
- if (!_dump_mda_header(cmd, 0, 0, 0, NULL, dev, 4096, mda1_size, &mda1_checksum, &found_header1))
+ if (!_dump_mda_header(cmd, set, 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))
+ if (!_dump_mda_header(cmd, set, 0, 0, 0, NULL, dev, mda2_offset, mda2_size, &mda2_checksum, &found_header2))
bad++;
}
@@ -1358,16 +1341,14 @@ static int _dump_found(struct cmd_context *cmd, uint64_t labelsector, struct dev
* zeroed/damaged.
*/
-static int _dump_search(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
+static int _dump_search(struct cmd_context *cmd, const char *dump, struct settings *set,
+ uint64_t labelsector, struct device *dev,
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;
@@ -1384,9 +1365,6 @@ static int _dump_search(struct cmd_context *cmd, uint64_t labelsector, struct de
&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 1MB.
@@ -1404,7 +1382,10 @@ static int _dump_search(struct cmd_context *cmd, uint64_t labelsector, struct de
* at the end of device (mod 1MB extra) can make mda2 even
* larger.
*/
- if (mda_num == 1) {
+ if (set->mda_offset_set && set->mda_size_set) {
+ mda_offset = set->mda_offset;
+ mda_size = set->mda_size;
+ } else if (mda_num == 1) {
mda_offset = 4096;
mda_size = ONE_MB_IN_BYTES - 4096;
} else if (mda_num == 2) {
@@ -1436,7 +1417,7 @@ static int _dump_search(struct cmd_context *cmd, uint64_t labelsector, struct de
(unsigned long long)mda2_size);
}
- log_print("Searching for metadata in mda%d at offset %llu size %llu", mda_num,
+ log_print("Searching for metadata at offset %llu size %llu",
(unsigned long long)mda_offset, (unsigned long long)mda_size);
if (!(buf = malloc(mda_size)))
@@ -1450,28 +1431,88 @@ static int _dump_search(struct cmd_context *cmd, uint64_t labelsector, struct de
return ECMD_FAILED;
}
- mh = (struct mda_header *)buf;
+ _dump_all_text(cmd, set, tofile, dev, mda_num, mda_offset, mda_size, 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));
+ free(buf);
+ return ECMD_PROCESSED;
+}
- _check_mda_header(mh, mda_num, mda_offset, mda_size, &found_header);
+static int _get_one_setting(struct cmd_context *cmd, struct settings *set, char *key, char *val)
+{
+ if (!strncmp(key, "metadata_offset", strlen("metadata_offset"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->metadata_offset) != 1)
+ goto_bad;
+ set->metadata_offset_set = 1;
+ return 1;
+ }
- log_print("searching for metadata text");
+ if (!strncmp(key, "mda_offset", strlen("mda_offset"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->mda_offset) != 1)
+ goto_bad;
+ set->mda_offset_set = 1;
+ return 1;
+ }
- _dump_all_text(cmd, tofile, dev, mda_num, mda_offset, mda_size, buf);
+ if (!strncmp(key, "mda_size", strlen("mda_size"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&set->mda_size) != 1)
+ goto_bad;
+ set->mda_size_set = 1;
+ return 1;
+ }
+bad:
+ log_error("Invalid setting: %s", key);
+ return 0;
+}
- free(buf);
- return ECMD_PROCESSED;
+static int _get_settings(struct cmd_context *cmd, struct settings *set)
+{
+ struct arg_value_group_list *group;
+ const char *str;
+ char key[64];
+ char val[64];
+ int num;
+ int pos;
+
+ memset(set, 0, sizeof(struct settings));
+
+ /*
+ * "grouped" means that multiple --settings options can be used.
+ * Each option is also allowed to contain multiple key = val pairs.
+ */
+
+ dm_list_iterate_items(group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(group->arg_values, settings_ARG))
+ continue;
+
+ if (!(str = grouped_arg_str_value(group->arg_values, settings_ARG, NULL)))
+ break;
+
+ pos = 0;
+
+ while (pos < strlen(str)) {
+ /* scan for "key1=val1 key2 = val2 key3= val3" */
+
+ memset(key, 0, sizeof(key));
+ memset(val, 0, sizeof(val));
+
+ if (sscanf(str + pos, " %63[^=]=%63s %n", key, val, &num) != 2) {
+ log_error("Invalid setting at: %s", str+pos);
+ return 0;
+ }
+
+ pos += num;
+
+ if (!_get_one_setting(cmd, set, key, val))
+ return_0;
+ }
+ }
+
+ return 1;
}
int pvck(struct cmd_context *cmd, int argc, char **argv)
{
+ struct settings set;
struct device *dev;
const char *dump;
const char *pv_name;
@@ -1496,6 +1537,9 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
}
}
+ if (!_get_settings(cmd, &set))
+ return ECMD_FAILED;
+
label_scan_setup_bcache();
if (arg_is_set(cmd, dump_ARG)) {
@@ -1504,19 +1548,19 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
dump = arg_str_value(cmd, dump_ARG, NULL);
if (!strcmp(dump, "metadata"))
- return _dump_metadata(cmd, labelsector, dev, argc, argv, PRINT_CURRENT, 0);
+ return _dump_metadata(cmd, dump, &set, labelsector, dev, argc, argv, PRINT_CURRENT, 0);
if (!strcmp(dump, "metadata_all"))
- return _dump_metadata(cmd, labelsector, dev, argc, argv, PRINT_ALL, 0);
+ return _dump_metadata(cmd, dump, &set, labelsector, dev, argc, argv, PRINT_ALL, 0);
if (!strcmp(dump, "metadata_area"))
- return _dump_metadata(cmd, labelsector, dev, argc, argv, 0, 1);
+ return _dump_metadata(cmd, dump, &set, labelsector, dev, argc, argv, 0, 1);
if (!strcmp(dump, "metadata_search"))
- return _dump_search(cmd, labelsector, dev, argc, argv);
+ return _dump_search(cmd, dump, &set, labelsector, dev, argc, argv);
if (!strcmp(dump, "headers"))
- return _dump_headers(cmd, labelsector, dev, argc, argv);
+ return _dump_headers(cmd, dump, &set, labelsector, dev, argc, argv);
log_error("Unknown dump value.");
return ECMD_FAILED;
@@ -1535,7 +1579,7 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
continue;
}
- if (!_dump_found(cmd, labelsector, dev))
+ if (!_dump_found(cmd, &set, labelsector, dev))
bad++;
}
4 years
master - pvck: move some arg processing
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=53126ceada394f7c12c...
Commit: 53126ceada394f7c12ce95c4cd8d824132d418eb
Parent: 94076245df6e99492a7a0bb2d8c2f2bc2616259d
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Mon Oct 21 16:39:46 2019 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Nov 27 11:13:47 2019 -0600
pvck: move some arg processing
---
tools/pvck.c | 103 +++++++++++++++++++++-------------------------------------
1 files changed, 37 insertions(+), 66 deletions(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index 231faf9..d2c2f9d 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -807,8 +807,8 @@ static int _dump_current_text(struct device *dev,
return 1;
}
-static int _dump_label_and_pv_header(struct cmd_context *cmd, int print_fields,
- struct device *dev,
+static int _dump_label_and_pv_header(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
+ int print_fields,
int *found_label,
uint64_t *mda1_offset, uint64_t *mda1_size,
uint64_t *mda2_offset, uint64_t *mda2_size,
@@ -824,22 +824,11 @@ static int _dump_label_and_pv_header(struct cmd_context *cmd, int print_fields,
uint64_t pvhe_offset;
uint64_t dlocn_offset;
char *buf;
- uint64_t labelsector;
uint64_t tmp;
int mda_count = 0;
int bad = 0;
int di;
- /*
- * 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));
- else
- labelsector = 1;
-
lh_offset = labelsector * 512; /* from start of disk */
if (!(buf = zalloc(512)))
@@ -1194,26 +1183,15 @@ static int _dump_mda_header(struct cmd_context *cmd,
return 1;
}
-static int _dump_headers(struct cmd_context *cmd,
+static int _dump_headers(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
int argc, char **argv)
{
- struct device *dev;
- const char *pv_name;
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum, mda2_checksum;
int mda_count = 0;
int bad = 0;
- 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 (!_dump_label_and_pv_header(cmd, 1, dev, NULL,
+ if (!_dump_label_and_pv_header(cmd, labelsector, dev, 1, NULL,
&mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count))
bad++;
@@ -1261,12 +1239,10 @@ static int _dump_headers(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
-static int _dump_metadata(struct cmd_context *cmd,
+static int _dump_metadata(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
int argc, char **argv,
int print_metadata, int print_area)
{
- struct device *dev;
- const char *pv_name;
const char *tofile = NULL;
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum, mda2_checksum;
@@ -1283,16 +1259,7 @@ static int _dump_metadata(struct cmd_context *cmd,
if (arg_is_set(cmd, pvmetadatacopies_ARG))
mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
- 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 (!_dump_label_and_pv_header(cmd, 0, dev, NULL,
+ if (!_dump_label_and_pv_header(cmd, labelsector, dev, 0, NULL,
&mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count))
bad++;
@@ -1338,8 +1305,7 @@ static int _dump_metadata(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
-static int _dump_found(struct cmd_context *cmd, struct device *dev,
- uint64_t labelsector)
+static int _dump_found(struct cmd_context *cmd, uint64_t labelsector, struct device *dev)
{
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum = 0, mda2_checksum = 0;
@@ -1347,7 +1313,7 @@ static int _dump_found(struct cmd_context *cmd, struct device *dev,
int mda_count = 0;
int bad = 0;
- if (!_dump_label_and_pv_header(cmd, 0, dev, &found_label,
+ if (!_dump_label_and_pv_header(cmd, labelsector, dev, 0, &found_label,
&mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count))
bad++;
@@ -1392,12 +1358,10 @@ static int _dump_found(struct cmd_context *cmd, struct device *dev,
* zeroed/damaged.
*/
-static int _dump_search(struct cmd_context *cmd,
+static int _dump_search(struct cmd_context *cmd, uint64_t labelsector, struct device *dev,
int argc, char **argv)
{
char str[256];
- struct device *dev;
- const char *pv_name;
const char *tofile = NULL;
char *buf;
struct mda_header *mh;
@@ -1416,16 +1380,7 @@ static int _dump_search(struct cmd_context *cmd,
if (arg_is_set(cmd, pvmetadatacopies_ARG))
mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
- 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();
-
- _dump_label_and_pv_header(cmd, 0, dev, NULL,
+ _dump_label_and_pv_header(cmd, labelsector, dev, 0, NULL,
&mda1_offset, &mda1_size, &mda2_offset, &mda2_size, &mda_count);
/*
@@ -1524,23 +1479,44 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
int bad = 0;
int i;
+ /*
+ * 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));
+
if (arg_is_set(cmd, dump_ARG)) {
+ 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;
+
dump = arg_str_value(cmd, dump_ARG, NULL);
if (!strcmp(dump, "metadata"))
- return _dump_metadata(cmd, argc, argv, PRINT_CURRENT, 0);
+ return _dump_metadata(cmd, labelsector, dev, argc, argv, PRINT_CURRENT, 0);
if (!strcmp(dump, "metadata_all"))
- return _dump_metadata(cmd, argc, argv, PRINT_ALL, 0);
+ return _dump_metadata(cmd, labelsector, dev, argc, argv, PRINT_ALL, 0);
if (!strcmp(dump, "metadata_area"))
- return _dump_metadata(cmd, argc, argv, 0, 1);
+ return _dump_metadata(cmd, labelsector, dev, argc, argv, 0, 1);
if (!strcmp(dump, "metadata_search"))
- return _dump_search(cmd, argc, argv);
+ return _dump_search(cmd, labelsector, dev, argc, argv);
if (!strcmp(dump, "headers"))
- return _dump_headers(cmd, argc, argv);
+ return _dump_headers(cmd, labelsector, dev, argc, argv);
log_error("Unknown dump value.");
return ECMD_FAILED;
@@ -1551,11 +1527,6 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
* but this is here to preserve the historical output.
*/
- if (arg_is_set(cmd, labelsector_ARG))
- labelsector = arg_uint64_value(cmd, labelsector_ARG, UINT64_C(0));
-
- label_scan_setup_bcache();
-
for (i = 0; i < argc; i++) {
pv_name = argv[i];
@@ -1564,7 +1535,7 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
continue;
}
- if (!_dump_found(cmd, dev, labelsector))
+ if (!_dump_found(cmd, labelsector, dev))
bad++;
}
4 years