mirror of
git://sourceware.org/git/lvm2.git
synced 2025-01-02 01:18:26 +03:00
pvck: new dump option to extract metadata
The new command 'pvck --dump metadata PV' will extract the current version of VG metadata from a PV for testing and debugging. --dump metadata_area extracts the entire text metadata area.
This commit is contained in:
parent
1022b88a66
commit
52586b1039
31
lib/cache/lvmcache.c
vendored
31
lib/cache/lvmcache.c
vendored
@ -2301,3 +2301,34 @@ int lvmcache_vginfo_has_pvid(struct lvmcache_vginfo *vginfo, char *pvid)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct metadata_area *lvmcache_get_mda(struct cmd_context *cmd,
|
||||
const char *vgname,
|
||||
struct device *dev,
|
||||
int use_mda_num)
|
||||
{
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
struct lvmcache_info *info;
|
||||
struct metadata_area *mda;
|
||||
|
||||
if (!use_mda_num)
|
||||
use_mda_num = 1;
|
||||
|
||||
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, NULL)))
|
||||
return NULL;
|
||||
|
||||
dm_list_iterate_items(info, &vginfo->infos) {
|
||||
if (info->dev != dev)
|
||||
continue;
|
||||
|
||||
dm_list_iterate_items(mda, &info->mdas) {
|
||||
if ((use_mda_num == 1) && (mda->status & MDA_PRIMARY))
|
||||
return mda;
|
||||
if ((use_mda_num == 2) && !(mda->status & MDA_PRIMARY))
|
||||
return mda;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
5
lib/cache/lvmcache.h
vendored
5
lib/cache/lvmcache.h
vendored
@ -168,6 +168,11 @@ unsigned lvmcache_mda_count(struct lvmcache_info *info);
|
||||
int lvmcache_vgid_is_cached(const char *vgid);
|
||||
uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info);
|
||||
|
||||
struct metadata_area *lvmcache_get_mda(struct cmd_context *cmd,
|
||||
const char *vgname,
|
||||
struct device *dev,
|
||||
int use_mda_num);
|
||||
|
||||
int lvmcache_found_duplicate_pvs(void);
|
||||
int lvmcache_found_duplicate_vgnames(void);
|
||||
|
||||
|
@ -2602,3 +2602,221 @@ bad:
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *_read_metadata_text(struct cmd_context *cmd, struct device *dev,
|
||||
uint64_t area_start, uint64_t area_size,
|
||||
uint32_t *len, uint64_t *disk_offset)
|
||||
{
|
||||
struct mda_header *mh;
|
||||
struct raw_locn *rlocn_slot0;
|
||||
uint64_t text_offset, text_size;
|
||||
char *area_buf;
|
||||
char *text_buf;
|
||||
|
||||
/*
|
||||
* Read the entire metadata area, including mda_header and entire
|
||||
* circular buffer.
|
||||
*/
|
||||
if (!(area_buf = malloc(area_size)))
|
||||
return_NULL;
|
||||
|
||||
if (!dev_read_bytes(dev, area_start, area_size, area_buf)) {
|
||||
log_error("Failed to read device %s at %llu size %llu",
|
||||
dev_name(dev),
|
||||
(unsigned long long)area_start,
|
||||
(unsigned long long)area_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mh = (struct mda_header *)area_buf;
|
||||
_xlate_mdah(mh);
|
||||
|
||||
rlocn_slot0 = &mh->raw_locns[0];
|
||||
text_offset = rlocn_slot0->offset;
|
||||
text_size = rlocn_slot0->size;
|
||||
|
||||
/*
|
||||
* Copy and return the current metadata text out of the metadata area.
|
||||
*/
|
||||
|
||||
if (!(text_buf = malloc(text_size)))
|
||||
return_NULL;
|
||||
|
||||
memcpy(text_buf, area_buf + text_offset, text_size);
|
||||
|
||||
if (len)
|
||||
*len = (uint32_t)text_size;
|
||||
if (disk_offset)
|
||||
*disk_offset = area_start + text_offset;
|
||||
|
||||
free(area_buf);
|
||||
|
||||
return text_buf;
|
||||
}
|
||||
|
||||
int dump_metadata_text(struct cmd_context *cmd,
|
||||
const char *vgname,
|
||||
const char *vgid,
|
||||
struct device *dev,
|
||||
struct metadata_area *mda,
|
||||
const char *tofile)
|
||||
{
|
||||
char *textbuf;
|
||||
struct format_instance *fid;
|
||||
struct format_instance_ctx fic;
|
||||
struct mda_context *mdac;
|
||||
struct volume_group *vg;
|
||||
unsigned use_previous_vg = 0;
|
||||
uint32_t textlen = 0;
|
||||
uint32_t textcrc;
|
||||
uint64_t text_disk_offset;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Set up overhead/abstractions for reading a given vgname
|
||||
* (fmt/fid/fic/vgid).
|
||||
*/
|
||||
|
||||
fic.type = FMT_INSTANCE_MDAS | FMT_INSTANCE_AUX_MDAS;
|
||||
fic.context.vg_ref.vg_name = vgname;
|
||||
fic.context.vg_ref.vg_id = vgid;
|
||||
|
||||
if (!(fid = _text_create_text_instance(cmd->fmt, &fic))) {
|
||||
log_error("Failed to create format instance");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mdac = mda->metadata_locn;
|
||||
|
||||
/*
|
||||
* Read the VG metadata from the device as a raw chunk of original text.
|
||||
*/
|
||||
textbuf = _read_metadata_text(cmd, dev,
|
||||
mdac->area.start, mdac->area.size,
|
||||
&textlen, &text_disk_offset);
|
||||
if (!textbuf || !textlen) {
|
||||
log_error("No metadata text found on %s", dev_name(dev));
|
||||
_text_destroy_instance(fid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
textcrc = calc_crc(INITIAL_CRC, (uint8_t *)textbuf, textlen);
|
||||
|
||||
/*
|
||||
* Read the same VG metadata, but imported/parsed into a vg struct
|
||||
* format so we know it's valid/parsable, and can look at values in it.
|
||||
*/
|
||||
if (!(vg = _vg_read_raw(fid, vgname, mda, NULL, &use_previous_vg))) {
|
||||
log_warn("WARNING: parse error for metadata on %s.", dev_name(dev));
|
||||
_text_destroy_instance(fid);
|
||||
}
|
||||
|
||||
log_print("Metadata for %s from %s at %llu size %u with seqno %u checksum 0x%x.",
|
||||
vgname, dev_name(dev),
|
||||
(unsigned long long)text_disk_offset, textlen,
|
||||
vg ? vg->seqno : 0, textcrc);
|
||||
|
||||
if (!tofile) {
|
||||
log_print("---");
|
||||
printf("%s\n", textbuf);
|
||||
log_print("---");
|
||||
} else {
|
||||
FILE *fp;
|
||||
if (!(fp = fopen(tofile, "wx"))) {
|
||||
log_error("Failed to create file %s", tofile);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(fp, "%s", textbuf);
|
||||
|
||||
if (fflush(fp))
|
||||
stack;
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
}
|
||||
|
||||
if (vg)
|
||||
release_vg(vg);
|
||||
|
||||
free(textbuf);
|
||||
ret = 1;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *_read_metadata_area(struct cmd_context *cmd, struct device *dev,
|
||||
uint64_t area_start, uint64_t area_size)
|
||||
{
|
||||
char *area_buf;
|
||||
|
||||
/*
|
||||
* Read the entire metadata area, including mda_header and entire
|
||||
* circular buffer.
|
||||
*/
|
||||
if (!(area_buf = malloc(area_size)))
|
||||
return_NULL;
|
||||
|
||||
if (!dev_read_bytes(dev, area_start, area_size, area_buf)) {
|
||||
log_error("Failed to read device %s at %llu size %llu",
|
||||
dev_name(dev),
|
||||
(unsigned long long)area_start,
|
||||
(unsigned long long)area_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return area_buf;
|
||||
}
|
||||
|
||||
int dump_metadata_area(struct cmd_context *cmd,
|
||||
const char *vgname,
|
||||
const char *vgid,
|
||||
struct device *dev,
|
||||
struct metadata_area *mda,
|
||||
const char *tofile)
|
||||
{
|
||||
char *areabuf;
|
||||
char *textbuf;
|
||||
struct mda_context *mdac;
|
||||
int ret = 0;
|
||||
|
||||
mdac = mda->metadata_locn;
|
||||
|
||||
areabuf = _read_metadata_area(cmd, dev,
|
||||
mdac->area.start, mdac->area.size);
|
||||
if (!areabuf) {
|
||||
log_error("No metadata area found on %s", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_print("Metadata buffer for %s from %s in area at %llu size %llu offset 512.",
|
||||
vgname, dev_name(dev),
|
||||
(unsigned long long)mdac->area.start,
|
||||
(unsigned long long)mdac->area.size);
|
||||
|
||||
/* text starts after mda_header which uses 512 bytes */
|
||||
textbuf = areabuf + 512;
|
||||
|
||||
if (!tofile) {
|
||||
/* N.B. this will often include unprintable data */
|
||||
log_print("---");
|
||||
fwrite(textbuf, mdac->area.size - 512, 1, stdout);
|
||||
log_print("---");
|
||||
} else {
|
||||
FILE *fp;
|
||||
if (!(fp = fopen(tofile, "wx"))) {
|
||||
log_error("Failed to create file %s", tofile);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fwrite(textbuf, mdac->area.size - 512, 1, fp);
|
||||
|
||||
if (fflush(fp))
|
||||
stack;
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
}
|
||||
ret = 1;
|
||||
out:
|
||||
free(areabuf);
|
||||
return ret;
|
||||
}
|
||||
|
@ -76,4 +76,18 @@ struct data_area_list {
|
||||
struct disk_locn disk_locn;
|
||||
};
|
||||
|
||||
int dump_metadata_text(struct cmd_context *cmd,
|
||||
const char *vgname,
|
||||
const char *vgid,
|
||||
struct device *dev,
|
||||
struct metadata_area *mda,
|
||||
const char *tofile);
|
||||
|
||||
int dump_metadata_area(struct cmd_context *cmd,
|
||||
const char *vgname,
|
||||
const char *vgid,
|
||||
struct device *dev,
|
||||
struct metadata_area *mda,
|
||||
const char *tofile);
|
||||
|
||||
#endif
|
||||
|
@ -1 +1,8 @@
|
||||
pvck checks the LVM metadata for consistency on PVs.
|
||||
pvck checks 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.)
|
||||
|
||||
|
@ -213,6 +213,11 @@ 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 extract the current text metadata, and \\fBmetadata_area\\fP\n"
|
||||
"to extract the entire text metadata area.\n")
|
||||
|
||||
arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
|
||||
"Specifies thin pool behavior when data space is exhausted.\n"
|
||||
"When yes, device-mapper will immediately return an error\n"
|
||||
|
@ -1427,6 +1427,12 @@ ID: pvresize_general
|
||||
pvck PV ...
|
||||
OO: --labelsector Number
|
||||
ID: pvck_general
|
||||
DESC: Check for metadata on a device
|
||||
|
||||
pvck --dump String PV
|
||||
OO: --file String, --pvmetadatacopies MetadataCopiesPV
|
||||
ID: pvck_dumpmetadata
|
||||
DESC: Dump raw metadata from a device
|
||||
|
||||
---
|
||||
|
||||
|
@ -114,7 +114,7 @@ xx(pvresize,
|
||||
0)
|
||||
|
||||
xx(pvck,
|
||||
"Check the consistency of physical volume(s)",
|
||||
"Check metadata on physical volumes",
|
||||
LOCKD_VG_SH)
|
||||
|
||||
xx(pvcreate,
|
||||
|
99
tools/pvck.c
99
tools/pvck.c
@ -15,17 +15,115 @@
|
||||
|
||||
#include "base/memory/zalloc.h"
|
||||
#include "tools.h"
|
||||
#include "lib/format_text/format-text.h"
|
||||
|
||||
/*
|
||||
* TODO: option to dump all copies of metadata that are found
|
||||
*
|
||||
* TODO: option to intelligently search for mda locations on
|
||||
* disk in case the pv_header and/or mda_header are damaged.
|
||||
*/
|
||||
|
||||
static int _dump_metadata(struct cmd_context *cmd, int argc, char **argv, int full_area)
|
||||
{
|
||||
struct dm_list devs;
|
||||
struct device_list *devl;
|
||||
struct device *dev;
|
||||
const char *pv_name;
|
||||
const char *vgname;
|
||||
const char *vgid;
|
||||
struct lvmcache_info *info;
|
||||
struct metadata_area *mda;
|
||||
const char *tofile = NULL;
|
||||
int mda_num = 1;
|
||||
int ret;
|
||||
|
||||
dm_list_init(&devs);
|
||||
|
||||
if (arg_is_set(cmd, file_ARG)) {
|
||||
if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
/* 1: dump metadata from first mda, 2: dump metadata from second mda */
|
||||
if (arg_is_set(cmd, pvmetadatacopies_ARG))
|
||||
mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
|
||||
|
||||
pv_name = argv[0];
|
||||
|
||||
if (!(dev = dev_cache_get(cmd, pv_name, cmd->filter))) {
|
||||
log_error("No device found for %s %s.", pv_name, dev_cache_filtered_reason(pv_name));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(devl = zalloc(sizeof(*devl))))
|
||||
return ECMD_FAILED;
|
||||
|
||||
devl->dev = dev;
|
||||
dm_list_add(&devs, &devl->list);
|
||||
|
||||
label_scan_setup_bcache();
|
||||
label_scan_devs(cmd, cmd->filter, &devs);
|
||||
|
||||
if (!dev->pvid[0]) {
|
||||
log_error("No PV ID found for %s", dev_name(dev));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
|
||||
log_error("No VG info found for %s", dev_name(dev));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(vgname = lvmcache_vgname_from_info(info))) {
|
||||
log_error("No VG name found for %s", dev_name(dev));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(vgid = lvmcache_vgid_from_vgname(cmd, vgname))) {
|
||||
log_error("No VG ID found for %s", dev_name(dev));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(mda = lvmcache_get_mda(cmd, vgname, dev, mda_num))) {
|
||||
log_error("No mda %d found for %s", mda_num, dev_name(dev));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (full_area)
|
||||
ret = dump_metadata_area(cmd, vgname, vgid, dev, mda, tofile);
|
||||
else
|
||||
ret = dump_metadata_text(cmd, vgname, vgid, dev, mda, tofile);
|
||||
|
||||
if (!ret)
|
||||
return ECMD_FAILED;
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
int pvck(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
struct dm_list devs;
|
||||
struct device_list *devl;
|
||||
struct device *dev;
|
||||
const char *dump;
|
||||
const char *pv_name;
|
||||
uint64_t labelsector;
|
||||
int i;
|
||||
int ret_max = ECMD_PROCESSED;
|
||||
|
||||
if (arg_is_set(cmd, dump_ARG)) {
|
||||
dump = arg_str_value(cmd, dump_ARG, NULL);
|
||||
|
||||
if (!strcmp(dump, "metadata"))
|
||||
return _dump_metadata(cmd, argc, argv, 0);
|
||||
|
||||
if (!strcmp(dump, "metadata_area"))
|
||||
return _dump_metadata(cmd, argc, argv, 1);
|
||||
|
||||
log_error("Unknown dump value.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
labelsector = arg_uint64_value(cmd, labelsector_ARG, UINT64_C(0));
|
||||
|
||||
dm_list_init(&devs);
|
||||
@ -53,7 +151,6 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
|
||||
label_scan_devs(cmd, cmd->filter, &devs);
|
||||
|
||||
dm_list_iterate_items(devl, &devs) {
|
||||
|
||||
/*
|
||||
* The scan above will populate lvmcache with any info from the
|
||||
* standard locations at the start of the device. Now populate
|
||||
|
Loading…
Reference in New Issue
Block a user