[lvm-devel] master - pvck: dump metadata_all

David Teigland teigland at sourceware.org
Wed Jun 5 17:26:06 UTC 2019


Gitweb:        https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=bada89a224b0135ac4415d43e6b128989bbf3b2b
Commit:        bada89a224b0135ac4415d43e6b128989bbf3b2b
Parent:        4d9f41b11945df83b37b55439a44372b894a78f5
Author:        David Teigland <teigland at redhat.com>
AuthorDate:    Wed Jun 5 11:10:37 2019 -0500
Committer:     David Teigland <teigland at redhat.com>
CommitterDate: Wed Jun 5 12:25:34 2019 -0500

pvck: dump metadata_all

This searches the entire metadata area for any
copy of the metadata and dumps it to file.
---
 tools/args.h |    7 +-
 tools/pvck.c |  355 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 348 insertions(+), 14 deletions(-)

diff --git a/tools/args.h b/tools/args.h
index 83c1cfe..69f7e07 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 9e690a8..c9b833a 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);




More information about the lvm-devel mailing list