Files
reiserfsprogs/utils/fsck/leaf.c
T

358 lines
8.9 KiB
C

/*
* 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 <assert.h>
/* 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;
}