/* * Copyright 1996-2004 by Hans Reiser, licensing governed by * reiserfsprogs/README * * The code to check the leaf structure. * Leaf is not supposed to be in the tree, it is rather a single one. * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "fsck.h" #include "misc/unaligned.h" #include "misc/malloc.h" #include /* 1 if some of fields in the block head of bh look bad */ int fsck_leaf_check_header(reiserfs_filsys_t * fs, reiserfs_bh_t * bh) { reiserfs_node_head_t * blkh; int free_space, counted; blkh = NODE_HEAD (bh); if (!reiserfs_leaf_head(bh)) { /* We should not get here on rebuild. */ fsck_log ("block %lu: The block does not look like a leaf.\n", bh->b_blocknr); one_more_corruption (fs, FATAL); return 1; } if (reiserfs_nh_get_items (blkh) == 0) return 0; counted = reiserfs_leaf_count_items(bh); if (counted < reiserfs_nh_get_items (blkh)) { fsck_log ("block %lu: The number of items (%lu) is incorrect, " "should be (%lu)", bh->b_blocknr, reiserfs_nh_get_items(blkh), counted); if (fsck_mode(fs) == FSCK_REBUILD) { reiserfs_nh_set_items(blkh, counted); fsck_log (" - corrected\n"); reiserfs_buffer_mkdirty (bh); } else { fsck_log ("\n"); one_more_corruption (fs, FATAL); return 1; } } free_space = reiserfs_leaf_free_count(bh); if (reiserfs_nh_get_free (blkh) != free_space) { fsck_log ("block %lu: The free space (%lu) is incorrect, should " "be (%lu)", bh->b_blocknr, reiserfs_nh_get_free (blkh), free_space); if (fsck_mode(fs) != FSCK_CHECK && fsck_mode(fs) != FSCK_AUTO) { reiserfs_nh_set_free (blkh, free_space); fsck_log (" - corrected\n"); reiserfs_buffer_mkdirty (bh); } else { fsck_log ("\n"); one_more_corruption (fs, FIXABLE); return 1; } } return 0; } static int is_bad_sd (reiserfs_ih_t * ih, char * item) { if (reiserfs_key_get_off1 (&ih->ih_key) || reiserfs_key_get_uni (&ih->ih_key)) { fsck_log ("vpf-10610: StatData item %k has non " "zero offset found.\n", &ih->ih_key); return 1; } if (reiserfs_ih_get_len (ih) == REISERFS_SD_SIZE_V1) { /* looks like old stat data */ if (reiserfs_ih_get_format (ih) != KEY_FORMAT_1) { fsck_log ("vpf-10620: StatData item %k has wrong " "format.\n", &ih->ih_key); } } return 0; } static int is_bad_directory (reiserfs_ih_t * ih, char * item, int dev, int blocksize) { int i; char * name; int namelen, entrylen; reiserfs_deh_t * deh = (reiserfs_deh_t *)item; __u32 prev_offset = 0; __u16 prev_location = reiserfs_ih_get_len (ih); int min_entry_size = 1;/* we have no way to understand whether the filesystem wes created in 3.6 format or converted to it. So, we assume that minimal name length is 1 */ if (reiserfs_ih_get_len (ih) / (REISERFS_DEH_SIZE + min_entry_size) < reiserfs_ih_get_entries (ih)) { /* entry count is too big */ return 1; } for (i = 0; i < reiserfs_ih_get_entries (ih); i ++, deh ++) { entrylen = reiserfs_direntry_entry_len(ih, deh, i); if (entrylen > REISERFS_NAME_MAX) return 1; if (reiserfs_deh_get_off (deh) <= prev_offset) return 1; prev_offset = reiserfs_deh_get_off (deh); if (reiserfs_deh_get_loc(deh) + entrylen != prev_location) return 1; prev_location = reiserfs_deh_get_loc (deh); namelen = reiserfs_direntry_name_len (ih, deh, i); name = reiserfs_deh_name (deh, i); if (!reiserfs_hash_correct (&fs->hash, name, namelen, reiserfs_deh_get_off (deh))) { return 1; } } return 0; } /* change incorrect block adresses by 0. Do not consider such item as incorrect */ static int is_bad_extent (reiserfs_ih_t * ih, char * item, int dev, int blocksize) { unsigned long blocks; unsigned int i; int bad = 0; if (reiserfs_ih_get_len(ih) % REISERFS_EXT_SIZE) { fsck_log ("is_bad_extent: extent item of %H of invalid length\n", ih); return 1; } blocks = reiserfs_sb_get_blocks (fs->fs_ondisk_sb); for (i = 0; i < reiserfs_ext_count (ih); i ++) { __u32 * ind = (__u32 *)item; if (d32_get (ind, i) >= blocks) { bad ++; fsck_log ("is_bad_extent: %d-th pointer of item %H " "looks bad (%lu)\n", i, ih, d32_get (ind, i)); continue; } } return bad; } /* this is used by check.c: fsck_leaf_check */ int fsck_leaf_item_check (reiserfs_bh_t * bh, reiserfs_ih_t * ih, char * item) { int blocksize, dev; blocksize = bh->b_size; dev = bh->b_dev; // FIXME: refuse transparently bad items if (reiserfs_key_get_did (&ih->ih_key) == reiserfs_key_get_oid (&ih->ih_key)) { return 1; } if (!reiserfs_key_get_did (&ih->ih_key) || !reiserfs_key_get_oid (&ih->ih_key)) { return 1; } if (reiserfs_ih_stat(ih)) return is_bad_sd (ih, item); if (reiserfs_ih_dir (ih)) return is_bad_directory (ih, item, dev, blocksize); if (reiserfs_ih_ext (ih)) return is_bad_extent (ih, item, dev, blocksize); if (reiserfs_ih_direct (ih)) return 0; return 1; } /* 1 if i-th and (i-1)-th items can not be neighbors in a leaf */ int fsck_leaf_check_neigh (reiserfs_filsys_t * fs, reiserfs_bh_t * bh, int pos) { reiserfs_ih_t * ih; ih = reiserfs_ih_at (bh, pos); if (reiserfs_key_comp (&((ih - 1)->ih_key), &ih->ih_key) != -1) { if (fsck_mode (fs) != FSCK_REBUILD) one_more_corruption (fs, FATAL); return 1; } if (reiserfs_ih_stat (ih)) /* left item must be of another object */ if (reiserfs_key_comp2 (&((ih - 1)->ih_key), &ih->ih_key) != -1) { if (fsck_mode (fs) != FSCK_REBUILD) one_more_corruption (fs, FATAL); return 1; } if (reiserfs_key_get_oid (&ih->ih_key) == REISERFS_BAD_OID) { /* BAD BLOCK LIST SUPPORT. */ if (reiserfs_key_get_did(&ih->ih_key) == REISERFS_BAD_DID && reiserfs_ih_ext(ih) && reiserfs_key_comp2(&((ih - 1)->ih_key), &ih->ih_key)) { return 0; } } else { /* Safe link support. */ if (reiserfs_key_get_did (&ih->ih_key) == (__u32)-1) { if (reiserfs_key_comp2 (&((ih - 1)->ih_key), &ih->ih_key) == 0) { if (reiserfs_ih_ext (ih - 1) && reiserfs_ih_direct(ih)) return 0; /* safe link */ /* they do not look like safe links */ } else { if (reiserfs_ih_ext (ih) || reiserfs_ih_direct(ih)) return 0; /* safe link */ /* it does not look like safe link */ } } } if (reiserfs_ih_direct(ih)) { /* left item must be extent or stat data item of the same file */ if (reiserfs_key_comp2 (&((ih - 1)->ih_key), &ih->ih_key)) { if (fsck_mode (fs) != FSCK_REBUILD) one_more_corruption (fs, FATAL); return 1; } if (!((reiserfs_ih_stat (ih - 1) && reiserfs_key_get_off (&ih->ih_key) == 1) || (reiserfs_ih_ext (ih - 1) && reiserfs_key_get_off (&(ih - 1)->ih_key) + reiserfs_leaf_ibytes (ih-1, bh->b_size) == reiserfs_key_get_off (&ih->ih_key)))) { if (fsck_mode (fs) != FSCK_REBUILD) one_more_corruption (fs, FATAL); return 1; } } if (reiserfs_ih_ext (ih) || reiserfs_ih_dir (ih)) { /* left item must be stat data of the same object */ if (reiserfs_key_comp2 (&((ih - 1)->ih_key), &ih->ih_key) || !reiserfs_ih_stat (ih - 1)) { if (fsck_mode (fs) != FSCK_REBUILD) one_more_corruption (fs, FATAL); return 1; } } return 0; } int fsck_leaf_check (reiserfs_bh_t * bh) { int i; reiserfs_ih_t * ih; int bad = 0; assert (reiserfs_leaf_head (bh)); for (i = 0, ih = reiserfs_ih_at (bh, 0); i < reiserfs_node_items (bh); i ++, ih ++) { if (fsck_leaf_item_check (bh, ih, reiserfs_item_by_ih (bh, ih))) { fsck_log ("%s: block %lu, item %d: The corrupted item " "found (%H)\n", __FUNCTION__, bh->b_blocknr, i, ih); bad = 1; continue; } if (i && fsck_leaf_check_neigh (fs, bh, i)) { fsck_log ("%s: block %lu items %d and %d: Wrong order of " "items:\n\t%H\n\t%H\n", __FUNCTION__, bh->b_blocknr, i - 1, i, ih - 1, ih); bad = 1; } } return bad; } /* this item is in tree. All unformatted pointer are correct. Do not check them */ void fsck_item_save(reiserfs_path_t * path, saveitem_t ** head) { reiserfs_ih_t * ih = REISERFS_PATH_IH(path); saveitem_t *si, *cur; si = misc_getmem (sizeof (*si)); si->si_dnm_data = misc_getmem (reiserfs_ih_get_len(ih)); /*si->si_blocknr = blocknr;*/ memcpy (&(si->si_ih), ih, REISERFS_IH_SIZE); memcpy (si->si_dnm_data, REISERFS_PATH_ITEM(path), reiserfs_ih_get_len(ih)); if (*head == 0) { *head = si; } else { cur = *head; while (cur->si_next) cur = cur->si_next; cur->si_next = si; } return; } saveitem_t * fsck_item_free(saveitem_t * si) { saveitem_t * tmp = si->si_next; misc_freemem (si->si_dnm_data); misc_freemem (si); return tmp; }