summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2019-06-05 11:10:37 -0500
committerDavid Teigland <teigland@redhat.com>2019-06-05 12:25:34 -0500
commitbada89a224b0135ac4415d43e6b128989bbf3b2b (patch)
treefd5c4c8178bb7cd861e39d9d8907fbc79bcbba8f
parent4d9f41b11945df83b37b55439a44372b894a78f5 (diff)
downloadlvm2-bada89a224b0135ac4415d43e6b128989bbf3b2b.tar.gz
pvck: dump metadata_all
This searches the entire metadata area for any copy of the metadata and dumps it to file.
-rw-r--r--tools/args.h7
-rw-r--r--tools/pvck.c355
2 files changed, 348 insertions, 14 deletions
diff --git a/tools/args.h b/tools/args.h
index 83c1cfe3e..69f7e0790 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -215,9 +215,10 @@ arg(driverloaded_ARG, '\0', "driverloaded", bool_VAL, 0, 0,
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, \\fBmetadata_area\\fP\n"
- "to extract the entire text metadata area, and \\fBheaders\\fP\n"
- "to print and check LVM headers.\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(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
"Specifies thin pool behavior when data space is exhausted.\n"
diff --git a/tools/pvck.c b/tools/pvck.c
index 9e690a89a..c9b833ad1 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -291,17 +291,326 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
return 1;
}
+/*
+ * Search for any instance of id_str[] in the metadata area,
+ * where the id_str indicates the start of a metadata copy
+ * (which could be complete or a fragment.)
+ * id_str is an open brace followed by id = <uuid>.
+ *
+ * {\n
+ * id = "lL7Mnk-oCGn-Bde2-9B6S-44Z7-VrHa-wvfC3v"
+ *
+ * 1\23456789012345678901234567890123456789012345678
+ * 10 20 30 40
+ */
+#define ID_STR_SIZE 48
+
+#define SEARCH_VGNAME_LEN 512
+
+static int _dump_meta_all(struct device *dev, const char *tofile,
+ uint64_t mda_offset, uint64_t mda_size,
+ uint64_t meta_offset, uint64_t meta_size,
+ char *meta_buf)
+{
+ FILE *fp = NULL;
+ struct dm_config_tree *cft;
+ char vgname[SEARCH_VGNAME_LEN];
+ char id_str[ID_STR_SIZE];
+ char *text_buf, *new_buf;
+ char *p, *brace, *start, *buf_begin, *buf_end;
+ uint64_t search_offset, start_offset;
+ uint32_t brace_dist, new_len, len_a, len_b;
+ uint32_t left_count, right_count;
+ uint32_t seqno;
+ int search_wrapped = 0;
+ int p_wrapped;
+ int save_bad;
+ int i;
+
+ /*
+ * metadata begins:
+ *
+ * <vgname> {
+ * id = "<uuid>"
+ *
+ * Search the metadata buffer for each instance
+ * of the string:
+ *
+ * {
+ * id = "<uuid>"
+ *
+ * Then reverse by the length of the <vgname> +1
+ * to get to the start of the metadata. The
+ * vgname must come from the original/current copy
+ * of metadata found through the mda_header pointer.
+ *
+ * From the start of the metadata, find the end of
+ * the metadata by searching foward until \0 is found.
+ * Metadata ends with the three bytes:
+ * \n\n\0 (0x0a0a00)
+ */
+
+ memset(vgname, 0, sizeof(vgname));
+ memset(id_str, 0, sizeof(id_str));
+
+ for (i = 0; i < SEARCH_VGNAME_LEN; i++) {
+ if (meta_buf[i] == ' ')
+ break;
+ vgname[i] = meta_buf[i];
+ }
+
+ if (!(p = strchr(meta_buf, '{')))
+ return_0;
+
+ for (i = 0; i < ID_STR_SIZE; i++) {
+ id_str[i] = *p;
+ p++;
+ }
+
+ if (!(text_buf = malloc(mda_size)))
+ return_0;
+ memset(text_buf, 0, mda_size);
+
+ if (!dev_read_bytes(dev, mda_offset, mda_size, text_buf)) {
+ log_print("CHECK: failed to read metadata area at offset %llu size %llu",
+ (unsigned long long)mda_offset, (unsigned long long)mda_size);
+ free(text_buf);
+ return 0;
+ }
+
+ search_offset = meta_offset + meta_size;
+
+ search_next:
+ if (search_offset > mda_size) {
+ if (search_wrapped)
+ goto done;
+ search_offset = 512;
+ search_wrapped = 1;
+ }
+
+ /*
+ * Search between buf_begin and buf_end for next open brace.
+ */
+ buf_begin = text_buf + search_offset;
+ buf_end = text_buf + mda_size;
+ brace = NULL;
+
+ for (p = buf_begin; p < buf_end; p++) {
+ if (*p != '{')
+ continue;
+ brace = p;
+ break;
+ }
+
+ if (!brace && search_wrapped)
+ goto done;
+ if (!brace) {
+ search_offset = 512;
+ search_wrapped = 1;
+ goto search_next;
+ }
+
+ /*
+ * brace_dist is the distance from the last place we
+ * began searching to the brace we are testing for
+ * metadata. If this brace is not the start of valid
+ * metadata, then advance brace_dist and search for
+ * the next brace to check.
+ */
+ brace_dist = (uint32_t)(brace - buf_begin);
+
+ /*
+ * Found an open brace, check if it's the start of new metadata
+ * by checking if brace is followed by id = "<uuid>".
+ */
+
+ if (memcmp(brace, id_str, ID_STR_SIZE)) {
+ /* It's not, look for next open brace. */
+ search_offset += (brace_dist + 1);
+ goto search_next;
+ }
+
+ /*
+ * This looks like a new instance of metadata, check if it's complete.
+ * The start of the metadata is the vgname preceding the open brace,
+ * so step backward through the text_buf to find the start of vgname.
+ * There is no delimiter preceding the vgname, there can be any
+ * text or data in the byte immediately before vgname (this means
+ * we cannot handle extracting metadata prior to a vgrename.)
+ *
+ * <vgname> {
+ * id = "..."
+ */
+ start = brace - (strlen(vgname) + 1);
+
+ /* Offset from the begininng of device to the start of this metadata. */
+ start_offset = (uint64_t)(start - text_buf) + mda_offset;
+
+ /*
+ * The end of the metadata is found by searching forward in text_buf
+ * until \0, at which point open and close braces should match.
+ * This forward search may wrap around to the start of text_buf.
+ *
+ * Metadata ends with the three bytes \n\n\0: 0a 0a 00
+ */
+ p = start;
+ p_wrapped = 0;
+ len_a = 0;
+ len_b = 0;
+ new_len = 0;
+ new_buf = NULL;
+ left_count = 0;
+ right_count = 0;
+ save_bad = 0;
+ seqno = 0;
+
+ while (1) {
+ p++;
+
+ if (p == (buf_end)) {
+ p = text_buf + 512;
+ p_wrapped = 1;
+ }
+
+ if (*p == '{')
+ left_count++;
+ else if (*p == '}')
+ right_count++;
+ else if (*p == '\0')
+ break;
+ }
+
+ /* \0 should be preceded by \n\n (0x0a0a) */
+ if ((*(p - 2) != 0x0a) || (*(p - 1) != 0x0a))
+ log_print("Unexpected metadata end bytes.");
+
+ if (p_wrapped) {
+ len_a = (uint32_t)(buf_end - start);
+ len_b = (uint32_t)(p - (text_buf + 512));
+ new_len = len_a + len_b;
+ search_wrapped = 1;
+ } else {
+ new_len = (uint32_t)(p - start);
+ }
+
+ /*
+ * A couple simple tests indicate if this could be valid metadata
+ * before attempting to parse it. (min length is probably greater
+ * than 256, so this could be increased.)
+ *
+ * If this is complete but corrupt, we should save it.
+ * TODO: If this is a fragment we should skip it.
+ */
+ if ((left_count != right_count) || (new_len < 256)) {
+ /*
+ * To skip this:
+ * search_offset += (brace_dist + 1);
+ * goto search_next;
+ */
+ log_print("Found incorrect metadata at %llu length %u with braces %u %u",
+ (unsigned long long)start_offset, new_len, left_count, right_count);
+ save_bad = 1;
+ }
+
+ /*
+ * Copy the potential metadata into a new buffer to parse.
+ */
+ if (!(new_buf = malloc(new_len + 1))) {
+ search_offset += (brace_dist + 1);
+ log_print("No memory for metadata at %llu length %u with %u sections",
+ (unsigned long long)start_offset, new_len, right_count);
+ goto search_next;
+ }
+ memset(new_buf, 0, new_len + 1);
+
+ if (p_wrapped) {
+ memcpy(new_buf, start, len_a);
+ memcpy(new_buf + len_a, text_buf + 512, len_b);
+ } else {
+ memcpy(new_buf, start, new_len);
+ }
+
+ if (save_bad)
+ goto save;
+
+ /*
+ * Check the metadata is parsable.
+ * If this is complete but corrupt, we should save it.
+ * TODO: If this is a fragment we should skip it.
+ */
+ if ((cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0))) {
+ if (!dm_config_parse(cft, new_buf, new_buf + new_len)) {
+ /*
+ * To skip this:
+ * search_offset += (brace_dist + 1);
+ * goto search_next;
+ */
+ log_print("Found unparsable metadata at %llu length %u with %u sections",
+ (unsigned long long)start_offset, new_len, right_count);
+ config_destroy(cft);
+ goto save;
+ }
+
+ if (cft->root && cft->root->child)
+ dm_config_get_uint32(cft->root->child, "seqno", &seqno);
+ config_destroy(cft);
+ }
+
+ log_print("Found metadata at %llu length %u seqno %u with %u sections",
+ (unsigned long long)start_offset, new_len, seqno, right_count);
+
+ save:
+ if (!fp && tofile) {
+ if (!(fp = fopen(tofile, "a"))) {
+ log_error("Failed to open file %s", tofile);
+ goto out;
+ }
+ }
+
+ if (fp) {
+ fprintf(fp, "%s", new_buf);
+ fprintf(fp, "\n--\n");
+ }
+
+ out:
+ free(new_buf);
+
+ /*
+ * Look for another id_str instance after the metadata
+ * that was just finished.
+ */
+
+ if (p_wrapped)
+ search_offset = len_b;
+ else
+ search_offset += new_len;
+ goto search_next;
+
+ done:
+ if (fp) {
+ if (fflush(fp))
+ stack;
+ if (fclose(fp))
+ stack;
+ }
+
+ free(text_buf);
+ return 1;
+}
+
static int _dump_meta_text(struct device *dev,
int print_fields, int print_metadata, const char *tofile,
int mda_num, int rlocn_index,
uint64_t mda_offset, uint64_t mda_size,
uint64_t meta_offset, uint64_t meta_size,
- uint32_t meta_checksum)
+ uint32_t meta_checksum,
+ char **meta_buf_out)
{
char *meta_buf;
struct dm_config_tree *cft;
const char *vgname = NULL;
uint32_t crc;
+ uint32_t seqno = 0;
int mn = mda_num; /* 1 or 2 */
int ri = rlocn_index; /* 0 or 1 */
int bad = 0;
@@ -367,14 +676,17 @@ static int _dump_meta_text(struct device *dev,
(unsigned long long)meta_size);
bad++;
} else {
- vgname = strdup(cft->root->key);
+ if (cft->root && cft->root->key)
+ vgname = strdup(cft->root->key);
+ if (cft->root && cft->root->child)
+ dm_config_get_uint32(cft->root->child, "seqno", &seqno);
}
config_destroy(cft);
}
- log_print("metadata text at %llu crc 0x%x # vgname %s",
+ log_print("metadata text at %llu crc 0x%x # vgname %s seqno %u",
(unsigned long long)(mda_offset + meta_offset), crc,
- vgname ? vgname : "?");
+ vgname ? vgname : "?", seqno);
if (!print_metadata)
goto out;
@@ -399,6 +711,8 @@ static int _dump_meta_text(struct device *dev,
}
out:
+ *meta_buf_out = meta_buf;
+
if (bad)
return 0;
return 1;
@@ -662,6 +976,7 @@ static int _dump_mda_header(struct cmd_context *cmd,
{
char str[256];
char *buf;
+ char *meta_buf = NULL;
struct mda_header *mh;
struct raw_locn *rlocn0, *rlocn1;
uint64_t rlocn0_offset, rlocn1_offset;
@@ -749,8 +1064,24 @@ static int _dump_mda_header(struct cmd_context *cmd,
if (!meta_offset)
goto out;
- if (!_dump_meta_text(dev, print_fields, print_metadata, tofile, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum))
- bad++;
+ /*
+ * looking at the current copy of metadata referenced by raw_locn
+ */
+ if (print_metadata < 2) {
+ if (!_dump_meta_text(dev, print_fields, print_metadata, tofile, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum, &meta_buf))
+ bad++;
+ }
+
+ /*
+ * looking at all copies of the metadata in the area
+ */
+ if (print_metadata == 2) {
+ if (!_dump_meta_text(dev, 0, 0, NULL, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum, &meta_buf))
+ bad++;
+
+ if (!_dump_meta_all(dev, tofile, mda_offset, mda_size, meta_offset, meta_size, meta_buf))
+ bad++;
+ }
/* Should we also check text metadata if it exists in rlocn1? */
out:
@@ -821,15 +1152,14 @@ static int _dump_headers(struct cmd_context *cmd,
}
static int _dump_metadata(struct cmd_context *cmd,
- int argc, char **argv, int full_area)
+ int argc, char **argv,
+ int print_metadata, int print_area)
{
struct device *dev;
const char *pv_name;
const char *tofile = NULL;
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum, mda2_checksum;
- int print_metadata = !full_area;
- int print_area = full_area;
int mda_num = 1;
int bad = 0;
@@ -908,10 +1238,13 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
dump = arg_str_value(cmd, dump_ARG, NULL);
if (!strcmp(dump, "metadata"))
- return _dump_metadata(cmd, argc, argv, 0);
+ return _dump_metadata(cmd, argc, argv, 1, 0);
+
+ if (!strcmp(dump, "metadata_all"))
+ return _dump_metadata(cmd, argc, argv, 2, 0);
if (!strcmp(dump, "metadata_area"))
- return _dump_metadata(cmd, argc, argv, 1);
+ return _dump_metadata(cmd, argc, argv, 0, 1);
if (!strcmp(dump, "headers"))
return _dump_headers(cmd, argc, argv);