Exemple #1
0
// comment?  maybe something like set de to point to what the path points to?
static inline void set_de_item_location (struct reiserfs_dir_entry * de, struct path * path)
{
    de->de_bh = get_last_bh (path);
    de->de_ih = get_ih (path);
    de->de_deh = B_I_DEH (de->de_bh, de->de_ih);
    de->de_item_num = PATH_LAST_POSITION (path);
} 
int reiserfs_remove_entry (reiserfs_filsys_t fs, struct key * key)
{
    struct path path;
    struct tree_balance tb;
    struct item_head * ih;
    struct reiserfs_de_head * deh;

    if (_search_by_entry_key (fs, key, &path) != POSITION_FOUND) {
	pathrelse (&path);
	return 1;
    }

    ih = get_ih (&path);
    if (ih_entry_count (ih) == 1) {
	_init_tb_struct (&tb, fs, &path, -(IH_SIZE + ih_item_len (ih)));
	if (fix_nodes (M_DELETE, &tb, 0) != CARRY_ON) {
	    unfix_nodes (&tb);
	    return 1;
	}
	do_balance (&tb, 0, 0, M_DELETE, 0);
	return 0;
    }

    deh = B_I_DEH (get_bh (&path), ih) + path.pos_in_item;
    _init_tb_struct (&tb, fs, &path, -(DEH_SIZE + entry_length (ih, deh, path.pos_in_item)));
    if (fix_nodes (M_CUT, &tb, 0) != CARRY_ON) {
	unfix_nodes (&tb);
	return 1;
    }
    do_balance (&tb, 0, 0, M_CUT, 0);
    return 0;
}
Exemple #3
0
/* returns 1 if it finds an indirect item and gets valid hint info
 * from it, otherwise 0
 */
static int get_left_neighbor(reiserfs_blocknr_hint_t *hint)
{
    struct path * path;
    struct buffer_head * bh;
    struct item_head * ih;
    int pos_in_item;
    __u32 * item;
    int ret = 0;

    if (!hint->path)		/* reiserfs code can call this function w/o pointer to path
				 * structure supplied; then we rely on supplied search_start */
        return 0;

    path = hint->path;
    bh = get_last_bh(path);
    RFALSE( !bh, "green-4002: Illegal path specified to get_left_neighbor");
    ih = get_ih(path);
    pos_in_item = path->pos_in_item;
    item = get_item (path);

    hint->search_start = bh->b_blocknr;

    if (!hint->formatted_node && is_indirect_le_ih (ih)) {
        /* for indirect item: go to left and look for the first non-hole entry
           in the indirect item */
        if (pos_in_item == I_UNFM_NUM (ih))
            pos_in_item--;
//	    pos_in_item = I_UNFM_NUM (ih) - 1;
        while (pos_in_item >= 0) {
            int t=get_block_num(item,pos_in_item);
            if (t) {
                hint->search_start = t;
                ret = 1;
                break;
            }
            pos_in_item --;
        }
    }

    /* does result value fit into specified region? */
    return ret;
}
/*
 * process, that is going to call fix_nodes/do_balance must hold only
 * one path. If it holds 2 or more, it can get into endless waiting in
 * get_empty_nodes or its clones
 */
static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
			   struct inode *new_dir, struct dentry *new_dentry)
{
	int retval;
	INITIALIZE_PATH(old_entry_path);
	INITIALIZE_PATH(new_entry_path);
	INITIALIZE_PATH(dot_dot_entry_path);
	struct item_head new_entry_ih, old_entry_ih, dot_dot_ih;
	struct reiserfs_dir_entry old_de, new_de, dot_dot_de;
	struct inode *old_inode, *new_dentry_inode;
	struct reiserfs_transaction_handle th;
	int jbegin_count;
	umode_t old_inode_mode;
	unsigned long savelink = 1;
	struct timespec ctime;

	/* three balancings: (1) old name removal, (2) new name insertion
	   and (3) maybe "save" link insertion
	   stat data updates: (1) old directory,
	   (2) new directory and (3) maybe old object stat data (when it is
	   directory) and (4) maybe stat data of object to which new entry
	   pointed initially and (5) maybe block containing ".." of
	   renamed directory
	   quota updates: two parent directories */
	jbegin_count =
	    JOURNAL_PER_BALANCE_CNT * 3 + 5 +
	    4 * REISERFS_QUOTA_TRANS_BLOCKS(old_dir->i_sb);

	dquot_initialize(old_dir);
	dquot_initialize(new_dir);

	old_inode = old_dentry->d_inode;
	new_dentry_inode = new_dentry->d_inode;

	// make sure, that oldname still exists and points to an object we
	// are going to rename
	old_de.de_gen_number_bit_string = NULL;
	reiserfs_write_lock(old_dir->i_sb);
	retval =
	    reiserfs_find_entry(old_dir, old_dentry->d_name.name,
				old_dentry->d_name.len, &old_entry_path,
				&old_de);
	pathrelse(&old_entry_path);
	if (retval == IO_ERROR) {
		reiserfs_write_unlock(old_dir->i_sb);
		return -EIO;
	}

	if (retval != NAME_FOUND || old_de.de_objectid != old_inode->i_ino) {
		reiserfs_write_unlock(old_dir->i_sb);
		return -ENOENT;
	}

	old_inode_mode = old_inode->i_mode;
	if (S_ISDIR(old_inode_mode)) {
		// make sure, that directory being renamed has correct ".."
		// and that its new parent directory has not too many links
		// already

		if (new_dentry_inode) {
			if (!reiserfs_empty_dir(new_dentry_inode)) {
				reiserfs_write_unlock(old_dir->i_sb);
				return -ENOTEMPTY;
			}
		}

		/* directory is renamed, its parent directory will be changed,
		 ** so find ".." entry
		 */
		dot_dot_de.de_gen_number_bit_string = NULL;
		retval =
		    reiserfs_find_entry(old_inode, "..", 2, &dot_dot_entry_path,
					&dot_dot_de);
		pathrelse(&dot_dot_entry_path);
		if (retval != NAME_FOUND) {
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}

		/* inode number of .. must equal old_dir->i_ino */
		if (dot_dot_de.de_objectid != old_dir->i_ino) {
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}
	}

	retval = journal_begin(&th, old_dir->i_sb, jbegin_count);
	if (retval) {
		reiserfs_write_unlock(old_dir->i_sb);
		return retval;
	}

	/* add new entry (or find the existing one) */
	retval =
	    reiserfs_add_entry(&th, new_dir, new_dentry->d_name.name,
			       new_dentry->d_name.len, old_inode, 0);
	if (retval == -EEXIST) {
		if (!new_dentry_inode) {
			reiserfs_panic(old_dir->i_sb, "vs-7050",
				       "new entry is found, new inode == 0");
		}
	} else if (retval) {
		int err = journal_end(&th, old_dir->i_sb, jbegin_count);
		reiserfs_write_unlock(old_dir->i_sb);
		return err ? err : retval;
	}

	reiserfs_update_inode_transaction(old_dir);
	reiserfs_update_inode_transaction(new_dir);

	/* this makes it so an fsync on an open fd for the old name will
	 ** commit the rename operation
	 */
	reiserfs_update_inode_transaction(old_inode);

	if (new_dentry_inode)
		reiserfs_update_inode_transaction(new_dentry_inode);

	while (1) {
		// look for old name using corresponding entry key (found by reiserfs_find_entry)
		if ((retval =
		     search_by_entry_key(new_dir->i_sb, &old_de.de_entry_key,
					 &old_entry_path,
					 &old_de)) != NAME_FOUND) {
			pathrelse(&old_entry_path);
			journal_end(&th, old_dir->i_sb, jbegin_count);
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}

		copy_item_head(&old_entry_ih, get_ih(&old_entry_path));

		reiserfs_prepare_for_journal(old_inode->i_sb, old_de.de_bh, 1);

		// look for new name by reiserfs_find_entry
		new_de.de_gen_number_bit_string = NULL;
		retval =
		    reiserfs_find_entry(new_dir, new_dentry->d_name.name,
					new_dentry->d_name.len, &new_entry_path,
					&new_de);
		// reiserfs_add_entry should not return IO_ERROR, because it is called with essentially same parameters from
		// reiserfs_add_entry above, and we'll catch any i/o errors before we get here.
		if (retval != NAME_FOUND_INVISIBLE && retval != NAME_FOUND) {
			pathrelse(&new_entry_path);
			pathrelse(&old_entry_path);
			journal_end(&th, old_dir->i_sb, jbegin_count);
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}

		copy_item_head(&new_entry_ih, get_ih(&new_entry_path));

		reiserfs_prepare_for_journal(old_inode->i_sb, new_de.de_bh, 1);

		if (S_ISDIR(old_inode->i_mode)) {
			if ((retval =
			     search_by_entry_key(new_dir->i_sb,
						 &dot_dot_de.de_entry_key,
						 &dot_dot_entry_path,
						 &dot_dot_de)) != NAME_FOUND) {
				pathrelse(&dot_dot_entry_path);
				pathrelse(&new_entry_path);
				pathrelse(&old_entry_path);
				journal_end(&th, old_dir->i_sb, jbegin_count);
				reiserfs_write_unlock(old_dir->i_sb);
				return -EIO;
			}
			copy_item_head(&dot_dot_ih,
				       get_ih(&dot_dot_entry_path));
			// node containing ".." gets into transaction
			reiserfs_prepare_for_journal(old_inode->i_sb,
						     dot_dot_de.de_bh, 1);
		}
		/* we should check seals here, not do
		   this stuff, yes? Then, having
		   gathered everything into RAM we
		   should lock the buffers, yes?  -Hans */
		/* probably.  our rename needs to hold more
		 ** than one path at once.  The seals would
		 ** have to be written to deal with multi-path
		 ** issues -chris
		 */
		/* sanity checking before doing the rename - avoid races many
		 ** of the above checks could have scheduled.  We have to be
		 ** sure our items haven't been shifted by another process.
		 */
		if (item_moved(&new_entry_ih, &new_entry_path) ||
		    !entry_points_to_object(new_dentry->d_name.name,
					    new_dentry->d_name.len,
					    &new_de, new_dentry_inode) ||
		    item_moved(&old_entry_ih, &old_entry_path) ||
		    !entry_points_to_object(old_dentry->d_name.name,
					    old_dentry->d_name.len,
					    &old_de, old_inode)) {
			reiserfs_restore_prepared_buffer(old_inode->i_sb,
							 new_de.de_bh);
			reiserfs_restore_prepared_buffer(old_inode->i_sb,
							 old_de.de_bh);
			if (S_ISDIR(old_inode_mode))
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 dot_dot_de.
								 de_bh);
			continue;
		}
		if (S_ISDIR(old_inode_mode)) {
			if (item_moved(&dot_dot_ih, &dot_dot_entry_path) ||
			    !entry_points_to_object("..", 2, &dot_dot_de,
						    old_dir)) {
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 old_de.de_bh);
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 new_de.de_bh);
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 dot_dot_de.
								 de_bh);
				continue;
			}
		}

		RFALSE(S_ISDIR(old_inode_mode) &&
		       !buffer_journal_prepared(dot_dot_de.de_bh), "");

		break;
	}

	/* ok, all the changes can be done in one fell swoop when we
	   have claimed all the buffers needed. */

	mark_de_visible(new_de.de_deh + new_de.de_entry_num);
	set_ino_in_dir_entry(&new_de, INODE_PKEY(old_inode));
	journal_mark_dirty(&th, old_dir->i_sb, new_de.de_bh);

	mark_de_hidden(old_de.de_deh + old_de.de_entry_num);
	journal_mark_dirty(&th, old_dir->i_sb, old_de.de_bh);
	ctime = CURRENT_TIME_SEC;
	old_dir->i_ctime = old_dir->i_mtime = ctime;
	new_dir->i_ctime = new_dir->i_mtime = ctime;
	/* thanks to Alex Adriaanse <*****@*****.**> for patch which adds ctime update of
	   renamed object */
	old_inode->i_ctime = ctime;

	if (new_dentry_inode) {
		// adjust link number of the victim
		if (S_ISDIR(new_dentry_inode->i_mode)) {
			clear_nlink(new_dentry_inode);
		} else {
			drop_nlink(new_dentry_inode);
		}
		new_dentry_inode->i_ctime = ctime;
		savelink = new_dentry_inode->i_nlink;
	}

	if (S_ISDIR(old_inode_mode)) {
		/* adjust ".." of renamed directory */
		set_ino_in_dir_entry(&dot_dot_de, INODE_PKEY(new_dir));
		journal_mark_dirty(&th, new_dir->i_sb, dot_dot_de.de_bh);

		if (!new_dentry_inode)
			/* there (in new_dir) was no directory, so it got new link
			   (".."  of renamed directory) */
			INC_DIR_INODE_NLINK(new_dir);

		/* old directory lost one link - ".. " of renamed directory */
		DEC_DIR_INODE_NLINK(old_dir);
	}
	// looks like in 2.3.99pre3 brelse is atomic. so we can use pathrelse
	pathrelse(&new_entry_path);
	pathrelse(&dot_dot_entry_path);

	// FIXME: this reiserfs_cut_from_item's return value may screw up
	// anybody, but it will panic if will not be able to find the
	// entry. This needs one more clean up
	if (reiserfs_cut_from_item
	    (&th, &old_entry_path, &(old_de.de_entry_key), old_dir, NULL,
	     0) < 0)
		reiserfs_error(old_dir->i_sb, "vs-7060",
			       "couldn't not cut old name. Fsck later?");

	old_dir->i_size -= DEH_SIZE + old_de.de_entrylen;

	reiserfs_update_sd(&th, old_dir);
	reiserfs_update_sd(&th, new_dir);
	reiserfs_update_sd(&th, old_inode);

	if (new_dentry_inode) {
		if (savelink == 0)
			add_save_link(&th, new_dentry_inode,
				      0 /* not truncate */ );
		reiserfs_update_sd(&th, new_dentry_inode);
	}

	retval = journal_end(&th, old_dir->i_sb, jbegin_count);
	reiserfs_write_unlock(old_dir->i_sb);
	return retval;
}
/* Allocates blocks for a file to fulfil write request.
   Maps all unmapped but prepared pages from the list.
   Updates metadata with newly allocated blocknumbers as needed */
int reiserfs_allocate_blocks_for_region(
				struct reiserfs_transaction_handle *th,
				struct inode *inode, /* Inode we work with */
				loff_t pos, /* Writing position */
				int num_pages, /* number of pages write going
						  to touch */
				int write_bytes, /* amount of bytes to write */
				struct page **prepared_pages, /* array of
							         prepared pages
							       */
				int blocks_to_allocate /* Amount of blocks we
							  need to allocate to
							  fit the data into file
							 */
				)
{
    struct cpu_key key; // cpu key of item that we are going to deal with
    struct item_head *ih; // pointer to item head that we are going to deal with
    struct buffer_head *bh; // Buffer head that contains items that we are going to deal with
    __u32 * item; // pointer to item we are going to deal with
    INITIALIZE_PATH(path); // path to item, that we are going to deal with.
    b_blocknr_t *allocated_blocks; // Pointer to a place where allocated blocknumbers would be stored.
    reiserfs_blocknr_hint_t hint; // hint structure for block allocator.
    size_t res; // return value of various functions that we call.
    int curr_block; // current block used to keep track of unmapped blocks.
    int i; // loop counter
    int itempos; // position in item
    unsigned int from = (pos & (PAGE_CACHE_SIZE - 1)); // writing position in
						       // first page
    unsigned int to = ((pos + write_bytes - 1) & (PAGE_CACHE_SIZE - 1)) + 1; /* last modified byte offset in last page */
    __u64 hole_size ; // amount of blocks for a file hole, if it needed to be created.
    int modifying_this_item = 0; // Flag for items traversal code to keep track
				 // of the fact that we already prepared
				 // current block for journal
    int will_prealloc = 0;

    RFALSE(!blocks_to_allocate, "green-9004: tried to allocate zero blocks?");

    /* only preallocate if this is a small write */
    if (REISERFS_I(inode)->i_prealloc_count ||
       (!(write_bytes & (inode->i_sb->s_blocksize -1)) &&
        blocks_to_allocate <
        REISERFS_SB(inode->i_sb)->s_alloc_options.preallocsize))
        will_prealloc = REISERFS_SB(inode->i_sb)->s_alloc_options.preallocsize;

    allocated_blocks = kmalloc((blocks_to_allocate + will_prealloc) *
    					sizeof(b_blocknr_t), GFP_NOFS);

    /* First we compose a key to point at the writing position, we want to do
       that outside of any locking region. */
    make_cpu_key (&key, inode, pos+1, TYPE_ANY, 3/*key length*/);

    /* If we came here, it means we absolutely need to open a transaction,
       since we need to allocate some blocks */
    reiserfs_write_lock(inode->i_sb); // Journaling stuff and we need that.
    journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); // Wish I know if this number enough
    reiserfs_update_inode_transaction(inode) ;

    /* Look for the in-tree position of our write, need path for block allocator */
    res = search_for_position_by_key(inode->i_sb, &key, &path);
    if ( res == IO_ERROR ) {
	res = -EIO;
	goto error_exit;
    }
   
    /* Allocate blocks */
    /* First fill in "hint" structure for block allocator */
    hint.th = th; // transaction handle.
    hint.path = &path; // Path, so that block allocator can determine packing locality or whatever it needs to determine.
    hint.inode = inode; // Inode is needed by block allocator too.
    hint.search_start = 0; // We have no hint on where to search free blocks for block allocator.
    hint.key = key.on_disk_key; // on disk key of file.
    hint.block = inode->i_blocks>>(inode->i_sb->s_blocksize_bits-9); // Number of disk blocks this file occupies already.
    hint.formatted_node = 0; // We are allocating blocks for unformatted node.
    hint.preallocate = will_prealloc;

    /* Call block allocator to allocate blocks */
    res = reiserfs_allocate_blocknrs(&hint, allocated_blocks, blocks_to_allocate, blocks_to_allocate);
    if ( res != CARRY_ON ) {
	if ( res == NO_DISK_SPACE ) {
	    /* We flush the transaction in case of no space. This way some
	       blocks might become free */
	    SB_JOURNAL(inode->i_sb)->j_must_wait = 1;
	    restart_transaction(th, inode, &path);

	    /* We might have scheduled, so search again */
	    res = search_for_position_by_key(inode->i_sb, &key, &path);
	    if ( res == IO_ERROR ) {
		res = -EIO;
		goto error_exit;
	    }

	    /* update changed info for hint structure. */
	    res = reiserfs_allocate_blocknrs(&hint, allocated_blocks, blocks_to_allocate, blocks_to_allocate);
	    if ( res != CARRY_ON ) {
		res = -ENOSPC; 
		pathrelse(&path);
		goto error_exit;
	    }
	} else {
	    res = -ENOSPC;
	    pathrelse(&path);
	    goto error_exit;
	}
    }

#ifdef __BIG_ENDIAN
        // Too bad, I have not found any way to convert a given region from
        // cpu format to little endian format
    {
        int i;
        for ( i = 0; i < blocks_to_allocate ; i++)
            allocated_blocks[i]=cpu_to_le32(allocated_blocks[i]);
    }
#endif

    /* Blocks allocating well might have scheduled and tree might have changed,
       let's search the tree again */
    /* find where in the tree our write should go */
    res = search_for_position_by_key(inode->i_sb, &key, &path);
    if ( res == IO_ERROR ) {
	res = -EIO;
	goto error_exit_free_blocks;
    }

    bh = get_last_bh( &path ); // Get a bufferhead for last element in path.
    ih = get_ih( &path );      // Get a pointer to last item head in path.
    item = get_item( &path );  // Get a pointer to last item in path

    /* Let's see what we have found */
    if ( res != POSITION_FOUND ) { /* position not found, this means that we
				      might need to append file with holes
				      first */
	// Since we are writing past the file's end, we need to find out if
	// there is a hole that needs to be inserted before our writing
	// position, and how many blocks it is going to cover (we need to
	//  populate pointers to file blocks representing the hole with zeros)

	{
	    int item_offset = 1;
	    /*
	     * if ih is stat data, its offset is 0 and we don't want to
	     * add 1 to pos in the hole_size calculation
	     */
	    if (is_statdata_le_ih(ih))
	        item_offset = 0;
	    hole_size = (pos + item_offset -
	            (le_key_k_offset( get_inode_item_key_version(inode),
		    &(ih->ih_key)) +
		    op_bytes_number(ih, inode->i_sb->s_blocksize))) >>
		    inode->i_sb->s_blocksize_bits;
	}
/* returns 0 if name is not found in a directory and objectid of
   pointed object otherwise and returns minimal not used generation
   counter.  dies if found object is not a directory. */
int reiserfs_find_entry (reiserfs_filsys_t fs, struct key * dir, char * name, 
			 int * min_gen_counter)
{
    struct key entry_key;
    int retval;
    int i;
    INITIALIZE_PATH (path);
    struct item_head * ih;
    struct reiserfs_de_head * deh;
    struct key * rdkey;
    __u32 hash;

    entry_key.k_dir_id = cpu_to_le32(dir->k_dir_id);
    entry_key.k_objectid = cpu_to_le32(dir->k_objectid);
    hash = hash_value (fs, name);
    set_type_and_offset (KEY_FORMAT_1, &entry_key, hash, TYPE_DIRENTRY);    
    *min_gen_counter = 0;

    if (_search_by_entry_key (fs, &entry_key, &path) == DIRECTORY_NOT_FOUND) {
	pathrelse (&path);
	return 0;
    }

    do {
	ih = get_ih (&path);
	deh = B_I_DEH (get_bh (&path), ih) + path.pos_in_item;
	for (i = path.pos_in_item; i < ih_entry_count (ih); i ++, deh ++) {
	    if (GET_HASH_VALUE (deh_offset (deh)) != GET_HASH_VALUE (hash)) {
		/* all entries having the same hash were scanned */
		pathrelse (&path);
		return 0;
	    }

	    if (GET_GENERATION_NUMBER (deh_offset (deh)) == *min_gen_counter)
		(*min_gen_counter) ++;

	    if (!memcmp (name_in_entry (deh, i), name, strlen (name))) {
		pathrelse (&path);
		return deh_objectid (deh) ? deh_objectid (deh) : 1;
	    }
	}

	rdkey = _get_rkey (&path);
	if (!rdkey || not_of_one_file (rdkey, dir)) {
	    pathrelse (&path);
	    return 0;
	}
	
	if (!is_direntry_key (rdkey))
	    reiserfs_panic ("reiserfs_find_entry: can not find name in broken directory yet");

	/* next item is the item of the directory we are looking name in */
	if (GET_HASH_VALUE (get_offset (rdkey)) != hash) {
	    /* but there is no names with given hash */
	    pathrelse (&path);
	    return 0;
	}

	/* first name of that item may be a name we are looking for */
	entry_key = *rdkey;
	pathrelse (&path);
	retval = _search_by_entry_key (fs, &entry_key, &path);
	if (retval != POSITION_FOUND)
	    reiserfs_panic ("reiserfs_find_entry: wrong delimiting key in the tree");

    } while (1);
    
    return 0;
}
/* NOTE: this only should be used to look for keys who exists */
int _search_by_entry_key (reiserfs_filsys_t fs, struct key * key, 
			  struct path * path)
{
    struct buffer_head * bh;
    int item_pos;
    struct item_head * ih;
    struct key tmpkey;

    if (_search_by_key (fs, key, path) == ITEM_FOUND) {
        path->pos_in_item = 0;
        return POSITION_FOUND;
    }

    bh = get_bh (path);
    item_pos = get_item_pos (path);
    ih = get_ih (path);

    if (item_pos == 0) {
	/* key is less than the smallest key in the tree */
	if (not_of_one_file (&(ih->ih_key), key))
	    /* there are no items of that directory */
	    return DIRECTORY_NOT_FOUND;

	if (!is_direntry_ih (ih))
	    reiserfs_panic ("_search_by_entry_key: found item is not of directory type %H",
			    ih);

	/* key we looked for should be here */
        path->pos_in_item = 0;
	return POSITION_NOT_FOUND;
    }

    /* take previous item */
    item_pos --;
    ih --;
    PATH_LAST_POSITION (path) --;

    if (not_of_one_file (&(ih->ih_key), key) || !is_direntry_ih (ih)) {
        /* previous item belongs to another object or is stat data, check next
           item */

	item_pos ++;
	PATH_LAST_POSITION (path) ++;

        if (item_pos < B_NR_ITEMS (bh)) {
	    /* next item is in the same node */
	    ih ++;
            if (not_of_one_file (&(ih->ih_key), key)) {
		/* there are no items of that directory */
                path->pos_in_item = 0;
                return DIRECTORY_NOT_FOUND;
            }

            if (!is_direntry_ih (ih))
		reiserfs_panic ("_search_by_entry_key: %k is not a directory",
				key);
        } else {
	    /* next item is in right neighboring node */
            struct key * next_key = _get_rkey (path);

            if (next_key == 0 || not_of_one_file (next_key, key)) {
                /* there are no items of that directory */
                path->pos_in_item = 0;
                return DIRECTORY_NOT_FOUND;
            }

            if (!is_direntry_key (next_key))
		reiserfs_panic ("_search_by_entry_key: %k is not a directory",
				key);

            /* we got right delimiting key - search for it - the entry will be
	       pasted in position 0 */
            copy_key (&tmpkey, next_key);
            pathrelse (path);
            if (_search_by_key (fs, &tmpkey, path) != ITEM_FOUND || PATH_LAST_POSITION (path) != 0)
                reiserfs_panic ("_search_by_entry_key: item corresponding to delimiting key %k not found",
				&tmpkey);
        }

        /* next item is the part of this directory */
        path->pos_in_item = 0;
        return POSITION_NOT_FOUND;
    }


    /* previous item is part of desired directory */
    if (_bin_search (&(key->u.k_offset_v1.k_offset), B_I_DEH (bh, ih), ih_entry_count (ih),
		     DEH_SIZE, &(path->pos_in_item), comp_dir_entries) == ITEM_FOUND)
	return POSITION_FOUND;

    return POSITION_NOT_FOUND;
}