1
0
mirror of git://sourceware.org/git/lvm2.git synced 2026-01-02 20:32:47 +03:00

Compare commits

..

76 Commits

Author SHA1 Message Date
David Teigland
cf16309c35 dm-integrity support
dm-integrity stores checksums of the data written to an
LV, and returns an error if data read from the LV does
not match the previously saved checksum.

Create a linear LV with a dm-integrity layer above it:

lvcreate --type integrity --integrity String [options]

Create a raid1 LV with a dm-integrity layer over each
raid image:

lvcreate --type raid1 --integrity y [options]

Add a dm-integrity layer to an existing linear LV,
or to each image of an existing raid1 LV:

lvconvert --integrity y LV

Remove the dm-integrity layer from a linear LV, or
from the images of a raid1 LV:

lvconvert --integrity n LV

Integrity metadata:

The --integrity String specifies if the dm-integrity
metadata (checksums) should be interleaved with data
blocks, or written to a separate external LV.

  --integrity external (default)
  Use integrity with metadata on a separate LV.
  Allows removing integrity from the LV later.

  --integrity y
  Same as integrity external.

  --integrity n
  Remove integrity (external metadata only.)

  --integrity internal
  Use integrity with metadata interleaved with
  data on the same LV.
  Only allowed with new linear LVs.
  Internal integrity cannot be removed from an LV.
  Around 1% of the LV size is used for integrity metadata.

Command variations:

lvcreate --type integrity -n Name -L Size VG
  [Uses integrity external, the default.]

lvcreate --integrity y|external -n Name -L Size VG
  [Uses type integrity, which is implied.]

lvcreate --integrity internal -n Name -L Size VG
  [Uses type integrity, which is implied.]

lvcreate --type raid1 --integrity y -m Num -n Name -L Size VG
  [Uses type integrity for each raid image.]

lvconvert --type integrity LV
  [Converts linear LV to type integrity.]

lvconvert --integrity y|external LV
  [Converts linear LV to type integrity, or each
   image to type integrity in a raid1 LV.]

Options:

  --integritymetadata LV
  Use the specified LV for external metadata.
  Allows specific device placement of metadata.
  Without this option, the command creates a
  hidden LV (with an _imeta suffix) to hold the
  metadata.  (Not usable with raid1+integrity.)

  --integritysettings String
  set dm-integrity parameters, e.g. to use a journal
  instead of bitmap, --integritysettings "mode=J".

Example:

$ lvcreate --integrity external -n lvex -L1G vg
$ lvs -a vg
  LV           VG Attr       LSize  Origin
  lvex         vg gwi-a-----  1.00g [lvex_iorig]
  [lvex_imeta] vg ewi-ao---- 12.00m
  [lvex_iorig] vg -wi-ao----  1.00g

$ lvcreate --integrity internal -n lvin -L1G vg
$ lvs -a vg
  LV           VG Attr       LSize Origin
  lvin         vg gwi-a----- 1.00g [lvin_iorig]
  [lvin_iorig] vg -wi-ao---- 1.00g

$ lvcreate --type raid1 --integrity y -m 1 -n lver -L1G vg
$ lvs -a vg
  LV                    VG Attr       LSize  Origin
  lver                  vg rwi-a-r---  1.00g
  [lver_rimage_0]       vg gwi-aor---  1.00g [lver_rimage_0_iorig]
  [lver_rimage_0_imeta] vg ewi-ao---- 12.00m
  [lver_rimage_0_iorig] vg -wi-ao----  1.00g
  [lver_rimage_1]       vg gwi-aor---  1.00g [lver_rimage_1_iorig]
  [lver_rimage_1_imeta] vg ewi-ao---- 12.00m
  [lver_rimage_1_iorig] vg -wi-ao----  1.00g
  [lver_rmeta_0]        vg ewi-aor---  4.00m
  [lver_rmeta_1]        vg ewi-aor---  4.00m
2019-12-12 15:11:36 -06:00
David Teigland
2173bdb821 drop warnings about missing pvs in foreign vgs
When a foreign VG is ignored, don't print warnings that
it is missing PVs.
2019-12-11 12:56:15 -06:00
David Teigland
2da6f01c15 pvck: show specific dump option values 2019-12-10 11:07:07 -06:00
Zdenek Kabelac
4a52855899 tests: improve secure test
Validate we capture core while original task sleeps.
2019-12-10 15:44:16 +01:00
Zdenek Kabelac
611d4107a4 test: fix missing waiting on udev
After device creation we need to wait for a cookie so it's not forgotten
in the system.
2019-12-10 15:44:16 +01:00
Zdenek Kabelac
3b6defcf1f test: fail on device create
Correct validation of prepared device and fail if the device can't
be created.
2019-12-10 15:44:16 +01:00
Zdenek Kabelac
8ab1d489f3 test: aux setup
Avoid endless loop if there was no 'remove' progress.
2019-12-10 15:44:16 +01:00
Zdenek Kabelac
89d839e541 clenaup: simpler form 2019-12-10 15:44:16 +01:00
Zdenek Kabelac
abc0a8faba vg_read: use else for 3 case
Make it visible we check for ==, >, <  of same var.
2019-12-10 15:44:16 +01:00
Zdenek Kabelac
5555765cfc debug: enhance messages
Drop 'extra' stack trace where errors are already logged from function.
Add some missing dots in messages.
2019-12-10 15:44:16 +01:00
Zdenek Kabelac
cff16b062b debug: avoid to slashes in debug message 2019-12-10 15:44:16 +01:00
Nikhil Kshirsagar
e70d5d470c debug: print VG name in log messages for segment errors
Signed-off-by: Nikhil Kshirsagar <nkshirsa@redhat.com>
2019-12-10 15:44:06 +01:00
Zdenek Kabelac
4353823306 libdm: set maj:min while creating and reloading device
Add maj:min to the task structure for RELOAD - which is now
handled in _flatten() and will just skip passing device name.
2019-12-10 15:42:59 +01:00
Zdenek Kabelac
df0bc5081c libdm: support device RELOAD with maj:min and devname set
When devices are created - we were not giving meaning error messages
when the failure happened on 'reload' part of creation.

With this patch we are now able to report both name and major:minor.

Enhancment is most visible with 'crypto' devices,
which are using 'secure' memory erase bit.
2019-12-10 15:42:59 +01:00
David Teigland
338f4df54b man pvck: describe settings 2019-12-06 16:24:27 -06:00
David Teigland
3f381784f2 update option description for settings 2019-12-06 16:21:26 -06:00
David Teigland
ec71df6fec pvck: deal with coverity warnings 2019-12-02 11:16:02 -06:00
Marian Csontos
91f91b80f1 post-release 2019-11-30 14:46:56 +01:00
Marian Csontos
3d7f755674 pre-release 2019-11-30 14:45:51 +01:00
Marian Csontos
0a7495e680 build: make generate 2019-11-30 14:24:22 +01:00
David Teigland
5a88b2ce7f pvck: use zalloc in more places 2019-11-27 11:17:15 -06:00
David Teigland
3145a85583 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>
2019-11-27 11:13:47 -06:00
David Teigland
2e0f273008 pvck: dump functions cleanup args and return vals 2019-11-27 11:13:47 -06:00
David Teigland
d051e899a5 pvck: dump show most recent metadata 2019-11-27 11:13:47 -06:00
David Teigland
9cf08836ef 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.
2019-11-27 11:13:47 -06:00
David Teigland
53126ceada pvck: move some arg processing 2019-11-27 11:13:47 -06:00
David Teigland
94076245df scan: add simple scan to find a pvid 2019-11-27 11:13:47 -06:00
David Teigland
74ad2cd76f metadata: add vg_from_config_tree
Add cmd/fmt args to import functions so that
they can be used without the fid arg which.
2019-11-27 11:13:47 -06:00
David Teigland
13c629fb78 Revert "cov: use zalloc"
This reverts commit 9af1d63b4d.

fixes folded into subsequent pvck commit
2019-11-27 11:13:43 -06:00
David Teigland
39bd9b111b Revert "pvck: check result of dev_get_size"
This reverts commit 1f4968289c.

fixes folded into subsequent pvck commit
2019-11-27 11:13:40 -06:00
David Teigland
4485b8edca Revert "cov: fix mem leaking buffer"
This reverts commit d67ce9e140.

fixes folded into subsequent pvck commit
2019-11-27 11:13:36 -06:00
David Teigland
657d42e879 Revert "cov: avoid passing NULL to strstr function"
This reverts commit 0bad3977df.

fixes folded into subsequent pvck commit
2019-11-27 11:13:32 -06:00
David Teigland
595aa1d452 Revert "cov: check for retvalue"
This reverts commit 153e55c20e.

fixes folded into subsequent pvck commit
2019-11-27 11:13:09 -06:00
David Teigland
98a8099da9 scanning: use bool type for _scan_text_mismatch 2019-11-27 09:26:49 -06:00
David Teigland
b400353c71 tests hints: update check for io count
Running a reporting command on a VG now includes one
additional read to check the mda_header for any change
to the vg between scan and lock.
2019-11-26 16:52:28 -06:00
David Teigland
a61272a6f0 Revert "lvs: disable scanning optimization"
This reverts commit 7474440d3b.

lvs can use the scanning optimization again since it has
been changed in:
"scanning: optimize by checking text offset and checksum"
2019-11-26 16:52:28 -06:00
David Teigland
0c1316cda8 scanning: optimize by checking text offset and checksum
After the VG lock is taken for vg_read, reread the mda_header
and compare the metadata text offset and checksum to what was
seen during label scan.  If it is unchanged, then the metadata
has not changed since the label scan, and the metadata does not
need to be reread under the lock for command processing.

For commands that do not make changes (e.g. reporting), the
mda_header is reread and checked on one mda to decide if the
full metadata rereading can be skipped.  For other commands
(e.g. modifying the vg) the mda_header is reread and checked
from all PVs.  (These could probably just check one mda also.)
2019-11-26 16:52:28 -06:00
David Teigland
56a295f78c bcache: add invalidate_bytes function 2019-11-26 16:52:28 -06:00
Heinz Mauelshagen
29db9c6325 lvcreate: ensure striped raid region size is at least stripe size
The kernel MD runtime requires region size to be larger than stripe size
on striped raid layouts, thus the dm-raid target's constructor rejects
such request.

This causes e.g. an 'lvcreate --type raid10 -i3 -I4096 -R2048 -n lv vg' to fail.

Avoid failing late in the kernel by enforcing region size to be
larger or equal to stripe size.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1698225
2019-11-26 22:31:58 +01:00
David Teigland
2037476008 pvcreate,pvremove: fix reacquiring global lock after prompt
When pvcreate/pvremove prompt the user, they first release
the global lock, then acquire it again after the prompt,
to avoid blocking other commands while waiting for a user
response.  This release/reacquire changes the locking
order with respect to the hints flock (and potentially other
locks).  So, to avoid deadlock, use a nonblocking request
when reacquiring the global lock.
2019-11-26 14:34:43 -06:00
David Teigland
1c9b36618e writecache: modprobe dm-writecache 2019-11-26 11:21:09 -06:00
David Teigland
bbd8badaef tests: update to md dev name
Restore WAIT_MD_DEV in teardown.

NOTE: The name of MD device may have changed.

(cherry picked from commit c2ff8876f9)
2019-11-26 10:34:51 +01:00
Marian Csontos
b690258518 tests: Find md name using lsblk
After stopping MD device and rescanning the leg, it is created with
different name.
2019-11-26 09:13:17 +01:00
Marian Csontos
4757ce4c2a Partial revert "tests: update to md dev name"
This partially reverts commit c2ff8876f9.

Not all MD devices are stopped. Something is missing there...
2019-11-25 09:23:02 +01:00
Marian Csontos
1e669ab315 test: Fix handling leftovers from previous tests
teardown fails current PREFIX is prefix of previously failed test with
leftovers in dmtable.
2019-11-20 15:27:03 +01:00
David Teigland
7474440d3b lvs: disable scanning optimization
The scanning optimization can produce warnings from
'lvs' when run concurrently with commands modifying LVs,
so disable the optimization until it can be improved.

Without the scanning optimization, lvs will always
read all PVs twice:

1. read metadata from all PVs, saving it in memory
2. for each VG
3. lock VG
4. reread metadata from all PVs in VG, replacing metadata
   saved from step 1
5. run command on VG
6. unlock VG

The optimization would usually cause step 4 to be skipped,
and PVs would be read only once.

Running the command in step 5 using metadata that was not
read under the VG lock is usually fine, except for the
fact that lvs attempts to validate the metadata by comparing
it to current dm state.  If other commands are modifying dm
state while lvs is running, lvs may see differences between
metadata from step 1 and dm state checked during step 5,
and print warnings.

(A better fix may be to detect the concurrent change and
fall back to rereading metadata in step 4 only when needed.)
2019-11-19 10:56:12 -06:00
Zdenek Kabelac
f88f7c0fdc tests: add more tracing info 2019-11-15 12:37:44 +01:00
Zdenek Kabelac
496c368528 tests: reduce amount of written date
Since we reduced created LV to 4M - dd also just 4M.
2019-11-15 12:37:44 +01:00
Zdenek Kabelac
dccc50f6f6 revert "dmeventd: vdo plugin link lvm library"
This reverts commit cbabdf2fca.
and add extra comment why this code may look unused, but
in runtime is necessary.
2019-11-15 12:37:41 +01:00
David Teigland
7ea71a9eb9 Revert "hints: rewrite function"
This reverts commit 70fb31b5d6.
2019-11-14 12:15:05 -06:00
David Teigland
31a862a6be Revert "debug: enhance debug messages"
This reverts commit e92d3bd1f7.
2019-11-14 12:11:53 -06:00
Zdenek Kabelac
91df257b53 tests: enusure lib is initilized 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
e92d3bd1f7 debug: enhance debug messages 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
14e01d6316 hints: drop unneeded memset
strncpy will zero buffer itself.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1760b96368 hints: no need to check for NULL before free
free() itself checks for NULL.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
9af1d63b4d cov: use zalloc
Instead of malloc() memset() -> zalloc()
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
33c1d2e921 cov: add explicit ret value ignoring
We don't need to check for any error result codes here.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
ad0343d8cb cov: remove unused headers 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
9ee3af7efc cov: more checks for failing syscalls 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
cbabdf2fca dmeventd: vdo plugin link lvm library
Since we fixed linking of proper version of 'libdevmapper' with
linking lvm2 plugin correctly - we already have correct function
available linked with internal lvm library.
So drop unneeded include of parsing function.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1da5fd8226 cov: inline _build_desc_write
Embed function into the code, since the function is actually
simpler written this as there are no memleak troubles
with failing allocation error path.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
61a483a654 hints: check for _touch_hints
Exit when !_touch_hints().
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
c38be06531 hints: fix mem leaking buffers 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1349a52626 hints: validate allocation result 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
219fe72359 hints: validate sscanf results 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
d4d82dbb70 hints: allocate hint only when needed
Avoid mem leaking hint on every loop continue and
allocate hint only when it's going to be added into list.

Switch to use 'dm_strncpy()' and validate sizes.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
70fb31b5d6 hints: rewrite function 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
1f4968289c pvck: check result of dev_get_size
Don't use garbage value for later computations.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
d67ce9e140 cov: fix mem leaking buffer
Free allocated buffer on function's exit.
Also check for fwrite() results.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
0bad3977df cov: avoid passing NULL to strstr function
When 'str1' would be NULL, there is no point to run 2nd. strstr().
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
153e55c20e cov: check for retvalue 2019-11-14 18:06:42 +01:00
Zdenek Kabelac
44bf9c9a6a cov: fix memleak for duplicate device
For  dev_in_device_list() != 0 allocated  'devl' was
actually leaking - so instead allocate 'devl' only
when !dev_in_device_list() and indent code around.
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
82e6b820b8 cov: check for NULL
Since we check for NULL pointers earlier we need
to be consistent across function - since the NULL
would applies across whole function.

When dropping 'mda' check - we are actually
already dereferencing it before - so it can't
be NULL at that places (and it's validated
before entering  _read_mda_header_and_metadata).
2019-11-14 18:06:42 +01:00
Zdenek Kabelac
43f149526d devtype: simplify code
Update code with simpler form and check for fclose().
2019-11-14 18:06:14 +01:00
Zdenek Kabelac
33c8e4de33 cov: fix memory leak
Reapply 23cc7ddc50 to internal version
of libdm.
2019-11-14 18:05:41 +01:00
Heming Zhao
13c254fc05 fix dev_unset_last_byte after write error
dev_unset_last_byte() must be called while the fd is still valid.
After a write error, dev_unset_last_byte() must be called before
closing the dev and resetting the fd.

In the write error path, dev_unset_last_byte() was being called
after label_scan_invalidate() which meant that it would not unset
the last_byte values.

After a write error, dev_unset_last_byte() is now called in
dev_write_bytes() before label_scan_invalidate(), instead of by
the caller of dev_write_bytes().

In the common case of a successful write, the sequence is still:
dev_set_last_byte(); dev_write_bytes(); dev_unset_last_byte();

Signed-off-by: Zhao Heming <heming.zhao@suse.com>
2019-11-13 09:36:58 -06:00
77 changed files with 5074 additions and 887 deletions

View File

@@ -1 +1 @@
2.03.07(2)-git (2019-10-23)
2.03.08(2)-git (2019-11-30)

View File

@@ -1 +1 @@
1.02.167-git (2019-10-23)
1.02.169-git (2019-11-30)

View File

@@ -1,7 +1,13 @@
Version 2.03.07 -
===================================
Version 2.03.08 -
====================================
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.

View File

@@ -1,5 +1,9 @@
Version 1.02.167 -
====================================
Version 1.02.169 -
=====================================
Enhance error messages for device creation.
Version 1.02.167 - 30th November 2019
=====================================
Version 1.02.165 - 23rd October 2019
====================================

View File

@@ -1745,7 +1745,8 @@ static void _init_thread_signals(void)
sigdelset(&my_sigset, SIGHUP);
sigdelset(&my_sigset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &my_sigset, NULL);
if (pthread_sigmask(SIG_BLOCK, &my_sigset, NULL))
log_sys_error("pthread_sigmask", "SIG_BLOCK");
}
/*

View File

@@ -16,7 +16,12 @@
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
#include "daemons/dmeventd/libdevmapper-event.h"
/* Use parser from new device_mapper library */
/*
* Use parser from new device_mapper library.
* Although during compilation we can see dm_vdo_status_parse()
* in runtime we are linked agains systems libdm 'older' library
* which does not provide this symbol and plugin fails to load
*/
#include "device_mapper/vdo/status.c"
#include <sys/wait.h>

View File

@@ -392,6 +392,15 @@ struct dm_status_writecache {
int dm_get_status_writecache(struct dm_pool *mem, const char *params,
struct dm_status_writecache **status);
struct dm_status_integrity {
uint64_t number_of_mismatches;
uint64_t provided_data_sectors;
uint64_t recalc_sector;
};
int dm_get_status_integrity(struct dm_pool *mem, const char *params,
struct dm_status_integrity **status);
/*
* Parse params from STATUS call for snapshot target
*
@@ -970,6 +979,36 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
uint32_t writecache_block_size,
struct writecache_settings *settings);
struct integrity_settings {
char mode[8];
uint32_t tag_size;
const char *internal_hash;
uint32_t journal_sectors;
uint32_t interleave_sectors;
uint32_t buffer_sectors;
uint32_t journal_watermark;
uint32_t commit_time;
uint32_t block_size;
uint32_t bitmap_flush_interval;
uint64_t sectors_per_bit;
unsigned journal_sectors_set:1;
unsigned interleave_sectors_set:1;
unsigned buffer_sectors_set:1;
unsigned journal_watermark_set:1;
unsigned commit_time_set:1;
unsigned block_size_set:1;
unsigned bitmap_flush_interval_set:1;
unsigned sectors_per_bit_set:1;
};
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
uint64_t size,
const char *origin_uuid,
const char *meta_uuid,
struct integrity_settings *settings,
int recalculate);
/*
* VDO target

View File

@@ -2012,7 +2012,8 @@ static int _sysfs_get_kernel_name(uint32_t major, uint32_t minor, char *buf, siz
log_sys_error("readlink", sysfs_path);
else {
log_sys_debug("readlink", sysfs_path);
return _sysfs_find_kernel_name(major, minor, buf, buf_size);
r = _sysfs_find_kernel_name(major, minor, buf, buf_size);
goto out;
}
goto bad;
}
@@ -2033,6 +2034,7 @@ static int _sysfs_get_kernel_name(uint32_t major, uint32_t minor, char *buf, siz
strcpy(buf, name);
r = 1;
bad:
out:
free(temp_buf);
free(sysfs_path);

View File

@@ -38,6 +38,7 @@ enum {
SEG_STRIPED,
SEG_ZERO,
SEG_WRITECACHE,
SEG_INTEGRITY,
SEG_THIN_POOL,
SEG_THIN,
SEG_VDO,
@@ -78,6 +79,7 @@ static const struct {
{ SEG_STRIPED, "striped" },
{ SEG_ZERO, "zero"},
{ SEG_WRITECACHE, "writecache"},
{ SEG_INTEGRITY, "integrity"},
{ SEG_THIN_POOL, "thin-pool"},
{ SEG_THIN, "thin"},
{ SEG_VDO, "vdo" },
@@ -221,6 +223,11 @@ struct load_segment {
int writecache_pmem; /* writecache, 1 if pmem, 0 if ssd */
uint32_t writecache_block_size; /* writecache, in bytes */
struct writecache_settings writecache_settings; /* writecache */
uint64_t integrity_data_sectors; /* integrity (provided_data_sectors) */
struct dm_tree_node *integrity_meta_node; /* integrity */
struct integrity_settings integrity_settings; /* integrity */
int integrity_recalculate; /* integrity */
};
/* Per-device properties */
@@ -2705,6 +2712,88 @@ static int _writecache_emit_segment_line(struct dm_task *dmt,
return 1;
}
static int _integrity_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
{
struct integrity_settings *set = &seg->integrity_settings;
int pos = 0;
int count;
char origin_dev[DM_FORMAT_DEV_BUFSIZE];
char meta_dev[DM_FORMAT_DEV_BUFSIZE];
if (!_build_dev_string(origin_dev, sizeof(origin_dev), seg->origin))
return_0;
if (seg->integrity_meta_node &&
!_build_dev_string(meta_dev, sizeof(meta_dev), seg->integrity_meta_node))
return_0;
count = 1; /* for internal_hash which we always pass in */
if (seg->integrity_meta_node)
count++;
if (seg->integrity_recalculate)
count++;
if (set->journal_sectors_set)
count++;
if (set->interleave_sectors_set)
count++;
if (set->buffer_sectors_set)
count++;
if (set->journal_watermark_set)
count++;
if (set->commit_time_set)
count++;
if (set->block_size_set)
count++;
if (set->bitmap_flush_interval_set)
count++;
if (set->sectors_per_bit_set)
count++;
EMIT_PARAMS(pos, "%s 0 %u %s %d internal_hash:%s",
origin_dev,
set->tag_size,
set->mode,
count,
set->internal_hash);
if (seg->integrity_meta_node)
EMIT_PARAMS(pos, " meta_device:%s", meta_dev);
if (seg->integrity_recalculate)
EMIT_PARAMS(pos, " recalculate");
if (set->journal_sectors_set)
EMIT_PARAMS(pos, " journal_sectors:%u", set->journal_sectors);
if (set->interleave_sectors_set)
EMIT_PARAMS(pos, " ineterleave_sectors:%u", set->interleave_sectors);
if (set->buffer_sectors_set)
EMIT_PARAMS(pos, " buffer_sectors:%u", set->buffer_sectors);
if (set->journal_watermark_set)
EMIT_PARAMS(pos, " journal_watermark:%u", set->journal_watermark);
if (set->commit_time_set)
EMIT_PARAMS(pos, " commit_time:%u", set->commit_time);
if (set->block_size_set)
EMIT_PARAMS(pos, " block_size:%u", set->block_size);
if (set->bitmap_flush_interval_set)
EMIT_PARAMS(pos, " bitmap_flush_interval:%u", set->bitmap_flush_interval);
if (set->sectors_per_bit_set)
EMIT_PARAMS(pos, " sectors_per_bit:%llu", (unsigned long long)set->sectors_per_bit);
return 1;
}
static int _thin_pool_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
@@ -2889,6 +2978,10 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
if (!_writecache_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
case SEG_INTEGRITY:
if (!_integrity_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
}
switch(seg->type) {
@@ -2901,6 +2994,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
case SEG_THIN:
case SEG_CACHE:
case SEG_WRITECACHE:
case SEG_INTEGRITY:
break;
case SEG_CRYPT:
case SEG_LINEAR:
@@ -3738,6 +3832,42 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
return 1;
}
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
uint64_t size,
const char *origin_uuid,
const char *meta_uuid,
struct integrity_settings *settings,
int recalculate)
{
struct load_segment *seg;
if (!(seg = _add_segment(node, SEG_INTEGRITY, size)))
return_0;
if (meta_uuid) {
if (!(seg->integrity_meta_node = dm_tree_find_node_by_uuid(node->dtree, meta_uuid))) {
log_error("Missing integrity's meta uuid %s.", meta_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->integrity_meta_node))
return_0;
}
if (!(seg->origin = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
log_error("Missing integrity's origin uuid %s.", origin_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->origin))
return_0;
memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
seg->integrity_recalculate = recalculate;
return 1;
}
int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
uint64_t size,
const char *rlog_uuid,

View File

@@ -380,6 +380,35 @@ int dm_get_status_writecache(struct dm_pool *mem, const char *params,
return 1;
}
int dm_get_status_integrity(struct dm_pool *mem, const char *params,
struct dm_status_integrity **status)
{
struct dm_status_integrity *s;
char recalc_str[8];
if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_integrity))))
return_0;
memset(recalc_str, 0, sizeof(recalc_str));
if (sscanf(params, "%llu %llu %s",
(unsigned long long *)&s->number_of_mismatches,
(unsigned long long *)&s->provided_data_sectors,
recalc_str) != 3) {
log_error("Failed to parse integrity params: %s.", params);
dm_pool_free(mem, s);
return 0;
}
if (recalc_str[0] == '-')
s->recalc_sector = 0;
else
s->recalc_sector = strtoull(recalc_str, NULL, 0);
*status = s;
return 1;
}
int parse_thin_pool_status(const char *params, struct dm_status_thin_pool *s)
{
int pos;

View File

@@ -20,6 +20,7 @@ SOURCES =\
activate/activate.c \
cache/lvmcache.c \
writecache/writecache.c \
integrity/integrity.c \
cache_segtype/cache.c \
commands/toolcontext.c \
config/config.c \
@@ -67,6 +68,7 @@ SOURCES =\
log/log.c \
metadata/cache_manip.c \
metadata/writecache_manip.c \
metadata/integrity_manip.c \
metadata/lv.c \
metadata/lv_manip.c \
metadata/merge.c \

View File

@@ -325,25 +325,11 @@ int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned or
{
return 1;
}
int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv)
{
return 1;
}
int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
int *activate_lv, const struct logical_volume *lv)
{
return 1;
}
int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan,
int temporary, const struct logical_volume *lv)
{
return 1;
}
int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive,
int noscan, int temporary, const struct logical_volume *lv)
{
return 1;
}
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
{
return 1;
@@ -2413,7 +2399,7 @@ static int _lv_has_open_snapshots(const struct logical_volume *lv)
return r;
}
int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv)
static int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv)
{
struct lvinfo info;
static const struct lv_activate_opts laopts = { .skip_in_use = 1 };
@@ -2609,34 +2595,6 @@ out:
return r;
}
/* Activate LV */
int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive,
int noscan, int temporary, const struct logical_volume *lv)
{
struct lv_activate_opts laopts = { .exclusive = exclusive,
.noscan = noscan,
.temporary = temporary };
if (!_lv_activate(cmd, lvid_s, &laopts, 0, lv))
return_0;
return 1;
}
/* Activate LV only if it passes filter */
int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive,
int noscan, int temporary, const struct logical_volume *lv)
{
struct lv_activate_opts laopts = { .exclusive = exclusive,
.noscan = noscan,
.temporary = temporary };
if (!_lv_activate(cmd, lvid_s, &laopts, 1, lv))
return_0;
return 1;
}
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
{
int r;
@@ -2867,11 +2825,18 @@ int deactivate_lv_with_sub_lv(const struct logical_volume *lv)
return 1;
}
int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
int activate_lv_opts(struct cmd_context *cmd, const struct logical_volume *lv,
struct lv_activate_opts *laopts)
{
const struct logical_volume *active_lv;
int ret;
if (lv->status & LV_NOSCAN)
laopts->noscan = 1;
if (lv->status & LV_TEMPORARY)
laopts->temporary = 1;
/*
* When trying activating component LV, make sure none of sub component
* LV or LVs that are using it are active.
@@ -2888,14 +2853,18 @@ int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
goto out;
}
ret = lv_activate_with_filter(cmd, NULL, 0,
(lv->status & LV_NOSCAN) ? 1 : 0,
(lv->status & LV_TEMPORARY) ? 1 : 0,
lv_committed(lv));
ret = _lv_activate(cmd, NULL, laopts, 1, lv_committed(lv));
out:
return ret;
}
int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
{
struct lv_activate_opts laopts = { 0 };
return activate_lv_opts(cmd, lv, &laopts);
}
int deactivate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
{
int ret;

View File

@@ -39,6 +39,7 @@ typedef enum {
SEG_STATUS_THIN_POOL,
SEG_STATUS_VDO_POOL,
SEG_STATUS_WRITECACHE,
SEG_STATUS_INTEGRITY,
SEG_STATUS_UNKNOWN
} lv_seg_status_type_t;
@@ -53,6 +54,7 @@ struct lv_seg_status {
struct dm_status_thin *thin;
struct dm_status_thin_pool *thin_pool;
struct dm_status_writecache *writecache;
struct dm_status_integrity *integrity;
struct lv_status_vdo vdo_pool;
};
};
@@ -72,6 +74,7 @@ struct lv_activate_opts {
int no_merging;
int send_messages;
int skip_in_use;
int integrity_recalculate;
unsigned revert;
unsigned read_only;
unsigned noscan; /* Mark this LV to avoid its scanning. This also
@@ -122,9 +125,6 @@ int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s,
unsigned origin_only, unsigned exclusive, unsigned revert, const struct logical_volume *lv);
int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive,
int noscan, int temporary, const struct logical_volume *lv);
int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive,
int noscan, int temporary, const struct logical_volume *lv);
int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv);
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
@@ -132,6 +132,8 @@ int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv);
int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv);
int deactivate_lv(struct cmd_context *cmd, const struct logical_volume *lv);
int activate_lv_opts(struct cmd_context *cmd, const struct logical_volume *lv,
struct lv_activate_opts *laopts);
int suspend_lv(struct cmd_context *cmd, const struct logical_volume *lv);
int suspend_lv_origin(struct cmd_context *cmd, const struct logical_volume *lv);
int resume_lv(struct cmd_context *cmd, const struct logical_volume *lv);
@@ -260,6 +262,7 @@ void fs_unlock(void);
#define TARGET_NAME_CACHE "cache"
#define TARGET_NAME_WRITECACHE "writecache"
#define TARGET_NAME_INTEGRITY "integrity"
#define TARGET_NAME_ERROR "error"
#define TARGET_NAME_ERROR_OLD "erro" /* Truncated in older kernels */
#define TARGET_NAME_LINEAR "linear"
@@ -277,6 +280,7 @@ void fs_unlock(void);
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
#define MODULE_NAME_CACHE TARGET_NAME_CACHE
#define MODULE_NAME_WRITECACHE TARGET_NAME_WRITECACHE
#define MODULE_NAME_INTEGRITY TARGET_NAME_INTEGRITY
#define MODULE_NAME_ERROR TARGET_NAME_ERROR
#define MODULE_NAME_LOG_CLUSTERED "log-clustered"
#define MODULE_NAME_LOG_USERSPACE "log-userspace"

View File

@@ -46,7 +46,7 @@ typedef enum {
} action_t;
/* This list must match lib/misc/lvm-string.c:build_dm_uuid(). */
const char *uuid_suffix_list[] = { "pool", "cdata", "cmeta", "cvol", "tdata", "tmeta", "vdata", "vpool", NULL};
const char *uuid_suffix_list[] = { "pool", "cdata", "cmeta", "cvol", "tdata", "tmeta", "vdata", "vpool", "imeta", NULL};
struct dlid_list {
struct dm_list list;
@@ -222,6 +222,10 @@ static int _get_segment_status_from_target_params(const char *target_name,
if (!dm_get_status_writecache(seg_status->mem, params, &(seg_status->writecache)))
return_0;
seg_status->type = SEG_STATUS_WRITECACHE;
} else if (segtype_is_integrity(segtype)) {
if (!dm_get_status_integrity(seg_status->mem, params, &(seg_status->integrity)))
return_0;
seg_status->type = SEG_STATUS_INTEGRITY;
} else
/*
* TODO: Add support for other segment types too!
@@ -299,6 +303,9 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
if (lv_is_vdo_pool(seg_status->seg->lv))
length = get_vdo_pool_virtual_size(seg_status->seg);
if (lv_is_integrity(seg_status->seg->lv))
length = seg_status->seg->integrity_data_sectors;
do {
target = dm_get_next_target(dmt, target, &target_start,
&target_length, &target_name, &target_params);
@@ -2620,6 +2627,10 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
if (!_add_lv_to_dtree(dm, dtree, seg->writecache, dm->activation ? origin_only : 1))
return_0;
}
if (seg->integrity_meta_dev && seg_is_integrity(seg)) {
if (!_add_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, dm->activation ? origin_only : 1))
return_0;
}
if (seg->pool_lv &&
(lv_is_cache_pool(seg->pool_lv) || lv_is_cache_vol(seg->pool_lv) || dm->track_external_lv_deps) &&
/* When activating and not origin_only detect linear 'overlay' over pool */
@@ -3076,6 +3087,11 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
lv_layer(seg->writecache)))
return_0;
if (seg->integrity_meta_dev && !laopts->origin_only &&
!_add_new_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, laopts,
lv_layer(seg->integrity_meta_dev)))
return_0;
/* Add any LVs used by this segment */
for (s = 0; s < seg->area_count; ++s) {
if ((seg_type(seg, s) == AREA_LV) &&

54
lib/cache/lvmcache.c vendored
View File

@@ -21,7 +21,6 @@
#include "lib/locking/locking.h"
#include "lib/metadata/metadata.h"
#include "lib/mm/memlock.h"
#include "lib/datastruct/str_list.h"
#include "lib/format_text/format-text.h"
#include "lib/config/config.h"
@@ -239,6 +238,30 @@ void lvmcache_get_bad_mdas(struct cmd_context *cmd,
}
}
void lvmcache_get_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *mda_list)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
struct mda_list *mdal;
struct metadata_area *mda, *mda2;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
log_error(INTERNAL_ERROR "lvmcache_get_mdas no vginfo %s", vgname);
return;
}
dm_list_iterate_items(info, &vginfo->infos) {
dm_list_iterate_items_safe(mda, mda2, &info->mdas) {
if (!(mdal = zalloc(sizeof(*mdal))))
continue;
mdal->mda = mda;
dm_list_add(mda_list, &mdal->list);
}
}
}
static void _vginfo_attach_info(struct lvmcache_vginfo *vginfo,
struct lvmcache_info *info)
{
@@ -1952,24 +1975,25 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller,
strncpy(dev->pvid, pvid_s, sizeof(dev->pvid));
/*
* Keep the existing PV/dev in lvmcache, and save the
* new duplicate in the list of duplicates. After
* scanning is complete, compare the duplicate devs
* with those in lvmcache to check if one of the
* duplicates is preferred and if so switch lvmcache to
* use it.
*/
if (!(devl = zalloc(sizeof(*devl))))
return_NULL;
devl->dev = dev;
/* shouldn't happen */
if (dev_in_device_list(dev, &_initial_duplicates))
log_debug_cache("Initial duplicate already in list %s", dev_name(dev));
else
else {
/*
* Keep the existing PV/dev in lvmcache, and save the
* new duplicate in the list of duplicates. After
* scanning is complete, compare the duplicate devs
* with those in lvmcache to check if one of the
* duplicates is preferred and if so switch lvmcache to
* use it.
*/
if (!(devl = zalloc(sizeof(*devl))))
return_NULL;
devl->dev = dev;
dm_list_add(&_initial_duplicates, &devl->list);
}
if (is_duplicate)
*is_duplicate = 1;

View File

@@ -216,4 +216,8 @@ void lvmcache_get_bad_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *bad_mda_list);
void lvmcache_get_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *mda_list);
#endif

View File

@@ -1362,6 +1362,9 @@ static int _init_segtypes(struct cmd_context *cmd)
return 0;
#endif
if (!init_integrity_segtypes(cmd, &seglib))
return 0;
return 1;
}

View File

@@ -79,6 +79,21 @@ bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len,
return true;
}
bool bcache_invalidate_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
{
block_address bb, be;
bool result = true;
byte_range_to_block_range(cache, start, len, &bb, &be);
for (; bb != be; bb++) {
if (!bcache_invalidate(cache, fd, bb))
result = false;
}
return result;
}
//----------------------------------------------------------------
// Writing bytes and zeroing bytes are very similar, so we factor out

View File

@@ -163,6 +163,7 @@ bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len,
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
bool bcache_set_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, uint8_t val);
bool bcache_invalidate_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
void bcache_set_last_byte(struct bcache *cache, int fd, uint64_t offset, int sector_size);
void bcache_unset_last_byte(struct bcache *cache, int fd);

View File

@@ -1522,7 +1522,7 @@ struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t dev, struct
sysfs_dir = dm_sysfs_dir();
if (sysfs_dir && *sysfs_dir) {
/* First check if dev is sysfs to avoid useless scan */
if (dm_snprintf(path, sizeof(path), "%s/dev/block/%d:%d",
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d",
sysfs_dir, (int)MAJOR(dev), (int)MINOR(dev)) < 0) {
log_error("dm_snprintf partition failed.");
return NULL;

View File

@@ -42,7 +42,6 @@ int dev_is_pmem(struct device *dev)
{
FILE *fp;
char path[PATH_MAX];
char buffer[64];
int is_pmem = 0;
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/queue/dax",
@@ -56,27 +55,16 @@ int dev_is_pmem(struct device *dev)
if (!(fp = fopen(path, "r")))
return 0;
if (!fgets(buffer, sizeof(buffer), fp)) {
log_warn("Failed to read %s.", path);
if (fclose(fp))
log_sys_debug("fclose", path);
return 0;
} else if (sscanf(buffer, "%d", &is_pmem) != 1) {
log_warn("Failed to parse %s '%s'.", path, buffer);
if (fclose(fp))
log_sys_debug("fclose", path);
return 0;
}
if (fscanf(fp, "%d", &is_pmem) != 1)
log_warn("Failed to parse DAX %s.", path);
if (is_pmem)
log_debug("%s is pmem", dev_name(dev));
if (fclose(fp))
log_sys_debug("fclose", path);
if (is_pmem) {
log_debug("%s is pmem", dev_name(dev));
return 1;
}
return 0;
return is_pmem ? 1 : 0;
}
int dev_is_lv(struct device *dev)
@@ -84,6 +72,7 @@ int dev_is_lv(struct device *dev)
FILE *fp;
char path[PATH_MAX];
char buffer[64];
int ret = 0;
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/dm/uuid",
dm_sysfs_dir(),
@@ -96,17 +85,15 @@ int dev_is_lv(struct device *dev)
if (!(fp = fopen(path, "r")))
return 0;
if (!fgets(buffer, sizeof(buffer), fp)) {
if (!fgets(buffer, sizeof(buffer), fp))
log_warn("Failed to read %s.", path);
fclose(fp);
return 0;
}
else if (!strncmp(buffer, "LVM-", 4))
ret = 1;
fclose(fp);
if (fclose(fp))
log_sys_debug("fclose", path);
if (!strncmp(buffer, "LVM-", 4))
return 1;
return 0;
return ret;
}
struct dev_types *create_dev_types(const char *proc_dir,

View File

@@ -104,6 +104,8 @@ static const struct flag _lv_flags[] = {
{LV_VDO_POOL, NULL, 0},
{LV_VDO_POOL_DATA, NULL, 0},
{WRITECACHE, NULL, 0},
{INTEGRITY, NULL, 0},
{INTEGRITY_METADATA, NULL, 0},
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
{LV_REMOVED, NULL, 0},
{0, NULL, 0}

View File

@@ -277,7 +277,6 @@ static int _raw_write_mda_header(const struct format_type *fmt,
dev_set_last_byte(dev, start_byte + MDA_HEADER_SIZE);
if (!dev_write_bytes(dev, start_byte, MDA_HEADER_SIZE, mdah)) {
dev_unset_last_byte(dev);
log_error("Failed to write mda header to %s fd %d", dev_name(dev), dev->bcache_fd);
return 0;
}
@@ -342,7 +341,7 @@ static struct raw_locn *_read_metadata_location_vg(struct device_area *dev_area,
* Don't try to check existing metadata
* if given vgname is an empty string.
*/
if (!*vgname)
if (!vgname || !*vgname)
return rlocn;
/*
@@ -532,29 +531,6 @@ static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid,
return vg;
}
#define MAX_DESC_LEN 2048
static char *_build_desc_write(struct cmd_context *cmd, struct volume_group *vg)
{
size_t len = strlen(cmd->cmd_line) + 32;
char *desc;
if (len > MAX_DESC_LEN)
len = MAX_DESC_LEN;
if (!(desc = zalloc(len)))
return_NULL;
vg->write_count++;
if (vg->write_count == 1)
dm_snprintf(desc, len, "Write from %s.", cmd->cmd_line);
else
dm_snprintf(desc, len, "Write[%u] from %s.", vg->write_count, cmd->cmd_line);
return desc;
}
/*
* VG metadata updates:
*
@@ -599,6 +575,7 @@ static char *_build_desc_write(struct cmd_context *cmd, struct volume_group *vg)
static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
char desc[2048];
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
struct raw_locn *rlocn_old;
@@ -673,12 +650,15 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
write_buf_size = fidtc->write_buf_size;
new_size = fidtc->new_metadata_size;
} else {
char *desc = _build_desc_write(fid->fmt->cmd, vg);
if (!vg->write_count++)
(void) dm_snprintf(desc, sizeof(desc), "Write from %s.", vg->cmd->cmd_line);
else
(void) dm_snprintf(desc, sizeof(desc), "Write[%u] from %s.", vg->write_count, vg->cmd->cmd_line);
new_size = text_vg_export_raw(vg, desc, &write_buf, &write_buf_size);
fidtc->write_buf = write_buf;
fidtc->write_buf_size = write_buf_size;
fidtc->new_metadata_size = new_size;
free(desc);
}
if (!new_size || !write_buf) {
@@ -989,7 +969,6 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
if (!dev_write_bytes(mdac->area.dev, write1_start, (size_t)write1_size, write_buf)) {
log_error("Failed to write metadata to %s fd %d", devname, mdac->area.dev->bcache_fd);
dev_unset_last_byte(mdac->area.dev);
goto out;
}
@@ -1002,7 +981,6 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
if (!dev_write_bytes(mdac->area.dev, write2_start, write2_size,
write_buf + new_size - new_wrap)) {
log_error("Failed to write metadata wrap to %s fd %d", devname, mdac->area.dev->bcache_fd);
dev_unset_last_byte(mdac->area.dev);
goto out;
}
}
@@ -1529,6 +1507,7 @@ static int _vg_remove_file(struct format_instance *fid __attribute__((unused)),
}
int read_metadata_location_summary(const struct format_type *fmt,
struct metadata_area *mda,
struct mda_header *mdah, int primary_mda, struct device_area *dev_area,
struct lvmcache_vgsummary *vgsummary, uint64_t *mda_free_sectors)
{
@@ -1586,6 +1565,17 @@ int read_metadata_location_summary(const struct format_type *fmt,
return 0;
}
/*
* This function is used to read the vg summary during label scan.
* Save the text start location and checksum during scan. After the VG
* lock is acquired in vg_read, we can reread the mda_header, and
* compare rlocn->offset,checksum to what was saved during scan. If
* unchanged, it means that the metadata was not changed between scan
* and the read.
*/
mda->scan_text_offset = rlocn->offset;
mda->scan_text_checksum = rlocn->checksum;
/*
* When the current metadata wraps around the end of the metadata area
* (so some is located at the end and some is located at the

View File

@@ -47,11 +47,15 @@ enum pv_vg_lv_e {
struct text_vg_version_ops {
int (*check_version) (const struct dm_config_tree * cf);
struct volume_group *(*read_vg) (struct format_instance * fid,
const struct dm_config_tree *cf,
unsigned allow_lvmetad_extensions);
struct volume_group *(*read_vg) (struct cmd_context *cmd,
const struct format_type *fmt,
struct format_instance *fid,
const struct dm_config_tree *cft);
void (*read_desc) (struct dm_pool * mem, const struct dm_config_tree *cf,
time_t *when, char **desc);
int (*read_vgsummary) (const struct format_type *fmt,
const struct dm_config_tree *cft,
struct lvmcache_vgsummary *vgsummary);

View File

@@ -15,6 +15,7 @@
#include "lib/misc/lib.h"
#include "lib/metadata/metadata.h"
#include "lib/commands/toolcontext.h"
#include "import-export.h"
/* FIXME Use tidier inclusion method */
@@ -181,7 +182,7 @@ struct volume_group *text_read_metadata(struct format_instance *fid,
if (!(*vsn)->check_version(cft))
continue;
if (!(vg = (*vsn)->read_vg(fid, cft, 0)))
if (!(vg = (*vsn)->read_vg(fid->fmt->cmd, fid->fmt, fid, cft)))
goto_out;
(*vsn)->read_desc(vg->vgmem, cft, when, desc);
@@ -210,9 +211,9 @@ struct volume_group *text_read_metadata_file(struct format_instance *fid,
when, desc);
}
static struct volume_group *_import_vg_from_config_tree(const struct dm_config_tree *cft,
static struct volume_group *_import_vg_from_config_tree(struct cmd_context *cmd,
struct format_instance *fid,
unsigned allow_lvmetad_extensions)
const struct dm_config_tree *cft)
{
struct volume_group *vg = NULL;
struct text_vg_version_ops **vsn;
@@ -227,7 +228,7 @@ static struct volume_group *_import_vg_from_config_tree(const struct dm_config_t
* The only path to this point uses cached vgmetadata,
* so it can use cached PV state too.
*/
if (!(vg = (*vsn)->read_vg(fid, cft, allow_lvmetad_extensions)))
if (!(vg = (*vsn)->read_vg(cmd, fid->fmt, fid, cft)))
stack;
else {
set_pv_devices(fid, vg, NULL);
@@ -243,8 +244,21 @@ static struct volume_group *_import_vg_from_config_tree(const struct dm_config_t
return vg;
}
struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft,
struct format_instance *fid)
struct volume_group *import_vg_from_config_tree(struct cmd_context *cmd,
struct format_instance *fid,
const struct dm_config_tree *cft)
{
return _import_vg_from_config_tree(cft, fid, 0);
return _import_vg_from_config_tree(cmd, fid, cft);
}
struct volume_group *vg_from_config_tree(struct cmd_context *cmd, const struct dm_config_tree *cft)
{
static struct text_vg_version_ops *ops;
_init_text_import();
ops = _text_vsn_list[0];
return ops->read_vg(cmd, cmd->fmt, NULL, cft);
}

View File

@@ -993,7 +993,7 @@ static int _read_lvsegs(struct cmd_context *cmd,
}
static int _read_sections(struct cmd_context *cmd,
struct format_type *fmt,
const struct format_type *fmt,
struct format_instance *fid,
struct dm_pool *mem,
const char *section, section_fn fn,
@@ -1016,19 +1016,18 @@ static int _read_sections(struct cmd_context *cmd,
}
for (n = n->child; n; n = n->sib) {
if (!fn(cmd, fmt, fid, mem, vg, vgsummary, n, vgn, pv_hash, lv_hash))
if (!fn(cmd, (struct format_type *)fmt, fid, mem, vg, vgsummary, n, vgn, pv_hash, lv_hash))
return_0;
}
return 1;
}
static struct volume_group *_read_vg(struct format_instance *fid,
const struct dm_config_tree *cft,
unsigned allow_lvmetad_extensions)
static struct volume_group *_read_vg(struct cmd_context *cmd,
const struct format_type *fmt,
struct format_instance *fid,
const struct dm_config_tree *cft)
{
struct cmd_context *cmd = fid->fmt->cmd;
struct format_type *fmt = (struct format_type *)fid->fmt;
struct dm_pool *mem;
const struct dm_config_node *vgn;
const struct dm_config_value *cv;
@@ -1234,7 +1233,8 @@ static struct volume_group *_read_vg(struct format_instance *fid,
dm_hash_destroy(pv_hash);
dm_hash_destroy(lv_hash);
vg_set_fid(vg, fid);
if (fid)
vg_set_fid(vg, fid);
/*
* Finished.

View File

@@ -104,7 +104,8 @@ struct mda_context {
#define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize())
#define MDA_ORIGINAL_ALIGNMENT 512 /* Original alignment used for start of VG metadata content */
int read_metadata_location_summary(const struct format_type *fmt, struct mda_header *mdah, int primary_mda,
int read_metadata_location_summary(const struct format_type *fmt,
struct metadata_area *mda, struct mda_header *mdah, int primary_mda,
struct device_area *dev_area, struct lvmcache_vgsummary *vgsummary,
uint64_t *mda_free_sectors);

View File

@@ -332,14 +332,12 @@ static int _read_mda_header_and_metadata(const struct format_type *fmt,
log_warn("WARNING: bad metadata header on %s at %llu.",
dev_name(mdac->area.dev),
(unsigned long long)mdac->area.start);
if (mda)
mda->header_start = mdac->area.start;
mda->header_start = mdac->area.start;
*bad_fields |= BAD_MDA_HEADER;
return 0;
}
if (mda)
mda->header_start = mdah->start;
mda->header_start = mdah->start;
mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
@@ -351,7 +349,7 @@ static int _read_mda_header_and_metadata(const struct format_type *fmt,
return 1;
}
if (!read_metadata_location_summary(fmt, mdah, mda_is_primary(mda), &mdac->area,
if (!read_metadata_location_summary(fmt, mda, mdah, mda_is_primary(mda), &mdac->area,
vgsummary, &mdac->free_sectors)) {
if (vgsummary->zero_offset)
return 1;

329
lib/integrity/integrity.c Normal file
View File

@@ -0,0 +1,329 @@
/*
* Copyright (C) 2013-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/metadata/segtype.h"
#include "lib/display/display.h"
#include "lib/format_text/text_export.h"
#include "lib/config/config.h"
#include "lib/datastruct/str_list.h"
#include "lib/misc/lvm-string.h"
#include "lib/activate/activate.h"
#include "lib/metadata/metadata.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/config/defaults.h"
#define SEG_LOG_ERROR(t, p...) \
log_error(t " segment %s of logical volume %s.", ## p, \
dm_config_parent_name(sn), seg->lv->name), 0;
static void _integrity_display(const struct lv_segment *seg)
{
/* TODO: lvdisplay segments */
}
static int _integrity_text_import(struct lv_segment *seg,
const struct dm_config_node *sn,
struct dm_hash_table *pv_hash __attribute__((unused)))
{
struct integrity_settings *set;
struct logical_volume *origin_lv = NULL;
struct logical_volume *meta_lv = NULL;
const char *origin_name = NULL;
const char *meta_dev = NULL;
const char *mode = NULL;
const char *hash = NULL;
memset(&seg->integrity_settings, 0, sizeof(struct integrity_settings));
set = &seg->integrity_settings;
/* origin always set */
if (!dm_config_has_node(sn, "origin"))
return SEG_LOG_ERROR("origin not specified in");
if (!dm_config_get_str(sn, "origin", &origin_name))
return SEG_LOG_ERROR("origin must be a string in");
if (!(origin_lv = find_lv(seg->lv->vg, origin_name)))
return SEG_LOG_ERROR("Unknown LV specified for integrity origin %s in", origin_name);
if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0))
return_0;
/* data_sectors always set */
if (!dm_config_get_uint64(sn, "data_sectors", &seg->integrity_data_sectors))
return SEG_LOG_ERROR("integrity data_sectors must be set in");
/* mode always set */
if (!dm_config_get_str(sn, "mode", &mode))
return SEG_LOG_ERROR("integrity mode must be set in");
if (strlen(mode) > 7)
return SEG_LOG_ERROR("integrity mode invalid in");
strncpy(set->mode, mode, 7);
/* tag_size always set */
if (!dm_config_get_uint32(sn, "tag_size", &set->tag_size))
return SEG_LOG_ERROR("integrity tag_size must be set in");
/* internal_hash always set */
if (!dm_config_get_str(sn, "internal_hash", &hash))
return SEG_LOG_ERROR("integrity internal_hash must be set in");
if (!(set->internal_hash = strdup(hash)))
return SEG_LOG_ERROR("integrity internal_hash failed to be set in");
/* meta_dev optional */
if (dm_config_has_node(sn, "meta_dev")) {
if (!dm_config_get_str(sn, "meta_dev", &meta_dev))
return SEG_LOG_ERROR("meta_dev must be a string in");
if (!(meta_lv = find_lv(seg->lv->vg, meta_dev)))
return SEG_LOG_ERROR("Unknown logical volume %s specified for integrity in", meta_dev);
}
/* the rest are optional */
if (dm_config_has_node(sn, "journal_sectors")) {
if (!dm_config_get_uint32(sn, "journal_sectors", &set->journal_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->journal_sectors_set = 1;
}
if (dm_config_has_node(sn, "interleave_sectors")) {
if (!dm_config_get_uint32(sn, "interleave_sectors", &set->interleave_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->interleave_sectors_set = 1;
}
if (dm_config_has_node(sn, "buffer_sectors")) {
if (!dm_config_get_uint32(sn, "buffer_sectors", &set->buffer_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->buffer_sectors_set = 1;
}
if (dm_config_has_node(sn, "journal_watermark")) {
if (!dm_config_get_uint32(sn, "journal_watermark", &set->journal_watermark))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->journal_watermark_set = 1;
}
if (dm_config_has_node(sn, "commit_time")) {
if (!dm_config_get_uint32(sn, "commit_time", &set->commit_time))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->commit_time_set = 1;
}
if (dm_config_has_node(sn, "block_size")) {
if (!dm_config_get_uint32(sn, "block_size", &set->block_size))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->block_size_set = 1;
}
if (dm_config_has_node(sn, "bitmap_flush_interval")) {
if (!dm_config_get_uint32(sn, "bitmap_flush_interval", &set->bitmap_flush_interval))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->bitmap_flush_interval_set = 1;
}
if (dm_config_has_node(sn, "sectors_per_bit")) {
if (!dm_config_get_uint64(sn, "sectors_per_bit", &set->sectors_per_bit))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->sectors_per_bit_set = 1;
}
seg->origin = origin_lv;
seg->integrity_meta_dev = meta_lv;
seg->lv->status |= INTEGRITY;
if (meta_lv)
meta_lv->status |= INTEGRITY_METADATA;
if (meta_lv && !add_seg_to_segs_using_this_lv(meta_lv, seg))
return_0;
return 1;
}
static int _integrity_text_import_area_count(const struct dm_config_node *sn,
uint32_t *area_count)
{
*area_count = 1;
return 1;
}
static int _integrity_text_export(const struct lv_segment *seg,
struct formatter *f)
{
const struct integrity_settings *set = &seg->integrity_settings;
outf(f, "origin = \"%s\"", seg_lv(seg, 0)->name);
outf(f, "data_sectors = %llu", (unsigned long long)seg->integrity_data_sectors);
outf(f, "mode = \"%s\"", set->mode);
outf(f, "tag_size = %u", set->tag_size);
outf(f, "internal_hash = \"%s\"", set->internal_hash);
if (seg->integrity_meta_dev)
outf(f, "meta_dev = \"%s\"", seg->integrity_meta_dev->name);
if (set->journal_sectors_set)
outf(f, "journal_sectors = %u", set->journal_sectors);
if (set->interleave_sectors_set)
outf(f, "interleave_sectors = %u", set->interleave_sectors);
if (set->buffer_sectors_set)
outf(f, "buffer_sectors = %u", set->buffer_sectors);
if (set->journal_watermark_set)
outf(f, "journal_watermark = %u", set->journal_watermark);
if (set->commit_time_set)
outf(f, "commit_time = %u", set->commit_time);
if (set->block_size_set)
outf(f, "block_size = %u", set->block_size);
if (set->bitmap_flush_interval)
outf(f, "bitmap_flush_interval = %u", set->bitmap_flush_interval);
if (set->sectors_per_bit)
outf(f, "sectors_per_bit = %llu", (unsigned long long)set->sectors_per_bit);
return 1;
}
static void _destroy(struct segment_type *segtype)
{
free((void *) segtype);
}
#ifdef DEVMAPPER_SUPPORT
static int _target_present(struct cmd_context *cmd,
const struct lv_segment *seg __attribute__((unused)),
unsigned *attributes __attribute__((unused)))
{
static int _integrity_checked = 0;
static int _integrity_present = 0;
if (!activation())
return 0;
if (!_integrity_checked) {
_integrity_checked = 1;
_integrity_present = target_present(cmd, TARGET_NAME_INTEGRITY, 0);
}
return _integrity_present;
}
static int _modules_needed(struct dm_pool *mem,
const struct lv_segment *seg __attribute__((unused)),
struct dm_list *modules)
{
if (!str_list_add(mem, modules, MODULE_NAME_INTEGRITY)) {
log_error("String list allocation failed for integrity module.");
return 0;
}
return 1;
}
#endif /* DEVMAPPER_SUPPORT */
#ifdef DEVMAPPER_SUPPORT
static int _integrity_add_target_line(struct dev_manager *dm,
struct dm_pool *mem,
struct cmd_context *cmd __attribute__((unused)),
void **target_state __attribute__((unused)),
struct lv_segment *seg,
const struct lv_activate_opts *laopts,
struct dm_tree_node *node, uint64_t len,
uint32_t *pvmove_mirror_count __attribute__((unused)))
{
char *origin_uuid;
char *meta_uuid = NULL;
if (!seg_is_integrity(seg)) {
log_error(INTERNAL_ERROR "Passed segment is not integrity.");
return 0;
}
if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0), NULL)))
return_0;
if (seg->integrity_meta_dev) {
if (!(meta_uuid = build_dm_uuid(mem, seg->integrity_meta_dev, NULL)))
return_0;
}
if (!seg->integrity_data_sectors) {
log_error("_integrity_add_target_line zero size");
return_0;
}
if (!dm_tree_node_add_integrity_target(node, seg->integrity_data_sectors,
origin_uuid, meta_uuid,
&seg->integrity_settings,
laopts->integrity_recalculate))
return_0;
return 1;
}
#endif /* DEVMAPPER_SUPPORT */
static struct segtype_handler _integrity_ops = {
.display = _integrity_display,
.text_import = _integrity_text_import,
.text_import_area_count = _integrity_text_import_area_count,
.text_export = _integrity_text_export,
#ifdef DEVMAPPER_SUPPORT
.add_target_line = _integrity_add_target_line,
.target_present = _target_present,
.modules_needed = _modules_needed,
#endif
.destroy = _destroy,
};
int init_integrity_segtypes(struct cmd_context *cmd,
struct segtype_library *seglib)
{
struct segment_type *segtype = zalloc(sizeof(*segtype));
if (!segtype) {
log_error("Failed to allocate memory for integrity segtype");
return 0;
}
segtype->name = SEG_TYPE_NAME_INTEGRITY;
segtype->flags = SEG_INTEGRITY;
segtype->ops = &_integrity_ops;
if (!lvm_register_segtype(seglib, segtype))
return_0;
log_very_verbose("Initialised segtype: %s", segtype->name);
return 1;
}

View File

@@ -638,7 +638,8 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
char devpath[PATH_MAX];
FILE *fp;
struct dev_iter *iter;
struct hint *hint;
struct hint hint;
struct hint *alloc_hint;
struct device *dev;
char *split[HINT_LINE_WORDS];
char *name, *pvid, *devn, *vgname, *p, *filter_str = NULL;
@@ -662,11 +663,7 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
split[i] = NULL;
while (fgets(_hint_line, sizeof(_hint_line), fp)) {
if (!(hint = zalloc(sizeof(struct hint)))) {
ret = 0;
break;
}
memset(&hint, 0, sizeof(hint));
if (_hint_line[0] == '#')
continue;
@@ -704,13 +701,11 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
_filter_to_str(cmd, devices_global_filter_CFG, &filter_str);
if (!filter_str || strcmp(filter_str, _hint_line + keylen)) {
log_debug("ignore hints with different global_filter");
if (filter_str)
free(filter_str);
free(filter_str);
*needs_refresh = 1;
break;
}
if (filter_str)
free(filter_str);
free(filter_str);
continue;
}
@@ -719,23 +714,20 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
_filter_to_str(cmd, devices_filter_CFG, &filter_str);
if (!filter_str || strcmp(filter_str, _hint_line + keylen)) {
log_debug("ignore hints with different filter");
if (filter_str)
free(filter_str);
free(filter_str);
*needs_refresh = 1;
break;
}
if (filter_str)
free(filter_str);
free(filter_str);
continue;
}
keylen = strlen("scan_lvs:");
if (!strncmp(_hint_line, "scan_lvs:", keylen)) {
int scan_lvs = 0;
sscanf(_hint_line + keylen, "%u", &scan_lvs);
if (scan_lvs != cmd->scan_lvs) {
log_debug("ignore hints with different scan_lvs");
if ((sscanf(_hint_line + keylen, "%u", &scan_lvs) != 1) ||
scan_lvs != cmd->scan_lvs) {
log_debug("ignore hints with different or unreadable scan_lvs");
*needs_refresh = 1;
break;
}
@@ -744,7 +736,11 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
keylen = strlen("devs_hash:");
if (!strncmp(_hint_line, "devs_hash:", keylen)) {
sscanf(_hint_line + keylen, "%u %u", &read_hash, &read_count);
if (sscanf(_hint_line + keylen, "%u %u", &read_hash, &read_count) != 2) {
log_debug("ignore hints with invalid devs_hash");
*needs_refresh = 1;
break;
}
continue;
}
@@ -764,19 +760,28 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
vgname = split[3];
if (name && !strncmp(name, "scan:", 5))
strncpy(hint->name, name+5, PATH_MAX);
if (!dm_strncpy(hint.name, name + 5, sizeof(hint.name)))
continue;
if (pvid && !strncmp(pvid, "pvid:", 5))
strncpy(hint->pvid, pvid+5, ID_LEN);
if (!dm_strncpy(hint.pvid, pvid + 5, sizeof(hint.pvid)))
continue;
if (devn && sscanf(devn, "devn:%d:%d", &major, &minor) == 2)
hint->devt = makedev(major, minor);
hint.devt = makedev(major, minor);
if (vgname && (strlen(vgname) > 3) && (vgname[4] != '-'))
strncpy(hint->vgname, vgname+3, NAME_LEN);
if (!dm_strncpy(hint.vgname, vgname + 3, sizeof(hint.vgname)))
continue;
log_debug("add hint %s %s %d:%d %s", hint->name, hint->pvid, major, minor, vgname);
dm_list_add(hints, &hint->list);
if (!(alloc_hint = malloc(sizeof(struct hint)))) {
ret = 0;
break;
}
memcpy(alloc_hint, &hint, sizeof(hint));
log_debug("add hint %s %s %d:%d %s", hint.name, hint.pvid, major, minor, vgname);
dm_list_add(hints, &alloc_hint->list);
found++;
}
@@ -911,13 +916,11 @@ int write_hint_file(struct cmd_context *cmd, int newhints)
_filter_to_str(cmd, devices_global_filter_CFG, &filter_str);
fprintf(fp, "global_filter:%s\n", filter_str ?: "-");
if (filter_str)
free(filter_str);
free(filter_str);
_filter_to_str(cmd, devices_filter_CFG, &filter_str);
fprintf(fp, "filter:%s\n", filter_str ?: "-");
if (filter_str)
free(filter_str);
free(filter_str);
fprintf(fp, "scan_lvs:%d\n", cmd->scan_lvs);
@@ -951,7 +954,6 @@ int write_hint_file(struct cmd_context *cmd, int newhints)
* detect when the devices on the system change, which
* invalidates the existing hints.
*/
memset(devpath, 0, sizeof(devpath));
strncpy(devpath, dev_name(dev), PATH_MAX);
hash = calc_crc(hash, (const uint8_t *)devpath, strlen(devpath));
count++;
@@ -1179,7 +1181,8 @@ static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
if (!(st = strchr(arg, '/'))) {
/* simple vgname */
name = strdup(arg);
if (!(name = strdup(arg)))
return;
goto check;
}
@@ -1187,7 +1190,8 @@ static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
for (p = arg; p < st; p++)
namebuf[i++] = *p;
name = strdup(namebuf);
if (!(name = strdup(namebuf)))
return;
check:
/*
@@ -1202,6 +1206,8 @@ check:
return;
}
}
free(name);
}
/*
@@ -1261,8 +1267,9 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
*/
if (_newhints_exists()) {
log_debug("get_hints: newhints file");
if (!_hints_exists())
_touch_hints();
if (!_hints_exists() && !_touch_hints())
return 0;
if (!_lock_hints(cmd, LOCK_EX, NONBLOCK))
return 0;
/* create new hints after scan */
@@ -1355,6 +1362,9 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
dm_list_size(devs_out), dm_list_size(devs_in));
dm_list_splice(hints_out, &hints_list);
free(vgname);
return 1;
}

View File

@@ -24,6 +24,8 @@
#include "lib/activate/activate.h"
#include "lib/label/hints.h"
#include "lib/metadata/metadata.h"
#include "lib/format_text/format-text.h"
#include "lib/format_text/layout.h"
#include <sys/stat.h>
#include <fcntl.h>
@@ -218,7 +220,7 @@ int label_write(struct device *dev, struct label *label)
if (!dev_write_bytes(dev, offset, LABEL_SIZE, buf)) {
log_debug_devs("Failed to write label to %s", dev_name(dev));
r = 0;
return 0;
}
dev_unset_last_byte(dev);
@@ -655,7 +657,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
int submit_count;
int scan_failed;
int is_lvm_device;
int error;
int ret;
dm_list_init(&wait_devs);
@@ -702,12 +703,11 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
dm_list_iterate_items_safe(devl, devl2, &wait_devs) {
bb = NULL;
error = 0;
scan_failed = 0;
is_lvm_device = 0;
if (!bcache_get(scan_bcache, devl->dev->bcache_fd, 0, 0, &bb)) {
log_debug_devs("Scan failed to read %s error %d.", dev_name(devl->dev), error);
log_debug_devs("Scan failed to read %s.", dev_name(devl->dev));
scan_failed = 1;
scan_read_errors++;
scan_failed_count++;
@@ -921,6 +921,74 @@ static void _prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_d
#endif
}
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out)
{
char buf[LABEL_SIZE] __attribute__((aligned(8)));
struct dm_list devs;
struct dev_iter *iter;
struct device_list *devl, *devl2;
struct device *dev;
struct pv_header *pvh;
int ret = 0;
dm_list_init(&devs);
dev_cache_scan();
if (!(iter = dev_iter_create(cmd->filter, 0))) {
log_error("Scanning failed to get devices.");
return 0;
}
log_debug_devs("Filtering devices to scan");
while ((dev = dev_iter_get(cmd, iter))) {
if (!(devl = zalloc(sizeof(*devl))))
continue;
devl->dev = dev;
dm_list_add(&devs, &devl->list);
};
dev_iter_destroy(iter);
if (!scan_bcache) {
if (!_setup_bcache())
goto_out;
}
log_debug_devs("Reading labels for pvid");
dm_list_iterate_items(devl, &devs) {
dev = devl->dev;
memset(buf, 0, sizeof(buf));
if (!label_scan_open(dev))
continue;
if (!dev_read_bytes(dev, 512, LABEL_SIZE, buf)) {
_scan_dev_close(dev);
goto out;
}
pvh = (struct pv_header *)(buf + 32);
if (!memcmp(pvh->pv_uuid, pvid, ID_LEN)) {
*dev_out = devl->dev;
_scan_dev_close(dev);
break;
}
_scan_dev_close(dev);
}
ret = 1;
out:
dm_list_iterate_items_safe(devl, devl2, &devs) {
dm_list_del(&devl->list);
free(devl);
}
return ret;
}
/*
* Scan devices on the system to discover which are LVM devices.
* Info about the LVM devices (PVs) is saved in lvmcache in a
@@ -1451,6 +1519,7 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
if (!bcache_write_bytes(scan_bcache, dev->bcache_fd, start, len, data)) {
log_error("Error writing device %s at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
dev_unset_last_byte(dev);
label_scan_invalidate(dev);
return false;
}
@@ -1458,12 +1527,18 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
if (!bcache_flush(scan_bcache)) {
log_error("Error writing device %s at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
dev_unset_last_byte(dev);
label_scan_invalidate(dev);
return false;
}
return true;
}
bool dev_invalidate_bytes(struct device *dev, uint64_t start, size_t len)
{
return bcache_invalidate_bytes(scan_bcache, dev->bcache_fd, start, len);
}
bool dev_write_zeros(struct device *dev, uint64_t start, size_t len)
{
if (test_mode())

View File

@@ -118,6 +118,8 @@ int label_scan_open(struct device *dev);
int label_scan_open_excl(struct device *dev);
int label_scan_open_rw(struct device *dev);
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out);
/*
* Wrappers around bcache equivalents.
* (these make it easier to disable bcache and revert to direct rw if needed)
@@ -126,6 +128,7 @@ bool dev_read_bytes(struct device *dev, uint64_t start, size_t len, void *data);
bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data);
bool dev_write_zeros(struct device *dev, uint64_t start, size_t len);
bool dev_set_bytes(struct device *dev, uint64_t start, size_t len, uint8_t val);
bool dev_invalidate_bytes(struct device *dev, uint64_t start, size_t len);
void dev_set_last_byte(struct device *dev, uint64_t offset);
void dev_unset_last_byte(struct device *dev);

View File

@@ -338,7 +338,7 @@ int sync_local_dev_names(struct cmd_context* cmd)
* an explicitly acquired ex global lock to sh in process_each.
*/
static int _lockf_global(struct cmd_context *cmd, const char *mode, int convert)
static int _lockf_global(struct cmd_context *cmd, const char *mode, int convert, int nonblock)
{
uint32_t flags = 0;
int ret;
@@ -346,6 +346,9 @@ static int _lockf_global(struct cmd_context *cmd, const char *mode, int convert)
if (convert)
flags |= LCK_CONVERT;
if (nonblock)
flags |= LCK_NONBLOCK;
if (!strcmp(mode, "ex")) {
flags |= LCK_WRITE;
@@ -379,7 +382,7 @@ static int _lockf_global(struct cmd_context *cmd, const char *mode, int convert)
int lockf_global(struct cmd_context *cmd, const char *mode)
{
return _lockf_global(cmd, mode, 0);
return _lockf_global(cmd, mode, 0, 0);
}
int lockf_global_convert(struct cmd_context *cmd, const char *mode)
@@ -388,7 +391,12 @@ int lockf_global_convert(struct cmd_context *cmd, const char *mode)
if (cmd->lockf_global_ex && !strcmp(mode, "ex"))
return 1;
return _lockf_global(cmd, mode, 1);
return _lockf_global(cmd, mode, 1, 0);
}
int lockf_global_nonblock(struct cmd_context *cmd, const char *mode)
{
return _lockf_global(cmd, mode, 0, 1);
}
int lock_global(struct cmd_context *cmd, const char *mode)

View File

@@ -75,6 +75,7 @@ int activate_lvs(struct cmd_context *cmd, struct dm_list *lvs, unsigned exclusiv
int lockf_global(struct cmd_context *cmd, const char *mode);
int lockf_global_convert(struct cmd_context *cmd, const char *mode);
int lockf_global_nonblock(struct cmd_context *cmd, const char *mode);
int lock_global(struct cmd_context *cmd, const char *mode);
int lock_global_convert(struct cmd_context *cmd, const char *mode);

View File

@@ -23,7 +23,6 @@
#include "lib/config/defaults.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/misc/lvm-signal.h"
#include "lib/activate/dev_manager.h"
/* https://github.com/jthornber/thin-provisioning-tools/blob/master/caching/cache_metadata_size.cc */
#define DM_TRANSACTION_OVERHEAD 4096 /* KiB */

View File

@@ -0,0 +1,738 @@
/*
* Copyright (C) 2014-2015 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib/misc/lib.h"
#include "lib/metadata/metadata.h"
#include "lib/locking/locking.h"
#include "lib/misc/lvm-string.h"
#include "lib/commands/toolcontext.h"
#include "lib/display/display.h"
#include "lib/metadata/segtype.h"
#include "lib/activate/activate.h"
#include "lib/config/defaults.h"
#include "lib/activate/dev_manager.h"
#define DEFAULT_TAG_SIZE 4 /* bytes */
#define DEFAULT_MODE 'B'
#define DEFAULT_INTERNAL_HASH "crc32c"
#define ONE_MB_IN_BYTES 1048576
int lv_is_integrity_origin(const struct logical_volume *lv)
{
struct seg_list *sl;
dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
if (!sl->seg || !sl->seg->lv || !sl->seg->origin)
continue;
if (lv_is_integrity(sl->seg->lv) && (sl->seg->origin == lv))
return 1;
}
return 0;
}
/*
* Every 500M of data needs 4M of metadata.
* (From trial and error testing.)
*/
static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes)
{
return ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);
}
/*
* The user wants external metadata, but did not specify an existing
* LV to hold metadata, so create an LV for metadata.
*/
static int _lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg,
struct lvcreate_params *lp,
struct logical_volume **meta_lv)
{
char metaname[NAME_LEN];
uint64_t lv_size_bytes, meta_bytes, meta_sectors;
struct logical_volume *lv;
struct lvcreate_params lp_meta = {
.activate = CHANGE_AN,
.alloc = ALLOC_INHERIT,
.major = -1,
.minor = -1,
.permission = LVM_READ | LVM_WRITE,
.pvh = &vg->pvs,
.read_ahead = DM_READ_AHEAD_NONE,
.stripes = 1,
.vg_name = vg->name,
.zero = 0,
.wipe_signatures = 0,
.suppress_zero_warn = 1,
};
if (lp->lv_name &&
dm_snprintf(metaname, NAME_LEN, "%s_imeta", lp->lv_name) < 0) {
log_error("Failed to create metadata LV name.");
return 0;
}
if (!lp->lv_name &&
!generate_lv_name(vg, "lvol%d_imeta", metaname, sizeof(metaname))) {
log_error("Failed to generate LV name");
return 0;
}
if (!(lp_meta.lv_name = strdup(metaname)))
return_0;
lp_meta.pvh = lp->pvh;
lv_size_bytes = lp->extents * vg->extent_size * 512;
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
meta_sectors = meta_bytes / 512;
lp_meta.extents = meta_sectors / vg->extent_size;
log_print("Creating integrity metadata LV %s with size %s.",
metaname, display_size(cmd, meta_sectors));
dm_list_init(&lp_meta.tags);
if (!(lp_meta.segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
if (!(lv = lv_create_single(vg, &lp_meta))) {
log_error("Failed to create integrity metadata LV");
return 0;
}
*meta_lv = lv;
return 1;
}
static int _get_provided_data_sectors(struct logical_volume *lv, uint64_t *provided_data_sectors)
{
struct lv_with_info_and_seg_status status;
uint64_t data_sectors, extra_sectors;
memset(&status, 0, sizeof(status));
status.seg_status.type = SEG_STATUS_NONE;
status.seg_status.seg = first_seg(lv);
/* FIXME: why reporter_pool? */
if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024))) {
log_error("Failed to get mem for LV status.");
return 0;
}
if (!lv_info_with_seg_status(lv->vg->cmd, first_seg(lv), &status, 1, 1)) {
log_error("Failed to get device mapper status for %s", display_lvname(lv));
goto fail;
}
if (!status.info.exists) {
log_error("No device mapper info exists for %s", display_lvname(lv));
goto fail;
}
if (status.seg_status.type != SEG_STATUS_INTEGRITY) {
log_error("Invalid device mapper status type (%d) for %s",
(uint32_t)status.seg_status.type, display_lvname(lv));
goto fail;
}
data_sectors = status.seg_status.integrity->provided_data_sectors;
if ((extra_sectors = (data_sectors % lv->vg->extent_size))) {
data_sectors -= extra_sectors;
log_debug("Reduce provided_data_sectors by %llu to %llu for extent alignment",
(unsigned long long)extra_sectors, (unsigned long long)data_sectors);
}
*provided_data_sectors = data_sectors;
dm_pool_destroy(status.seg_status.mem);
return 1;
fail:
dm_pool_destroy(status.seg_status.mem);
return 0;
}
int lv_remove_integrity_from_raid(struct logical_volume *lv)
{
struct lv_segment *seg_top, *seg_image;
struct logical_volume *lv_image;
struct logical_volume *lv_iorig;
struct logical_volume *lv_imeta;
uint32_t area_count, s;
seg_top = first_seg(lv);
if (!seg_is_raid1(seg_top)) {
log_error("LV %s segment is not raid1.", display_lvname(lv));
return 0;
}
area_count = seg_top->area_count;
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
seg_image = first_seg(lv_image);
if (!(lv_imeta = seg_image->integrity_meta_dev)) {
log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
return 0;
}
if (!(lv_iorig = seg_lv(seg_image, 0))) {
log_error("LV %s integrity segment has no origin", display_lvname(lv));
return 0;
}
if (!remove_seg_from_segs_using_this_lv(seg_image->integrity_meta_dev, seg_image))
return_0;
lv_set_visible(seg_image->integrity_meta_dev);
lv_image->status &= ~INTEGRITY;
lv_imeta->status &= ~INTEGRITY_METADATA;
seg_image->integrity_meta_dev = NULL;
seg_image->integrity_data_sectors = 0;
memset(&seg_image->integrity_settings, 0, sizeof(seg_image->integrity_settings));
if (!remove_layer_from_lv(lv_image, lv_iorig))
return_0;
if (!lv_remove(lv_iorig))
return_0;
if (!lv_remove(lv_imeta))
log_warn("WARNING: failed to remove integrity metadata LV.");
}
return 1;
}
int lv_remove_integrity(struct logical_volume *lv)
{
struct lv_segment *seg = first_seg(lv);
struct logical_volume *origin;
struct logical_volume *meta_lv;
if (!seg_is_integrity(seg)) {
log_error("LV %s segment is not integrity.", display_lvname(lv));
return 0;
}
if (!seg->integrity_meta_dev) {
log_error("Internal integrity cannot be removed.");
return 0;
}
if (!(meta_lv = seg->integrity_meta_dev)) {
log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
return 0;
}
if (!(origin = seg_lv(seg, 0))) {
log_error("LV %s integrity segment has no origin", display_lvname(lv));
return 0;
}
if (!remove_seg_from_segs_using_this_lv(seg->integrity_meta_dev, seg))
return_0;
lv_set_visible(seg->integrity_meta_dev);
lv->status &= ~INTEGRITY;
meta_lv->status &= ~INTEGRITY_METADATA;
seg->integrity_meta_dev = NULL;
if (!remove_layer_from_lv(lv, origin))
return_0;
if (!lv_remove(origin))
return_0;
if (!lv_remove(meta_lv))
log_warn("WARNING: failed to remove integrity metadata LV.");
return 1;
}
/*
* Add integrity to each raid image.
*
* for each rimage_N:
* . create and allocate a new linear LV rimage_N_imeta
* . move the segments from rimage_N to a new rimage_N_iorig
* . add an integrity segment to rimage_N with
* origin=rimage_N_iorig, meta_dev=rimage_N_imeta
*
* Before:
* rimage_0
* segment1: striped: pv0:A
* rimage_1
* segment1: striped: pv1:B
*
* After:
* rimage_0
* segment1: integrity: rimage_0_iorig, rimage_0_imeta
* rimage_1
* segment1: integrity: rimage_1_iorig, rimage_1_imeta
* rimage_0_iorig
* segment1: striped: pv0:A
* rimage_1_iorig
* segment1: striped: pv1:B
* rimage_0_imeta
* segment1: striped: pv2:A
* rimage_1_imeta
* segment1: striped: pv2:B
*
*/
int lv_add_integrity_to_raid(struct logical_volume *lv, const char *arg,
struct integrity_settings *settings,
struct dm_list *pvh)
{
struct lvcreate_params lp;
struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
struct logical_volume *lv_image, *lv_imeta, *lv_iorig;
struct lv_segment *seg_top, *seg_image;
const struct segment_type *segtype;
struct integrity_settings *set;
uint64_t status_data_sectors = 0;
uint32_t area_count, s;
int external = 0, internal = 0;
int ret = 1;
memset(imeta_lvs, 0, sizeof(imeta_lvs));
if (!arg || !strcmp(arg, "y") || !strcmp(arg, "external"))
external = 1;
else if (!strcmp(arg, "internal"))
internal = 1;
else {
log_error("Invalid --integrity arg for lvcreate.");
return 0;
}
if (dm_list_size(&lv->segments) != 1)
return_0;
seg_top = first_seg(lv);
area_count = seg_top->area_count;
if (!seg_is_raid1(seg_top)) {
log_error("Integrity can only be added to raid1.");
return 0;
}
if (internal) {
/*
* FIXME: raid on internal integrity might not be used widely
* enough to enable, given the additional complexity/support.
* i.e. nearly everyone may just use external metadata.
*
* FIXME: _info_run() needs code to adjust the length, like
* is done for if (lv_is_integrity()) length = ...
*/
/* goto skip_imeta; */
log_error("Internal integrity metadata is not yet supported with raid.");
return 0;
}
/*
* For each rimage, create an _imeta LV for integrity metadata.
* Each needs to be zeroed.
*/
for (s = 0; s < area_count; s++) {
struct logical_volume *meta_lv;
struct wipe_params wipe;
if (s >= DEFAULT_RAID_MAX_IMAGES)
return_0;
lv_image = seg_lv(seg_top, s);
if (!seg_is_striped(first_seg(lv_image))) {
log_error("raid1 image must be linear to add integrity");
return_0;
}
/*
* allocate a new linear LV NAME_rimage_N_imeta
*/
memset(&lp, 0, sizeof(lp));
lp.lv_name = lv_image->name;
lp.pvh = pvh;
lp.extents = lv_image->size / vg->extent_size;
if (!_lv_create_integrity_metadata(cmd, vg, &lp, &meta_lv))
return_0;
/*
* dm-integrity requires the metadata LV header to be zeroed.
*/
if (!activate_lv(cmd, meta_lv)) {
log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
return 0;
}
memset(&wipe, 0, sizeof(wipe));
wipe.do_zero = 1;
wipe.zero_sectors = 8;
if (!wipe_lv(meta_lv, wipe)) {
log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
return 0;
}
if (!deactivate_lv(cmd, meta_lv)) {
log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
return 0;
}
/* Used below to set up the new integrity segment. */
imeta_lvs[s] = meta_lv;
}
/* skip_imeta: */
/*
* For each rimage, move its segments to a new rimage_iorig and give
* the rimage a new integrity segment.
*/
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
return_0;
log_debug("Adding integrity to raid image %s", lv_image->name);
/*
* "lv_iorig" is a new LV with new id, but with the segments
* from "lv_image". "lv_image" keeps the existing name and id,
* but gets a new integrity segment, in place of the segments
* that were moved to lv_iorig.
*/
if (!(lv_iorig = insert_layer_for_lv(cmd, lv_image, INTEGRITY, "_iorig")))
return_0;
lv_image->status |= INTEGRITY;
/*
* Set up the new first segment of lv_image as integrity.
*/
seg_image = first_seg(lv_image);
seg_image->segtype = segtype;
if (external) {
lv_imeta = imeta_lvs[s]; /* external metadata lv created above */
lv_imeta->status |= INTEGRITY_METADATA;
lv_set_hidden(lv_imeta);
seg_image->integrity_data_sectors = lv_image->size;
seg_image->integrity_meta_dev = lv_imeta;
}
memcpy(&seg_image->integrity_settings, settings, sizeof(struct integrity_settings));
set = &seg_image->integrity_settings;
if (!set->mode[0])
set->mode[0] = DEFAULT_MODE;
if (!set->tag_size)
set->tag_size = DEFAULT_TAG_SIZE;
if (!set->internal_hash)
set->internal_hash = DEFAULT_INTERNAL_HASH;
}
/*
* When using internal metadata, we have to temporarily activate the
* integrity image with size 1 to get provided_data_sectors from the
* dm-integrity module.
*/
if (internal) {
/* Get size from the first image, others will be the same. */
lv_image = seg_lv(seg_top, 0);
lv_image->status |= LV_TEMPORARY;
lv_image->status |= LV_NOSCAN;
seg_image = first_seg(lv_image);
seg_image->integrity_data_sectors = 1;
/* write-commit allows activating the LV to get data_sectors */
if (!vg_write(vg) || !vg_commit(vg)) {
log_error("Preliminary internal integrity write commit error");
ret = 0;
goto out;
}
log_debug("Activating temporary integrity LV to get data sectors.");
if (!activate_lv(cmd, lv_image)) {
log_error("Failed to activate temporary integrity.");
ret = 0;
goto out;
}
if (!_get_provided_data_sectors(lv_image, &status_data_sectors)) {
log_error("Failed to get data sectors from dm-integrity");
ret = 0;
} else {
log_print("Found integrity provided_data_sectors %llu", (unsigned long long)status_data_sectors);
ret = 1;
}
if (!status_data_sectors) {
log_error("LV size too small to include metadata.");
ret = 0;
}
if (!deactivate_lv(cmd, lv_image)) {
log_error("Failed to deactivate temporary integrity.");
ret = 0;
}
if (!ret)
goto_out;
lv_image->status &= ~LV_NOSCAN;
lv_image->status &= ~LV_TEMPORARY;
/* The main point, setting integrity_data_sectors. */
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
seg_image = first_seg(lv_image);
seg_image->integrity_data_sectors = status_data_sectors;
}
}
log_debug("Write VG with integrity added to LV");
if (!vg_write(vg) || !vg_commit(vg))
ret = 0;
out:
return ret;
}
int lv_add_integrity(struct logical_volume *lv, const char *arg,
const char *meta_name,
struct integrity_settings *settings,
struct dm_list *pvh)
{
char imeta_name[NAME_LEN];
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
struct integrity_settings *segset;
struct logical_volume *lv_orig;
struct logical_volume *meta_lv = NULL;
const struct segment_type *segtype;
struct lv_segment *seg;
uint64_t lv_size_sectors;
int external = 0, internal = 0;
int ret = 1;
lv_size_sectors = lv->size;
/*
* --integrity <arg> is y|external|internal
*/
if (!arg || !strcmp(arg, "y") || !strcmp(arg, "external"))
external = 1;
else if (!strcmp(arg, "internal"))
internal = 1;
else {
log_error("Invalid --integrity arg for lvcreate.");
return 0;
}
if (internal && meta_name) {
log_error("Internal integrity cannot be used with integritymetadata option.");
return 0;
}
if (external && !meta_name) {
struct lvcreate_params lp;
memset(&lp, 0, sizeof(lp));
lp.lv_name = lv->name;
lp.pvh = pvh;
lp.extents = lv->size / vg->extent_size;
if (!_lv_create_integrity_metadata(cmd, vg, &lp, &meta_lv))
goto_out;
} else if (external && meta_name) {
uint64_t meta_bytes, meta_sectors;
if (!(meta_lv = find_lv(vg, meta_name))) {
log_error("LV %s not found.", meta_name);
return_0;
}
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_sectors * 512);
meta_sectors = meta_bytes / 512;
if (meta_lv->size < meta_sectors) {
log_error("Integrity metadata needs %s, metadata LV is only %s.",
display_size(cmd, meta_sectors), display_size(cmd, meta_lv->size));
return 0;
}
}
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
return_0;
/*
* "lv_orig" is a new LV with new id, but with the segments from "lv".
* "lv" keeps the existing name and id, but gets a new integrity segment,
* in place of the segments that were moved to lv_orig.
*/
if (!(lv_orig = insert_layer_for_lv(cmd, lv, INTEGRITY, "_iorig")))
return_0;
seg = first_seg(lv);
seg->segtype = segtype;
lv->status |= INTEGRITY;
memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
segset = &seg->integrity_settings;
if (!segset->mode[0])
segset->mode[0] = DEFAULT_MODE;
if (!segset->tag_size)
segset->tag_size = DEFAULT_TAG_SIZE;
if (!segset->internal_hash)
segset->internal_hash = DEFAULT_INTERNAL_HASH;
/*
* When not using a meta_dev, dm-integrity needs to tell us what the
* usable size of the LV is, which is the dev size minus the integrity
* overhead. To find that, we need to do a special, temporary activation
* of the new LV, specifying a dm dev size of 1, then check the dm dev
* status field provided_data_sectors, which is the actual size of the
* LV. We need to include provided_data_sectors in the metadata for the
* new LV, and use this as the dm dev size for normal LV activation.
*
* When using a meta_dev, the dm dev size is the size of the data
* device. The necessary size of the meta_dev for the given data
* device needs to be estimated.
*/
if (meta_lv) {
struct wipe_params wipe;
if (!sync_local_dev_names(cmd)) {
log_error("Failed to sync local devices.");
return 0;
}
log_verbose("Zeroing LV for integrity metadata");
if (!lv_is_active(meta_lv)) {
if (!activate_lv(cmd, meta_lv)) {
log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
return 0;
}
}
memset(&wipe, 0, sizeof(wipe));
wipe.do_zero = 1;
wipe.zero_sectors = 8;
if (!wipe_lv(meta_lv, wipe)) {
log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
return 0;
}
if (!deactivate_lv(cmd, meta_lv)) {
log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
return 0;
}
if (meta_name) {
/* LVM tradition to add a suffix to an existing LV when using it. */
if (dm_snprintf(imeta_name, sizeof(imeta_name), "%s_imeta", meta_lv->name) < 0) {
log_error("Can't prepare new imeta name for %s", display_lvname(meta_lv));
return 0;
}
if (!lv_rename_update(cmd, meta_lv, imeta_name, 0))
return_0;
}
meta_lv->status |= INTEGRITY_METADATA;
seg->integrity_data_sectors = lv_size_sectors;
seg->integrity_meta_dev = meta_lv;
lv_set_hidden(meta_lv);
} else {
/* dm-integrity wants temp/fake size of 1 to report usable size */
seg->integrity_data_sectors = 1;
/* write-commit allows activating the LV to get data_sectors */
if (!vg_write(vg) || !vg_commit(vg)) {
log_error("Preliminary internal integrity write commit error");
return 0;
}
lv->status |= LV_TEMPORARY;
lv->status |= LV_NOSCAN;
log_debug("Activating temporary integrity LV to get data sectors.");
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate temporary integrity.");
ret = 0;
goto out;
}
if (!_get_provided_data_sectors(lv, &seg->integrity_data_sectors)) {
log_error("Failed to get data sectors from dm-integrity");
ret = 0;
} else {
log_debug("Found integrity provided_data_sectors %llu", (unsigned long long)seg->integrity_data_sectors);
ret = 1;
}
if (!seg->integrity_data_sectors) {
log_error("LV size too small to include metadata.");
ret = 0;
}
if (!deactivate_lv(cmd, lv)) {
log_error("Failed to deactivate temporary integrity.");
ret = 0;
}
lv->status &= ~LV_NOSCAN;
lv->status &= ~LV_TEMPORARY;
}
log_debug("Write VG with integrity added to LV");
if (!vg_write(vg) || !vg_commit(vg))
ret = 0;
out:
return ret;
}

View File

@@ -585,6 +585,8 @@ struct logical_volume *lv_origin_lv(const struct logical_volume *lv)
origin = first_seg(lv)->external_lv;
else if (lv_is_writecache(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
else if (lv_is_integrity(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
return origin;
}
@@ -1200,10 +1202,13 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
else if (lv_is_pool_metadata(lv) ||
lv_is_pool_metadata_spare(lv) ||
lv_is_raid_metadata(lv))
lv_is_raid_metadata(lv) ||
lv_is_integrity_metadata(lv))
repstr[0] = 'e';
else if (lv_is_cache_type(lv) || lv_is_writecache(lv))
repstr[0] = 'C';
else if (lv_is_integrity(lv))
repstr[0] = 'g';
else if (lv_is_raid(lv))
repstr[0] = (lv_is_not_synced(lv)) ? 'R' : 'r';
else if (lv_is_mirror(lv))

View File

@@ -134,7 +134,9 @@ enum {
LV_TYPE_SANLOCK,
LV_TYPE_CACHEVOL,
LV_TYPE_WRITECACHE,
LV_TYPE_WRITECACHEORIGIN
LV_TYPE_WRITECACHEORIGIN,
LV_TYPE_INTEGRITY,
LV_TYPE_INTEGRITYORIGIN
};
static const char *_lv_type_names[] = {
@@ -190,6 +192,8 @@ static const char *_lv_type_names[] = {
[LV_TYPE_CACHEVOL] = "cachevol",
[LV_TYPE_WRITECACHE] = "writecache",
[LV_TYPE_WRITECACHEORIGIN] = "writecacheorigin",
[LV_TYPE_INTEGRITY] = "integrity",
[LV_TYPE_INTEGRITYORIGIN] = "integrityorigin",
};
static int _lv_layout_and_role_mirror(struct dm_pool *mem,
@@ -461,6 +465,43 @@ bad:
return 0;
}
static int _lv_layout_and_role_integrity(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
struct dm_list *role,
int *public_lv)
{
int top_level = 0;
/* non-top-level LVs */
if (lv_is_integrity_metadata(lv)) {
if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_METADATA]))
goto_bad;
} else if (lv_is_integrity_origin(lv)) {
if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_ORIGIN]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITYORIGIN]))
goto_bad;
} else
top_level = 1;
if (!top_level) {
*public_lv = 0;
return 1;
}
/* top-level LVs */
if (lv_is_integrity(lv)) {
if (!str_list_add_no_dup_check(mem, layout, _lv_type_names[LV_TYPE_INTEGRITY]))
goto_bad;
}
return 1;
bad:
return 0;
}
static int _lv_layout_and_role_thick_origin_snapshot(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
@@ -577,6 +618,11 @@ int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv,
!_lv_layout_and_role_cache(mem, lv, *layout, *role, &public_lv))
goto_bad;
/* Integrity related */
if ((lv_is_integrity(lv) || lv_is_integrity_origin(lv) || lv_is_integrity_metadata(lv)) &&
!_lv_layout_and_role_integrity(mem, lv, *layout, *role, &public_lv))
goto_bad;
/* VDO and related */
if (lv_is_vdo_type(lv) &&
!_lv_layout_and_role_vdo(mem, lv, *layout, *role, &public_lv))
@@ -1457,6 +1503,15 @@ static int _lv_reduce(struct logical_volume *lv, uint32_t extents, int delete)
return_0;
}
if (delete && seg_is_integrity(seg)) {
/* Remove integrity origin in addition to integrity layer. */
if (!lv_remove(seg_lv(seg, 0)))
return_0;
/* Remove integrity metadata. */
if (seg->integrity_meta_dev && !lv_remove(seg->integrity_meta_dev))
return_0;
}
if ((pool_lv = seg->pool_lv)) {
if (!detach_pool_lv(seg))
return_0;
@@ -5654,6 +5709,11 @@ int lv_resize(struct logical_volume *lv,
return 0;
}
if (lv_is_integrity(lv)) {
log_error("Resize not yet allowed on LVs with integrity.");
return 0;
}
if (!_lvresize_check(lv, lp))
return_0;
@@ -7679,6 +7739,15 @@ static int _should_wipe_lv(struct lvcreate_params *lp,
first_seg(first_seg(lv)->pool_lv)->zero_new_blocks))
return 0;
/*
* dm-integrity requires the first sector (integrity superblock) to be
* zero when creating a new integrity device.
* TODO: print a warning or error if the user specifically
* asks for no wiping or zeroing?
*/
if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg))
return 1;
/* Cannot zero read-only volume */
if ((lv->status & LVM_WRITE) &&
(lp->zero || lp->wipe_signatures))
@@ -7934,6 +8003,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
/* FIXME Eventually support raid/mirrors with -m */
if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
} else if (seg_is_integrity(lp)) {
if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
} else if (seg_is_mirrored(lp) || (seg_is_raid(lp) && !seg_is_any_raid0(lp))) {
if (!(lp->region_size = adjusted_mirror_region_size(vg->cmd,
vg->extent_size,
@@ -8277,6 +8351,19 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
goto revert_new_lv;
}
lv->status &= ~LV_TEMPORARY;
} else if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg)) {
/*
* Activate the new origin LV so it can be zeroed/wiped
* below before adding integrity.
*/
lv->status |= LV_TEMPORARY;
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate new LV for wiping.");
goto revert_new_lv;
}
lv->status &= ~LV_TEMPORARY;
} else if (!lv_active_change(cmd, lv, lp->activate)) {
log_error("Failed to activate new LV %s.", display_lvname(lv));
goto deactivate_and_revert_new_lv;
@@ -8296,6 +8383,47 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
}
}
if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg)) {
struct lv_activate_opts laopts = { 0 };
log_verbose("Adding integrity to new LV");
/* Origin is active from zeroing, deactivate to add integrity. */
if (!deactivate_lv(cmd, lv)) {
log_error("Failed to deactivate LV to add integrity");
goto revert_new_lv;
}
if (seg_is_raid1(lp)) {
if (!lv_add_integrity_to_raid(lv, lp->integrity_arg,
&lp->integrity_settings, lp->pvh))
goto revert_new_lv;
} else {
if (!lv_add_integrity(lv, lp->integrity_arg, lp->integrity_meta_name,
&lp->integrity_settings, lp->pvh))
goto revert_new_lv;
}
backup(vg);
/*
* The integrity metadata (checksums) need to be initialized.
* Activating the new integrity LVs with the recalculate
* option will cause dm-integrity to initialize.
*/
laopts.integrity_recalculate = 1;
if (!activate_lv_opts(cmd, lv, &laopts)) {
log_error("Failed to activate LV %s.", display_lvname(lv));
log_error("The full LV should be zeroed to initialize integrity metadata.");
goto out;
}
log_print("LV %s activated to initialize integrity.", display_lvname(lv));
goto out;
}
if (seg_is_vdo_pool(lp)) {
if (!convert_vdo_pool_lv(lv, &lp->vdo_params, &lp->virtual_extents)) {
stack;

View File

@@ -73,7 +73,7 @@ int lv_merge_segments(struct logical_volume *lv)
#define seg_error(msg) do { \
log_error("LV %s, segment %u invalid: %s for %s segment.", \
seg->lv->name, seg_count, (msg), lvseg_name(seg)); \
display_lvname(seg->lv), seg_count, (msg), lvseg_name(seg)); \
if ((*error_count)++ > ERROR_MAX) \
return; \
} while (0)
@@ -86,14 +86,14 @@ int lv_merge_segments(struct logical_volume *lv)
*/
#define raid_seg_error(msg) do { \
log_error("LV %s invalid: %s for %s segment", \
seg->lv->name, (msg), lvseg_name(seg)); \
display_lvname(seg->lv), (msg), lvseg_name(seg)); \
if ((*error_count)++ > ERROR_MAX) \
return; \
} while (0)
#define raid_seg_error_val(msg, val) do { \
log_error("LV %s invalid: %s (is %u) for %s segment", \
seg->lv->name, (msg), (val), lvseg_name(seg)); \
display_lvname(seg->lv), (msg), (val), lvseg_name(seg)); \
if ((*error_count)++ > ERROR_MAX) \
return; \
} while(0)
@@ -742,6 +742,8 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
seg_found++;
if (seg->metadata_lv == lv || seg->pool_lv == lv || seg->writecache == lv)
seg_found++;
if (seg->integrity_meta_dev == lv)
seg_found++;
if (seg_is_thin_volume(seg) && (seg->origin == lv || seg->external_lv == lv))
seg_found++;

View File

@@ -84,12 +84,14 @@
#define CONVERTING UINT64_C(0x0000000000400000) /* LV */
#define MISSING_PV UINT64_C(0x0000000000800000) /* PV */
#define INTEGRITY UINT64_C(0x0000000000800000) /* LV - Internal use only */
#define PV_MOVED_VG UINT64_C(0x4000000000000000) /* PV - Moved to a new VG */
#define PARTIAL_LV UINT64_C(0x0000000001000000) /* LV - derived flag, not
written out in metadata*/
//#define POSTORDER_FLAG UINT64_C(0x0000000002000000) /* Not real flags, reserved for
//#define POSTORDER_OPEN_FLAG UINT64_C(0x0000000004000000) temporary use inside vg_read_internal. */
#define INTEGRITY_METADATA UINT64_C(0x0000000004000000) /* LV - Internal use only */
#define VIRTUAL_ORIGIN UINT64_C(0x0000000008000000) /* LV - internal use only */
#define MERGING UINT64_C(0x0000000010000000) /* LV SEG */
@@ -261,6 +263,8 @@
#define lv_is_pool_metadata_spare(lv) (((lv)->status & POOL_METADATA_SPARE) ? 1 : 0)
#define lv_is_lockd_sanlock_lv(lv) (((lv)->status & LOCKD_SANLOCK_LV) ? 1 : 0)
#define lv_is_writecache(lv) (((lv)->status & WRITECACHE) ? 1 : 0)
#define lv_is_integrity(lv) (((lv)->status & INTEGRITY) ? 1 : 0)
#define lv_is_integrity_metadata(lv) (((lv)->status & INTEGRITY_METADATA) ? 1 : 0)
#define lv_is_vdo(lv) (((lv)->status & LV_VDO) ? 1 : 0)
#define lv_is_vdo_pool(lv) (((lv)->status & LV_VDO_POOL) ? 1 : 0)
@@ -272,9 +276,11 @@
/* Recognize component LV (matching lib/misc/lvm-string.c _lvname_has_reserved_component_string()) */
#define lv_is_component(lv) (lv_is_cache_origin(lv) || \
lv_is_writecache_origin(lv) || \
lv_is_integrity_origin(lv) || \
((lv)->status & (\
CACHE_POOL_DATA |\
CACHE_POOL_METADATA |\
INTEGRITY_METADATA |\
LV_CACHE_VOL |\
LV_VDO_POOL_DATA |\
MIRROR_IMAGE |\
@@ -519,6 +525,10 @@ struct lv_segment {
uint32_t writecache_block_size; /* For writecache */
struct writecache_settings writecache_settings; /* For writecache */
uint64_t integrity_data_sectors;
struct logical_volume *integrity_meta_dev;
struct integrity_settings integrity_settings;
struct dm_vdo_target_params vdo_params; /* For VDO-pool */
uint32_t vdo_pool_header_size; /* For VDO-pool */
uint32_t vdo_pool_virtual_extents; /* For VDO-pool */
@@ -992,6 +1002,10 @@ struct lvcreate_params {
alloc_policy_t alloc; /* all */
struct dm_vdo_target_params vdo_params; /* vdo */
const char *integrity_arg;
const char *integrity_meta_name; /* external LV is user-specified */
struct integrity_settings integrity_settings;
struct dm_list tags; /* all */
int yes;
@@ -1086,6 +1100,8 @@ int lv_is_cache_origin(const struct logical_volume *lv);
int lv_is_writecache_origin(const struct logical_volume *lv);
int lv_is_writecache_cachevol(const struct logical_volume *lv);
int lv_is_integrity_origin(const struct logical_volume *lv);
int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
int cow_has_min_chunks(const struct volume_group *vg, uint32_t cow_extents, uint32_t chunk_size);
@@ -1385,4 +1401,13 @@ int vg_is_foreign(struct volume_group *vg);
void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg);
int lv_add_integrity(struct logical_volume *lv, const char *arg,
const char *meta_name, struct integrity_settings *settings,
struct dm_list *pvh);
int lv_add_integrity_to_raid(struct logical_volume *lv, const char *arg,
struct integrity_settings *settings,
struct dm_list *pvh);
int lv_remove_integrity(struct logical_volume *lv);
int lv_remove_integrity_from_raid(struct logical_volume *lv);
#endif

View File

@@ -999,7 +999,7 @@ static int _vg_update_embedded_copy(struct volume_group *vg, struct volume_group
if (!(cft = export_vg_to_config_tree(vg)))
return_0;
if (!(*vg_embedded = import_vg_from_config_tree(cft, vg->fid))) {
if (!(*vg_embedded = import_vg_from_config_tree(vg->cmd, vg->fid, cft))) {
dm_config_destroy(cft);
return_0;
}
@@ -3564,7 +3564,8 @@ static void _set_pv_device(struct format_instance *fid,
if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
buffer[0] = '\0';
if (cmd && !cmd->pvscan_cache_single)
if (cmd && !cmd->pvscan_cache_single &&
(!vg_is_foreign(vg) && !cmd->include_foreign_vgs))
log_warn("WARNING: Couldn't find device with uuid %s.", buffer);
else
log_debug_metadata("Couldn't find device with uuid %s.", buffer);
@@ -4571,6 +4572,112 @@ void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg)
}
}
/*
* Reread an mda_header. If the text offset is the same as was seen and saved
* by label scan, it means the metadata is unchanged and we do not need to
* reread metadata.
*/
static bool _scan_text_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid)
{
struct dm_list mda_list;
struct mda_list *mdal, *safe;
struct metadata_area *mda;
struct mda_context *mdac;
struct device_area *area;
struct mda_header *mdah;
struct raw_locn *rlocn;
struct device *dev;
uint32_t bad_fields;
bool ret = true;
/*
* if cmd->can_use_one_scan, check one mda_header is unchanged,
* else check that all mda_headers are unchanged.
*/
dm_list_init(&mda_list);
lvmcache_get_mdas(cmd, vgname, vgid, &mda_list);
dm_list_iterate_items(mdal, &mda_list) {
mda = mdal->mda;
if (!mda->scan_text_offset)
continue;
if (mda->mda_num != 1)
continue;
if (!(dev = mda_get_device(mda))) {
log_debug("rescan for text mismatch - no mda dev");
goto out;
}
bad_fields = 0;
mdac = mda->metadata_locn;
area = &mdac->area;
/*
* Invalidate mda_header in bcache so it will be reread from disk.
*/
if (!dev_invalidate_bytes(dev, 4096, 512)) {
log_debug("rescan for text mismatch - cannot invalidate");
goto out;
}
if (!(mdah = raw_read_mda_header(cmd->fmt, area, 1, 0, &bad_fields))) {
log_debug("rescan for text mismatch - no mda header");
goto out;
}
rlocn = mdah->raw_locns;
if (bad_fields) {
log_debug("rescan for text mismatch - bad_fields");
} else if (rlocn->checksum != mda->scan_text_checksum) {
log_debug("rescan for text checksum mismatch - now %x prev %x",
rlocn->checksum, mda->scan_text_checksum);
} else if (rlocn->offset != mda->scan_text_offset) {
log_debug("rescan for text offset mismatch - now %llu prev %llu",
(unsigned long long)rlocn->offset,
(unsigned long long)mda->scan_text_offset);
} else {
/* the common case where fields match and no rescan needed */
ret = false;
}
dm_pool_free(cmd->mem, mdah);
/* For can_use_one_scan commands, return result from checking one mda. */
if (cmd->can_use_one_scan)
goto out;
/* For other commands, return mismatch immediately. */
if (ret)
goto_out;
}
if (ret) {
/* shouldn't happen */
log_debug("rescan for text mismatch - no mdas");
goto out;
}
out:
if (!ret)
log_debug("rescan skipped - unchanged offset %llu checksum %x",
(unsigned long long)mda->scan_text_offset,
mda->scan_text_checksum);
dm_list_iterate_items_safe(mdal, safe, &mda_list) {
dm_list_del(&mdal->list);
free(mdal);
}
return ret;
}
static struct volume_group *_vg_read(struct cmd_context *cmd,
const char *vgname,
const char *vgid,
@@ -4625,19 +4732,23 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
* find the same inconsistency. The VG repair (mistakenly done by
* vg_read below) is supposed to fix that.
*
* FIXME: sort out the usage of the global lock (which is mixed up
* with the orphan lock), and when we can tell that the global
* lock is taken prior to the label scan, and still held here,
* we can also skip the rescan in that case.
* If the VG was not modified between the time we scanned the PVs
* and now, when we hold the lock, then we don't need to rescan.
* We can read the mda_header, and look at the text offset/checksum,
* and if the current text offset/checksum matches what was seen during
* label scan, we know that metadata is unchanged and doesn't need
* to be rescanned. For reporting/display commands (CAN_USE_ONE_SCAN/
* can_use_one_scan), we check that the text offset/checksum are unchanged
* in just one mda before deciding to skip rescanning. For other commands,
* we check that they are unchanged in all mdas. This added checking is
* probably unnecessary; all commands could likely just check a single mda.
*/
if (!cmd->can_use_one_scan || lvmcache_scan_mismatch(cmd, vgname, vgid)) {
if (lvmcache_scan_mismatch(cmd, vgname, vgid) || _scan_text_mismatch(cmd, vgname, vgid)) {
log_debug_metadata("Rescanning devices for %s %s", vgname, writing ? "rw" : "");
if (writing)
lvmcache_label_rescan_vg_rw(cmd, vgname, vgid);
else
lvmcache_label_rescan_vg(cmd, vgname, vgid);
} else {
log_debug_metadata("Skipped rescanning devices for %s", vgname);
}
/* Now determine the correct vgname if none was supplied */
@@ -4768,10 +4879,7 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
if (vg->seqno == vg_ret->seqno) {
release_vg(vg);
continue;
}
if (vg->seqno > vg_ret->seqno) {
} else if (vg->seqno > vg_ret->seqno) {
log_warn("WARNING: ignoring metadata seqno %u on %s for seqno %u on %s for VG %s.",
vg_ret->seqno, dev_name(dev_ret),
vg->seqno, dev_name(mda_dev), vg->name);
@@ -4780,22 +4888,18 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
vg_ret = vg;
dev_ret = mda_dev;
vg_fmtdata = NULL;
continue;
}
if (vg_ret->seqno > vg->seqno) {
} else { /* vg->seqno < vg_ret->seqno */
log_warn("WARNING: ignoring metadata seqno %u on %s for seqno %u on %s for VG %s.",
vg->seqno, dev_name(mda_dev),
vg_ret->seqno, dev_name(dev_ret), vg->name);
found_old_metadata = 1;
release_vg(vg);
vg_fmtdata = NULL;
continue;
}
}
if (found_old_metadata)
log_warn("WARNING: Inconsistent metadata found for VG %s", vgname);
log_warn("WARNING: Inconsistent metadata found for VG %s.", vgname);
vg = NULL;
@@ -4826,16 +4930,15 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
if (!(dev->flags & DEV_IS_MD_COMPONENT))
continue;
log_debug_metadata("Drop dev for MD component from cache %s", dev_name(dev));
log_debug_metadata("Drop dev for MD component from cache %s.", dev_name(dev));
lvmcache_del_dev(dev);
dm_list_iterate_items(mda, &fid->metadata_areas_in_use) {
if (mda_get_device(mda) != dev)
continue;
log_debug_metadata("Drop mda from MD component from mda list %s", dev_name(dev));
dm_list_del(&mda->list);
break;
}
dm_list_iterate_items(mda, &fid->metadata_areas_in_use)
if (mda_get_device(mda) == dev) {
log_debug_metadata("Drop mda from MD component from mda list %s.", dev_name(dev));
dm_list_del(&mda->list);
break;
}
}
}
@@ -4900,7 +5003,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
int activating = (vg_read_flags & READ_FOR_ACTIVATE);
if (is_orphan_vg(vg_name)) {
log_very_verbose("Reading orphan VG %s", vg_name);
log_very_verbose("Reading orphan VG %s.", vg_name);
vg = vg_read_orphans(cmd, vg_name);
*error_flags = 0;
*error_vg = NULL;
@@ -4910,7 +5013,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!validate_name(vg_name)) {
log_error("Volume group name \"%s\" has invalid characters.", vg_name);
failure |= FAILED_NOTFOUND;
goto_bad;
goto bad;
}
/*
@@ -4925,15 +5028,15 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!(vg_read_flags & READ_WITHOUT_LOCK) &&
!lock_vol(cmd, vg_name, (writing || activating) ? LCK_VG_WRITE : LCK_VG_READ, NULL)) {
log_error("Can't get lock for %s", vg_name);
log_error("Can't get lock for %s.", vg_name);
failure |= FAILED_LOCKING;
goto_bad;
goto bad;
}
if (!(vg = _vg_read(cmd, vg_name, vgid, 0, writing))) {
/* Some callers don't care if the VG doesn't exist and don't want an error message. */
if (!(vg_read_flags & READ_OK_NOTFOUND))
log_error("Volume group \"%s\" not found", vg_name);
log_error("Volume group \"%s\" not found.", vg_name);
failure |= FAILED_NOTFOUND;
goto_bad;
}
@@ -4977,7 +5080,9 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!pvl->pv->dev) {
/* The obvious and common case of a missing device. */
if (pvl->pv->device_hint)
if (vg_is_foreign(vg) && !cmd->include_foreign_vgs)
log_debug("VG %s is missing PV %s (last written to %s)", vg_name, uuidstr, pvl->pv->device_hint ?: "na");
else if (pvl->pv->device_hint)
log_warn("WARNING: VG %s is missing PV %s (last written to %s).", vg_name, uuidstr, pvl->pv->device_hint);
else
log_warn("WARNING: VG %s is missing PV %s.", vg_name, uuidstr);
@@ -5004,14 +5109,14 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!check_pv_segments(vg)) {
log_error(INTERNAL_ERROR "PV segments corrupted in %s.", vg->name);
failure |= FAILED_INTERNAL_ERROR;
goto_bad;
goto bad;
}
dm_list_iterate_items(lvl, &vg->lvs) {
if (!check_lv_segments(lvl->lv, 0)) {
log_error(INTERNAL_ERROR "LV segments corrupted in %s.", lvl->lv->name);
failure |= FAILED_INTERNAL_ERROR;
goto_bad;
goto bad;
}
}
@@ -5020,7 +5125,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!check_lv_segments(lvl->lv, 1)) {
log_error(INTERNAL_ERROR "LV segments corrupted in %s.", lvl->lv->name);
failure |= FAILED_INTERNAL_ERROR;
goto_bad;
goto bad;
}
}
@@ -5056,23 +5161,23 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
*/
if (writing || activating) {
if (!(vg->status & LVM_WRITE)) {
log_error("Volume group %s is read-only", vg->name);
log_error("Volume group %s is read-only.", vg->name);
failure |= FAILED_READ_ONLY;
goto_bad;
goto bad;
}
if (!cmd->handles_missing_pvs && (missing_pv_dev || missing_pv_flag)) {
log_error("Cannot change VG %s while PVs are missing.", vg->name);
log_error("See vgreduce --removemissing and vgextend --restoremissing.");
failure |= FAILED_NOT_ENABLED;
goto_bad;
goto bad;
}
}
if (writing && !cmd->handles_unknown_segments && vg_has_unknown_segments(vg)) {
log_error("Cannot change VG %s with unknown segments in it!", vg->name);
failure |= FAILED_NOT_ENABLED; /* FIXME new failure code here? */
goto_bad;
goto bad;
}
/*
@@ -5087,31 +5192,31 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (dm_pool_locked(vg->vgmem)) {
/* FIXME: can this happen? */
log_warn("WARNING: vg_read no vg copy: pool locked");
log_warn("WARNING: vg_read no vg copy: pool locked.");
goto out;
}
if (vg->vg_committed) {
/* FIXME: can this happen? */
log_warn("WARNING: vg_read no vg copy: copy exists");
log_warn("WARNING: vg_read no vg copy: copy exists.");
release_vg(vg->vg_committed);
vg->vg_committed = NULL;
}
if (vg->vg_precommitted) {
/* FIXME: can this happen? */
log_warn("WARNING: vg_read no vg copy: pre copy exists");
log_warn("WARNING: vg_read no vg copy: pre copy exists.");
release_vg(vg->vg_precommitted);
vg->vg_precommitted = NULL;
}
if (!(cft = export_vg_to_config_tree(vg))) {
log_warn("WARNING: vg_read no vg copy: copy export failed");
log_warn("WARNING: vg_read no vg copy: copy export failed.");
goto out;
}
if (!(vg->vg_committed = import_vg_from_config_tree(cft, vg->fid)))
log_warn("WARNING: vg_read no vg copy: copy import failed");
if (!(vg->vg_committed = import_vg_from_config_tree(cmd, vg->fid, cft)))
log_warn("WARNING: vg_read no vg copy: copy import failed.");
dm_config_destroy(cft);
} else {

View File

@@ -187,6 +187,8 @@ struct metadata_area {
void *metadata_locn;
uint32_t status;
uint64_t header_start; /* mda_header.start */
uint64_t scan_text_offset; /* rlocn->offset seen during scan */
uint32_t scan_text_checksum; /* rlocn->checksum seen during scan */
int mda_num;
uint32_t bad_fields; /* BAD_MDA_ flags are set to indicate errors found when reading */
uint32_t ignore_bad_fields; /* BAD_MDA_ flags are set to indicate errors to ignore */
@@ -475,8 +477,10 @@ void lv_calculate_readahead(const struct logical_volume *lv, uint32_t *read_ahea
* For internal metadata caching.
*/
struct dm_config_tree *export_vg_to_config_tree(struct volume_group *vg);
struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft,
struct format_instance *fid);
struct volume_group *import_vg_from_config_tree(struct cmd_context *cmd,
struct format_instance *fid,
const struct dm_config_tree *cft);
struct volume_group *vg_from_config_tree(struct cmd_context *cmd, const struct dm_config_tree *cft);
/*
* Mirroring functions

View File

@@ -266,7 +266,6 @@ static int _write_log_header(struct cmd_context *cmd, struct logical_volume *lv)
dev_set_last_byte(dev, sizeof(log_header));
if (!dev_write_bytes(dev, UINT64_C(0), sizeof(log_header), &log_header)) {
dev_unset_last_byte(dev);
log_error("Failed to write log header to %s.", name);
return 0;
}

View File

@@ -67,6 +67,7 @@ struct dev_manager;
#define SEG_RAID6_N_6 (1ULL << 35)
#define SEG_RAID6 SEG_RAID6_ZR
#define SEG_WRITECACHE (1ULL << 36)
#define SEG_INTEGRITY (1ULL << 37)
#define SEG_STRIPED_TARGET (1ULL << 39)
#define SEG_LINEAR_TARGET (1ULL << 40)
@@ -84,6 +85,7 @@ struct dev_manager;
#define SEG_TYPE_NAME_CACHE "cache"
#define SEG_TYPE_NAME_CACHE_POOL "cache-pool"
#define SEG_TYPE_NAME_WRITECACHE "writecache"
#define SEG_TYPE_NAME_INTEGRITY "integrity"
#define SEG_TYPE_NAME_ERROR "error"
#define SEG_TYPE_NAME_FREE "free"
#define SEG_TYPE_NAME_ZERO "zero"
@@ -117,6 +119,7 @@ struct dev_manager;
#define segtype_is_cache(segtype) ((segtype)->flags & SEG_CACHE ? 1 : 0)
#define segtype_is_cache_pool(segtype) ((segtype)->flags & SEG_CACHE_POOL ? 1 : 0)
#define segtype_is_writecache(segtype) ((segtype)->flags & SEG_WRITECACHE ? 1 : 0)
#define segtype_is_integrity(segtype) ((segtype)->flags & SEG_INTEGRITY ? 1 : 0)
#define segtype_is_mirrored(segtype) ((segtype)->flags & SEG_AREAS_MIRRORED ? 1 : 0)
#define segtype_is_mirror(segtype) ((segtype)->flags & SEG_MIRROR ? 1 : 0)
#define segtype_is_pool(segtype) ((segtype)->flags & (SEG_CACHE_POOL | SEG_THIN_POOL) ? 1 : 0)
@@ -179,6 +182,7 @@ struct dev_manager;
#define seg_is_cache(seg) segtype_is_cache((seg)->segtype)
#define seg_is_cache_pool(seg) segtype_is_cache_pool((seg)->segtype)
#define seg_is_writecache(seg) segtype_is_writecache((seg)->segtype)
#define seg_is_integrity(seg) segtype_is_integrity((seg)->segtype)
#define seg_is_used_cache_pool(seg) (seg_is_cache_pool(seg) && (!dm_list_empty(&(seg->lv)->segs_using_this_lv)))
#define seg_is_linear(seg) (seg_is_striped(seg) && ((seg)->area_count == 1))
#define seg_is_mirror(seg) segtype_is_mirror((seg)->segtype)
@@ -347,6 +351,8 @@ int init_vdo_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
int init_writecache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
int init_integrity_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
#define CACHE_FEATURE_POLICY_MQ (1U << 0)
#define CACHE_FEATURE_POLICY_SMQ (1U << 1)
#define CACHE_FEATURE_METADATA2 (1U << 2)

View File

@@ -503,11 +503,11 @@ int update_pool_lv(struct logical_volume *lv, int activate)
* which Node has pool active.
*/
if (!activate_lv(lv->vg->cmd, lv)) {
init_dmeventd_monitor(monitored);
(void) init_dmeventd_monitor(monitored);
return_0;
}
if (!lv_is_active(lv)) {
init_dmeventd_monitor(monitored);
(void) init_dmeventd_monitor(monitored);
log_error("Cannot activate thin pool %s, perhaps skipped in lvm.conf volume_list?",
display_lvname(lv));
return 0;

View File

@@ -21,8 +21,6 @@
#include "lib/metadata/segtype.h"
#include "lib/activate/activate.h"
#include "lib/config/defaults.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/misc/lvm-signal.h"
#include "lib/activate/dev_manager.h"
int lv_is_writecache_origin(const struct logical_volume *lv)

View File

@@ -164,7 +164,7 @@ static int _do_write_priority_flock(const char *file, int *fd, int operation, ui
strcpy(file_aux, file);
strcat(file_aux, AUX_LOCK_SUFFIX);
if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, nonblock))) {
if (operation == LOCK_EX) {
r = _do_flock(file, fd, operation, nonblock);
_undo_flock(file_aux, fd_aux);

View File

@@ -166,7 +166,9 @@ static const char *_lvname_has_reserved_component_string(const char *lvname)
"_rmeta",
"_tdata",
"_tmeta",
"_vdata"
"_vdata",
"_imeta",
"_iorig"
};
unsigned i;
@@ -252,10 +254,12 @@ char *build_dm_uuid(struct dm_pool *mem, const struct logical_volume *lv,
/* Suffixes used here MUST match lib/activate/dev_manager.c */
layer = lv_is_cache_origin(lv) ? "real" :
lv_is_writecache_origin(lv) ? "real" :
lv_is_integrity_origin(lv) ? "real" :
(lv_is_cache(lv) && lv_is_pending_delete(lv)) ? "real" :
lv_is_cache_pool_data(lv) ? "cdata" :
lv_is_cache_pool_metadata(lv) ? "cmeta" :
lv_is_cache_vol(lv) ? "cvol" :
lv_is_integrity_metadata(lv) ? "imeta" :
// FIXME: dm-tree needs fixes for mirrors/raids
//lv_is_mirror_image(lv) ? "mimage" :
//lv_is_mirror_log(lv) ? "mlog" :

View File

@@ -240,6 +240,26 @@ static int _raid_text_export(const struct lv_segment *seg, struct formatter *f)
return _raid_text_export_raid(seg, f);
}
static int _image_integrity_data_sectors(struct lv_segment *seg, uint64_t *data_sectors)
{
struct logical_volume *lv_image;
struct lv_segment *seg_image;
int s;
*data_sectors = 0;
for (s = 0; s < seg->area_count; s++) {
lv_image = seg_lv(seg, s);
if (lv_is_integrity(lv_image)) {
seg_image = first_seg(lv_image);
*data_sectors = seg_image->integrity_data_sectors;
return 1;
}
}
return 1;
}
static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
struct dm_pool *mem __attribute__((unused)),
struct cmd_context *cmd __attribute__((unused)),
@@ -256,6 +276,7 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
uint64_t writemostly[RAID_BITMAP_SIZE] = { 0 };
struct dm_tree_node_raid_params_v2 params = { 0 };
unsigned attrs;
uint64_t integrity_data_sectors = 0;
if (seg_is_raid4(seg)) {
if (!_raid_target_present(cmd, NULL, &attrs) ||
@@ -351,6 +372,21 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
params.stripe_size = seg->stripe_size;
params.flags = flags;
/*
* The len needs to be reduced only when the raid images are using
* integrity with internal metadata, which reduces the usable
* size of the image, and needs to be reflected in the dm table
* that's loaded. (internal metadata is not currently allowed
* with raid, so this is unused.)
*/
if (!_image_integrity_data_sectors(seg, &integrity_data_sectors))
return_0;
if (integrity_data_sectors) {
log_debug("Reducing raid size from %llu to integrity_data_sectors %llu",
(unsigned long long)len, (unsigned long long)integrity_data_sectors);
len = integrity_data_sectors;
}
if (!dm_tree_node_add_raid_target_with_params_v2(node, len, &params))
return_0;

View File

@@ -214,7 +214,7 @@ static int _target_present(struct cmd_context *cmd,
if (!_writecache_checked) {
_writecache_checked = 1;
_writecache_present = target_present(cmd, TARGET_NAME_WRITECACHE, 0);
_writecache_present = target_present(cmd, TARGET_NAME_WRITECACHE, 1);
}
return _writecache_present;

View File

@@ -656,7 +656,8 @@ void daemon_start(daemon_state s)
failed = 1; /* FD out of available selectable set */
sigfillset(&new_set);
sigprocmask(SIG_SETMASK, NULL, &old_set);
if (sigprocmask(SIG_SETMASK, NULL, &old_set))
perror("sigprocmask error");
while (!failed) {
_reset_timeout(s);

View File

@@ -1238,8 +1238,12 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
}
/* FIXME Until resume ioctl supplies name, use dev_name for readahead */
if (DEV_NAME(dmt) && (dmt->type != DM_DEVICE_RESUME || dmt->minor < 0 ||
dmt->major < 0))
if (DEV_NAME(dmt) &&
(((dmt->type != DM_DEVICE_RESUME) &&
(dmt->type != DM_DEVICE_RELOAD)) ||
(dmt->minor < 0) || (dmt->major < 0)))
/* When RESUME or RELOAD sets maj:min and dev_name, use just maj:min,
* passed dev_name is useful for better error/debug messages */
strncpy(dmi->name, DEV_NAME(dmt), sizeof(dmi->name));
if (DEV_UUID(dmt))
@@ -1425,6 +1429,7 @@ static int _check_uevent_generated(struct dm_ioctl *dmi)
static int _create_and_load_v4(struct dm_task *dmt)
{
struct dm_info info;
struct dm_task *task;
int r;
uint32_t cookie;
@@ -1455,6 +1460,9 @@ static int _create_and_load_v4(struct dm_task *dmt)
if (!dm_task_run(task))
goto_bad;
if (!dm_task_get_info(task, &info) || !info.exists)
goto_bad;
dm_task_destroy(task);
/* Next load the table */
@@ -1472,6 +1480,8 @@ static int _create_and_load_v4(struct dm_task *dmt)
goto revert;
}
task->major = info.major;
task->minor = info.minor;
task->read_only = dmt->read_only;
task->head = dmt->head;
task->tail = dmt->tail;
@@ -1904,7 +1914,8 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
log_verbose("device-mapper: %s ioctl on %s %s%s%.0d%s%.0d%s%s "
"failed: %s",
_cmd_data_v4[dmt->type].name,
dmi->name, dmi->uuid,
dmi->name[0] ? dmi->name : DEV_NAME(dmt) ? : "",
dmi->uuid[0] ? dmi->uuid : DEV_UUID(dmt) ? : "",
dmt->major > 0 ? "(" : "",
dmt->major > 0 ? dmt->major : 0,
dmt->major > 0 ? ":" : "",
@@ -1916,7 +1927,8 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
log_error("device-mapper: %s ioctl on %s %s%s%.0d%s%.0d%s%s "
"failed: %s",
_cmd_data_v4[dmt->type].name,
dmi->name, dmi->uuid,
dmi->name[0] ? dmi->name : DEV_NAME(dmt) ? : "",
dmi->uuid[0] ? dmi->uuid : DEV_UUID(dmt) ? : "",
dmt->major > 0 ? "(" : "",
dmt->major > 0 ? dmt->major : 0,
dmt->major > 0 ? ":" : "",

View File

@@ -1,8 +1,129 @@
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.)
.SS Settings
The --settings option controls or overrides certain dump or repair
behaviors. All offset and size values in settings are in bytes (units are
not recognized.) These settings are subject to change.
.B mda_num=1|2
.br
Select which metadata area should be used. By default the first metadata
area (1) is used. mda1 is always located at offset 4096. mda2, at the
end of the device, often does not exist (it's not created by default.) If
mda1 is erased, mda2, if it exists, will often still have metadata.
\fBmetadata_offset=\fP\fIbytes\fP
.br
Select metadata text at this offset. Use with metadata_search to
print/save one instance of metadata text.
\fBmda_offset=\fP\fIbytes\fP \fBmda_size=\fP\fIbytes\fP
.br
Refers to a metadata area (mda) location and size. An mda includes an
mda_header and circular text buffer. Setting this forces metadata_search
look for metadata in the given area instead of the normal locations. When
set to zero with repair, it indicates no metadata areas should exist.
\fBmda2_offset=\fP\fIbytes\fP \fBmda2_size=\fP\fIbytes\fP
.br
When repairing a pv_header, this forces a specific offset and size for
mda2 that should be recorded in the pv_header.
\fBpv_uuid=\fP\fIuuid\fP
.br
Specify the PV UUID of the device being repaired. When not specified,
repair will attempt to determine the correct PV UUID by matching a device
name in the metadata.
\fBdevice_size=\fP\fIbytes\fP
.br
\fBdata_offset=\fP\fIbytes\fP
.br
When repairing a pv_header, the device_size, data_offset, and pvid can all
be specified directly, in which case these values are not taken from a
metadata file (where they usually come from), and the metadata file can be
omitted. data_offset is the starting location of the first physical
extent (data), which follows the first metadata area.

View File

@@ -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.

View File

@@ -315,7 +315,7 @@ lib/dm-version-expected: $(top_srcdir)/VERSION_DM .lib-dir-stamp
CMDS = lvm $(shell cat $(top_builddir)/tools/.commands 2>/dev/null)
LIB = $(addprefix lib/, $(LIB_SECURETEST) $(LIB_DMSECURETEST) $(LIB_SHARED) $(LIB_LOCAL) $(LIB_NOT) $(LIB_LINK_NOT) $(LIB_FLAVOURS))
.tests-stamp: $(ALL) $(LIB) $(SUBDIRS) lib/version-expected lib/dm-version-expected unit-test
.tests-stamp: .lib-dir-stamp $(ALL) $(LIB) $(SUBDIRS) lib/version-expected lib/dm-version-expected unit-test
@echo " [TEST-STAMP]"
@if test "$(srcdir)" != . ; then \
echo "Linking tests to builddir."; \

View File

@@ -382,7 +382,7 @@ teardown_devs_prefixed() {
while :; do
local sortby="name"
local num_devs=0
local progress=0
# HACK: sort also by minors - so we try to close 'possibly later' created device first
test "$i" = 0 || sortby="-minor"
@@ -392,23 +392,23 @@ teardown_devs_prefixed() {
DM_NAME=${dm##DM_NAME=}
DM_NAME=${DM_NAME%%;DM_OPEN*}
DM_OPEN=${dm##*;DM_OPEN=}
local force="-f"
if test "$i" = 0; then
if test "$once" = 1 ; then
once=0
echo "## removing stray mapped devices with names beginning with $prefix: "
fi
test "$DM_OPEN" = 0 || break # stop loop with 1st. opened device
dmsetup remove "$DM_NAME" --mangle none || true # &>/dev/null || touch REMOVE_FAILED &
else
dmsetup remove -f "$DM_NAME" --mangle none || true
force=""
fi
num_devs=$(( num_devs + 1 ))
# Succesfull 'remove' signals progress
dmsetup remove $force "$DM_NAME" --mangle none && progress=1
done
test "$i" = 0 || break
test "$num_devs" -gt 0 || break
test "$progress" = 1 || break
udev_wait
wait
@@ -520,7 +520,7 @@ teardown() {
if test ! -f SKIP_THIS_TEST ; then
# Evaluate left devices only for non-skipped tests
TEST_LEAKED_DEVICES=$(dmsetup table | grep "$PREFIX" | grep -v "${PREFIX}pv") || true
TEST_LEAKED_DEVICES=$(dmsetup table | grep "$PREFIX" | grep -Ev "${PREFIX}(pv|[0-9])") || true
fi
kill_tagged_processes
@@ -874,7 +874,11 @@ prepare_devs() {
wait
finish_udev_transaction
if test -f CREATE_FAILED -a -n "$LVM_TEST_BACKING_DEVICE"; then
if test -f CREATE_FAILED ; then
if test -z "$LVM_TEST_BACKING_DEVICE"; then
echo "failed"
return 1
fi
LVM_TEST_BACKING_DEVICE=
rm -f BACKING_DEV CREATE_FAILED
prepare_devs "$@"

View File

@@ -48,6 +48,7 @@ int main (int argc, char *argv[])
char aes[] = "434r0pono02pn68sson9268222p3789q703sr62427o78o308518o3228s6n2122";
const char *device = (argc > 1) ? argv[1] : "/dev/loop0"; /* device for use */
const char *devname = (argc > 2) ? argv[2] : "test-secure"; /* name of dm device */
const char *cipher = (argc > 3) ? argv[3] : "aes-xts-plain64"; /* name of dm device */
uint32_t cookie = 0;
char table[300];
struct dm_task *dmt;
@@ -63,7 +64,7 @@ int main (int argc, char *argv[])
(void) dm_task_set_name(dmt, devname);
(void) dm_task_secure_data(dmt);
rot13(aes);
snprintf(table, sizeof(table), "aes-xts-plain64 %s 0 %s %u", aes, device, sz);
snprintf(table, sizeof(table), "%s %s 0 %s %u", cipher, aes, device, sz);
memset(aes, 0, sizeof(aes));
(void) dm_task_add_target(dmt, 0, sz, "crypt", table);
memset(table, 0, sizeof(table));
@@ -71,10 +72,9 @@ int main (int argc, char *argv[])
(void) dm_task_set_cookie(dmt, &cookie, DM_UDEV_DISABLE_LIBRARY_FALLBACK);
(void) dm_task_run(dmt);
(void) dm_task_destroy(dmt);
(void) dm_udev_wait(cookie); /* Finish udev processing */
}
dm_task_update_nodes();
/* At this point there should be no memory trace from a secure table line */
#ifdef SLEEP

View File

@@ -45,7 +45,9 @@ sleep .5
dmsetup status "$DMTEST"
# generate core file for running&sleeping binary
gcore "$PID"
gcore "$PID" | tee out
# check we capture core while dmsecuretest was already sleeping
grep "nanosleep" out
kill "$PID" || true
wait
@@ -55,7 +57,7 @@ cat cmdout
not grep "$SECURE" "core.$PID" || {
## cp "core.$PID" /dev/shm/core
rm -f "core.$PID"
dmsetup remove "$DMTEST"
should dmsetup remove "$DMTEST" # go around weird bugs
die "!!! Secure string $SECURE found present in core.$PID !!!"
}
rm -f "core.$PID"

View File

@@ -368,7 +368,7 @@ aux enable_dev "$dev2"
aux aux udev_wait
cat /proc/mdstat
# for some reason enabling dev2 starts an odd md dev
mdadm --stop "$mddev" || true
mdadm --stop $(lsblk -al -o NAME --noheadings "$dev2" | grep '^md') || true
cat /proc/mdstat
aux wipefs_a "$dev1" || true
aux wipefs_a "$dev2" || true
@@ -433,7 +433,7 @@ aux enable_dev "$dev2"
aux aux udev_wait
cat /proc/mdstat
# for some reason enabling dev2 starts an odd md dev
mdadm --stop "$mddev" || true
mdadm --stop $(lsblk -al -o NAME --noheadings "$dev2" | grep '^md') || true
cat /proc/mdstat
aux wipefs_a "$dev1" || true
aux wipefs_a "$dev2" || true

View File

@@ -415,7 +415,7 @@ aux enable_dev "$dev2"
aux udev_wait
cat /proc/mdstat
# for some reason enabling dev2 starts an odd md dev
mdadm --stop "$mddev" || true
mdadm --stop $(lsblk -al -o NAME --noheadings "$dev2" | grep '^md') || true
cat /proc/mdstat
aux wipefs_a "$dev1" || true
aux wipefs_a "$dev2" || true
@@ -479,7 +479,7 @@ aux enable_dev "$dev2"
aux udev_wait
cat /proc/mdstat
# for some reason enabling dev2 starts an odd md dev
mdadm --stop "$mddev" || true
mdadm --stop $(lsblk -al -o NAME --noheadings "$dev2" | grep '^md') || true
cat /proc/mdstat
aux wipefs_a "$dev1" || true
aux wipefs_a "$dev2" || true
@@ -617,7 +617,7 @@ aux enable_dev "$dev4"
aux udev_wait
cat /proc/mdstat
# for some reason enabling dev2 starts an odd md dev
mdadm --stop "$mddev" || true
mdadm --stop $(lsblk -al -o NAME --noheadings "$dev2" | grep '^md') || true
cat /proc/mdstat
aux wipefs_a "$dev1" || true
aux wipefs_a "$dev2" || true

View File

@@ -52,15 +52,17 @@ pvs
grep -v -E "$dev1|$dev2" $HINTS > tmptest
not grep scan: tmptest
# test that 'pvs' submits only two reads, one for each PV in hints
# test that 'pvs' submits only three reads, one for each PV in hints
# for initial scan, and one more in vg_read rescan check
if [ -e "/usr/bin/strace" ]; then
strace -e io_submit pvs 2>&1|tee tmptest
test "$(grep io_submit tmptest | wc -l)" -eq 2
test "$(grep io_submit tmptest | wc -l)" -eq 3
# test that 'pvs -a' submits six reads, one for each device
# test that 'pvs -a' submits seven reads, one for each device,
# and one more in vg_read rescan check
strace -e io_submit pvs -a 2>&1|tee tmptest
test "$(grep io_submit tmptest | wc -l)" -eq 6
test "$(grep io_submit tmptest | wc -l)" -eq 7
fi
#

167
test/shell/integrity.sh Normal file
View File

@@ -0,0 +1,167 @@
#!/usr/bin/env bash
# Copyright (C) 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
# Test writecache usage
SKIP_WITH_LVMPOLLD=1
. lib/inittest
# aux have_integrity 1 0 0 || skip
which mkfs.xfs || skip
mnt="mnt"
mkdir -p $mnt
aux prepare_devs 4 64
for i in `seq 1 16384`; do echo -n "A" >> fileA; done
for i in `seq 1 16384`; do echo -n "B" >> fileB; done
for i in `seq 1 16384`; do echo -n "C" >> fileC; done
_prepare_vg() {
# zero devs so we are sure to find the correct file data
# on the underlying devs when corrupting it
dd if=/dev/zero of="$dev1" || true
dd if=/dev/zero of="$dev2" || true
dd if=/dev/zero of="$dev3" || true
dd if=/dev/zero of="$dev4" || true
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3" "$dev4"
}
_test_fs_with_error() {
mkfs.xfs -f "$DM_DEV_DIR/$vg/$lv1"
mount "$DM_DEV_DIR/$vg/$lv1" $mnt
# add original data
cp fileA $mnt
cp fileB $mnt
cp fileC $mnt
umount $mnt
lvchange -an $vg/$lv1
# corrupt the original data on the underying dev
# flip one bit in fileB, changing a 0x42 to 0x43
# the bit is changed in the last 4096 byte block
# of the file, so when reading back the file we
# will get the first three 4096 byte blocks, for
# a total of 12288 bytes before getting an error
# on the last 4096 byte block.
xxd "$dev1" > dev1.txt
tac dev1.txt > dev1.rev
sed -e '0,/4242 4242 4242 4242 4242 4242 4242 4242/ s/4242 4242 4242 4242 4242 4242 4242 4242/4242 4242 4242 4242 4242 4242 4242 4243/' dev1.rev > dev1.rev.bad
tac dev1.rev.bad > dev1.bad
xxd -r dev1.bad > "$dev1"
rm dev1.txt dev1.rev dev1.rev.bad dev1.bad
lvchange -ay $vg/$lv1
mount "$DM_DEV_DIR/$vg/$lv1" $mnt
# read complete fileA which was not corrupted
dd if=$mnt/fileA of=tmp bs=1k
ls -l tmp
stat -c %s tmp
diff fileA tmp
rm tmp
# read partial fileB which was corrupted
not dd if=$mnt/fileB of=tmp bs=1k
ls -l tmp
stat -c %s tmp | grep 12288
not diff fileB tmp
rm tmp
umount $mnt
}
_test_fs_with_raid1() {
mkfs.xfs -f "$DM_DEV_DIR/$vg/$lv1"
mount "$DM_DEV_DIR/$vg/$lv1" $mnt
# add original data
cp fileA $mnt
cp fileB $mnt
cp fileC $mnt
umount $mnt
lvchange -an $vg/$lv1
xxd "$dev1" > dev1.txt
tac dev1.txt > dev1.rev
sed -e '0,/4242 4242 4242 4242 4242 4242 4242 4242/ s/4242 4242 4242 4242 4242 4242 4242 4242/4242 4242 4242 4242 4242 4242 4242 4243/' dev1.rev > dev1.rev.bad
tac dev1.rev.bad > dev1.bad
xxd -r dev1.bad > "$dev1"
rm dev1.txt dev1.rev dev1.rev.bad dev1.bad
lvchange -ay $vg/$lv1
mount "$DM_DEV_DIR/$vg/$lv1" $mnt
# read complete fileA which was not corrupted
dd if=$mnt/fileA of=tmp bs=1k
ls -l tmp
stat -c %s tmp | grep 16384
diff fileA tmp
rm tmp
# read complete fileB, corruption is corrected by raid1
dd if=$mnt/fileB of=tmp bs=1k
ls -l tmp
stat -c %s tmp | grep 16384
diff fileB tmp
rm tmp
umount $mnt
}
_prepare_vg
lvcreate --integrity y -n $lv1 -l 8 $vg "$dev1"
_test_fs_with_error
lvchange -an $vg/$lv1
lvconvert --integrity n $vg/$lv1
lvremove $vg/$lv1
vgremove -ff $vg
_prepare_vg
lvcreate -y --integrity internal -n $lv1 -l 8 $vg "$dev1"
_test_fs_with_error
lvchange -an $vg/$lv1
not lvconvert --integrity n $vg/$lv1
lvremove $vg/$lv1
vgremove -ff $vg
_prepare_vg
lvcreate -an -n meta -L4M $vg "$dev2"
lvcreate --integrity y --integritymetadata meta -n $lv1 -l 8 $vg "$dev1"
_test_fs_with_error
lvchange -an $vg/$lv1
lvconvert --integrity n $vg/$lv1
lvremove $vg/$lv1
vgremove -ff $vg
_prepare_vg
lvcreate --type raid1 -m1 --integrity y -n $lv1 -l 8 $vg
_test_fs_with_raid1
lvchange -an $vg/$lv1
lvconvert --integrity n $vg/$lv1
lvremove $vg/$lv1
vgremove -ff $vg
_prepare_vg
lvcreate --type raid1 -m2 --integrity y -n $lv1 -l 8 $vg
_test_fs_with_raid1
lvchange -an $vg/$lv1
lvconvert --integrity n $vg/$lv1
lvremove $vg/$lv1
vgremove -ff $vg

View File

@@ -56,7 +56,7 @@ mount "$DM_DEV_DIR/$vg/$lv1" "$mount_dir"
echo 3 >/proc/sys/vm/drop_caches
# FIXME: This is filling up ram disk. Use sane amount of data please! Rate limit the data written!
dd if=/dev/urandom of="$mount_dir/random" bs=1M count=50 conv=fdatasync
dd if=/dev/urandom of="$mount_dir/random" bs=1M count=4 conv=fdatasync
checksum_ "$mount_dir/random" >MD5
# FIXME: wait_for_sync - is this really testing anything under load?

View File

@@ -74,6 +74,8 @@ check active $vg mirror12
vgchange -a n $vg
aux enable_dev "$dev3"
aux disable_dev "$dev4"
dmsetup table
dmsetup info -c
vgchange -aey $vg
not vgck $vg

452
test/shell/pvck-repair.sh Normal file
View File

@@ -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

View File

@@ -213,12 +213,14 @@ arg(driverloaded_ARG, '\0', "driverloaded", bool_VAL, 0, 0,
"If set to no, the command will not attempt to use device-mapper.\n"
"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")
arg(dump_ARG, '\0', "dump", dumptype_VAL, 0, 0,
"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"
@@ -230,9 +232,6 @@ arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
"(Also see dm-thin-pool kernel module option no_space_timeout.)\n"
"See \\fBlvmthin\\fP(7) for more information.\n")
arg(file_long_ARG, '\0', "file", string_VAL, 0, 0,
"File name.\n")
arg(force_long_ARG, '\0', "force", 0, ARG_COUNTABLE, 0,
"Force metadata restore even with thin pool LVs.\n"
"Use with extreme caution. Most changes to thin metadata\n"
@@ -275,6 +274,18 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0,
"and \\fBdiff\\fP types include unsupported settings in their output by default,\n"
"all the other types ignore unsupported settings.\n")
arg(integrity_ARG, '\0', "integrity", integrity_VAL, 0, 0,
"Enable or disable integrity metadata for an LV.\n"
"\\fBy|external\\fP adds integrity with metadata stored on a separate, external LV.\n"
"\\fBinternal\\fP adds integrity with metadata interleaved with data\n"
"(cannot be removed.) Use \\fBn\\fP to remove integrity.\n")
arg(integritymetadata_ARG, '\0', "integritymetadata", lv_VAL, 0, 0,
"The name of an LV to hold integrity metadata.\n")
arg(integritysettings_ARG, '\0', "integritysettings", string_VAL, ARG_GROUPABLE, 0,
"Set dm-integrity parameters.\n")
arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
"By default the PV is labelled with an LVM2 identifier in its second\n"
"sector (sector 1). This lets you use a different sector near the\n"
@@ -462,6 +473,11 @@ 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"
"Combine multiple settings in quotes, or repeat the settings\n"
"option for each.\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"
@@ -543,9 +559,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"
@@ -1000,6 +1022,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"

View File

@@ -719,12 +719,12 @@ FLAGS: SECONDARY_SYNTAX
# and the LV type is known.
lvconvert --repair LV_cache_cachepool_mirror_raid_thinpool
OO: --usepolicies, --interval Number, --poolmetadataspare Bool, --file_long String, OO_LVCONVERT
OO: --usepolicies, --interval Number, --poolmetadataspare Bool, OO_LVCONVERT
OP: PV ...
ID: lvconvert_repair
DESC: Replace failed PVs in a raid or mirror LV.
DESC: Repair a thin pool.
DESC: Repair a cache pool or cache.
DESC: Repair a cache pool.
RULE: all not lv_is_locked lv_is_pvmove
RULE: --poolmetadataspare and LV_cache LV_cachepool LV_thinpool
@@ -757,6 +757,20 @@ FLAGS: SECONDARY_SYNTAX
---
lvconvert --type integrity LV_linear_striped
OO: OO_LVCONVERT, --integrity IntegrityType, --integritymetadata LV, --integritysettings String
OP: PV ...
ID: lvconvert_integrity
DESC: Convert LV to type integrity.
lvconvert --integrity IntegrityType LV_linear_striped_raid_integrity
OO: OO_LVCONVERT, --integritymetadata LV, --integritysettings String
OP: PV ...
ID: lvconvert_integrity
DESC: Add or remove integrity to LV, or to an LV's raid images.
---
# --extents is not specified; it's an automatic alternative for --size
OO_LVCREATE: --addtag Tag, --alloc Alloc, --autobackup Bool, --activate Active,
@@ -870,7 +884,8 @@ DESC: Create a raid1 or mirror LV (infers --type raid1|mirror).
# R9,R10,R11,R12 (--type raid with any use of --stripes/--mirrors)
lvcreate --type raid --size SizeMB VG
OO: --mirrors PNumber, --stripes Number, --stripesize SizeKB,
--regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB, OO_LVCREATE
--regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
--integrity IntegrityType, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_raid_any
DESC: Create a raid LV (a specific raid level must be used, e.g. raid1).
@@ -1269,6 +1284,20 @@ FLAGS: SECONDARY_SYNTAX
---
lvcreate --type integrity --size SizeMB VG
OO: --integrity IntegrityType, --integritymetadata LV, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_integrity
DESC: Create a LV with integrity.
lvcreate --integrity IntegrityType --size SizeMB VG
OO: --type integrity, --integritymetadata LV, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_integrity
DESC: Create a LV with integrity (infers --type integrity).
---
lvdisplay
OO: --aligned, --all, --binary, --colon, --columns,
--configreport ConfigReport, --foreign, --history, --ignorelockingfailure,
@@ -1436,10 +1465,21 @@ OO: --labelsector Number
ID: pvck_general
DESC: Check for metadata on a device
pvck --dump String PV
OO: --file String, --pvmetadatacopies MetadataCopiesPV, --labelsector Number
pvck --dump DumpType PV
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
---

View File

@@ -122,6 +122,9 @@ 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; }
static inline int dumptype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
static inline int integritytype_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

View File

@@ -34,5 +34,6 @@ lvt(raid10_LVT, "raid10", NULL)
lvt(error_LVT, "error", NULL)
lvt(zero_LVT, "zero", NULL)
lvt(writecache_LVT, "writecache", NULL)
lvt(integrity_LVT, "integrity", NULL)
lvt(LVT_COUNT, "", NULL)

View File

@@ -2480,339 +2480,10 @@ deactivate_pmslv:
return 1;
}
/*
* Use cache_repair to read metadata from cachevol_lv and
* write repaired metadata to new file.
/* TODO: lots of similar code with thinpool repair
* investigate possible better code sharing...
*/
static int _run_cache_repair_from_cachevol(struct cmd_context *cmd,
struct logical_volume *cachevol_lv,
const char *meta_file)
{
char cachevol_lv_path[PATH_MAX];
char *cachevol_lv_name;
const char *dmdir = dm_dir();
const char *cache_repair = find_config_tree_str_allow_empty(cmd, global_cache_repair_executable_CFG, NULL);
const struct dm_config_node *cn;
const struct dm_config_value *cv;
const char *argv[MAX_PDATA_ARGS + 7]; /* Max supported args */
int args = 0;
int status;
if (!cache_repair || !cache_repair[0]) {
log_error("No cache_repair command set in lvm.conf. Repair is disabled.");
return 0;
}
if (!(cachevol_lv_name = dm_build_dm_name(cmd->mem, cachevol_lv->vg->name, cachevol_lv->name, NULL))) {
log_error("Failed to create dm name for %s.", display_lvname(cachevol_lv));
return 0;
}
if (dm_snprintf(cachevol_lv_path, sizeof(cachevol_lv_path), "%s/%s", dmdir, cachevol_lv_name) < 0) {
log_error("Failed to create path for %s.", display_lvname(cachevol_lv));
return 0;
}
if (!(cn = find_config_tree_array(cmd, global_cache_repair_options_CFG, NULL))) {
log_error(INTERNAL_ERROR "Unable to find configuration for global/cache_repair_options");
return 0;
}
for (cv = cn->v; cv && args < MAX_PDATA_ARGS; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_error("Invalid string in config file: "
"global/cache_repair_options");
return 0;
}
argv[++args] = cv->v.str;
}
if (args >= MAX_PDATA_ARGS) {
log_error("Too many cache_repair_options set in lvm.conf.");
return 0;
}
argv[0] = cache_repair;
argv[++args] = "-i";
argv[++args] = cachevol_lv_path;
argv[++args] = "-o";
argv[++args] = meta_file;
argv[++args] = NULL;
if (!exec_cmd(cmd, (const char * const *)argv, &status, 1)) {
log_error("The cache_repair command failed (status %d).", status);
return 0;
}
log_print("cache_repair of metadata completed from %s to %s", display_lvname(cachevol_lv), meta_file);
return 1;
}
static int _run_cache_writeback_from_cachevol(struct cmd_context *cmd,
struct logical_volume *cache_lv,
struct logical_volume *cachevol_lv,
const char *meta_file,
uint64_t data_offset_bytes)
{
char orig_path[PATH_MAX];
char data_path[PATH_MAX];
char *orig_name;
char *data_name;
const char *dmdir = dm_dir();
const char *cache_writeback = "cache_writeback";
const char *argv[MAX_PDATA_ARGS + 7]; /* Max supported args */
char offset_str[16];
int args = 0;
int status;
if (!(orig_name = dm_build_dm_name(cmd->mem, cache_lv->vg->name, cache_lv->name, NULL))) {
log_error("Failed to create dm name for %s.", display_lvname(cache_lv));
}
if (!(data_name = dm_build_dm_name(cmd->mem, cachevol_lv->vg->name, cachevol_lv->name, NULL))) {
log_error("Failed to create dm name for %s.", display_lvname(cachevol_lv));
}
if (dm_snprintf(orig_path, sizeof(orig_path), "%s/%s", dmdir, orig_name) < 0) {
log_error("Failed to create path for %s.", display_lvname(cache_lv));
return 0;
}
/* _off suffix is temp hack to allow manual offset */
if (dm_snprintf(data_path, sizeof(data_path), "%s/%s_off", dmdir, data_name) < 0) {
log_error("Failed to create path for %s.", display_lvname(cachevol_lv));
return 0;
}
if (dm_snprintf(offset_str, sizeof(offset_str), "%llu",
(unsigned long long)data_offset_bytes) < 0)
return_0;
/* let the user set up an _off dm wrapper */
if (yes_no_prompt("Done creating wrapper %s with offset %u sectors? [y/n]: ",
data_path, (uint32_t)data_offset_bytes/512) == 'n')
return_0;
argv[0] = cache_writeback;
argv[++args] = "--metadata-device";
argv[++args] = meta_file;
argv[++args] = "--origin-device";
argv[++args] = orig_path;
argv[++args] = "--fast-device";
argv[++args] = data_path;
/*
argv[++args] = "--fast-device-offset";
argv[++args] = offset_str;
*/
argv[++args] = "--no-metadata-update";
argv[++args] = NULL;
if (!exec_cmd(cmd, (const char * const *)argv, &status, 1)) {
log_error("The cache_writeback command failed (status %d).", status);
return 0;
}
log_print("cache_writeback of data completed from %s to %s",
display_lvname(cachevol_lv), display_lvname(cache_lv));
return 1;
}
static int _lvconvert_cache_repair_cachevol(struct cmd_context *cmd,
struct logical_volume *cache_lv,
struct dm_list *pvh)
{
char filepath[PATH_MAX];
char zero_sector_buf[512];
struct volume_group *vg = cache_lv->vg;
struct logical_volume *cachevol_lv;
struct lv_segment *cache_seg, *cachevol_seg;
uint64_t metadata_len_sectors, data_offset_bytes;
uint64_t orig_cachevol_seglen;
const char *meta_file = NULL;
FILE *fp;
uint32_t prealloc_sectors_written = 0;
int cache_mode;
int ret = 0;
memset(zero_sector_buf, 0, 512);
if (!(cache_seg = first_seg(cache_lv)))
return_0;
if (!(cachevol_lv = cache_seg->pool_lv))
return_0;
if (!lv_is_cache_vol(cachevol_lv))
return_0;
if (cache_seg->metadata_start) {
/* metadata is always at 0, but check in case it changes in future */
log_error("Cannot handle non-zero metadata start locations.");
return 0;
}
if (lv_is_active(cache_lv)) {
log_error("LV %s must not be active.", display_lvname(cache_lv));
return 0;
}
if (lv_is_active(cachevol_lv)) {
log_error("LV %s must not be active.", display_lvname(cachevol_lv));
return 0;
}
if (vg_is_shared(vg)) {
/* cache LV using the cachevol cannot be active elsewhere */
if (!lockd_lv(cmd, cache_lv, "ex", 0))
return_0;
/* cachevol cannot be active elsewhere. */
if (!lockd_lv(cmd, cachevol_lv, "ex", 0))
return_0;
}
metadata_len_sectors = cache_seg->metadata_len;
cache_mode = cache_seg->cache_mode;
/* Disable syncing the cache during split */
if (cache_mode != CACHE_MODE_WRITETHROUGH)
cache_seg->cache_mode = CACHE_MODE_WRITETHROUGH;
if (!_lvconvert_split_and_keep_cachevol(cmd, cache_lv, cachevol_lv)) {
log_error("Failed to detach cachevol, not repairing.");
return_0;
}
/*
* Note: following the spilt, cache_lv is no longer technically a cache
* and cachevol_lv is no longer technically a cachevol. The variable
* names represent their previous types.
*/
if (cache_mode == CACHE_MODE_WRITETHROUGH) {
log_print("No repair or writeback required for writethrough cache mode.");
return 1;
}
/*
* Prevent the active LVs from being exposed and used outside of lvm.
*/
cachevol_lv->status |= LV_TEMPORARY;
cache_lv->status |= LV_TEMPORARY;
/*
* Temporarily change the cachevol LV size to be only the
* metadata size, so cache_repair will only look at the
* metadata portion.
* seg->len and seg->metadata_len units are sectors
*/
cachevol_seg = first_seg(cachevol_lv);
orig_cachevol_seglen = cachevol_seg->len;
cachevol_seg->len = metadata_len_sectors / vg->extent_size;
if (!activate_lv(cmd, cachevol_lv)) {
log_error("Failed to activate LV %s for cache repair and writeback.", display_lvname(cachevol_lv));
goto out;
}
if (!sync_local_dev_names(cmd)) {
log_error("Failed to sync local devices before cache_repair.");
goto out;
}
/*
* cache_repair wants the destination file to be preallocated.
* FIXME: what's the relationship between the metadata size and
* the file size?
*/
if (arg_is_set(cmd, file_long_ARG))
meta_file = arg_str_value(cmd, file_long_ARG, NULL);
else {
snprintf(filepath, PATH_MAX, "/tmp/lvconvert-cache-repair-metadata-%d", getpid());
meta_file = filepath;
}
log_print("Allocating %u MB for metadata in %s",
(uint32_t)(metadata_len_sectors * 512 / 1048576), meta_file);
if (!(fp = fopen(meta_file, "w+"))) {
log_error("Failed to open file for metadata %s", meta_file);
meta_file = NULL;
goto out;
}
while (prealloc_sectors_written < metadata_len_sectors) {
if (!fwrite(zero_sector_buf, 512, 1, fp)) {
fclose(fp);
log_error("Failed to allocate space for metadata in %s", meta_file);
goto out;
}
prealloc_sectors_written++;
}
fflush(fp);
fclose(fp);
if (!_run_cache_repair_from_cachevol(cmd, cachevol_lv, meta_file)) {
log_error("cache_repair failed, cache data not written back to original volume.");
goto out;
}
/*
* Deactivate cachevol LV, and reactivate with correct size.
* Activate cache LV.
*/
if (!deactivate_lv(cmd, cachevol_lv)) {
log_error("Failed to deactivate LV %s", cachevol_lv->name);
goto out;
}
cachevol_seg->len = orig_cachevol_seglen;
if (!activate_lv(cmd, cachevol_lv)) {
log_error("Failed to activate LV %s for cache repair and writeback.", display_lvname(cachevol_lv));
goto out;
}
if (!activate_lv(cmd, cache_lv)) {
log_error("Failed to activate LV %s for cache repair and writeback.", display_lvname(cache_lv));
goto out;
}
if (!sync_local_dev_names(cmd)) {
log_error("Failed to sync local devices before cache_writeback.");
goto out;
}
data_offset_bytes = metadata_len_sectors * 512;
if (!_run_cache_writeback_from_cachevol(cmd, cache_lv, cachevol_lv, meta_file, data_offset_bytes)) {
log_error("cache_writeback failed");
goto_out;
}
log_print("cachevol writeback complete, %s now unused.", cachevol_lv->name);
ret = 1;
out:
if (!deactivate_lv(cmd, cache_lv))
log_error("Failed to deactivate LV %s", cache_lv->name);
if (!deactivate_lv(cmd, cachevol_lv))
log_error("Failed to deactivate LV %s", cachevol_lv->name);
if (meta_file)
unlink(meta_file);
if (!ret)
log_error("Cache data on %s not written back to original LV %s.",
display_lvname(cachevol_lv), display_lvname(cache_lv));
return ret;
}
static int _lvconvert_cache_repair_cachepool(struct cmd_context *cmd,
static int _lvconvert_cache_repair(struct cmd_context *cmd,
struct logical_volume *cache_lv,
struct dm_list *pvh, int poolmetadataspare)
{
@@ -2831,6 +2502,11 @@ static int _lvconvert_cache_repair_cachepool(struct cmd_context *cmd,
struct logical_volume *pmslv;
struct logical_volume *mlv;
if (lv_is_cache(cache_lv) && lv_is_cache_vol(first_seg(cache_lv)->pool_lv)) {
log_error("Manual repair required.");
return 0;
}
pool_lv = lv_is_cache_pool(cache_lv) ? cache_lv : first_seg(cache_lv)->pool_lv;
mlv = first_seg(pool_lv)->metadata_lv;
@@ -4093,7 +3769,7 @@ static int _lvconvert_repair_pvs(struct cmd_context *cmd, struct logical_volume
return ret ? ECMD_PROCESSED : ECMD_FAILED;
}
static int _lvconvert_repair_cache_or_thin(struct cmd_context *cmd, struct logical_volume *lv,
static int _lvconvert_repair_cachepool_thinpool(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle)
{
int poolmetadataspare = arg_int_value(cmd, poolmetadataspare_ARG, DEFAULT_POOL_METADATA_SPARE);
@@ -4114,13 +3790,8 @@ static int _lvconvert_repair_cache_or_thin(struct cmd_context *cmd, struct logic
if (!_lvconvert_thin_pool_repair(cmd, lv, use_pvh, poolmetadataspare))
return_ECMD_FAILED;
} else /* cache */ {
if (lv_is_cache(lv) && lv_is_cache_vol(first_seg(lv)->pool_lv)) {
if (!_lvconvert_cache_repair_cachevol(cmd, lv, use_pvh))
return_ECMD_FAILED;
} else {
if (!_lvconvert_cache_repair_cachepool(cmd, lv, use_pvh, poolmetadataspare))
return_ECMD_FAILED;
}
if (!_lvconvert_cache_repair(cmd, lv, use_pvh, poolmetadataspare))
return_ECMD_FAILED;
}
return ECMD_PROCESSED;
@@ -4132,7 +3803,7 @@ static int _lvconvert_repair_single(struct cmd_context *cmd, struct logical_volu
if (lv_is_thin_pool(lv) ||
lv_is_cache(lv) ||
lv_is_cache_pool(lv))
return _lvconvert_repair_cache_or_thin(cmd, lv, handle);
return _lvconvert_repair_cachepool_thinpool(cmd, lv, handle);
if (lv_is_raid(lv) || lv_is_mirror(lv))
return _lvconvert_repair_pvs(cmd, lv, handle);
@@ -4143,6 +3814,12 @@ static int _lvconvert_repair_single(struct cmd_context *cmd, struct logical_volu
return ECMD_FAILED;
}
/*
* FIXME: add option --repair-pvs to call _lvconvert_repair_pvs() directly,
* and option --repair-thinpool to call _lvconvert_repair_thinpool().
* and option --repair-cache to call _lvconvert_repair_cache().
* and option --repair-cachepool to call _lvconvert_repair_cachepool().
*/
int lvconvert_repair_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
@@ -6053,6 +5730,150 @@ int lvconvert_to_cache_with_cachevol_cmd(struct cmd_context *cmd, int argc, char
return ret;
}
static int _lvconvert_integrity_remove(struct cmd_context *cmd,
struct logical_volume *lv)
{
struct volume_group *vg = lv->vg;
if (!lv_is_integrity(lv) && !lv_is_raid(lv)) {
log_error("LV does not have integrity.");
return 0;
}
/* TODO: lift this restriction */
if (lv_info(cmd, lv, 1, NULL, 0, 0)) {
log_error("LV must be inactive to remove integrity.");
return 0;
}
if (!archive(vg))
return_0;
if (lv_is_integrity(lv)) {
if (!lv_remove_integrity(lv))
return_0;
} else if (lv_is_raid(lv)) {
if (!lv_remove_integrity_from_raid(lv))
return_0;
}
if (!vg_write(vg) || !vg_commit(vg))
return_0;
backup(vg);
log_print_unless_silent("Logical volume %s has removed integrity.", display_lvname(lv));
return 1;
}
static int _lvconvert_integrity_add(struct cmd_context *cmd,
struct logical_volume *lv,
const char *meta_name,
struct integrity_settings *set)
{
struct lv_activate_opts laopts = { 0 };
struct volume_group *vg = lv->vg;
struct lv_segment *seg;
struct dm_list *use_pvh;
int ret;
seg = first_seg(lv);
/* TODO: lift this restriction, if we can reload table to set recalculate */
if (lv_info(cmd, lv, 1, NULL, 0, 0)) {
log_error("LV must be inactive to add integrity.");
return 0;
}
/* ensure it's not active elsewhere. */
if (!lockd_lv(cmd, lv, "ex", 0))
return_0;
if (cmd->position_argc > 1) {
/* First pos arg is required LV, remaining are optional PVs. */
if (!(use_pvh = create_pv_list(cmd->mem, vg, cmd->position_argc - 1, cmd->position_argv + 1, 0)))
return_0;
} else
use_pvh = &vg->pvs;
if (!archive(vg))
return_0;
if (seg_is_raid(seg))
ret = lv_add_integrity_to_raid(lv, "external", set, use_pvh);
else
ret = lv_add_integrity(lv, "external", meta_name, set, use_pvh);
if (!ret)
return 0;
backup(vg);
/*
* The LV needs to be activated to start the integrity metadata
* initialization.
*/
laopts.integrity_recalculate = 1;
if (!activate_lv_opts(cmd, lv, &laopts)) {
log_error("Failed to activate LV %s.", display_lvname(lv));
log_error("The full LV should be zeroed to initialize integrity metadata.");
} else {
log_print("LV %s activated to initialize integrity.", display_lvname(lv));
}
log_print_unless_silent("Logical volume %s has added integrity.", display_lvname(lv));
return 1;
}
static int _lvconvert_integrity_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle)
{
struct integrity_settings settings;
const char *meta_name = NULL;
const char *arg = NULL;
int ret = 0;
memset(&settings, 0, sizeof(settings));
if (!get_integrity_options(cmd, &arg, &meta_name, &settings))
return 0;
if (!arg || !strcmp(arg, "external") || !strcmp(arg, "y"))
ret = _lvconvert_integrity_add(cmd, lv, meta_name, &settings);
else if (!strcmp(arg, "none") || !strcmp(arg, "n"))
ret = _lvconvert_integrity_remove(cmd, lv);
else
log_error("Invalid integrity option value.");
if (!ret)
return ECMD_FAILED;
return ECMD_PROCESSED;
}
int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
int ret;
if (!(handle = init_processing_handle(cmd, NULL))) {
log_error("Failed to initialize processing handle.");
return ECMD_FAILED;
}
cmd->cname->flags &= ~GET_VGNAME_FROM_OPTIONS;
ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle, NULL,
&_lvconvert_integrity_single);
destroy_processing_handle(cmd, handle);
return ret;
}
/*
* All lvconvert command defs have their own function,
* so the generic function name is unused.

View File

@@ -575,6 +575,13 @@ static int _read_raid_params(struct cmd_context *cmd,
log_error("Minimum recovery rate cannot be higher than maximum.");
return 0;
}
if (lp->region_size < lp->stripe_size) {
log_print_unless_silent("Adjusting %s %s region size to required minimum of stripe size %s.",
lp->segtype->name, display_size(cmd, (uint64_t)lp->region_size),
display_size(cmd, (uint64_t)lp->stripe_size));
lp->region_size = lp->stripe_size;
}
}
return 1;
@@ -785,6 +792,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
mirror_default_cfg = (arg_uint_value(cmd, stripes_ARG, 1) > 1)
? global_raid10_segtype_default_CFG : global_mirror_segtype_default_CFG;
segtype_str = find_config_tree_str(cmd, mirror_default_cfg, NULL);
} else if (arg_is_set(cmd, integrity_ARG)) {
segtype_str = SEG_TYPE_NAME_INTEGRITY;
} else
segtype_str = SEG_TYPE_NAME_STRIPED;
@@ -819,6 +828,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
readahead_ARG,\
setactivationskip_ARG,\
test_ARG,\
integrity_ARG,\
integritysettings_ARG,\
type_ARG
#define CACHE_POOL_ARGS \
@@ -1218,6 +1229,11 @@ static int _lvcreate_params(struct cmd_context *cmd,
}
}
if (seg_is_integrity(lp) || seg_is_raid(lp)) {
if (!get_integrity_options(cmd, &lp->integrity_arg, &lp->integrity_meta_name, &lp->integrity_settings))
return 0;
}
lcp->pv_count = argc;
lcp->pvs = argv;
@@ -1575,6 +1591,13 @@ static int _check_zero_parameters(struct cmd_context *cmd, struct lvcreate_param
if (seg_is_thin(lp))
return 1;
if (seg_is_integrity(lp)) {
if (lp->zero && !is_change_activating(lp->activate)) {
log_error("Zeroing integrity is not compatible with inactive creation (-an).");
return 0;
}
}
/* If there is some problem, buffer will not be empty */
if (dm_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
lp->origin_name ? "origin " : "",
@@ -1702,6 +1725,16 @@ static int _lvcreate_single(struct cmd_context *cmd, const char *vg_name,
lp->pool_name ? : "with generated name", lp->vg_name, lp->segtype->name);
}
if (seg_is_integrity(lp) && lp->integrity_arg &&
!strcmp(lp->integrity_arg, "internal")) {
log_warn("WARNING: integrity cannot be removed from LV with internal metadata.");
if (!arg_count(cmd, yes_ARG) &&
(yes_no_prompt("Create LV with internal integrity metadata?") == 'n')) {
log_error("LV not created.");
goto_out;
}
}
if (vg->lock_type && !strcmp(vg->lock_type, "sanlock")) {
if (!handle_sanlock_lv(cmd, vg)) {
log_error("No space for sanlock lock, extend the internal lvmlock LV.");
@@ -1775,5 +1808,6 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
_destroy_lvcreate_params(&lp);
destroy_processing_handle(cmd, handle);
return ret;
}

View File

@@ -149,6 +149,9 @@ static const struct command_function _command_functions[CMD_COUNT] = {
{ lvconvert_to_vdopool_CMD, lvconvert_to_vdopool_cmd },
{ lvconvert_to_vdopool_param_CMD, lvconvert_to_vdopool_param_cmd },
/* lvconvert for integrity */
{ lvconvert_integrity_CMD, lvconvert_integrity_cmd },
{ pvscan_display_CMD, pvscan_display_cmd },
{ pvscan_cache_CMD, pvscan_cache_cmd },
};
@@ -1077,6 +1080,37 @@ 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;
}
int dumptype_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "headers") ||
!strcmp(av->value, "metadata") ||
!strcmp(av->value, "metadata_all") ||
!strcmp(av->value, "metadata_search") ||
!strcmp(av->value, "metadata_area") ||
!strcmp(av->value, "backup_to_raw"))
return 1;
return 0;
}
int integritytype_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "y") ||
!strcmp(av->value, "n") ||
!strcmp(av->value, "external") ||
!strcmp(av->value, "internal"))
return 1;
return 0;
}
/*
* FIXME: there's been a confusing mixup among:
* resizeable, resizable, allocatable, allocation.

File diff suppressed because it is too large Load Diff

View File

@@ -420,7 +420,7 @@ check_duplicate:
log_error("pvscan[%d] PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
getpid(), dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
if (file_vgname[0] && strcmp(file_vgname, vgname))
if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
log_error("pvscan[%d] PV %s has unexpected VG %s vs %s.",
getpid(), dev_name(dev), vgname, file_vgname);

View File

@@ -1414,6 +1414,174 @@ out:
return ok;
}
static int _get_one_integrity_setting(struct cmd_context *cmd, struct integrity_settings *settings,
char *key, char *val)
{
if (!strncmp(key, "mode", strlen("mode"))) {
if (*val == 'D')
settings->mode[0] = 'D';
else if (*val == 'J')
settings->mode[0] = 'J';
else if (*val == 'B')
settings->mode[0] = 'B';
else if (*val == 'R')
settings->mode[0] = 'R';
else
goto_bad;
/* lvm assigns a default if the user doesn't. */
return 1;
}
if (!strncmp(key, "tag_size", strlen("tag_size"))) {
if (sscanf(val, "%u", &settings->tag_size) != 1)
goto_bad;
/* lvm assigns a default if the user doesn't. */
return 1;
}
if (!strncmp(key, "internal_hash", strlen("internal_hash"))) {
if (!(settings->internal_hash = strdup(val)))
goto_bad;
/* lvm assigns a default if the user doesn't. */
return 1;
}
/*
* For the following settings, lvm does not set a default value if the
* user does not specify their own value, and lets dm-integrity use its
* own default.
*/
if (!strncmp(key, "journal_sectors", strlen("journal_sectors"))) {
if (sscanf(val, "%u", &settings->journal_sectors) != 1)
goto_bad;
settings->journal_sectors_set = 1;
return 1;
}
if (!strncmp(key, "interleave_sectors", strlen("interleave_sectors"))) {
if (sscanf(val, "%u", &settings->interleave_sectors) != 1)
goto_bad;
settings->interleave_sectors_set = 1;
return 1;
}
if (!strncmp(key, "buffer_sectors", strlen("buffer_sectors"))) {
if (sscanf(val, "%u", &settings->buffer_sectors) != 1)
goto_bad;
settings->buffer_sectors_set = 1;
return 1;
}
if (!strncmp(key, "journal_watermark", strlen("journal_watermark"))) {
if (sscanf(val, "%u", &settings->journal_watermark) != 1)
goto_bad;
settings->journal_watermark_set = 1;
return 1;
}
if (!strncmp(key, "commit_time", strlen("commit_time"))) {
if (sscanf(val, "%u", &settings->commit_time) != 1)
goto_bad;
settings->commit_time_set = 1;
return 1;
}
if (!strncmp(key, "block_size", strlen("block_size"))) {
if (sscanf(val, "%u", &settings->block_size) != 1)
goto_bad;
settings->block_size_set = 1;
return 1;
}
if (!strncmp(key, "bitmap_flush_interval", strlen("bitmap_flush_interval"))) {
if (sscanf(val, "%u", &settings->bitmap_flush_interval) != 1)
goto_bad;
settings->bitmap_flush_interval_set = 1;
return 1;
}
if (!strncmp(key, "sectors_per_bit", strlen("sectors_per_bit"))) {
if (sscanf(val, "%llu", (unsigned long long *)&settings->sectors_per_bit) != 1)
goto_bad;
settings->sectors_per_bit_set = 1;
return 1;
}
log_error("Unknown setting: %s", key);
return 0;
bad:
log_error("Invalid setting: %s", key);
return 0;
}
static int _get_integrity_settings(struct cmd_context *cmd, struct integrity_settings *settings)
{
struct arg_value_group_list *group;
const char *str;
char key[64];
char val[64];
int num;
int pos;
/*
* "grouped" means that multiple --integritysettings 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, integritysettings_ARG))
continue;
if (!(str = grouped_arg_str_value(group->arg_values, integritysettings_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_integrity_setting(cmd, settings, key, val))
return_0;
}
}
return 1;
}
int get_integrity_options(struct cmd_context *cmd, const char **arg, const char **meta_name,
struct integrity_settings *set)
{
*arg = NULL;
*meta_name = NULL;
memset(set, 0, sizeof(struct integrity_settings));
if (arg_is_set(cmd, integrity_ARG))
*arg = arg_str_value(cmd, integrity_ARG, NULL);
if (arg_is_set(cmd, integritymetadata_ARG))
*meta_name = arg_str_value(cmd, integritymetadata_ARG, NULL);
if (arg_is_set(cmd, integritysettings_ARG)) {
if (!_get_integrity_settings(cmd, set))
return_0;
}
return 1;
}
/* FIXME move to lib */
static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag)
{
@@ -2579,6 +2747,8 @@ static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int l
return seg_is_raid10(seg);
case writecache_LVT:
return seg_is_writecache(seg);
case integrity_LVT:
return seg_is_integrity(seg);
case error_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR);
case zero_LVT:
@@ -2637,6 +2807,8 @@ int get_lvt_enum(struct logical_volume *lv)
return raid10_LVT;
if (seg_is_writecache(seg))
return writecache_LVT;
if (seg_is_integrity(seg))
return integrity_LVT;
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR))
return error_LVT;
@@ -4284,7 +4456,8 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
/*
* When processing only specific PVs, we can quit once they've all been found.
*/
if (!process_all_pvs && dm_list_empty(arg_tags) && dm_list_empty(arg_devices))
if (!process_all_pvs && dm_list_empty(arg_tags) &&
(!arg_devices || dm_list_empty(arg_devices)))
break;
log_set_report_object_name_and_id(NULL, NULL);
}
@@ -5576,10 +5749,11 @@ int pvcreate_each_device(struct cmd_context *cmd,
* Reacquire the lock that was released above before waiting, then
* check again that the devices can still be used. If the second loop
* finds them changed, or can't find them any more, then they aren't
* used.
* used. Use a non-blocking request when reacquiring to avoid
* potential deadlock since this is not the normal locking sequence.
*/
if (!lockf_global(cmd, "ex")) {
if (!lockf_global_nonblock(cmd, "ex")) {
log_error("Failed to reacquire global lock after prompt.");
goto_out;
}

View File

@@ -184,6 +184,9 @@ 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);
int dumptype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int integritytype_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);
@@ -233,6 +236,9 @@ struct lv_prop *get_lv_prop(int lvp_enum);
struct lv_type *get_lv_type(int lvt_enum);
struct command *get_command(int cmd_enum);
int get_integrity_options(struct cmd_context *cmd, const char **arg, const char **meta_name,
struct integrity_settings *set);
int lvchange_properties_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_refresh_cmd(struct cmd_context *cmd, int argc, char **argv);
@@ -272,6 +278,8 @@ int lvconvert_merge_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_param_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_display_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv);

View File

@@ -141,6 +141,9 @@ 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")
val(dumptype_VAL, dumptype_arg, "DumpType", "headers|metadata|metadata_all|metadata_search")
val(integrity_VAL, integritytype_arg, "IntegrityType", "y|n|external|internal")
/* this should always be last */
val(VAL_COUNT, NULL, NULL, NULL)