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

#include "fsck.h"

static struct key *trunc_links = NULL;
static __u32 links_num = 0;

int wrong_mode (struct key * key, __u16 * mode, __u64 real_size, int symlink);
int wrong_st_blocks(struct key * key, __u32 * blocks, __u32 sd_blocks, __u16 mode, 
		    int new_format);
int wrong_st_size (struct key * key, unsigned long long max_file_size, int blocksize,
		   __u64 * size, __u64 sd_size, int type);
int wrong_first_direct_byte (struct key * key, int blocksize, __u32 * first_direct_byte,
			     __u32 sd_first_direct_byte, __u32 size);
void get_object_key (struct reiserfs_de_head * deh, struct key * key, 
		     struct key * entry_key, struct item_head * ih);
void print_name (char * name, int len);
void erase_name (int len);


struct path_key
{
    struct short_key
    {
        __u32 k_dir_id;
        __u32 k_objectid;
    } key;
    struct path_key * next, * prev;
};

struct path_key * head_key = NULL;
struct path_key * tail_key = NULL;

static int check_path_key(struct key * key)
{
    struct path_key * cur = head_key;

    while(cur != NULL)
    {
        if (!comp_short_keys(&cur->key, key)) {
            fsck_log("\nsemantic check: The directory %k has 2 names.", key);
            return LOOP_FOUND;
        }
        cur = cur->next;
    }
    return 0;
}

static int add_path_key(struct key * key)
{
    if (check_path_key(key))
    	return LOOP_FOUND;

    if (tail_key == NULL)
    {
        tail_key = getmem(sizeof(struct path_key));
        head_key = tail_key;
        tail_key->prev = NULL;
    }else{
        tail_key->next = getmem(sizeof(struct path_key));
        tail_key->next->prev = tail_key;
        tail_key = tail_key->next;
    }
    copy_short_key (&tail_key->key, key);
    tail_key->next = NULL;

    return 0;
}

void del_path_key()
{
    if (tail_key == NULL)
        die("Wrong path_key structure");

    if (tail_key->prev == NULL)
    {
        freemem(tail_key);
        tail_key = head_key = NULL;
    }else{
        tail_key = tail_key->prev;
        freemem(tail_key->next);
        tail_key->next = NULL;
    }
}

/* path is path to stat data. If file will be relocated - new_ih will contain
   a key file was relocated with */
static int check_check_regular_file (struct path * path, void * sd,
                                     struct item_head * new_ih)
{
    int is_new_file;
//    struct key key, sd_key;
    __u16 mode;
    __u32 nlink;
    __u64 real_size, sd_size;
    __u32 blocks, sd_blocks;	/* proper values and value in stat data */
    __u32 first_direct_byte, sd_first_direct_byte;

    struct item_head * ih, sd_ih;
    int fix_sd;
    int symlnk = 0;
    int retval = OK;
    __u32 tmp_position;


    ih = get_ih (path);

    if (new_ih) {
	/* this objectid is used already */
	*new_ih = *ih;
	pathrelse (path);
	rewrite_file (new_ih, 1, 1);
	linked_already(&new_ih->ih_key);
	one_less_corruption (fs, FIXABLE);
	sem_pass_stat (fs)->oid_sharing_files_relocated ++;
	retval = RELOCATED;
	if (reiserfs_search_by_key_4 (fs, &(new_ih->ih_key), path) == ITEM_NOT_FOUND)
	    reiserfs_panic ("check_check_regular_file: Could not find a StatData of "
		"the relocated file %K", &new_ih->ih_key);
	/* stat data is marked unreachable again due to relocation, fix that */
	ih = get_ih (path);
	sd = get_item (path);
    }
    

    if (get_ih_item_len (ih) == SD_SIZE)
	is_new_file = 1;
    else
	is_new_file = 0;


    get_sd_nlink (ih, sd, &nlink);
    get_sd_mode (ih, sd, &mode);
    get_sd_size (ih, sd, &sd_size);
    get_sd_blocks (ih, sd, &sd_blocks);

/*	
    if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
    	// check and set nlink first 
    	nlink ++;
    	set_sd_nlink (ih, sd, &nlink);
    	mark_buffer_dirty (bh);

    	if (nlink > 1)
	    return OK;
    }
*/

    if (!is_new_file)
	get_sd_first_direct_byte (ih, sd, &sd_first_direct_byte);

    if (S_ISLNK (mode)) 	
	symlnk = 1;
    
    sd_ih = *ih;
//    sd_key = sd_ih.ih_key;
    pathrelse (path);

    if (are_file_items_correct (&sd_ih, sd, &real_size, &blocks, 0/* do not mark reachable */,
	&symlnk) != 1) 
    {
	one_more_corruption (fs, FATAL);
	fsck_log ("check_regular_file: The file %K with the corrupted structure found\n", 
	    &sd_ih.ih_key);
    } else {
	fix_sd = 0;
    
	fix_sd += wrong_mode (&sd_ih.ih_key, &mode, real_size, symlnk);
	if (!is_new_file)
	    fix_sd += wrong_first_direct_byte (&sd_ih.ih_key, fs->fs_blocksize,
		&first_direct_byte, sd_first_direct_byte, real_size);
	
	if (reiserfs_bin_search(&sd_ih.ih_key, trunc_links, links_num, sizeof(sd_ih.ih_key), 
	    &tmp_position, comp_short_keys) != POSITION_FOUND) 
	{
	    fix_sd += wrong_st_size (&sd_ih.ih_key, is_new_file ? MAX_FILE_SIZE_V2 : 
		MAX_FILE_SIZE_V1, fs->fs_blocksize, &real_size, sd_size, 
		symlnk ? TYPE_SYMLINK : 0);
	} else {
	    real_size = sd_size;
	}
	
	fix_sd += wrong_st_blocks (&sd_ih.ih_key, &blocks, sd_blocks, mode, is_new_file);

	if (fix_sd) {
	    if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
    		struct buffer_head * bh;
  	        /* find stat data and correct it */
  	        set_type_and_offset (KEY_FORMAT_1, &sd_ih.ih_key, SD_OFFSET, TYPE_STAT_DATA);
	        if (reiserfs_search_by_key_4 (fs, &sd_ih.ih_key, path) != ITEM_FOUND) {
		    fsck_log ("check_regular_file: A StatData of the file %K cannot be "
			"found\n", &sd_ih.ih_key);
                    one_more_corruption (fs, FATAL);
                    return STAT_DATA_NOT_FOUND;
	        }
	    
	        bh = get_bh (path);
	        ih = get_ih (path);
	        sd = get_item (path);
	        set_sd_size (ih, sd, &real_size);
	        set_sd_blocks (ih, sd, &blocks);
	        set_sd_mode (ih, sd, &mode);
	        if (!is_new_file)
		    set_sd_first_direct_byte (ih, sd, &first_direct_byte);
		mark_buffer_dirty (bh);
	    } else {
		fsck_check_stat (fs)->fixable_corruptions += fix_sd;
	    }
	}
    }    
    return retval;
}

/* returns buffer, containing found directory item.*/
static char * get_next_directory_item (
	struct key * key, /* on return this will contain key of next item in the tree */
	struct key * parent, struct item_head * ih, __u32 * pos_in_item, int dir_format)
{
    INITIALIZE_PATH (path);
    char * dir_item;
    struct key * rdkey;
    struct buffer_head * bh;
    struct reiserfs_de_head * deh;
    int i;
    int retval;


start_again:

    retval = reiserfs_search_by_entry_key (fs, key, &path);

    if (retval != POSITION_FOUND && get_offset (key) != DOT_OFFSET)
	reiserfs_panic ("get_next_directory_item: The current directory %k cannot be found", 
	    key);

    /* leaf containing directory item */
    bh = PATH_PLAST_BUFFER (&path);
    *pos_in_item = path.pos_in_item;
    *ih = *get_ih (&path);
    deh = B_I_DEH (bh, ih);

    /* position was not found for '.' or there is no '..' */
    if (retval != POSITION_FOUND || ((get_offset (key) == DOT_OFFSET) &&
	(get_ih_entry_count (ih) < 2 || name_in_entry_length (ih, deh + 1, 1) != 2 ||
	 strncmp (name_in_entry (deh + 1, 1), "..", 2)))) {

	fsck_log ("get_next_directory_item: The %s %k cannot be found in %k", 
	    (retval == POSITION_NOT_FOUND) ? "entry" : "directory", key, &ih->ih_key);
	
	if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
	    /* add "." and ".." exist */
	    pathrelse (&path);
	    reiserfs_add_entry (fs, key, ".",  name_length (".", dir_format), key, 0);
	    reiserfs_add_entry (fs, key, "..", name_length ("..", dir_format), parent, 0);
	    fsck_log (" - entry was added\n");
	    goto start_again;
	} else {
	    one_more_corruption (fs, FIXABLE);
	    fsck_log ("\n");
	    if (retval == DIRECTORY_NOT_FOUND)
	        return 0;
	}
    }

    /* make sure, that ".." exists as well */
/*
    if (get_offset (key) == DOT_OFFSET) {
	if (get_ih_entry_count (ih) < 2 ||
	    name_in_entry_length (ih, deh + 1, 1) != 2 ||
	    strncmp (name_in_entry (deh + 1, 1), "..", 2))
	{
	    fsck_log ("get_next_directory_item: \"..\" not found in %H\n", ih);
	    pathrelse (&path);
	    return 0;
	}
    }
*/
    /* mark hidden entries as visible, set "." and ".." correctly */
    deh += *pos_in_item;
    for (i = *pos_in_item; i < get_ih_entry_count (ih); i ++, deh ++) {
/*	int namelen;
	char * name;

	name = name_in_entry (deh, i);
	namelen = name_in_entry_length (ih, deh, i);
	if (de_hidden (deh)) // handled in check_tree
	    reiserfs_panic ("get_next_directory_item: item %k: hidden entry %d \'%.*s\'\n",
			    key, i, namelen, name);
*/

	if (get_deh_offset (deh) == DOT_OFFSET) {
	    if (not_of_one_file (&(deh->deh2_dir_id), key)) {
		/* "." must point to the directory it is in */
		
		//deh->deh_objectid != REISERFS_ROOT_PARENT_OBJECTID)/*????*/ {
		fsck_log ("get_next_directory_item: The entry \".\" of the directory %K "
		    "pointes to %K, instead of %K", key, (struct key *)(&(deh->deh2_dir_id)), 
		    key);
		if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
		    set_deh_dirid (deh, get_key_dirid (key));
		    set_deh_objectid (deh, get_key_objectid (key));
		    mark_buffer_dirty (bh);
		    fsck_log (" - corrected\n");
		} else {
		    one_more_corruption (fs, FIXABLE);
		    fsck_log ("\n");
		}
	    }
	}

	if (get_deh_offset (deh) == DOT_DOT_OFFSET) {
	    /* set ".." so that it points to the correct parent directory */
	    if (comp_short_keys (&(deh->deh2_dir_id), parent)) {
		fsck_log ("get_next_directory_item: The entry \"..\" of the directory %K "
		    "pointes to %K, instead of %K", key, (struct key *)(&(deh->deh2_dir_id)));
		if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
		    set_deh_dirid (deh, get_key_dirid (parent));
		    set_deh_objectid (deh, get_key_objectid (parent));
		    mark_buffer_dirty (bh);
		    fsck_log (" - corrected\n");
		} else {
		    one_more_corruption (fs, FIXABLE);
		    fsck_log ("\n");
		}
	    }
	}
    }

    /* copy directory item to the temporary buffer */
    dir_item = getmem (get_ih_item_len (ih));
    memcpy (dir_item, B_I_PITEM (bh, ih), get_ih_item_len (ih));


    /* next item key */
    if (PATH_LAST_POSITION (&path) == (B_NR_ITEMS (bh) - 1) &&
	(rdkey = uget_rkey (&path)))
	copy_key (key, rdkey);
    else {
	set_key_dirid (key, 0);
	set_key_objectid (key, 0);
    }

    if (fsck_mode (fs) == FSCK_REBUILD)
        mark_item_reachable (get_ih (&path), bh);
    pathrelse (&path);

    return dir_item;
}

/* semantic pass of --check */
static int check_semantic_pass (struct key * key, struct key * parent, int dot_dot, 
    struct item_head * new_ih)
{
    struct path path;
    void * sd;
    __u32 nlink;
    int is_new_dir;
    struct buffer_head * bh;
    struct item_head * ih;
    int retval;
    char * dir_item;
    __u32 pos_in_item;
    struct item_head tmp_ih;
    struct key next_item_key, entry_key, object_key;
    __u64 dir_size = 0;
    __u32 blocks;
    __u64 sd_size;
    __u32 sd_blocks;
    int fix_sd;
    /*int relocate;*/
    int dir_format = 0;
    __u16 mode;
    	
    retval = OK;

 /* start_again: when directory was relocated */

    if (!KEY_IS_STAT_DATA_KEY (key)) {
	fsck_log ("check_semantic_pass: The key %k must be key of a StatData\n", key);
	one_more_corruption (fs, FATAL);
        return STAT_DATA_NOT_FOUND;
    }

    /* look for stat data of an object */
    if (reiserfs_search_by_key_4 (fs, key, &path) == ITEM_NOT_FOUND) {
	pathrelse (&path);
	return STAT_DATA_NOT_FOUND;
    }

    /* stat data has been found */
    ih = get_ih (&path);
    sd = get_item(&path);

    get_sd_nlink (ih, sd, &nlink);

    /* It seems quite difficult to relocate objects on fix-fixable - 
     * rewrite_file calls reiserfs_file_write which can convert tails 
     * to unfm, plus unreachable, was_tail flags, etc. */
#if 0
    if ((/* relocate = */ should_be_relocated(&ih->ih_key))) {
	/*
	if (fsck_mode(fs) == FSCK_CHECK)
	    relocate = 0;
	*/
	one_more_corruption(fs, FATAL);
    }
#endif

    if (fix_obviously_wrong_sd_mode (&path)) {
        one_more_corruption (fs, FIXABLE);
	pathrelse (&path);
        return OK;
    }
    
    if (nlink == 0) {
	fsck_log ("%s: block %lu: The StatData %k has bad nlink number (%u)\n",
            __FUNCTION__, get_bh(&path)->b_blocknr, &ih->ih_key, nlink);
	one_more_corruption (fs, FATAL); 
    }
    
    if (not_a_directory (sd)) {
	fsck_check_stat (fs)->files ++;
	
	retval = check_check_regular_file (&path, sd, /* relocate ? new_ih : */ 0);
	pathrelse (&path);
	return retval;
    }

/*
    if (relocate) {
	if (!new_ih)
	    reiserfs_panic ("check_semantic_pass: Memory is not prepared for relocation of "
		"%K", &ih->ih_key);
	*new_ih = *ih;
	pathrelse (&path);
	sem_pass_stat (fs)->oid_sharing_dirs_relocated ++;
	relocate_dir (new_ih, 1);
	linked_already(&new_ih->ih_key);
	one_less_corruption (fs, FIXABLE);
	*key = new_ih->ih_key;
	retval = RELOCATED;
	goto start_again;
    }
*/

/* 
    if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
    	// it looks like stat data of a directory found 
    	if (nlink) {
	    // we saw this directory already 
	    if (!dot_dot) {
	    	// this name is not ".."  - and hard links are not allowed on directories 
	    	pathrelse (&path);
	    	return STAT_DATA_NOT_FOUND;
	    } else {
	    	// ".." found 
	    	nlink ++;
	    	set_sd_nlink (ih, sd, &nlink);
	    	mark_buffer_dirty (get_bh (&path));
	    	pathrelse (&path);
	    	return OK;
	    }
    	} // do not run it for dot_dot on check at all
    	
     	nlink = 2;
    	if (get_key_objectid (key) == REISERFS_ROOT_OBJECTID)
	    nlink ++;
    	set_sd_nlink (ih, sd, &nlink);
    	mark_buffer_dirty (get_bh (&path));    
    }
*/

    /* directory stat data found */
    if (get_ih_item_len (ih) == SD_SIZE)
	is_new_dir = 1;
    else
	is_new_dir = 0;

    /* save stat data's size and st_blocks */
    get_sd_size (ih, sd, &sd_size);
    get_sd_blocks (ih, sd, &sd_blocks);
    get_sd_mode (ih, sd, &mode);

    dir_format = (get_ih_item_len (get_ih (&path)) == SD_SIZE) ? KEY_FORMAT_2 : KEY_FORMAT_1;

    /* release path pointing to stat data */
    pathrelse (&path);

    fsck_check_stat (fs)->dirs ++;

    set_key_dirid (&next_item_key, get_key_dirid (key));
    set_key_objectid (&next_item_key, get_key_objectid (key));
    set_key_offset_v1 (&next_item_key, DOT_OFFSET);
    set_key_uniqueness (&next_item_key, DIRENTRY_UNIQUENESS);

    dir_size = 0;
    while ((dir_item = get_next_directory_item (&next_item_key, parent, &tmp_ih, 
	&pos_in_item, dir_format)) != 0) 
    {
	/* dir_item is copy of the item in separately allocated memory,
	   item_key is a key of next item in the tree */
	int i;
	char * name = 0;
	int namelen, entry_len;
	struct reiserfs_de_head * deh = (struct reiserfs_de_head *)dir_item + pos_in_item;
	
	
	for (i = pos_in_item; i < get_ih_entry_count (&tmp_ih); i ++, deh ++) {
	    struct item_head relocated_ih;
	    int ret = OK;
	    
	    if (name) {
		free (name);
		name = 0;
	    }
	
	    namelen = name_in_entry_length (&tmp_ih, deh, i);
	    asprintf (&name, "%.*s", namelen, name_in_entry (deh, i));	
	    entry_len = entry_length (&tmp_ih, deh, i);
	
	    get_object_key (deh, &object_key, &entry_key, &tmp_ih);

	    if ((dir_format == KEY_FORMAT_2) && (entry_len % 8 != 0)) {
		/* not alighed directory of new format - delete it */
		if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
		    fsck_log ("Entry %K (\"%.*s\") in the directory %K is not formated "
			"properly - deleted\n",	(struct key *)&(deh->deh2_dir_id), 
			namelen, name, &tmp_ih.ih_key);
		    reiserfs_remove_entry (fs, &entry_key);
		    entry_len = name_length (name, dir_format);
		    reiserfs_add_entry (fs, key, name, entry_len, 
			(struct key *)&(deh->deh2_dir_id), 0);
		} else {
		    fsck_log ("Entry %K (\"%.*s\") in the directory %K is not formated "
			"properly.\n", (struct key *)&(deh->deh2_dir_id), namelen, name, 
			&tmp_ih.ih_key);
		    one_more_corruption (fs, FIXABLE);
		}
	    }
	
	
	    print_name (name, namelen);
	    
	    if (!is_properly_hashed (fs, name, namelen, get_deh_offset (deh))) {
		one_more_corruption (fs, FATAL);
		fsck_log ("check_semantic_pass: Hash mismatch detected for (%.*s) in "
		    "directory %K\n", namelen, name, &tmp_ih.ih_key);
	    }
	
	    if (is_dot (name, namelen) || (is_dot_dot (name, namelen))) {
		/* do not go through "." and ".." */
		ret = OK;
	    } else {
		if ((ret = add_path_key (&object_key)) == 0) {
		    ret = check_semantic_pass (&object_key, key, 
			is_dot_dot(name, namelen), &relocated_ih);
		    del_path_key ();
		}
	    }
	    
	    erase_name (namelen);
	    
	    /* check what check_semantic_tree returned */
	    switch (ret) {
	    case OK:
		dir_size += DEH_SIZE + entry_len;
		break;
		
	    case STAT_DATA_NOT_FOUND:
		fsck_log ("check_semantic_pass: Name \"%.*s\" in directory %K points to "
		    "nowhere\n", namelen, name, &tmp_ih.ih_key);
	    case LOOP_FOUND:
		if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
		    reiserfs_remove_entry (fs, &entry_key);
		    fsck_log (" - removed");
		} else {
		    one_more_corruption (fs, FIXABLE);
		}
		fsck_log ("\n");
		break;
		    
	    case DIRECTORY_HAS_NO_ITEMS:
		fsck_log ("check_semantic_pass: Name \"%.*s\" in directory %K points a "
		    "directory without body\n", namelen, name, &tmp_ih.ih_key);
		
		/* fixme: stat data should be deleted as well */
		/*
		  if (fsck_fix_fixable (fs)) {
		  reiserfs_remove_entry (fs, &entry_key);
		  fsck_data(fs)->deleted_entries ++;
		  fsck_log (" - removed");
		  }
		  fsck_log ("\n");*/
		break;
		
	    case RELOCATED:
		/* file was relocated, update key in corresponding directory entry */
		if (reiserfs_search_by_entry_key (fs, &entry_key, &path) != POSITION_FOUND) {
		    fsck_log("Cannot find a name of the relocated file %K in the directory "
			"%K\n", &entry_key, &tmp_ih.ih_key);
		} else {
		    /* update key dir entry points to */
		    struct reiserfs_de_head * tmp_deh;
		    
		    tmp_deh = B_I_DEH (get_bh (&path), get_ih (&path)) + path.pos_in_item;
		    fsck_log ("The directory %K pointing to %K (\"%.*s\") updated to point "
			"to ",	&tmp_ih.ih_key, &tmp_deh->deh2_dir_id, namelen, name);
		    set_deh_dirid (tmp_deh, get_key_dirid (&relocated_ih.ih_key));
		    set_deh_objectid (tmp_deh, get_key_objectid (&relocated_ih.ih_key));

		    fsck_log ("%K (\"%.*s\")\n",  &tmp_deh->deh2_dir_id, namelen, name);
		    mark_buffer_dirty (get_bh (&path));
		}
		dir_size += DEH_SIZE + entry_len;
		pathrelse (&path);
		break;
	    }
	} /* for */
	
	freemem (dir_item);
	free (name);
	name = 0;

	if (not_of_one_file (&next_item_key, key))
	    /* next key is not of this directory */
	    break;
	
    } /* while (dir_item) */
    
    
    if (dir_size == 0)
	/* FIXME: is it possible? */
	return DIRECTORY_HAS_NO_ITEMS;
    
    /* calc correct value of sd_blocks field of stat data */
    blocks = dir_size2st_blocks (dir_size);
    
    fix_sd = 0;
    fix_sd += wrong_st_blocks (key, &blocks, sd_blocks, mode, is_new_dir);
    fix_sd += wrong_st_size (key, is_new_dir ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1,
			     fs->fs_blocksize, &dir_size, sd_size, TYPE_DIRENTRY);

    if (fix_sd) {
	if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
	    /* we have to fix either sd_size or sd_blocks, so look for stat data again */
	    if (reiserfs_search_by_key_4 (fs, key, &path) != ITEM_FOUND) {
		fsck_log ("check_semantic_tree: The StatData of the file %K was not found\n", 
		    key);
		one_more_corruption(fs, FATAL);
                return STAT_DATA_NOT_FOUND;
	    }
	
	    bh = get_bh (&path);
	    ih = get_ih (&path);
	    sd = get_item (&path);
	
	    set_sd_size (ih, sd, &dir_size);
	    set_sd_blocks (ih, sd, &blocks);
	    mark_buffer_dirty (bh);
	    pathrelse (&path);
	} else {
	    fsck_check_stat (fs)->fixable_corruptions += fix_sd;
	}
    }
    
    return retval;
}

int check_safe_links ()
{
    struct path safe_link_path, path;
    struct key safe_link_key = {-1, 0, {{0, 0}}};
    struct key key = {0, 0, {{0, 0}}};
    struct key * rkey;
    struct item_head * tmp_ih;

    while (1) {
        if (get_key_dirid (&safe_link_key) == 0)
            break;

	reiserfs_search_by_key_4 (fs, &safe_link_key, &safe_link_path);
	
	if (get_blkh_nr_items ( B_BLK_HEAD (get_bh(&safe_link_path))) <= 
	    PATH_LAST_POSITION (&safe_link_path)) 
	{
	    pathrelse (&safe_link_path);
            break;
	}	
	
	tmp_ih = get_ih(&safe_link_path);
	
        if (get_key_dirid(&tmp_ih->ih_key) != (__u32)-1 ||
	    get_key_objectid(&tmp_ih->ih_key) == (__u32)-1) {
	    pathrelse (&safe_link_path);
            break;
	}

        if (get_ih_item_len (tmp_ih) != 4)
	    reiserfs_panic ("Safe Link %k cannot be of the size %d", 
		&tmp_ih->ih_key, get_ih_item_len (tmp_ih));
	
	set_key_dirid(&key, d32_get((__u32 *)get_item(&safe_link_path), 0));
	set_key_objectid(&key, get_key_objectid(&tmp_ih->ih_key));
	if ((rkey = reiserfs_next_key(&safe_link_path)) == NULL)
	    set_key_dirid (&safe_link_key, 0);
	else
	    safe_link_key = *rkey;
	
	if (reiserfs_search_by_key_4 (fs, &key, &path) == ITEM_NOT_FOUND) {
	    /*sware on check, delete on fix-fixable*/
	    if (fsck_mode(fs) == FSCK_CHECK) {
		fsck_log ("Invalid safe link %k: cannot find the pointed object (%K)\n", 
		    &tmp_ih->ih_key, &key);
		one_more_corruption (fs, FIXABLE);
	    } else if (fsck_mode(fs) == FSCK_FIX_FIXABLE) {
		fsck_log ("Invalid safe link %k: cannot find the pointed object (%K) - "
		    "safe link was deleted\n", &tmp_ih->ih_key, &key);
		d32_put((__u32 *)get_item(&safe_link_path), 0, 0);
		pathrelse (&path);
		reiserfsck_delete_item (&safe_link_path, 0);		
		continue;
	    }
	} else if (get_offset(&tmp_ih->ih_key) == 0x1) {
	    /* Truncate */
	    if (!not_a_directory (get_item(&path))) {
		/*truncate on directory should not happen*/
		/*sware on check, delete on fix-fixable*/
		if (fsck_mode(fs) == FSCK_CHECK) {
		    fsck_log ("Invalid 'truncate' safe link %k, cannot happen for "
			"directory (%K)\n", &tmp_ih->ih_key, &key);
		    one_more_corruption (fs, FIXABLE);
		} else if (fsck_mode(fs) == FSCK_FIX_FIXABLE) {
		    fsck_log ("Invalid 'truncate' safe link %k, cannot happen for "
			"a directory (%K) - safe link was deleted\n", &tmp_ih->ih_key, &key);
		    d32_put((__u32 *)get_item(&safe_link_path), 0, 0);
		    pathrelse (&path);
		    reiserfsck_delete_item (&safe_link_path, 0);		
		    continue;
		}
	    } else {
		/* save 'safe truncate links' to avoid swaring on wrong sizes. */
		__u32 position;
		
		if (reiserfs_bin_search (&key, trunc_links, links_num, sizeof(key), 
		    &position, comp_short_keys) != POSITION_FOUND) 
		{
		    blocklist__insert_in_position(&key, (void *)&trunc_links, &links_num, 
			sizeof(key), &position);
		}		
	    }
	}
	pathrelse (&path);
	pathrelse (&safe_link_path);
    }

    return OK;
}

void release_safe_links () {
    freemem(trunc_links);
}

/* called when --check is given */
void semantic_check (void)
{
    if (fsck_data (fs)->check.bad_nodes) {
        fsck_progress ("Bad nodes were found, Semantic pass skipped\n");
        goto clean;
    }

    if (fsck_data (fs)->check.fatal_corruptions) {
        fsck_progress ("Fatal corruptions were found, Semantic pass skipped\n");
        goto clean;
    }
 
    fsck_progress ("Checking Semantic tree:\n");

    if (fsck_mode(fs) == FSCK_FIX_FIXABLE) {
        /*create new_bitmap, and initialize new_bitmap & allocable bitmap*/
        fsck_new_bitmap (fs) = reiserfs_create_bitmap(get_sb_block_count(fs->fs_ondisk_sb));
        reiserfs_bitmap_copy (fsck_new_bitmap (fs), fs->fs_bitmap2);
        fsck_allocable_bitmap (fs) = reiserfs_create_bitmap(get_sb_block_count
	    (fs->fs_ondisk_sb));
        reiserfs_bitmap_copy (fsck_allocable_bitmap (fs), fs->fs_bitmap2);
	fs->block_allocator = reiserfsck_reiserfs_new_blocknrs;
	fs->block_deallocator = reiserfsck_reiserfs_free_block;
    }
    
    check_safe_links ();
    
    if (check_semantic_pass (&root_dir_key, &parent_root_dir_key, 0, 0) != OK) {
        fsck_log ("check_semantic_tree: No root directory found");
        one_more_corruption (fs, FATAL);
    }

    release_safe_links ();

    if (fsck_mode(fs) == FSCK_FIX_FIXABLE) {
        reiserfs_delete_bitmap (fs->fs_bitmap2);
	reiserfs_bitmap_delta (fsck_new_bitmap (fs), fsck_deallocate_bitmap (fs));	
        fs->fs_bitmap2 = fsck_new_bitmap (fs);
        reiserfs_delete_bitmap (fsck_allocable_bitmap (fs));	
        fsck_allocable_bitmap (fs) = NULL;
        set_sb_free_blocks (fs->fs_ondisk_sb, reiserfs_bitmap_zeros (fs->fs_bitmap2));
        mark_buffer_dirty (fs->fs_super_bh);
	add_badblock_list(fs, 1);
    }
    
    fsck_progress ("finished\n");
    
clean:
    if (fsck_deallocate_bitmap (fs))
	reiserfs_delete_bitmap (fsck_deallocate_bitmap (fs));
}
