commit 7ce660b860982feb4a4c0c08e3e778111d0700aa Author: Bob Peterson <rpeterso@redhat.com> Date: Thu Dec 17 17:27:04 2009 -0600 GFS2: gfs2_edit savemeta bugs This patch fixes a problem whereby directory hash tables were not being saved by gfs2_edit's savemeta option. Also, this introduces a new option to print individual records from a savemeta file: gfs2_edit printsavedmeta /home/bob/my.meta 0x12345 rhbz#528786 diff --git a/gfs2/edit/hexedit.c b/gfs2/edit/hexedit.c index 0d3af3e..49577f9 100644 --- a/gfs2/edit/hexedit.c +++ b/gfs2/edit/hexedit.c @@ -51,7 +51,7 @@ const char *allocdesc[2][5] = { int display(int identify_only); extern void savemeta(const char *out_fn, int slow); extern void restoremeta(const char *in_fn, const char *out_device, - int printblocksonly); + uint64_t printblocksonly); /* for assigning numeric fields: */ #define checkassign(strfield, struct, member, value) do { \ @@ -1820,8 +1820,7 @@ int display(int identify_only) if (read(sbd.device_fd, buf, sbd.bsize) != sbd.bsize) { fprintf(stderr, "read error: %s from %s:%d: " "offset %lld (0x%llx)\n", - strerror(errno), __FUNCTION__, - __LINE__, + strerror(errno), __FUNCTION__, __LINE__, (unsigned long long)dev_offset, (unsigned long long)dev_offset); exit(-1); @@ -3256,17 +3255,22 @@ void parameterpass1(int argc, char *argv[], int i) termlines = 0; else if (!strcasecmp(argv[i], "savergs")) termlines = 0; - else if (!strcasecmp(argv[i], "printsavedmeta")) - restoremeta(argv[i+1], argv[i+2], - TRUE); - else if (!strcasecmp(argv[i], "restoremeta")) + else if (!strcasecmp(argv[i], "printsavedmeta")) { + if (dmode == INIT_MODE) + dmode = GFS2_MODE; + restoremeta(argv[i+1], argv[i+2], TRUE); + } else if (!strcasecmp(argv[i], "restoremeta")) { + if (dmode == INIT_MODE) + dmode = HEX_MODE; /* hopefully not used */ restoremeta(argv[i+1], argv[i+2], FALSE); - else if (!strcmp(argv[i], "rgcount")) + } else if (!strcmp(argv[i], "rgcount")) termlines = 0; else if (!strcmp(argv[i], "rgflags")) termlines = 0; else if (!strcmp(argv[i], "rg")) termlines = 0; + else if (!strcasecmp(argv[i], "-x")) + dmode = HEX_MODE; else if (!device[0] && strchr(argv[i],'/')) strcpy(device, argv[i]); } @@ -3467,12 +3471,12 @@ int main(int argc, char *argv[]) memset(edit_col, 0, sizeof(edit_col)); memset(edit_size, 0, sizeof(edit_size)); memset(last_entry_onscreen, 0, sizeof(last_entry_onscreen)); - dmode = HEX_MODE; + dmode = INIT_MODE; sbd.bsize = 4096; type_alloc(buf, char, sbd.bsize); /* allocate/malloc a new 4K buffer */ block = starting_blk = 0x10; for (i = 0; i < BLOCK_STACK_SIZE; i++) { - blockstack[i].dmode = dmode; + blockstack[i].dmode = HEX_MODE; blockstack[i].block = block; for (j = 0; j < DMODES; j++) { blockstack[i].start_row[j] = 0; @@ -3488,6 +3492,8 @@ int main(int argc, char *argv[]) memset(device, 0, sizeof(device)); termlines = 30; /* assume interactive mode until we find -p */ process_parameters(argc, argv, 0); + if (dmode == INIT_MODE) + dmode = HEX_MODE; fd = open(device, O_RDWR); if (fd < 0) diff --git a/gfs2/edit/hexedit.h b/gfs2/edit/hexedit.h index d7dbe85..68116f5 100644 --- a/gfs2/edit/hexedit.h +++ b/gfs2/edit/hexedit.h @@ -42,7 +42,7 @@ #endif #define DMODES 3 -enum dsp_mode { HEX_MODE = 0, GFS2_MODE = 1, EXTENDED_MODE = 2 }; +enum dsp_mode { HEX_MODE = 0, GFS2_MODE = 1, EXTENDED_MODE = 2, INIT_MODE = 3 }; #define BLOCK_STACK_SIZE 256 #define GFS_FORMAT_SB (100) /* Super-Block */ @@ -184,6 +184,8 @@ EXTERN void gfs_jindex_in(struct gfs_jindex *jindex, char *buf); EXTERN void gfs_log_header_in(struct gfs_log_header *head, char *buf); EXTERN void gfs_log_header_print(struct gfs_log_header *lh); EXTERN void gfs_dinode_in(struct gfs_dinode *di, char *buf); +EXTERN int display(int identify_only); +EXTERN uint64_t check_keywords(const char *kword); struct gfs2_dirents { uint64_t block; diff --git a/gfs2/edit/savemeta.c b/gfs2/edit/savemeta.c index a2082ea..6c36b69 100644 --- a/gfs2/edit/savemeta.c +++ b/gfs2/edit/savemeta.c @@ -98,13 +98,7 @@ int get_gfs_struct_info(char *buf, int *block_type, int *struct_len) *struct_len = sbd.bsize; /*sizeof(struct gfs_leaf);*/ break; case GFS2_METATYPE_JD: /* 7 (journal data) */ - /* GFS1 keeps indirect pointers in GFS2_METATYPE_JD blocks - so we need to save the whole block. For GFS2, we don't - want to, or we might capture user data, which is bad. */ - if (gfs1) - *struct_len = sbd.bsize; - else - *struct_len = sizeof(struct gfs2_meta_header); + *struct_len = sbd.bsize; break; case GFS2_METATYPE_LH: /* 8 (log header) */ if (gfs1) @@ -325,11 +319,9 @@ void save_inode_data(int out_fd) osi_list_t *prev_list, *cur_list, *tmp; struct gfs2_buffer_head *metabh, *mybh; int i; - char *buf; for (i = 0; i < GFS2_MAX_META_HEIGHT; i++) osi_list_init(&metalist[i]); - buf = malloc(sbd.bsize); metabh = bread(&sbd.buf_list, block); if (gfs1) inode = inode_get(&sbd, metabh); @@ -338,8 +330,15 @@ void save_inode_data(int out_fd) height = inode->i_di.di_height; /* If this is a user inode, we don't follow to the file height. We stop one level less. That way we save off the indirect - pointer blocks but not the actual file contents. */ - if (height && !block_is_systemfile()) + pointer blocks but not the actual file contents. The exception + is directories, where the height represents the level at which + the hash table exists, and we have to save the directory data. */ + if (inode->i_di.di_flags & GFS2_DIF_EXHASH && + (S_ISDIR(inode->i_di.di_mode) || + (gfs1 && inode->i_di.__pad1 == GFS_FILE_DIR))) + height++; + else if (height && !block_is_systemfile() && + !S_ISDIR(inode->i_di.di_mode)) height--; osi_list_add(&metabh->b_altlist, &metalist[0]); for (i = 1; i <= height; i++){ @@ -398,7 +397,6 @@ void save_inode_data(int out_fd) brelse(metabh, not_updated); } inode_put(inode, not_updated); - free(buf); } void get_journal_inode_blocks(void) @@ -702,26 +700,26 @@ int restore_data(int fd, int in_fd, int printblocksonly) uint64_t buf64, writes = 0, highest_valid_block = 0; uint16_t buf16; int first = 1, pos; - char buf[256]; + char rdbuf[256]; char gfs_superblock_id[8] = {0x01, 0x16, 0x19, 0x70, 0x00, 0x00, 0x00, 0x01}; if (!printblocksonly) do_lseek(fd, 0); do_lseek(in_fd, 0); - rs = read(in_fd, buf, sizeof(buf)); - if (rs != sizeof(buf)) { + rs = read(in_fd, rdbuf, sizeof(rdbuf)); + if (rs != sizeof(rdbuf)) { fprintf(stderr, "Error: File is too small.\n"); return -1; } - for (pos = 0; pos < sizeof(buf) - sizeof(uint64_t) - sizeof(uint16_t); + for (pos = 0; pos < sizeof(rdbuf) - sizeof(uint64_t) - sizeof(uint16_t); pos++) { - if (!memcmp(&buf[pos + sizeof(uint64_t) + sizeof(uint16_t)], + if (!memcmp(&rdbuf[pos + sizeof(uint64_t) + sizeof(uint16_t)], gfs_superblock_id, sizeof(gfs_superblock_id))) { break; } } - if (pos == sizeof(buf) - sizeof(uint64_t) - sizeof(uint16_t)) + if (pos == sizeof(rdbuf) - sizeof(uint64_t) - sizeof(uint16_t)) pos = 0; do_lseek(in_fd, pos); blks_saved = total_out = 0; @@ -749,68 +747,80 @@ int restore_data(int fd, int in_fd, int printblocksonly) } rs = read(in_fd, &buf16, sizeof(uint16_t)); savedata->siglen = be16_to_cpu(buf16); - if (savedata->siglen <= sizeof(savedata->buf)) { - if (savedata->siglen) - do_read(in_fd, savedata->buf, - savedata->siglen); - if (first) { - struct gfs2_sb bufsb; - - memcpy(&bufsb, savedata->buf, sizeof(bufsb)); - gfs2_sb_in(&sbd.sd_sb, (void *)&bufsb); - sbd1 = (struct gfs_sb *)&sbd.sd_sb; - if (sbd1->sb_fs_format == GFS_FORMAT_FS && - sbd1->sb_header.mh_type == - GFS_METATYPE_SB && - sbd1->sb_header.mh_format == - GFS_FORMAT_SB && - sbd1->sb_multihost_format == - GFS_FORMAT_MULTI) { - gfs1 = TRUE; - } else if (check_sb(&sbd.sd_sb)) { - fprintf(stderr,"Error: Invalid superblock data.\n"); - return -1; - } - sbd.bsize = sbd.sd_sb.sb_bsize; - if (!printblocksonly) { - last_fs_block = - lseek(fd, 0, SEEK_END) / - sbd.bsize; - printf("There are %" PRIu64 " blocks of " \ - "%u bytes in the destination" \ - " file system.\n\n", - last_fs_block, sbd.bsize); - } else { - printf("This is %s metadata\n", gfs1 ? - "gfs (not gfs2)" : "gfs2"); - } - first = 0; + if (savedata->siglen > sizeof(savedata->buf)) { + fprintf(stderr, "\nBad record length: %d for block #%" + PRIu64 " (0x%" PRIx64").\n", savedata->siglen, + savedata->blk, savedata->blk); + return -1; + } + if (savedata->siglen && + read(in_fd, savedata->buf, savedata->siglen) != + savedata->siglen) { + fprintf(stderr, "read error: %s from %s:%d: " + "block %lld (0x%llx)\n", + strerror(errno), __FUNCTION__, __LINE__, + (unsigned long long)savedata->blk, + (unsigned long long)savedata->blk); + exit(-1); + } + if (first) { + struct gfs2_sb bufsb; + + memcpy(&bufsb, savedata->buf, sizeof(bufsb)); + gfs2_sb_in(&sbd.sd_sb, (void *)&bufsb); + sbd1 = (struct gfs_sb *)&sbd.sd_sb; + if (sbd1->sb_fs_format == GFS_FORMAT_FS && + sbd1->sb_header.mh_type == + GFS_METATYPE_SB && + sbd1->sb_header.mh_format == + GFS_FORMAT_SB && + sbd1->sb_multihost_format == + GFS_FORMAT_MULTI) { + gfs1 = TRUE; + } else if (check_sb(&sbd.sd_sb)) { + fprintf(stderr,"Error: Invalid superblock data.\n"); + return -1; } - if (printblocksonly) { + sbd.bsize = sbd.sd_sb.sb_bsize; + if (!printblocksonly) { + last_fs_block = + lseek(fd, 0, SEEK_END) / sbd.bsize; + printf("There are %" PRIu64 " blocks of " \ + "%u bytes in the destination" \ + " file system.\n\n", + last_fs_block, sbd.bsize); + } else { + printf("This is %s metadata\n", gfs1 ? + "gfs (not gfs2)" : "gfs2"); + } + first = 0; + } + if (printblocksonly) { + block = savedata->blk; + if (block > highest_valid_block) + highest_valid_block = block; + if (printblocksonly > 1 && printblocksonly == block) { + memcpy(buf, savedata->buf, sbd.bsize); + block_in_mem = block; + display(0); + return 0; + } else if (printblocksonly == 1) { print_gfs2("%d (l=0x%x): ", blks_saved, savedata->siglen); - block = savedata->blk; - if (block > highest_valid_block) - highest_valid_block = block; display_block_type(savedata->buf, TRUE); - } else { - warm_fuzzy_stuff(savedata->blk, FALSE, FALSE); - if (savedata->blk >= last_fs_block) { - printf("\nOut of space on the destination " - "device; quitting.\n"); - break; - } - do_lseek(fd, savedata->blk * sbd.bsize); - do_write(fd, savedata->buf, sbd.bsize); - writes++; } - blks_saved++; } else { - fprintf(stderr, "\nBad record length: %d for block #%" - PRIu64 " (0x%" PRIx64").\n", savedata->siglen, - savedata->blk, savedata->blk); - return -1; + warm_fuzzy_stuff(savedata->blk, FALSE, FALSE); + if (savedata->blk >= last_fs_block) { + printf("\nOut of space on the destination " + "device; quitting.\n"); + break; + } + do_lseek(fd, savedata->blk * sbd.bsize); + do_write(fd, savedata->buf, sbd.bsize); + writes++; } + blks_saved++; } if (!printblocksonly) warm_fuzzy_stuff(savedata->blk, TRUE, FALSE); @@ -830,7 +840,7 @@ void complain(const char *complaint) } void restoremeta(const char *in_fn, const char *out_device, - int printblocksonly) + uint64_t printblocksonly) { int in_fd, error; @@ -849,7 +859,9 @@ void restoremeta(const char *in_fn, const char *out_device, if (sbd.device_fd < 0) die("Can't open destination file system %s: %s\n", out_device, strerror(errno)); - } + } else if (out_device) /* for printsavedmeta, the out_device is an + optional block no */ + printblocksonly = check_keywords(out_device); savedata = malloc(sizeof(struct saved_metablock)); if (!savedata) die("Can't allocate memory for the restore operation.\n");