| /* |
| * Copyright 2000-2004 by Hans Reiser, licensing governed by |
| * reiserfsprogs/README |
| */ |
| |
| #include "debugreiserfs.h" |
| #include <sys/resource.h> |
| |
| |
| #define print_usage_and_exit() die ("Usage: %s [-v] [-b filename] device\n\ |
| -v prints blocks number of every block unpacked\n\ |
| -b filename saves bitmap of blocks unpacked to filename\n\ |
| -j filename stores journal in the filename\n", argv[0]); |
| |
| |
| /* when super block gets unpacked for the first time - create a bitmap |
| and mark in it what have been unpacked. Save that bitmap at the end */ |
| reiserfs_bitmap_t * what_unpacked = 0; |
| |
| |
| int leaves, full; |
| |
| int verbose = 0; |
| |
| int Default_journal = 1; |
| |
| static void unpack_offset (struct packed_item * pi, struct item_head * ih, int blocksize) |
| { |
| |
| if (get_pi_mask(pi) & OFFSET_BITS_64) { |
| __u64 v64; |
| |
| if (get_ih_key_format (ih) != KEY_FORMAT_2) |
| die ("unpack_offset: key format is not set or wrong"); |
| fread_le64 (&v64); |
| set_offset (KEY_FORMAT_2, &ih->ih_key, v64); |
| return; |
| } |
| |
| if (get_pi_mask(pi) & OFFSET_BITS_32) { |
| __u32 v32; |
| |
| fread_le32 (&v32); |
| set_offset (get_ih_key_format (ih), &ih->ih_key, v32); |
| return; |
| } |
| |
| if ((get_pi_mask(pi) & DIR_ID) == 0 && (get_pi_mask(pi) & OBJECT_ID) == 0) { |
| /* offset was not sent, as it can be calculated looking at the |
| previous item */ |
| if (is_stat_data_ih (ih - 1)) |
| set_offset (get_ih_key_format (ih), &ih->ih_key, 1); |
| if (is_indirect_ih (ih - 1)) |
| set_offset (get_ih_key_format (ih), &ih->ih_key, |
| get_offset (&(ih - 1)->ih_key) + get_bytes_number (ih - 1, blocksize)); |
| } |
| |
| // offset is 0 |
| return; |
| } |
| |
| |
| static void unpack_type (struct packed_item * pi, struct item_head * ih) |
| { |
| set_type (get_ih_key_format (ih), &ih->ih_key, get_pi_type(pi)); |
| if (type_unknown (&ih->ih_key)) |
| reiserfs_panic ("unpack_type: unknown type %d unpacked for %H\n", |
| get_pi_type(pi), ih); |
| } |
| |
| |
| /* direntry item comes in the following format: |
| for each entry |
| mask - 8 bits |
| entry length - 16 bits |
| entry itself |
| deh_objectid - 32 bits |
| maybe deh_dir_id (32 bits) |
| maybe gencounter (16) |
| maybe deh_state (16) |
| */ |
| static void unpack_direntry (struct packed_item * pi, struct buffer_head * bh, |
| struct item_head * ih, hashf_t hash_func) |
| { |
| __u16 entry_count, namelen, gen_counter, entry_len; |
| __u8 mask; |
| int i; |
| struct reiserfs_de_head * deh; |
| int location; |
| char * item; |
| |
| /* if (!hash_func) |
| die ("unpack_direntry: hash function is not set");*/ |
| |
| if (!(get_pi_mask(pi) & IH_FREE_SPACE)) |
| die ("ih_entry_count must be packed for directory items"); |
| |
| entry_count = get_ih_entry_count (ih); |
| /* if (!entry_count) |
| reiserfs_panic ("unpack_direntry: entry count should be set already");*/ |
| |
| item = bh->b_data + get_ih_location (ih); |
| deh = (struct reiserfs_de_head *)item; |
| location = get_pi_item_len(pi); |
| for (i = 0; i < entry_count; i ++, deh ++) { |
| fread8 (&mask); |
| fread_le16 (&entry_len); |
| location -= entry_len; |
| set_deh_location (deh, location); |
| fread (item + location, entry_len, 1, stdin); |
| |
| /* find name length */ |
| if (*(item + location + entry_len - 1)) |
| namelen = entry_len; |
| else |
| namelen = strlen (item + location); |
| |
| fread32 (&deh->deh2_objectid); |
| if (mask & HAS_DIR_ID) |
| fread32 (&deh->deh2_dir_id); |
| else |
| set_deh_dirid (deh, get_key_objectid (&ih->ih_key)); |
| |
| if (*(item + location) == '.' && namelen == 1) |
| /* old or new "." */ |
| set_deh_offset (deh, DOT_OFFSET); |
| else if (*(item + location) == '.' && *(item + location + 1) == '.' && namelen == 2) |
| /* old or new ".." */ |
| set_deh_offset (deh, DOT_DOT_OFFSET); |
| else if (hash_func) |
| set_deh_offset (deh, hash_value(hash_func, item + location, namelen)); |
| if (mask & HAS_GEN_COUNTER) { |
| fread_le16 (&gen_counter); |
| set_deh_offset (deh, get_deh_offset (deh) | gen_counter); |
| } |
| |
| if (mask & HAS_STATE) |
| fread16 (&deh->deh2_state); |
| else |
| set_deh_state (deh, (1 << DEH_Visible2)); |
| } |
| |
| return; |
| } |
| |
| |
| /* struct packed_item is already unpacked */ |
| static void unpack_stat_data (struct packed_item * pi, struct buffer_head * bh, |
| struct item_head * ih) |
| { |
| if (!(get_pi_mask(pi) & IH_FREE_SPACE)) { |
| /* ih_free_space was not packed - set default */ |
| set_ih_entry_count (ih, 0xffff); |
| } |
| |
| if (get_ih_key_format (ih) == KEY_FORMAT_1) { |
| /* stat data comes in the following format: |
| if this is old stat data: |
| mode - 16 bits |
| nlink - 16 bits |
| size - 32 bits |
| blocks/rdev - 32 bits |
| maybe first_direct byte 32 bits |
| */ |
| struct stat_data_v1 * sd; |
| |
| sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); |
| memset (sd, 0, sizeof (sd)); |
| |
| fread16 (&sd->sd_mode); |
| fread16 (&sd->sd_nlink); |
| fread32 (&sd->sd_size); |
| fread32 (&sd->u.sd_blocks); |
| |
| if (get_pi_mask(pi) & WITH_SD_FIRST_DIRECT_BYTE) { |
| fread32 (&sd->sd_first_direct_byte); |
| } else { |
| sd->sd_first_direct_byte = 0xffffffff; |
| } |
| } else { |
| /* for new stat data |
| mode - 16 bits |
| nlink in either 16 or 32 bits |
| size in either 32 or 64 bits |
| blocks - 32 bits |
| */ |
| struct stat_data * sd; |
| |
| sd = (struct stat_data *)B_I_PITEM (bh, ih); |
| memset (sd, 0, sizeof (sd)); |
| |
| fread16 (&sd->sd_mode); |
| |
| if (get_pi_mask(pi) & NLINK_BITS_32) { |
| fread32 (&sd->sd_nlink); |
| } else { |
| __u16 nlink16; |
| |
| fread16 (&nlink16); |
| set_sd_v2_nlink (sd, le16_to_cpu(nlink16)); |
| } |
| |
| if (get_pi_mask(pi) & SIZE_BITS_64) { |
| fread64 (&sd->sd_size); |
| } else { |
| __u32 size32; |
| |
| /* We need the endian conversions since sd->sd_size is 64 bit */ |
| fread_le32 (&size32); |
| set_sd_v2_size (sd, size32 ); |
| } |
| |
| fread32 (&sd->sd_blocks); |
| } |
| |
| return; |
| } |
| |
| |
| /* indirect item comes either in packed form or as is. ih_free_space |
| can go first */ |
| static void unpack_indirect (struct packed_item * pi, struct buffer_head * bh, |
| struct item_head * ih) |
| { |
| __u32 * ind_item, * end; |
| int i; |
| __u16 v16; |
| |
| if (!(get_pi_mask(pi) & IH_FREE_SPACE)) { |
| /* ih_free_space was not packed - set default */ |
| set_ih_entry_count (ih, 0); |
| } |
| |
| ind_item = (__u32 *)B_I_PITEM (bh, ih); |
| |
| if (get_pi_mask(pi) & SAFE_LINK) { |
| d32_put(ind_item, 0, get_key_dirid(&ih->ih_key)); |
| set_key_dirid(&ih->ih_key, (__u32)-1); |
| return; |
| } |
| |
| if (get_pi_mask(pi) & WHOLE_INDIRECT) { |
| fread (ind_item, get_pi_item_len(pi), 1, stdin); |
| return; |
| } |
| |
| end = ind_item + I_UNFM_NUM (ih); |
| while (ind_item < end) { |
| __u32 base; |
| fread32 (ind_item); |
| fread_le16 (&v16); |
| base = d32_get(ind_item, 0); |
| for (i = 1; i < v16; i ++) { |
| if (base != 0) |
| d32_put(ind_item, i, base + i); |
| else |
| d32_put(ind_item, i, 0); |
| } |
| ind_item += i; |
| } |
| return; |
| } |
| |
| |
| // FIXME: we have no way to preserve symlinks |
| static void unpack_direct (struct packed_item * pi, struct buffer_head * bh, |
| struct item_head * ih) |
| { |
| __u32 * d_item = (__u32 *)B_I_PITEM (bh, ih); |
| |
| if (!(get_pi_mask(pi) & IH_FREE_SPACE)) |
| /* ih_free_space was not packed - set default */ |
| set_ih_entry_count (ih, 0xffff); |
| |
| if (get_pi_mask(pi) & SAFE_LINK) { |
| d32_put(d_item, 0, get_key_dirid(&ih->ih_key)); |
| set_key_dirid(&ih->ih_key, (__u32)-1); |
| } else { |
| memset (d_item, 'a', get_pi_item_len(pi)); |
| } |
| return; |
| } |
| |
| |
| static void unpack_leaf (int dev, hashf_t hash_func, __u16 blocksize) |
| { |
| static int unpacked_leaves = 0; |
| struct buffer_head * bh; |
| struct packed_item pi; |
| struct item_head * ih; |
| int i; |
| __u16 v16; |
| __u32 v32; |
| |
| /* block number */ |
| fread_le32 (&v32); |
| |
| |
| /* item number */ |
| fread_le16 (&v16); |
| |
| if (verbose) |
| reiserfs_warning (stderr, "leaf %d: %d items\n", v32, v16); |
| |
| bh = getblk (dev, v32, blocksize); |
| if (!bh) |
| die ("unpack_leaf: getblk failed"); |
| |
| set_blkh_nr_items (B_BLK_HEAD (bh), v16); |
| set_blkh_level (B_BLK_HEAD (bh), DISK_LEAF_NODE_LEVEL); |
| set_blkh_free_space (B_BLK_HEAD (bh), MAX_FREE_SPACE (bh->b_size)); |
| |
| |
| ih = B_N_PITEM_HEAD (bh, 0); |
| for (i = 0; i < get_blkh_nr_items (B_BLK_HEAD (bh)); i ++, ih ++) { |
| #if 0 |
| fread32 (&v32); |
| if (v32 != ITEM_START_MAGIC) |
| die ("unpack_leaf: no start item magic found: block %lu, item %i", |
| bh->b_blocknr, i); |
| #endif |
| |
| fread (&pi, sizeof (struct packed_item), 1, stdin); |
| |
| /* dir_id - if it is there */ |
| if (get_pi_mask(&pi) & DIR_ID) { |
| fread32 (&v32); |
| set_key_dirid (&ih->ih_key, le32_to_cpu(v32)); |
| } else { |
| if (!i) |
| die ("unpack_leaf: dir_id is not set"); |
| set_key_dirid (&ih->ih_key, get_key_dirid (&(ih - 1)->ih_key)); |
| } |
| |
| /* object_id - if it is there */ |
| if (get_pi_mask(&pi) & OBJECT_ID) { |
| fread32 (&v32); |
| set_key_objectid (&ih->ih_key, le32_to_cpu(v32)); |
| } else { |
| if (!i) |
| die ("unpack_leaf: object_id is not set"); |
| set_key_objectid (&ih->ih_key, get_key_objectid (&(ih - 1)->ih_key)); |
| } |
| |
| // we need to set item format before offset unpacking |
| set_ih_key_format (ih, (get_pi_mask(&pi) & NEW_FORMAT) ? KEY_FORMAT_2 : KEY_FORMAT_1); |
| |
| // offset |
| unpack_offset (&pi, ih, bh->b_size); |
| |
| /* type */ |
| unpack_type (&pi, ih); |
| |
| /* ih_free_space and ih_format */ |
| if (get_pi_mask(&pi) & IH_FREE_SPACE) { |
| fread16 (&v16); |
| set_ih_entry_count (ih, le16_to_cpu(v16)); |
| } |
| |
| if (get_pi_mask(&pi) & IH_FORMAT) |
| fread16 (&ih->ih_format); |
| |
| /* item length and item location */ |
| set_ih_item_len (ih, get_pi_item_len(&pi)); |
| set_ih_location (ih, (i ? get_ih_location (ih - 1) : bh->b_size) - get_pi_item_len(&pi)); |
| |
| // item itself |
| if (is_direct_ih (ih)) { |
| unpack_direct (&pi, bh, ih); |
| } else if (is_indirect_ih (ih)) { |
| unpack_indirect (&pi, bh, ih); |
| } else if (is_direntry_ih (ih)) { |
| unpack_direntry (&pi, bh, ih, hash_func); |
| } else if (is_stat_data_ih (ih)) { |
| unpack_stat_data (&pi, bh, ih); |
| } |
| set_blkh_free_space (B_BLK_HEAD (bh), get_blkh_free_space (B_BLK_HEAD (bh)) - |
| (IH_SIZE + get_ih_item_len (ih))); |
| |
| #if 0 |
| fread32 (&v32); |
| if (v32 != ITEM_END_MAGIC) |
| die ("unpack_leaf: no end item magic found: block %lu, item %i", |
| bh->b_blocknr, i); |
| if (verbose) |
| reiserfs_warning (stderr, "%d: %H\n", i, ih); |
| #endif |
| } |
| |
| fread_le16 (&v16); |
| if (v16 != LEAF_END_MAGIC) |
| die ("unpack_leaf: wrong end signature found - %x, block %lu", |
| v16, bh->b_blocknr); |
| |
| mark_buffer_uptodate (bh, 1); |
| mark_buffer_dirty (bh); |
| bwrite (bh); |
| /* |
| if (!not_data_block (bh->b_blocknr)) |
| data_blocks_unpacked ++; |
| */ |
| brelse (bh); |
| |
| if (what_unpacked) |
| reiserfs_bitmap_set_bit (what_unpacked, bh->b_blocknr); |
| /*unpacked ++;*/ |
| |
| if (!(++ unpacked_leaves % 10)) |
| fprintf (stderr, "#"); |
| } |
| |
| |
| static void unpack_full_block (int dev, int blocksize) |
| { |
| static int full_blocks_unpacked = 0; |
| __u32 block; |
| struct buffer_head * bh; |
| |
| fread_le32 (&block); |
| |
| if (verbose) |
| fprintf (stderr, "full #%d\n", block); |
| |
| bh = getblk (dev, block, blocksize); |
| if (!bh) |
| die ("unpack_full_block: getblk failed"); |
| |
| fread (bh->b_data, bh->b_size, 1, stdin); |
| |
| if (who_is_this (bh->b_data, bh->b_size) == THE_SUPER && !what_unpacked) { |
| unsigned long blocks; |
| struct buffer_head * tmp; |
| |
| blocks = get_sb_block_count ((struct reiserfs_super_block *)(bh->b_data)); |
| fprintf (stderr, "There were %lu blocks on the device\n", blocks); |
| what_unpacked = reiserfs_create_bitmap (blocks); |
| |
| /* make file as long as filesystem is */ |
| tmp = getblk (dev, blocks - 1, blocksize); |
| mark_buffer_dirty (tmp); |
| mark_buffer_uptodate (tmp, 0); |
| bwrite (tmp); |
| brelse (tmp); |
| } |
| |
| mark_buffer_uptodate (bh, 1); |
| mark_buffer_dirty (bh); |
| bwrite (bh); |
| /* |
| if (!not_data_block (bh->b_blocknr)) |
| data_blocks_unpacked ++; |
| */ |
| brelse (bh); |
| |
| if (what_unpacked) |
| reiserfs_bitmap_set_bit (what_unpacked, block); |
| /*unpacked ++;*/ |
| |
| if (!(++ full_blocks_unpacked % 50)) |
| fprintf (stderr, "."); |
| } |
| |
| |
| /* just skip bitmaps of unformatted nodes */ |
| static void unpack_unformatted_bitmap (int dev, int blocksize) |
| { |
| __u16 bmap_num; |
| __u32 block_count; |
| int i; |
| char * buf; |
| |
| fread_le16 (&bmap_num); |
| fread_le32 (&block_count); |
| |
| buf = malloc (blocksize); |
| if (!buf) |
| reiserfs_panic ("unpack_unformatted_bitmap: malloc failed: %m"); |
| |
| for (i = 0; i < bmap_num; i ++) { |
| if (fread (buf, blocksize, 1, stdin) != 1) |
| reiserfs_panic ("unpack_unformatted_bitmap: " |
| "could not read bitmap #%d: %m", i); |
| } |
| free (buf); |
| } |
| |
| |
| // read packed reiserfs partition metadata from stdin |
| void unpack_partition (int fd, int jfd) |
| { |
| __u32 magic32; |
| long position; |
| __u16 magic16; |
| __u16 blocksize; |
| int dev = fd; |
| |
| fread_le32 (&magic32); |
| if (magic32 != REISERFS_SUPER_MAGIC) |
| die ("unpack_partition: reiserfs magic number (0x%x) not found - %x\n", |
| REISERFS_SUPER_MAGIC, magic32); |
| |
| fread_le16 (&blocksize); |
| |
| if (verbose) |
| fprintf (stderr, "Blocksize %d\n", blocksize); |
| |
| while (!feof (stdin)) { |
| char c[2]; |
| |
| fread (c, 1, 1, stdin); |
| switch (c[0]) { |
| case '.': |
| if (verbose) |
| fprintf (stderr, "\".\" skipped\n"); |
| continue; |
| |
| case '1': |
| fread (c, 1, 1, stdin); /* that was 100%, read in first 0 */ |
| case '2': |
| case '4': |
| case '6': |
| case '8': |
| fread (c, 1, 1, stdin); |
| case '0': |
| fread (c + 1, 1, 1, stdin); /* read % */ |
| |
| if (c[0] != '0' || c[1] != '%') |
| die ("0%% expected\n"); |
| |
| if (verbose) |
| fprintf (stderr, "0%% skipped\n"); |
| continue; |
| } |
| |
| fread16 (&magic16); |
| magic16 = le16_to_cpu(magic16); |
| |
| switch (magic16 & 0xff) { |
| case LEAF_START_MAGIC: |
| leaves ++; |
| unpack_leaf (dev, code2func (magic16 >> 8), blocksize); |
| break; |
| |
| case SEPARATED_JOURNAL_START_MAGIC: |
| if (Default_journal) |
| die ("file name for separated journal has to be specified"); |
| dev = jfd; |
| break; |
| |
| case SEPARATED_JOURNAL_END_MAGIC: |
| dev = fd; |
| break; |
| |
| case FULL_BLOCK_START_MAGIC: |
| full ++; |
| unpack_full_block (dev, blocksize); |
| break; |
| |
| case UNFORMATTED_BITMAP_START_MAGIC: |
| fprintf (stderr, "\nBitmap of unformatted - ignored\n"); |
| unpack_unformatted_bitmap (dev, blocksize); |
| break; |
| |
| case END_MAGIC: |
| goto out; |
| |
| default: |
| position = ftell(stdin); |
| if (position == ~(long)0) |
| die ("unpack_partition: bad magic found - %x", magic16 & 0xff); |
| else |
| die ("unpack_partition: bad magic found - %x, position %lu", |
| magic16 & 0xff, ftell(stdin)); |
| } |
| } |
| out: |
| fprintf (stderr, "Unpacked %d leaves, %d full blocks\n", leaves, full); |
| |
| |
| /* fclose (block_list);*/ |
| } |
| |
| |
| int do_unpack(char *host, char *j_filename, char *filename, int verbose) { |
| int fd, fdj = -2; |
| struct rlimit lim = {RLIM_INFINITY, RLIM_INFINITY}; |
| |
| if (filename == NULL) |
| filename = ".bitmap"; |
| |
| if (j_filename) |
| Default_journal = 0; |
| |
| /* with this 2.4.0-test9's file_write does not send SIGXFSZ */ |
| if (setrlimit (RLIMIT_FSIZE, &lim)) { |
| fprintf (stderr, "sertlimit failed: %m\n"); |
| } |
| |
| if (misc_device_mounted(host) > 0) { |
| fprintf(stderr, "%s seems mounted, umount it first\n", host); |
| return 0; |
| } |
| |
| fd = open (host, O_RDWR | O_LARGEFILE); |
| if (fd == -1) { |
| perror ("open failed"); |
| return 0; |
| } |
| |
| if (!Default_journal) { |
| fdj = open (j_filename, O_RDWR | O_LARGEFILE); |
| if (fdj == -1) { |
| perror ("open failed"); |
| return 0; |
| } |
| } |
| |
| unpack_partition (fd, fdj); |
| |
| if (what_unpacked && filename) { |
| FILE * file = open_file(filename, "w+"); |
| reiserfs_bitmap_save (file, what_unpacked); |
| close_file(file); |
| } |
| |
| close (fd); |
| if (!Default_journal) |
| close (fdj); |
| return 0; |
| } |