/*
 * Copyright 1999-2004 by Hans Reiser, licensing governed by 
 * reiserfsprogs/README
 */

#include "fsck.h"

#if 0
struct check_relocated {
    __u32 old_dir_id;
    __u32 old_objectid;
    /*mode_t mode;*/
    struct check_relocated * next;
};

static struct check_relocated * relocated_list;

void to_be_relocated (struct key * key)
{
    struct check_relocated * cur, * prev, * new_relocated;

    cur = relocated_list;
    prev = 0;

    while (cur && comp_short_keys(key, (struct key *)cur) != 1) {
	if (comp_short_keys (key, (struct key *)cur) == 0)
	    return;
	prev = cur;
        cur = cur->next;
    }
        
    new_relocated = getmem (sizeof (struct check_relocated));
    copy_short_key ((struct key *)new_relocated, key);
             
    if (prev) {
        new_relocated->next = prev->next;
        prev->next = new_relocated;
    } else {
        new_relocated->next = relocated_list;
        relocated_list = new_relocated;
    }
}

int should_be_relocated (struct key * key)
{
    struct check_relocated *cur, *prev;
    int ret = 0;

    if (!key)
	return 0;
    
    cur = relocated_list;

    prev = NULL;
    while (cur && comp_short_keys(key, (struct key *)cur) != 1) {
	if (comp_short_keys (key, (struct key *)cur) == 0) {
	    ret = 1;
	    break;
	}
	prev = cur;
	cur = cur->next;
    }
 
    if (ret) {
	/* cur is found */
	if (prev) /* not the first */
	    prev->next = cur->next;
	else /* first */
	    relocated_list = cur->next;
	freemem (cur);
    }
 
    return ret;    
}

void clear_relocated_list() {
    struct check_relocated *next;

    while (relocated_list) {
        next = relocated_list->next;
        freemem (relocated_list);
        relocated_list = next;
    }
}

#endif

//
//
//
// check_fs_tree stops and recommends to run fsck --rebuild-tree when:
// 1. read fails
// 2. node of wrong level found in the tree
// 3. something in the tree points to wrong block number
//      out of filesystem boundary is pointed by tree
//      to block marked as free in bitmap
//      the same block is pointed from more than one place
//      not data blocks (journal area, super block, bitmaps)
// 4. bad formatted node found
// 5. delimiting keys are incorrect
//      



/* mark every block we see in the tree in control bitmap, so, when to make
   sure, that no blocks are pointed to from more than one place we use
   additional bitmap (control_bitmap). If we see pointer to a block we set
   corresponding bit to 1. If it is set already - run fsck with --rebuild-tree */
static reiserfs_bitmap_t * control_bitmap;
static reiserfs_bitmap_t * source_bitmap;

static int tree_scanning_failed = 0;


/* 1 if block is not marked as used in the bitmap */
static int is_block_free (reiserfs_filsys_t * fs, unsigned long block)
{
    return !reiserfs_bitmap_test_bit (source_bitmap, block);
}


/*static unsigned int hits = 0;*/

/* we have seen this block in the tree, mark corresponding bit in the
   control bitmap */
static void we_met_it (unsigned long block)
{
    reiserfs_bitmap_set_bit (control_bitmap, block);
    /*hits ++;*/
}


/* have we seen this block somewhere in the tree before? */
static int did_we_meet_it (unsigned long block)
{
    return reiserfs_bitmap_test_bit (control_bitmap, block);
}


static void init_control_bitmap (reiserfs_filsys_t * fs)
{
    unsigned int i;
    unsigned long block;
    unsigned long reserved;
    unsigned int blocks = get_sb_block_count(fs->fs_ondisk_sb);
    

    control_bitmap = reiserfs_create_bitmap(blocks);
    if (!control_bitmap)
	die ("init_control_bitmap: Failed to allocate a control bitmap.");

    /*printf ("Initially number of zeros in control bitmap %d\n", 
	      reiserfs_bitmap_zeros (control_bitmap));*/

    /* skipped and super block */
    for (i = 0; i <= fs->fs_super_bh->b_blocknr; i ++)
    	we_met_it (i);

    /*printf ("SKIPPED: %u blocks marked used (%d)\n", hits,
              reiserfs_bitmap_zeros (control_bitmap));
      hits = 0;*/

    /* bitmaps */
    block = fs->fs_super_bh->b_blocknr + 1;
    for (i = 0; i < reiserfs_fs_bmap_nr(fs); i ++) {
        we_met_it (block);

	if (spread_bitmaps (fs))
	    block = (block / (fs->fs_blocksize * 8) + 1) * 
		    (fs->fs_blocksize * 8);
	else
	    block ++;	
    }
    
    /*printf ("BITMAPS: %u blocks marked used (%d)\n", hits,
	      reiserfs_bitmap_zeros (control_bitmap));
	      
      hits = 0;*/
    
    /* mark as used area of the main device either containing a journal or
       reserved to hold it */

    reserved = get_size_of_journal_or_reserved_area (fs->fs_ondisk_sb);
    
    /* where does journal area (or reserved journal area) start from */

    if (!is_new_sb_location (fs->fs_super_bh->b_blocknr, fs->fs_blocksize) &&
    	!is_old_sb_location (fs->fs_super_bh->b_blocknr, fs->fs_blocksize))
	die ("init_control_bitmap: Wrong super block location. You must run "
	     "--rebuild-sb.");

    block = get_journal_start_must (fs);

    for (i = block; i < reserved + block; i ++)
	we_met_it (i);

    if (fs->fs_badblocks_bm)
	for (i = 0; i < get_sb_block_count (fs->fs_ondisk_sb); i ++) {
	    if (reiserfs_bitmap_test_bit (fs->fs_badblocks_bm, i))
	    	we_met_it (i);	
	}
}


/* if we managed to complete tree scanning and if control bitmap and/or proper
   amount of free blocks mismatch with bitmap on disk and super block's
   s_free_blocks - we can fix that */
static void handle_bitmaps (reiserfs_filsys_t * fs)
{
    int problem = 0;

    if (tree_scanning_failed) {
	fsck_progress ("Could not scan the internal tree. --rebuild-tree "
		       "is required\n");
	return;
    }

    fsck_progress ("Comparing bitmaps..");

    /* check free block counter */
    if (get_sb_free_blocks (fs->fs_ondisk_sb) != 
	reiserfs_bitmap_zeros (control_bitmap)) 
    {
/*	fsck_log ("vpf-10630: The count of free blocks in the on-disk bitmap "
		  "(%lu) mismatches with the correct one (%lu).\n",  
		  get_sb_free_blocks (fs->fs_ondisk_sb),
		  reiserfs_bitmap_zeros (control_bitmap));
*/
	problem++;
    }

    if (reiserfs_bitmap_compare (source_bitmap, control_bitmap))
	problem++;    

    if (problem) {
        if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
	    fsck_log ("vpf-10630: The on-disk and the correct bitmaps differs. "
		"Will be fixed later.\n");
//            fsck_progress ("Trying to fix bitmap ..\n");
            /* mark blocks as used in source bitmap if they are used in 
	       control bitmap */
            reiserfs_bitmap_disjunction (source_bitmap, control_bitmap);
            /* change used blocks count accordinly source bitmap, copy bitmap 
	       changes to on_disk bitmap */
            set_sb_free_blocks (fs->fs_ondisk_sb, 
				reiserfs_bitmap_zeros(source_bitmap));
            reiserfs_bitmap_copy (fs->fs_bitmap2, source_bitmap);
            mark_buffer_dirty (fs->fs_super_bh);
/*
            // check again 
            if ((diff = reiserfs_bitmap_compare(source_bitmap, 
						control_bitmap)) != 0)  
	    {
                // do not mark them as fatal or fixable because one can live 
		// with leaked space. So this is not a fatal corruption, and 
		// fix-fixable cannot fix it 
                fsck_progress (" bitmaps were not recovered.\n\tYou can either "
		"run rebuild-tree or live with %d leaked blocks\n", diff);
            } else {
                fsck_progress ("finished\n");
            }
*/
        } else if (problem) {
	    fsck_log ("vpf-10640: The on-disk and the correct bitmaps differs.\n");
            while (problem) {
                /* fixable corruptions because we can try to recover them 
		   without rebuilding the tree */
        	one_more_corruption (fs, FIXABLE); 
        	problem --;
            }
        }
    } else 
        fsck_progress ("finished\n");
        
    return;
}

static int auto_handle_bitmaps (reiserfs_filsys_t *fs) {
    unsigned long i;
    
    if (source_bitmap->bm_byte_size != control_bitmap->bm_byte_size)
	return -1;
    
    for (i = 0; i < source_bitmap->bm_byte_size; i ++) {
	if (control_bitmap->bm_map[i] & ~source_bitmap->bm_map[i]) {
	    return 1;
	}
    }
    
    return 0;
}


/* is this block legal to be pointed to by some place of the tree? */
static int bad_block_number (reiserfs_filsys_t * fs, unsigned long block)
{
    if (block >= get_sb_block_count (fs->fs_ondisk_sb) ||
	not_data_block (fs, block)) {
	/* block has value which can not be used as a pointer in a tree */
	
	return 1;
/*
	if (is_unfm_pointer) {
	    // unformatted node pointer will be zeroed
	    one_more_corruption (fs, fixable);
	    return 1;
	}

	// internal nodes can not have wrong pointer
	one_more_corruption (fs, fatal);
	return 1;
*/

    }

    if (is_block_free (fs, block)) {
	/* block is marked free - bitmap problems will be handled later */
	//one_more_corruption (fs, fixable);
    }

    return 0;
}


static int got_already (reiserfs_filsys_t * fs, unsigned long block)
{
    if (did_we_meet_it (block)) {
	/* block is in tree at least twice */
    	return 1;
    }
    we_met_it (block);
    return 0;
}

/* 1 if it does not look like reasonable stat data */
static int bad_stat_data (reiserfs_filsys_t * fs, 
			  struct buffer_head * bh, 
			  struct item_head * ih)
{
    unsigned long objectid;
//    __u32 links;

    objectid = get_key_objectid (&ih->ih_key);
    if (!is_objectid_used (fs, objectid)) {
	/* FIXME: this could be cured right here */
	fsck_log ("bad_stat_data: The objectid (%lu) is marked free, but used "
		  "by an object %k\n", objectid, &ih->ih_key);

        /* if it is FIX_FIXABLE we flush objectid map at the end
           no way to call one_less_corruption later
        */
	if (fsck_mode (fs) != FSCK_FIX_FIXABLE)
	    one_more_corruption (fs, FIXABLE);
    }

    if (id_map_mark(proper_id_map (fs), objectid)) {
	fsck_log ("bad_stat_data: The objectid (%lu) is shared by at least two "
		  "files. Can be fixed with --rebuild-tree only.\n", objectid);
#if 0
	to_be_relocated (&ih->ih_key);
//	one_more_corruption (fs, FIXABLE);	
#endif
    }

    return 0;
/* Check this on semantic_check pass.    

    sd = (struct stat_data *)B_I_PITEM(bh,ih);
    get_sd_nlink (ih, sd, &links);
    if (S_ISDIR(sd->sd_mode)) {
        if (links < 2) {
            fsck_log ("%s: block %lu: The directory StatData %k has bad nlink number (%u)\n",
                __FUNCTION__, bh->b_blocknr, &ih->ih_key, links);
            one_more_corruption (fs, fatal);
        }
    } else {
        if (links == 0) {
            fsck_log ("%s: block %lu: The StatData %k has bad nlink number (%u)\n",
                __FUNCTION__, bh->b_blocknr, &ih->ih_key);
            one_more_corruption (fs, fatal);
        }
    }
*/    
}


/* it looks like we can check item length only */
static int bad_direct_item (reiserfs_filsys_t * fs, 
			    struct buffer_head * bh, 
			    struct item_head * ih)
{
    return 0;
}


inline void handle_one_pointer (reiserfs_filsys_t * fs, 
				struct buffer_head * bh,
				__u32 * item, int offset)
{
    if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
	fsck_log (" - zeroed");
	d32_put (item, offset, 0);
	mark_buffer_dirty (bh);
    } else {
	one_more_corruption (fs, FIXABLE);
    }
}


static int bad_badblocks_item (reiserfs_filsys_t * fs, struct buffer_head * bh,
			      struct item_head * ih) {
    __u32 i;
    __u32 * ind = (__u32 *)B_I_PITEM (bh, ih);

    if (get_ih_item_len (ih) % 4) {
	fsck_log ("%s: block %lu: item (%H) has bad length\n", __FUNCTION__,
		  bh->b_blocknr, ih);
	one_more_corruption (fs, FATAL);
	return 1;
    }
	
    /* All valid badblocks are given in badblock bitmap. Nothing to check anymore. */
    if (fs->fs_badblocks_bm)
	    return 0;
    
    for (i = 0; i < I_UNFM_NUM (ih); i ++) {
	if (!d32_get (ind, i)) {
/*	    fsck_log ("%s: block %lu: badblocks item (%H) has zero pointer.",
		      __FUNCTION__, bh->b_blocknr, ih);
	    
	    if (fsck_mode(fs) != FSCK_FIX_FIXABLE) {
		    fsck_log("Not an error, but could be deleted with --fix-fixable\n");
	    } else {
		    fsck_log("Will be deleted later.\n");
	    }*/
	    
	    continue;
	}

	/* check list of badblocks pointers */
	if (d32_get (ind, i) >= get_sb_block_count (fs->fs_ondisk_sb)) {
	    fsck_log ("%s: badblock pointer (block %lu) points out of disk spase (%lu)",
			__FUNCTION__, bh->b_blocknr, d32_get (ind, i));
	    handle_one_pointer (fs, bh, ind, i);
	    fsck_log ("\n");
	}

	if (did_we_meet_it (d32_get (ind, i))) {
	    /* it can be
	       1. not_data_block
	       		delete pointer
	       2. ind [i] or internal/leaf
	          advice to run fix-fixable if there is no fatal errors
	          with list of badblocks, say that it could fix it. */
	
	    if (not_data_block (fs, d32_get (ind, i))) {
		fsck_log ("%s: badblock pointer (block %lu) points on fs metadata (%lu)",
			  __FUNCTION__, bh->b_blocknr, d32_get (ind, i));
		handle_one_pointer (fs, bh, ind, i);
		fsck_log ("\n");
	    } else {
		one_more_corruption(fs, FIXABLE);
	        fsck_log ("%s: badblock pointer (block %lu) points to a block (%lu) "
			  "which is in the tree already. Use badblock option (-B) to"
			  " fix the problem\n", __FUNCTION__, bh->b_blocknr, 
			  d32_get (ind, i));
	    }

	    continue;
	}
	
	we_met_it(d32_get(ind, i));
    }

    return 0;
}

/* for each unformatted node pointer: make sure it points to data area and
   that it is not in the tree yet */
static int bad_indirect_item (reiserfs_filsys_t * fs, struct buffer_head * bh,
			      struct item_head * ih)
{
    __u32 * ind = (__u32 *)B_I_PITEM (bh, ih);
    unsigned int i;

    if (get_ih_item_len (ih) % 4) {
	fsck_log ("%s: block %lu: The item (%H) has the bad length (%u)\n",
	    __FUNCTION__, bh->b_blocknr, ih, get_ih_item_len (ih));
	one_more_corruption (fs, FATAL);
	return 1;
    }

    for (i = 0; i < I_UNFM_NUM (ih); i ++) {

	fsck_check_stat (fs)->unfm_pointers ++;
	if (!d32_get (ind, i)) {
	    fsck_check_stat (fs)->zero_unfm_pointers ++;
	    continue;
	}

	/* check unformatted node pointer and mark it used in the
           control bitmap */
	if (bad_block_number (fs, d32_get (ind, i))) {
	    fsck_log ("%s: block %lu: The item %k has the bad pointer (%d) to "
		      "the block (%lu)", __FUNCTION__, bh->b_blocknr, 
		      &ih->ih_key, i, d32_get (ind, i));
	    
	    handle_one_pointer (fs, bh, ind, i);
	    fsck_log ("\n");
	    continue;
	}

        if (got_already (fs, d32_get (ind, i))) {
	    fsck_log ("%s: block %lu: The item (%H) has the bad pointer (%d) "
		      "to the block (%lu), which is in tree already", 
		      __FUNCTION__, bh->b_blocknr, ih, i, d32_get (ind, i));
	    
	    handle_one_pointer (fs, bh, ind, i);
	    fsck_log ("\n");
            continue;
	}
    }

#if 0
    /* delete this check for 3.6 */
    if (get_ih_free_space (ih) > fs->fs_blocksize - 1) {
        if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
            /*FIXME: fix it if needed*/
        } else {
            fsck_log ("bad_indirect_item: %H has wrong ih_free_space\n", ih);
            one_more_corruption (fs, fixable);
        }
    }
#endif

    return 0;
}


/* FIXME: this was is_bad_directory from pass0.c */
static int bad_directory_item (reiserfs_filsys_t * fs, 
			       struct buffer_head * bh, 
			       struct item_head * ih)
{
    char *name, *prev_name;
    __u32 off, prev_off;
    unsigned int count, i;
    struct reiserfs_de_head * deh = B_I_DEH (bh, ih);
    int min_entry_size = 1;/* We have no way to understand whether the
                              filesystem was created in 3.6 format or
                              converted to it. So, we assume that minimal name
                              length is 1 */
    __u16 state;
    int namelen;

    count = get_ih_entry_count (ih);

    if (count == 0) {
	one_more_corruption (fs, FATAL);	
	return 1;
    }
    
    /* make sure item looks like a directory */
    if (get_ih_item_len (ih) / (DEH_SIZE + min_entry_size) < count) {
	/* entry count can not be that big */
	fsck_log ("%s: block %lu: The directory item %k has the exsessively "
		  "big entry count (%u)\n",  __FUNCTION__, bh->b_blocknr, 
		  &ih->ih_key, count);
	
	one_more_corruption (fs, FATAL);	
	return 1;
    }

    if (get_deh_location (&deh[count - 1]) != DEH_SIZE * count) {
	/* last entry should start right after array of dir entry headers */
	fsck_log ("%s: block %lu: The directory item %k has the corrupted "
		  "entry structure\n", __FUNCTION__, bh->b_blocknr, 
		  &ih->ih_key);
	
	one_more_corruption (fs, FATAL);
	return 1;
    }
    
    /* check name hashing */
    prev_name = B_I_PITEM(bh, ih) + get_ih_item_len(ih);
    prev_off = 0;

    for (i = 0; i < count; i ++, deh ++) {
	namelen = name_in_entry_length (ih, deh, i);
	name = name_in_entry (deh, i);
	off = get_deh_offset (deh);
	
	if (namelen > (int)REISERFS_MAX_NAME_LEN(fs->fs_blocksize) || 
	    name >= prev_name || off <= prev_off) 
	{
	    fsck_log ("%s: block %lu: The directory item %k has a broken entry "
		      "(%d)\n", __FUNCTION__, bh->b_blocknr, &ih->ih_key, i);
	    one_more_corruption (fs, FATAL);
	    return 1;
	}

	if (!is_properly_hashed (fs, name, namelen, off)) {
	    fsck_log ("%s: block %lu: The directory item %k has a not properly "
		      "hashed entry (%d)\n", __FUNCTION__, bh->b_blocknr, 
		      &ih->ih_key, i);
	    
	    one_more_corruption (fs, FATAL);
	    return 1;
	}

	prev_name = name;
	prev_off = off;
    }

    deh = B_I_DEH (bh, ih);
    state = (1 << DEH_Visible2);
    /* ok, items looks like a directory */
    for (i = 0; i < count; i ++, deh ++) {
	if (get_deh_state (deh) != state) {
	    fsck_log ("bad_directory_item: block %lu: The directory item %k "
		      "has the entry (%d) \"%.*s\" with a not legal state "
		      "(%o), (%o) expected", bh->b_blocknr, &ih->ih_key, i, 
		      name_in_entry_length (ih, deh, i), name_in_entry (deh, i), 
		      get_deh_state (deh), state);
	    
	    if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
		set_deh_state (deh, 1 << DEH_Visible2);
		mark_buffer_dirty (bh);
		fsck_log (" - corrected\n");
	    } else 
		one_more_corruption (fs, FIXABLE);
	    
	    fsck_log ("\n");
	}
    }

    return 0;
}


static int bad_item (reiserfs_filsys_t * fs, struct buffer_head * bh, int num)
{
    struct item_head * ih;
    int format;

    ih = B_N_PITEM_HEAD (bh, num);

    if ((get_ih_flags(ih)) != 0) {
	if (fsck_mode(fs) != FSCK_FIX_FIXABLE) {
	    one_more_corruption (fs, FIXABLE);
	    fsck_log ("%s: vpf-10570: block %lu: The item header (%d) has not "
		      "cleaned flags.\n", __FUNCTION__, bh->b_blocknr, num);
	} else {
	    fsck_log ("%s: vpf-10580: block %lu: Flags in the item header "
		      "(%d) were cleaned\n", __FUNCTION__, bh->b_blocknr, num);
	    
	    clean_ih_flags(ih);
	    mark_buffer_dirty(bh);
	}
    }

    
    if (is_stat_data_ih(ih) && get_ih_item_len(ih) == SD_SIZE)
	format = KEY_FORMAT_2;
    else if (is_stat_data_ih(ih) && get_ih_item_len(ih) == SD_V1_SIZE)
	format = KEY_FORMAT_1;
    else 
	format = key_format(&ih->ih_key);
    
    if (format != get_ih_key_format(ih)) {
	if (fsck_mode(fs) != FSCK_FIX_FIXABLE) {
	    one_more_corruption (fs, FIXABLE);
	    fsck_log ("%s: vpf-10710: block %lu: The format (%d) specified "
		      "in the item header (%d) differs from the key format "
		      "(%d).\n", __FUNCTION__, bh->b_blocknr, 
		      get_ih_key_format(ih), num, format);
	} else {
	    fsck_log ("%s: vpf-10720: block %lu: The format (%d) specified in "
		      "the item header (%d) was fixed to the key format (%d).\n", 
		      __FUNCTION__, bh->b_blocknr, get_ih_key_format(ih), num, 
		      format);

	    set_ih_key_format(ih, format);
	    mark_buffer_dirty(bh);
	}
    }

    if (get_key_objectid (&ih->ih_key) == BADBLOCK_OBJID) {
	if (get_key_dirid (&ih->ih_key) == BADBLOCK_DIRID && is_indirect_ih (ih)) {
		/* Bad Block support. */
		return bad_badblocks_item (fs, bh, ih);
	}

	goto error;
    } else {
	if (get_key_dirid (&ih->ih_key) == (__u32)-1) {
	    /*  Safe Link support. Allowable safe links are:
		-1 object_id           0x1	INDIRECT (truncate) or
		-1 object_id   blocksize+1	DIRECT   (unlink) */
	    if (is_direct_ih(ih) && get_offset(&ih->ih_key) == fs->fs_blocksize + 1) {
		if (get_ih_item_len (ih) == 4) {
		    /*fsck_log("vpf-00010: safe link found %k\n", &ih->ih_key);*/
		    fsck_check_stat(fs)->safe ++;
		    return 0;
		}
	    }

	    if (is_indirect_ih(ih) && get_offset(&ih->ih_key) == 0x1) {
		if (get_ih_item_len (ih) == 4) {
		    /*fsck_log("vpf-00020: safe link found %k\n", &ih->ih_key);*/
		    fsck_check_stat(fs)->safe ++;
		    return 0;
		}
	    }
	
	    /* it does not look like safe link */
	    goto error;
	}
    }
    
    if (is_stat_data_ih (ih))
	return bad_stat_data (fs, bh, ih);

    if (is_direct_ih (ih))
	return bad_direct_item (fs, bh, ih);

    if (is_indirect_ih(ih))
	return bad_indirect_item (fs, bh, ih);
    
    return bad_directory_item (fs, bh, ih);
 
 error:
    one_more_corruption (fs, FATAL);
    fsck_log ("%s: vpf-10310: block %lu, item %d: The item has a wrong "
	      "key %k\n", __FUNCTION__, num, bh->b_blocknr, &ih->ih_key);
    return 1;
}


/* 1 if i-th and (i-1)-th items can not be neighbors in a leaf */
int bad_pair (reiserfs_filsys_t * fs, struct buffer_head * bh, int pos) {
    struct item_head * ih;

    ih = B_N_PITEM_HEAD (bh, pos);

    if (comp_keys (&((ih - 1)->ih_key), &ih->ih_key) != -1) {
	if (fsck_mode (fs) != FSCK_REBUILD)
	    one_more_corruption (fs, FATAL);	
	return 1;
    }

    if (is_stat_data_ih (ih))
	/* left item must be of another object */
	if (comp_short_keys (&((ih - 1)->ih_key), &ih->ih_key) != -1) {
	    if (fsck_mode (fs) != FSCK_REBUILD)
		one_more_corruption (fs, FATAL);	
	    return 1;
	}

    if (get_key_objectid (&ih->ih_key) == BADBLOCK_OBJID) {
	/* BAD BLOCK LIST SUPPORT. */
	if (get_key_dirid(&ih->ih_key) == BADBLOCK_DIRID && 
	    is_indirect_ih(ih) && comp_short_keys(&((ih - 1)->ih_key), 
						  &ih->ih_key))
		return 0;
    } else {
	/* Safe link support. */
	if (get_key_dirid (&ih->ih_key) == (__u32)-1) {
	    if (comp_short_keys (&((ih - 1)->ih_key), &ih->ih_key) == 0) {
		if (is_indirect_ih (ih - 1) && is_direct_ih(ih))
		    return 0; /* safe link */
		/* they do not look like safe links */
	    } else {
		if (is_indirect_ih (ih) || is_direct_ih(ih))
		    return 0; /* safe link */
		/* it does not look like safe link */
	    }
	}
    }
    
    if (is_direct_ih(ih)) {
	/* left item must be indirect or stat data item of the same file */
	if (not_of_one_file (&((ih - 1)->ih_key), &ih->ih_key)) {
	    if (fsck_mode (fs) != FSCK_REBUILD)
		one_more_corruption (fs, FATAL);
	    
	    return 1;
	}

	if (!((is_stat_data_ih (ih - 1) && get_offset (&ih->ih_key) == 1) ||
	      (is_indirect_ih (ih - 1) && get_offset (&(ih - 1)->ih_key) + 
	       get_bytes_number (ih-1, bh->b_size) == get_offset (&ih->ih_key)))) 
	{
	    if (fsck_mode (fs) != FSCK_REBUILD)
		one_more_corruption (fs, FATAL);
	    
	    return 1;
	}
    }

    if (is_indirect_ih (ih) || is_direntry_ih (ih)) {
	/* left item must be stat data of the same object */
	if (not_of_one_file (&((ih - 1)->ih_key), &ih->ih_key) ||
	    !is_stat_data_ih (ih - 1)) {
	    if (fsck_mode (fs) != FSCK_REBUILD)
		one_more_corruption (fs, FATAL);
	    return 1;
	}
    }

    return 0;
}

/* 1 if block head or any of items is bad */
static int bad_leaf (reiserfs_filsys_t * fs, struct buffer_head * bh)
{
    int i;

    if (leaf_structure_check(fs, bh))
	return 1;
    
    for (i = 0; i < B_NR_ITEMS (bh); i ++) {
	if (bad_item (fs, bh, i)) {
	    fsck_log ("bad_leaf: block %lu, item %d: The corrupted item found "
		      "(%H)\n", bh->b_blocknr, i, B_N_PITEM_HEAD (bh, i));
	}

	if (i && bad_pair (fs, bh, i)) {
	    fsck_log ("bad_leaf: block %lu, items %d and %d: The wrong order "
		      "of items: %k, %k\n", bh->b_blocknr, i - 1, i, 
		      &B_N_PITEM_HEAD (bh, i-1)->ih_key, 
		      &B_N_PITEM_HEAD (bh, i)->ih_key);
	}
    }
    return 0;
}

/* 1 if bh does not look like internal node */
static int bad_internal (reiserfs_filsys_t * fs, struct buffer_head * bh)
{
    int i;

    for (i = 0; i <= B_NR_ITEMS (bh); i ++) {
        if (i != B_NR_ITEMS (bh) && i != B_NR_ITEMS (bh) - 1)
	    /* make sure that keys are in increasing order */
            if (comp_keys (B_N_PDELIM_KEY (bh, i), 
			   B_N_PDELIM_KEY (bh, i + 1)) != -1) 
	    {
		fsck_log ("%s: vpf-10320: block %lu, items %d and %d: The "
			  "wrong order of items: %k, %k\n", __FUNCTION__, 
			  bh->b_blocknr, i, i + 1, B_N_PDELIM_KEY (bh, i), 
			  B_N_PDELIM_KEY (bh, i + 1));
		
		one_more_corruption (fs, FATAL);
                return 1;
	    }

	/* make sure that the child is correct */
        if (bad_block_number (fs, get_dc_child_blocknr (B_N_CHILD (bh,i)))) {
	    fsck_log ("%s: vpf-10330: block %lu, item %d: The internal item "
		      "points to the not legal block (%lu)\n", __FUNCTION__, 
		      bh->b_blocknr, i, get_dc_child_blocknr (B_N_CHILD (bh,i)));
	    
            one_more_corruption (fs, FATAL);
            return 1;
	}
    }
    return 0;
}


/* h == 0 for root level. block head's level == 1 for leaf level  */
static inline int h_to_level (reiserfs_filsys_t * fs, int h)
{
    return get_sb_tree_height (fs->fs_ondisk_sb) - h - 1;
}

#if 0
int dc_fix(struct buffer_head *bh, int pos, __u32 block) {
	if (!bh || !bh->b_data)
		return -1;

	if (B_NR_ITEMS(bh) < pos)
		return -1;

	set_dc_child_blocknr(B_N_CHILD(bh,pos), block);

	mark_buffer_dirty(bh);
	bwrite(bh);

	return 0;
}

/* Removes @N-th key and @(N+1) pointer. */
int internal_remove(struct buffer_head *bh, int pos) {
	char *delete;
	__u32 nr;
	
	if (!bh || !bh->b_data)
		return -1;

	if (B_NR_ITEMS(bh) < pos)
		return -1;

	delete = (char *)B_N_CHILD(bh, pos + 2);
	memmove(delete - DC_SIZE, delete, 
		bh->b_size - (delete - bh->b_data));

	delete = (char *)B_N_PDELIM_KEY(bh, pos + 1);
	memmove(delete - KEY_SIZE, delete, 
		bh->b_size - (delete - bh->b_data));

	nr = B_NR_ITEMS(bh) - 1;
	
	set_blkh_nr_items(B_BLK_HEAD(bh), nr);
	set_blkh_free_space(B_BLK_HEAD(bh), bh->b_size - 
			    (BLKH_SIZE + KEY_SIZE * nr + DC_SIZE * (nr + 1)));
	
	mark_buffer_dirty(bh);
	bwrite(bh);
	
	return 0;
}

int leaf_fix_key_oid(struct buffer_head *bh, int pos, __u32 oid) {
	struct item_head * ih;


	if (!bh || !bh->b_data)
		return -1;

	if (B_NR_ITEMS(bh) < pos)
		return -1;

	ih = B_N_PITEM_HEAD (bh, pos);
	set_key_objectid(&ih->ih_key, oid);
	
	mark_buffer_dirty(bh);
	bwrite(bh);

	return 0;
}
#endif

/* bh must be formatted node. blk_level must be tree_height - h + 1 */
static int bad_node (reiserfs_filsys_t * fs, struct buffer_head ** path, int h)
{
    struct buffer_head ** pbh = &path[h];

    if (B_LEVEL (*pbh) != h_to_level (fs, h)) {
       	fsck_log ("block %lu: The level of the node (%d) is not correct, "
		  "(%d) expected\n", (*pbh)->b_blocknr, B_LEVEL (*pbh), 
		  h_to_level (fs, h));
	
	one_more_corruption (fs, FATAL);
        return 1;
    }

    if (bad_block_number (fs, (*pbh)->b_blocknr)) {
	one_more_corruption (fs, FATAL);
	fsck_log ("%s: vpf-10340: The node in the wrong block number (%lu) "
		  "found in the tree\n",  __FUNCTION__, (*pbh)->b_blocknr);
	return 1;
    }

    if (got_already (fs, (*pbh)->b_blocknr)) {
	fsck_log ("%s: vpf-10350: The block (%lu) is used more than once "
		  "in the tree.\n",  __FUNCTION__, (*pbh)->b_blocknr);
	one_more_corruption (fs, FATAL);
        return 1;
    }
    
    if (is_leaf_node (*pbh)) {
	fsck_check_stat (fs)->leaves ++;
	return bad_leaf (fs, *pbh);
    }
    
    fsck_check_stat (fs)->internals ++;
    return bad_internal (fs, *pbh);
}


/* internal node bh must point to block */
static int get_pos (struct buffer_head * bh, unsigned long block)
{
    int i;

    for (i = 0; i <= B_NR_ITEMS (bh); i ++) {
	if (get_dc_child_blocknr (B_N_CHILD (bh, i)) == block)
	    return i;
    }
    die ("An internal pointer to the block (%lu) cannot be found in the node (%lu)", 
	block, bh->b_blocknr);
    return 0;
}


/* path[h] - leaf node */
static struct key * lkey (struct buffer_head ** path, int h)
{
    int pos;

    while (h > 0) {
       pos = get_pos (path[h - 1], path[h]->b_blocknr);
       if (pos)
           return B_N_PDELIM_KEY(path[h - 1], pos - 1);
       h --;
    }
    return 0;
}


/* path[h] - leaf node */
static struct key * rkey (struct buffer_head ** path, int h)
{
    int pos;

    while (h > 0) {
       pos = get_pos (path[h - 1], path[h]->b_blocknr);
       if (pos != B_NR_ITEMS (path[h - 1]))
           return B_N_PDELIM_KEY (path[h - 1], pos);
       h --;
    }
    return 0;
}


/* are all delimiting keys correct */
static int bad_path (reiserfs_filsys_t * fs, struct buffer_head ** path, int h1)
{
    int h = 0;
    struct key * dk;
    int pos = 0;

    while (path[h])
	h ++;

    h--;
    
    // path[h] is leaf
    if (h != h1)
	die ("bad_path: The leaf is expected as the last element in the path");

    if (h)
	pos = get_pos (path[h - 1], path[h]->b_blocknr);

    dk = lkey (path, h);
    if (dk && comp_keys (dk, B_N_PKEY (path[h], 0))) {
	/* left delimiting key must be equal to the key of 0-th item in the
	   node */
	fsck_log ("bad_path: The left delimiting key %k of the node (%lu) must "
	    "be equal to the first element's key %k within the node.\n", dk, 
	    path[h]->b_blocknr, B_N_PKEY (path[h], 0));
	one_more_corruption (fs, FATAL);
	return 1;
    }
    
    dk = rkey (path, h);
    if (dk && comp_keys (dk, B_N_PKEY (path[h],
       get_blkh_nr_items (B_BLK_HEAD (path[h])) - 1)) != 1) {
	/* right delimiting key must be gt the key of the last item in the node */
	fsck_log ("bad_path: The right delimiting key %k of the node (%lu) must "
	    "be greater than the last (%d) element's key %k within the node.\n", 
	    dk, path[h]->b_blocknr, get_blkh_nr_items (B_BLK_HEAD (path[h])) - 1, 
	    B_N_PKEY (path[h], get_blkh_nr_items (B_BLK_HEAD (path[h])) - 1));
	one_more_corruption (fs, FATAL);
	return 1;
    }

    if (h && (get_dc_child_size (B_N_CHILD(path[h-1],pos)) +
    	get_blkh_free_space ((struct block_head *)path[h]->b_data) +
    	BLKH_SIZE != path[h]->b_size))
    {
	/* wrong dc_size */
	fsck_log ("bad_path: block %lu, pointer %d: The used space (%d) of the "
	    "child block (%lu)", path[h-1]->b_blocknr, pos, 
	    get_dc_child_size(B_N_CHILD(path[h-1],pos)), path[h]->b_blocknr);
	fsck_log (" is not equal to the (blocksize (4096) - free space (%d) - "
	    "header size (%u))", 
	    get_blkh_free_space((struct block_head *)path[h]->b_data), BLKH_SIZE);
	
	if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
	    set_dc_child_size (B_N_CHILD(path[h-1],pos), path[h]->b_size -
			       get_blkh_free_space ((struct block_head *)path[h]->b_data) 
			       - BLKH_SIZE);
	    fsck_log (" - corrected to (%lu)\n", 
		      get_dc_child_size (B_N_CHILD(path[h-1], pos)));
	    
	    mark_buffer_dirty (path[h-1]);
	} else {
	    one_more_corruption (fs, FIXABLE);
	    fsck_log ("\n");
	    return 1;
	}
    }

    return 0;
}

static void before_check_fs_tree (reiserfs_filsys_t * fs) {
    init_control_bitmap (fs);

    source_bitmap = reiserfs_create_bitmap (get_sb_block_count (fs->fs_ondisk_sb));
    reiserfs_bitmap_copy (source_bitmap, fs->fs_bitmap2);

    proper_id_map (fs) = id_map_init();
}

static void after_check_fs_tree (reiserfs_filsys_t * fs) {
    if (fsck_mode(fs) == FSCK_FIX_FIXABLE) {
        reiserfs_flush_to_ondisk_bitmap (fs->fs_bitmap2, fs);
        reiserfs_flush (fs);
        fs->fs_dirt = 1;
	reiserfs_bitmap_delta (source_bitmap, control_bitmap);
	fsck_deallocate_bitmap(fs) = source_bitmap;
    } else 
	reiserfs_delete_bitmap (source_bitmap);
    
    reiserfs_delete_bitmap (control_bitmap);
    flush_buffers (fs->fs_dev);
}

/* pass internal tree of filesystem */
void check_fs_tree (reiserfs_filsys_t * fs)
{
    before_check_fs_tree (fs);
    
    fsck_progress ("Checking internal tree..  ");
    pass_through_tree (fs, bad_node, bad_path, fsck_mode(fs) == FSCK_AUTO ? 2 : -1);
    /* internal tree is correct (including all objects have correct
       sequences of items) */
    fsck_progress ("finished\n");
    
    /* compare created bitmap with the original */
    if (fsck_mode(fs) == FSCK_AUTO) {
	if (auto_handle_bitmaps(fs)) {
	    fprintf(stderr, "The on-disk bitmap looks corrupted.");
	    one_more_corruption(fs, FIXABLE);
	}
	id_map_free(proper_id_map(fs));
    } else
	handle_bitmaps (fs);
    
    after_check_fs_tree (fs);
}

static int clean_attributes_handler (reiserfs_filsys_t * fs, 
				     struct buffer_head ** path, 
				     int h) 
{
    struct buffer_head * bh = path[h];
    int i;

    if (B_LEVEL (bh) != h_to_level (fs, h)) {
       	reiserfs_panic ("The node (%lu) with wrong level (%d) found in the "
			"tree, (%d) expected\n", bh->b_blocknr, B_LEVEL (bh), 
			h_to_level (fs, h));
    }

    if (!is_leaf_node (bh))
        return 0;

    for (i = 0; i < B_NR_ITEMS (bh); i ++) {
        if (is_stat_data_ih (B_N_PITEM_HEAD (bh, i)) &&
            get_ih_item_len (B_N_PITEM_HEAD (bh, i)) == SD_SIZE) {
	    set_sd_v2_sd_attrs((struct stat_data *)B_N_PITEM(bh, i), 0);
	    mark_buffer_dirty (bh);
	}
    }

    return 0;
}

void do_clean_attributes (reiserfs_filsys_t * fs) {
    pass_through_tree (fs, clean_attributes_handler, NULL, -1);
    set_sb_v2_flag (fs->fs_ondisk_sb, reiserfs_attrs_cleared);
    mark_buffer_dirty (fs->fs_super_bh);
}
