/*-
 * Copyright 2000 Hans Reiser
 * See README for licensing and copyright details
 * 
 * Ported to FreeBSD by Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
 * 
 * $FreeBSD$
 */

#include <gnu/fs/reiserfs/reiserfs_fs.h>

static b_strategy_t reiserfs_bufstrategy;

/*
 * Buffer operations for ReiserFS vnodes.
 * We punt on VOP_BMAP, so we need to do strategy on the file's vnode
 * rather than the underlying device's.
 */
static struct buf_ops reiserfs_vnbufops = {
	.bop_name	= "ReiserFS",
	.bop_strategy	= reiserfs_bufstrategy,
};

/* Default io size devuned in super.c */
extern int reiserfs_default_io_size;
void inode_set_bytes(struct reiserfs_node *ip, off_t bytes);

/* Args for the create parameter of reiserfs_get_block */
#define GET_BLOCK_NO_CREATE	0  /* Don't create new blocks or convert
				      tails */
#define GET_BLOCK_CREATE	1  /* Add anything you need to find block */
#define GET_BLOCK_NO_HOLE	2  /* Return ENOENT for file holes */
#define GET_BLOCK_READ_DIRECT	4  /* Read the tail if indirect item not
				      found */
#define GET_BLOCK_NO_ISEM	8  /* i_sem is not held, don't preallocate */
#define GET_BLOCK_NO_DANGLE	16 /* Don't leave any transactions running */

/* -------------------------------------------------------------------
 * vnode operations
 * -------------------------------------------------------------------*/

int
reiserfs_read(struct vop_read_args *ap)
{
	struct uio *uio;
	struct vnode *vp;
	struct reiserfs_node *ip;
	struct reiserfs_sb_info *sbi;

	int error;
	long size;
	daddr_t lbn;
	off_t bytesinfile, offset;

	uio = ap->a_uio;
	vp  = ap->a_vp;
	ip  = VTOI(vp);
	sbi = ip->i_reiserfs;

	size = sbi->s_blocksize;

	for (error = 0; uio->uio_resid > 0;) {
		if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
			break;

		/* Compute the logical block number and its offset */
		lbn    = uio->uio_offset / size;
		offset = uio->uio_offset % size;
		reiserfs_log(LOG_DEBUG, "logical block number: %ju\n",
		    (intmax_t)lbn);
		reiserfs_log(LOG_DEBUG, "block offset:         %ju\n",
		    (intmax_t)offset);

		/* Read file blocks */
		reiserfs_log(LOG_DEBUG, "reiserfs_get_block(%ju)\n",
		    (intmax_t)lbn);
		if ((error = reiserfs_get_block(ip, lbn, offset, uio)) != 0) {
			reiserfs_log(LOG_DEBUG,
			    "reiserfs_get_block returned the error %d\n",
			    error);
			break;
		}
	}

	return (error);
}

static void
reiserfs_bufstrategy(struct bufobj *bo, struct buf *bp)
{
	struct vnode *vp;
	int rc;

	vp = bo->bo_private;
	KASSERT(bo == &vp->v_bufobj, ("BO/VP mismatch: vp %p bo %p != %p",
	    vp, &vp->v_bufobj, bo));
	rc = VOP_STRATEGY(vp, bp);
	KASSERT(rc == 0, ("ReiserFS VOP_STRATEGY failed: bp=%p, "
	    "vp=%p, rc=%d", bp, vp, rc));
}

int
reiserfs_inactive(struct vop_inactive_args *ap)
{
	int error;
	struct vnode *vp;
	struct thread *td;
	struct reiserfs_node *ip;

	error = 0;
	vp = ap->a_vp;
	td = ap->a_td;
	ip = VTOI(vp);

	reiserfs_log(LOG_DEBUG, "deactivating inode used %d times\n",
	    vp->v_usecount);

#if 0
	/* Ignore inodes related to stale file handles. */
	if (ip->i_mode == 0)
		goto out;

out:
#endif

	/*
	 * If we are done with the inode, reclaim it so that it can be reused
	 * immediately.
	 */
	if (ip->i_mode == 0) {
		reiserfs_log(LOG_DEBUG, "recyling\n");
		vrecycle(vp, td);
	}

	return (error);
}

int
reiserfs_reclaim(struct vop_reclaim_args *ap)
{
	struct reiserfs_node *ip;
	struct vnode *vp;

	vp = ap->a_vp;

	reiserfs_log(LOG_DEBUG, "reclaiming inode used %d times\n",
	    vp->v_usecount);
	ip = VTOI(vp);

	/* XXX Update this node (write to the disk) */

	/* Remove the inode from its hash chain. */
	vfs_hash_remove(vp);

	reiserfs_log(LOG_DEBUG, "free private data\n");
	free(vp->v_data, M_REISERFSNODE);
	vp->v_data = NULL;
	vnode_destroy_vobject(vp);

	return (0);
}

/* -------------------------------------------------------------------
 * Functions from linux/fs/reiserfs/inode.c
 * -------------------------------------------------------------------*/

static void
_make_cpu_key(struct cpu_key *key, int version,
    uint32_t dirid, uint32_t objectid, off_t offset, int type, int length)
{

	key->version = version;

	key->on_disk_key.k_dir_id   = dirid;
	key->on_disk_key.k_objectid = objectid;
	set_cpu_key_k_offset(key, offset);
	set_cpu_key_k_type(key, type);
	key->key_length = length;
}

/*
 * Take base of inode_key (it comes from inode always) (dirid, objectid)
 * and version from an inode, set offset and type of key
 */
void
make_cpu_key(struct cpu_key *key, struct reiserfs_node *ip, off_t offset,
    int type, int length)
{

	_make_cpu_key(key, get_inode_item_key_version(ip),
	    le32toh(INODE_PKEY(ip)->k_dir_id),
	    le32toh(INODE_PKEY(ip)->k_objectid),
	    offset, type, length);
}

int
reiserfs_get_block(struct reiserfs_node *ip, long block, off_t offset,
    struct uio *uio)
{
	caddr_t blk = NULL, p;
	struct cpu_key key;
	/* unsigned long offset; */
	INITIALIZE_PATH(path);
	struct buf *bp, *blk_bp;
	struct item_head *ih;
	struct reiserfs_sb_info *sbi;
	int blocknr, chars, done = 0, ret = 0, args = 0;

	sbi = ip->i_reiserfs;

	/* Prepare the key to look for the 'block'-th block of file */
	reiserfs_log(LOG_DEBUG, "prepare cpu key\n");
	make_cpu_key(&key, ip, (off_t)block * sbi->s_blocksize + 1, TYPE_ANY, 3);

	/* research: */
	reiserfs_log(LOG_DEBUG, "search for position\n");
	if (search_for_position_by_key(sbi, &key, &path) != POSITION_FOUND) {
		reiserfs_log(LOG_DEBUG, "position not found\n");
		pathrelse(&path);
#if 0
		if (blk)
			kunmap(bh_result->b_page);
#endif
		/*
		 * We do not return ENOENT if there is a hole but page is
		 * uptodate, because it means that there is some MMAPED data
		 * associated with it that is yet to be written to disk.
		 */
		if ((args & GET_BLOCK_NO_HOLE)/* &&
		    !PageUptodate(bh_result->b_page)*/)
			return (ENOENT);
		return (0);
	}
	reiserfs_log(LOG_DEBUG, "position found\n");

	bp = get_last_bp(&path);
	ih = get_ih(&path);

	if (is_indirect_le_ih(ih)) {
		off_t xfersize;
		uint32_t *ind_item = (uint32_t *)B_I_PITEM(bp, ih);

		reiserfs_log(LOG_DEBUG, "item is INDIRECT\n");

		blocknr = get_block_num(ind_item, path.pos_in_item);
		reiserfs_log(LOG_DEBUG, "block number: %d "
		    "(ind_item=%p, pos_in_item=%u)\n",
		    blocknr, ind_item, path.pos_in_item);

		xfersize = MIN(sbi->s_blocksize - offset,
		    ip->i_size - uio->uio_offset);
		xfersize = MIN(xfersize, uio->uio_resid);

		if (blocknr) {
			ret = bread(sbi->s_devvp,
			    blocknr * btodb(sbi->s_blocksize),
			    sbi->s_blocksize, NOCRED, &blk_bp);
			reiserfs_log(LOG_DEBUG, "xfersize: %ju\n",
			    (intmax_t)xfersize);
			ret = uiomove(blk_bp->b_data + offset, xfersize, uio);
			brelse(blk_bp);
		} else {
			/*
			 * We do not return ENOENT if there is a hole but
			 * page is uptodate, because it means That there
			 * is some MMAPED data associated with it that
			 * is yet to be written to disk.
			 */
			if ((args & GET_BLOCK_NO_HOLE)/* &&
			    !PageUptodate(bh_result->b_page)*/)
				ret = (ENOENT);

			/* Skip this hole */
			uio->uio_resid  -= xfersize;
			uio->uio_offset += xfersize;
		}

		pathrelse(&path);
		return (ret);
	}

	reiserfs_log(LOG_DEBUG, "item should be DIRECT\n");

#if 0
	/* Requested data are in direct item(s) */
	if (!(args & GET_BLOCK_READ_DIRECT)) {
		/*
		 * We are called by bmap. FIXME: we can not map block of
		 * file when it is stored in direct item(s)
		 */
		pathrelse(&path);
#if 0
		if (blk)
			kunmap(bh_result->b_page);
#endif
		return (ENOENT);
	}
#endif

#if 0
	/*
	 * If we've got a direct item, and the buffer or page was uptodate, we
	 * don't want to pull data off disk again. Skip to the end, where we
	 * map the buffer and return
	 */
	if (buffer_uptodate(bh_result)) {
		goto finished;
	} else
		/*
		 * grab_tail_page can trigger calls to reiserfs_get_block
		 * on up to date pages without any buffers. If the page
		 * is up to date, we don't want read old data off disk.
		 * Set the up to date bit on the buffer instead and jump
		 * to the end
		 */
		if (!bh_result->b_page || PageUptodate(bh_result->b_page)) {
			set_buffer_uptodate(bh_result);
			goto finished;
		}
#endif

#if 0
	/* Read file tail into part of page */
	offset = (cpu_key_k_offset(&key) - 1) & (PAGE_CACHE_SIZE - 1);
	fs_gen = get_generation(ip->i_reiserfs);
	copy_item_head(&tmp_ih, ih);
#endif

#if 0
	/*
	 * We only want to kmap if we are reading the tail into the page. this
	 * is not the common case, so we don't kmap until we are sure we need
	 * to. But, this means the item might move if kmap schedules
	 */
	if (!blk) {
		blk = (char *)kmap(bh_result->b_page);
		if (fs_changed (fs_gen, sbi) && item_moved(&tmp_ih, &path))
			goto research;
	}
	blk += offset;
	memset(blk, 0, sbi->s_blocksize);
#endif
	if (!blk) {
		reiserfs_log(LOG_DEBUG, "allocating buffer\n");
		blk = malloc(ip->i_size, M_REISERFSNODE, M_WAITOK | M_ZERO);
		if (!blk)
			return (ENOMEM);
	}
	/* p += offset; */

	p = blk;
	do {
		if (!is_direct_le_ih(ih)) {
			reiserfs_log(LOG_ERR, "BUG\n");
			return (ENOENT); /* XXX Wrong error code */
		}

		/*
		 * Make sure we don't read more bytes than actually exist
		 * in the file. This can happen in odd cases where i_size
		 * isn't correct, and when direct item padding results in
		 * a few extra bytes at the end of the direct item
		 */
		if ((le_ih_k_offset(ih) + path.pos_in_item) > ip->i_size)
			break;

		if ((le_ih_k_offset(ih) - 1 + ih_item_len(ih)) > ip->i_size) {
			chars = ip->i_size - (le_ih_k_offset(ih) - 1) -
			    path.pos_in_item;
			done  = 1;
		} else {
			chars = ih_item_len(ih) - path.pos_in_item;
		}
		reiserfs_log(LOG_DEBUG, "copying %d bytes\n", chars);
		memcpy(p, B_I_PITEM(bp, ih) + path.pos_in_item, chars);
		if (done) {
			reiserfs_log(LOG_DEBUG, "copy done\n");
			break;
		}

		p += chars;

		if (PATH_LAST_POSITION(&path) != (B_NR_ITEMS(bp) - 1))
			/*
			 * We done, if read direct item is not the last
			 * item of node
			 * FIXME: we could try to check right delimiting
			 * key to see whether direct item continues in
			 * the right neighbor or rely on i_size
			 */
			break;

		/* Update key to look for the next piece */
		set_cpu_key_k_offset(&key, cpu_key_k_offset(&key) + chars);
		if (search_for_position_by_key(sbi, &key, &path) !=
		    POSITION_FOUND)
			/*
			 * We read something from tail, even if now we got
			 * IO_ERROR
			 */
			break;

		bp = get_last_bp(&path);
		ih = get_ih(&path);
	} while (1);

	/* finished: */
	pathrelse(&path);
	/*
	 * This buffer has valid data, but isn't valid for io. mapping it to
	 * block #0 tells the rest of reiserfs it just has a tail in it
	 */
	ret = uiomove(blk, ip->i_size, uio);
	free(blk, M_REISERFSNODE);
	return (ret);
}

/*
 * Compute real number of used bytes by file
 * Following three functions can go away when we'll have enough space in
 * stat item
 */
static int
real_space_diff(struct reiserfs_node *ip, int sd_size)
{
	int bytes;
	off_t blocksize = ip->i_reiserfs->s_blocksize;

	if (S_ISLNK(ip->i_mode) || S_ISDIR(ip->i_mode))
		return (sd_size);

	/* End of file is also in full block with indirect reference, so round
	 * up to the next block.
	 *
	 * There is just no way to know if the tail is actually packed on the
	 * file, so we have to assume it isn't. When we pack the tail, we add
	 * 4 bytes to pretend there really is an unformatted node pointer. */
	bytes = ((ip->i_size + (blocksize - 1)) >>
	    ip->i_reiserfs->s_blocksize_bits) * UNFM_P_SIZE + sd_size;

	return (bytes);
}

static inline off_t
to_real_used_space(struct reiserfs_node *ip, unsigned long blocks, int sd_size)
{

	if (S_ISLNK(ip->i_mode) || S_ISDIR(ip->i_mode)) {
		return ip->i_size + (off_t)(real_space_diff(ip, sd_size));
	}

	return ((off_t)real_space_diff(ip, sd_size)) + (((off_t)blocks) << 9);
}

void
inode_set_bytes(struct reiserfs_node *ip, off_t bytes)
{

	ip->i_blocks = bytes >> 9;
	ip->i_bytes  = bytes & 511;
}

/* Called by read_locked_inode */
static void
init_inode(struct reiserfs_node *ip, struct path *path)
{
	struct buf *bp;
	struct item_head *ih;
	uint32_t rdev;

	bp = PATH_PLAST_BUFFER(path);
	ih = PATH_PITEM_HEAD(path);

	reiserfs_log(LOG_DEBUG, "copy the key (objectid=%d, dirid=%d)\n",
	    ih->ih_key.k_objectid, ih->ih_key.k_dir_id);
	copy_key(INODE_PKEY(ip), &(ih->ih_key));
	/* ip->i_blksize = reiserfs_default_io_size; */

	reiserfs_log(LOG_DEBUG, "reset some inode structure members\n");
	REISERFS_I(ip)->i_flags = 0;
#if 0
	REISERFS_I(ip)->i_prealloc_block = 0;
	REISERFS_I(ip)->i_prealloc_count = 0;
	REISERFS_I(ip)->i_trans_id = 0;
	REISERFS_I(ip)->i_jl = NULL;
	REISERFS_I(ip)->i_acl_access = NULL;
	REISERFS_I(ip)->i_acl_default = NULL;
#endif

	if (stat_data_v1(ih)) {
		reiserfs_log(LOG_DEBUG, "reiserfs/init_inode: stat data v1\n");
		struct stat_data_v1 *sd;
		unsigned long blocks;

		sd = (struct stat_data_v1 *)B_I_PITEM(bp, ih);
		
		reiserfs_log(LOG_DEBUG,
		    "reiserfs/init_inode: filling more members\n");
		set_inode_item_key_version(ip, KEY_FORMAT_3_5);
		set_inode_sd_version(ip, STAT_DATA_V1);
		ip->i_mode          = sd_v1_mode(sd);
		ip->i_nlink         = sd_v1_nlink(sd);
		ip->i_uid           = sd_v1_uid(sd);
		ip->i_gid           = sd_v1_gid(sd);
		ip->i_size          = sd_v1_size(sd);
		ip->i_atime.tv_sec  = sd_v1_atime(sd);
		ip->i_mtime.tv_sec  = sd_v1_mtime(sd);
		ip->i_ctime.tv_sec  = sd_v1_ctime(sd);
		ip->i_atime.tv_nsec = 0;
		ip->i_ctime.tv_nsec = 0;
		ip->i_mtime.tv_nsec = 0;

		reiserfs_log(LOG_DEBUG, "  mode  = %08x\n", ip->i_mode);
		reiserfs_log(LOG_DEBUG, "  nlink = %d\n", ip->i_nlink);
		reiserfs_log(LOG_DEBUG, "  owner = %d:%d\n", ip->i_uid,
		    ip->i_gid);
		reiserfs_log(LOG_DEBUG, "  size  = %ju\n",
		    (intmax_t)ip->i_size);
		reiserfs_log(LOG_DEBUG, "  atime = %jd\n",
		    (intmax_t)ip->i_atime.tv_sec);
		reiserfs_log(LOG_DEBUG, "  mtime = %jd\n",
		    (intmax_t)ip->i_mtime.tv_sec);
		reiserfs_log(LOG_DEBUG, "  ctime = %jd\n",
		    (intmax_t)ip->i_ctime.tv_sec);

		ip->i_blocks     = sd_v1_blocks(sd);
		ip->i_generation = le32toh(INODE_PKEY(ip)->k_dir_id);
		blocks = (ip->i_size + 511) >> 9;
		blocks = _ROUND_UP(blocks, ip->i_reiserfs->s_blocksize >> 9);
		if (ip->i_blocks > blocks) {
			/*
			 * There was a bug in <= 3.5.23 when i_blocks could
			 * take negative values. Starting from 3.5.17 this
			 * value could even be stored in stat data. For such
			 * files we set i_blocks based on file size. Just 2
			 * notes: this can be wrong for sparce files. On-disk
			 * value will be only updated if file's inode will
			 * ever change.
			 */
			ip->i_blocks = blocks;
		}

		rdev = sd_v1_rdev(sd);
		REISERFS_I(ip)->i_first_direct_byte =
		    sd_v1_first_direct_byte(sd);

		/*
		 * An early bug in the quota code can give us an odd number
		 * for the block count. This is incorrect, fix it here.
		 */
		if (ip->i_blocks & 1) {
			ip->i_blocks++ ;
		}
		inode_set_bytes(ip, to_real_used_space(ip, ip->i_blocks,
		    SD_V1_SIZE));

		/*
		 * nopack is initially zero for v1 objects. For v2 objects,
		 * nopack is initialised from sd_attrs
		 */
		REISERFS_I(ip)->i_flags &= ~i_nopack_mask;
		reiserfs_log(LOG_DEBUG, "...done\n");
	} else {
		reiserfs_log(LOG_DEBUG, "stat data v2\n");
		/*
		 * New stat data found, but object may have old items
		 * (directories and symlinks)
		 */
		struct stat_data *sd = (struct stat_data *)B_I_PITEM(bp, ih);

		reiserfs_log(LOG_DEBUG, "filling more members\n");
		ip->i_mode          = sd_v2_mode(sd);
		ip->i_nlink         = sd_v2_nlink(sd);
		ip->i_uid           = sd_v2_uid(sd);
		ip->i_size          = sd_v2_size(sd);
		ip->i_gid           = sd_v2_gid(sd);
		ip->i_mtime.tv_sec  = sd_v2_mtime(sd);
		ip->i_atime.tv_sec  = sd_v2_atime(sd);
		ip->i_ctime.tv_sec  = sd_v2_ctime(sd);
		ip->i_ctime.tv_nsec = 0;
		ip->i_mtime.tv_nsec = 0;
		ip->i_atime.tv_nsec = 0;

		reiserfs_log(LOG_DEBUG, "  mode  = %08x\n", ip->i_mode);
		reiserfs_log(LOG_DEBUG, "  nlink = %d\n", ip->i_nlink);
		reiserfs_log(LOG_DEBUG, "  owner = %d:%d\n", ip->i_uid,
		    ip->i_gid);
		reiserfs_log(LOG_DEBUG, "  size  = %ju\n",
		    (intmax_t)ip->i_size);
		reiserfs_log(LOG_DEBUG, "  atime = %jd\n",
		    (intmax_t)ip->i_atime.tv_sec);
		reiserfs_log(LOG_DEBUG, "  mtime = %jd\n",
		    (intmax_t)ip->i_mtime.tv_sec);
		reiserfs_log(LOG_DEBUG, "  ctime = %jd\n",
		    (intmax_t)ip->i_ctime.tv_sec);

		ip->i_blocks = sd_v2_blocks(sd);
		rdev         = sd_v2_rdev(sd);
		reiserfs_log(LOG_DEBUG, "  blocks = %u\n", ip->i_blocks);

		if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
			ip->i_generation = le32toh(INODE_PKEY(ip)->k_dir_id);
		else
			ip->i_generation = sd_v2_generation(sd);

		if (S_ISDIR(ip->i_mode) || S_ISLNK(ip->i_mode))
			set_inode_item_key_version(ip, KEY_FORMAT_3_5);
		else
			set_inode_item_key_version(ip, KEY_FORMAT_3_6);

		REISERFS_I(ip)->i_first_direct_byte = 0;
		set_inode_sd_version(ip, STAT_DATA_V2);
		inode_set_bytes(ip, to_real_used_space(ip, ip->i_blocks,
		    SD_V2_SIZE));

		/*
		 * Read persistent inode attributes from sd and initalise
		 * generic inode flags from them
		 */
		REISERFS_I(ip)->i_attrs = sd_v2_attrs(sd);
		sd_attrs_to_i_attrs(sd_v2_attrs(sd), ip);
		reiserfs_log(LOG_DEBUG, "...done\n");
	}

	pathrelse(path);
	if (S_ISREG(ip->i_mode)) {
		reiserfs_log(LOG_DEBUG, "this inode is a regular file\n");
		//ip->i_op = &reiserfs_file_ip_operations;
		//ip->i_fop = &reiserfs_file_operations;
		//ip->i_mapping->a_ops = &reiserfs_address_space_operations ;
	} else if (S_ISDIR(ip->i_mode)) {
		reiserfs_log(LOG_DEBUG, "this inode is a directory\n");
		//ip->i_op = &reiserfs_dir_ip_operations;
		//ip->i_fop = &reiserfs_dir_operations;
	} else if (S_ISLNK(ip->i_mode)) {
		reiserfs_log(LOG_DEBUG, "this inode is a symlink\n");
		//ip->i_op = &reiserfs_symlink_ip_operations;
		//ip->i_mapping->a_ops = &reiserfs_address_space_operations;
	} else {
		reiserfs_log(LOG_DEBUG, "this inode is something unknown in "
		    "this universe\n");
		ip->i_blocks = 0;
		//ip->i_op = &reiserfs_special_ip_operations;
		//init_special_ip(ip, ip->i_mode, new_decode_dev(rdev));
	}
}

/*
 * reiserfs_read_locked_inode is called to read the inode off disk, and
 * it does a make_bad_inode when things go wrong. But, we need to make
 * sure and clear the key in the private portion of the inode, otherwise
 * a corresponding iput might try to delete whatever object the inode
 * last represented.
 */
static void
reiserfs_make_bad_inode(struct reiserfs_node *ip) {

	memset(INODE_PKEY(ip), 0, KEY_SIZE);
	//make_bad_inode(inode);
}

void
reiserfs_read_locked_inode(struct reiserfs_node *ip,
    struct reiserfs_iget_args *args)
{
	INITIALIZE_PATH(path_to_sd);
	struct cpu_key key;
	unsigned long dirino;
	int retval;

	dirino = args->dirid;

	/*
	 * Set version 1, version 2 could be used too, because stat data
	 * key is the same in both versions
	 */
	key.version = KEY_FORMAT_3_5;
	key.on_disk_key.k_dir_id = dirino;
	key.on_disk_key.k_objectid = ip->i_number;
	key.on_disk_key.u.k_offset_v1.k_offset = SD_OFFSET;
	key.on_disk_key.u.k_offset_v1.k_uniqueness = SD_UNIQUENESS;

	/* Look for the object's stat data */
	retval = search_item(ip->i_reiserfs, &key, &path_to_sd);
	if (retval == IO_ERROR) {
		reiserfs_log(LOG_ERR,
		    "I/O failure occured trying to find stat"
		    "data %u/%u\n",
		    key.on_disk_key.k_dir_id, key.on_disk_key.k_objectid);
		reiserfs_make_bad_inode(ip);
		return;
	}
	if (retval != ITEM_FOUND) {
		/*
		 * A stale NFS handle can trigger this without it being
		 * an error
		 */
		reiserfs_log(LOG_ERR,
		    "item not found (objectid=%u, dirid=%u)\n",
		    key.on_disk_key.k_objectid, key.on_disk_key.k_dir_id);
		pathrelse(&path_to_sd);
		reiserfs_make_bad_inode(ip);
		ip->i_nlink = 0;
		return;
	}

	init_inode(ip, &path_to_sd);

	/*
	 * It is possible that knfsd is trying to access inode of a file
	 * that is being removed from the disk by some other thread. As
	 * we update sd on unlink all that is required is to check for
	 * nlink here. This bug was first found by Sizif when debugging
	 * SquidNG/Butterfly, forgotten, and found again after Philippe
	 * Gramoulle <philippe.gramoulle@mmania.com> reproduced it.
	 * 
	 * More logical fix would require changes in fs/inode.c:iput() to
	 * remove inode from hash-table _after_ fs cleaned disk stuff up and
	 * in iget() to return NULL if I_FREEING inode is found in hash-table.
	 */
	/*
	 * Currently there is one place where it's ok to meet inode with
	 * nlink == 0: processing of open-unlinked and half-truncated files
	 * during mount (fs/reiserfs/super.c:finish_unfinished()).
	 */
	if((ip->i_nlink == 0) &&
	    !REISERFS_SB(ip->i_reiserfs)->s_is_unlinked_ok ) {
		reiserfs_log(LOG_WARNING, "dead inode read from disk. This is "
		    "likely to be race with knfsd. Ignore");
		reiserfs_make_bad_inode(ip);
	}

	/* Init inode should be relsing */
	reiserfs_check_path(&path_to_sd);
}

int
reiserfs_iget(
    struct mount *mp, const struct cpu_key *key,
    struct vnode **vpp, struct thread *td)
{
	int error, flags;
	struct cdev *dev;
	struct vnode *vp;
	struct reiserfs_node *ip;
	struct reiserfs_mount *rmp;

	struct reiserfs_iget_args args;

	//restart:
	/* Check if the inode cache contains it */
	// XXX LK_EXCLUSIVE ?
	flags = LK_EXCLUSIVE;
	error = vfs_hash_get(mp, key->on_disk_key.k_objectid, flags,
	    td, vpp, NULL, NULL);
	if (error || *vpp != NULL)
		return (error);

	rmp = VFSTOREISERFS(mp);
	dev = rmp->rm_dev;

	/*
	 * If this malloc() is performed after the getnewvnode() it might
	 * block, leaving a vnode with a NULL v_data to be found by
	 * reiserfs_sync() if a sync happens to fire right then, which
	 * will cause a panic because reiserfs_sync() blindly dereferences
	 * vp->v_data (as well it should).
	 */
	reiserfs_log(LOG_DEBUG, "malloc(struct reiserfs_node)\n");
	ip = malloc(sizeof(struct reiserfs_node), M_REISERFSNODE,
	    M_WAITOK | M_ZERO);

	/* Allocate a new vnode/inode. */
	reiserfs_log(LOG_DEBUG, "getnewvnode\n");
	if ((error =
	    getnewvnode("reiserfs", mp, &reiserfs_vnodeops, &vp)) != 0) {
		*vpp = NULL;
		free(ip, M_REISERFSNODE);
		reiserfs_log(LOG_DEBUG, "getnewvnode FAILED\n");
		return (error);
	}

	args.dirid = key->on_disk_key.k_dir_id;
	args.objectid = key->on_disk_key.k_objectid;

	reiserfs_log(LOG_DEBUG, "filling *ip\n");
	vp->v_data     = ip;
	ip->i_vnode    = vp;
	ip->i_dev      = dev;
	ip->i_number   = args.objectid;
	ip->i_ino      = args.dirid;
	ip->i_reiserfs = rmp->rm_reiserfs;

	vp->v_bufobj.bo_ops = &reiserfs_vnbufops;
	vp->v_bufobj.bo_private = vp;

	/* If this is the root node, set the VV_ROOT flag */
	if (ip->i_number == REISERFS_ROOT_OBJECTID &&
	    ip->i_ino == REISERFS_ROOT_PARENT_OBJECTID)
		vp->v_vflag |= VV_ROOT;

#if 0
	if (VOP_LOCK(vp, LK_EXCLUSIVE) != 0)
		panic("reiserfs/iget: unexpected lock failure");

	/*
	 * Exclusively lock the vnode before adding to hash. Note, that we
	 * must not release nor downgrade the lock (despite flags argument
	 * says) till it is fully initialized.
	 */
	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, (struct mtx *)0);
#endif

	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
	error = insmntque(vp, mp);
	if (error != 0) {
		free(ip, M_REISERFSNODE);
		*vpp = NULL;
		reiserfs_log(LOG_DEBUG, "insmntque FAILED\n");
		return (error);
	}
	error = vfs_hash_insert(vp, key->on_disk_key.k_objectid, flags,
	    td, vpp, NULL, NULL);
	if (error || *vpp != NULL)
		return (error);

	/* Read the inode */
	reiserfs_log(LOG_DEBUG, "call reiserfs_read_locked_inode ("
	    "objectid=%d,dirid=%d)\n", args.objectid, args.dirid);
	reiserfs_read_locked_inode(ip, &args);

	ip->i_devvp = rmp->rm_devvp;

	switch(vp->v_type = IFTOVT(ip->i_mode)) {
	case VBLK:
		reiserfs_log(LOG_DEBUG, "vnode type VBLK\n");
		vp->v_op = &reiserfs_specops;
		break;
#if 0
	case VCHR:
		reiserfs_log(LOG_DEBUG, "vnode type VCHR\n");
		vp->v_op = &reiserfs_specops;
		vp = addaliasu(vp, ip->i_rdev);
		ip->i_vnode = vp;
		break;
	case VFIFO:
		reiserfs_log(LOG_DEBUG, "vnode type VFIFO\n");
		vp->v_op = reiserfs_fifoop_p;
		break;
#endif
	default:
		break;
	}

	*vpp = vp;
	return (0);
}

void
sd_attrs_to_i_attrs(uint16_t sd_attrs, struct reiserfs_node *ip)
{

	if (reiserfs_attrs(ip->i_reiserfs)) {
#if 0
		if (sd_attrs & REISERFS_SYNC_FL)
			ip->i_flags |= S_SYNC;
		else
			ip->i_flags &= ~S_SYNC;
#endif
		if (sd_attrs & REISERFS_IMMUTABLE_FL)
			ip->i_flags |= IMMUTABLE;
		else
			ip->i_flags &= ~IMMUTABLE;
		if (sd_attrs & REISERFS_APPEND_FL)
			ip->i_flags |= APPEND;
		else
			ip->i_flags &= ~APPEND;
#if 0
		if (sd_attrs & REISERFS_NOATIME_FL)
			ip->i_flags |= S_NOATIME;
		else
			ip->i_flags &= ~S_NOATIME;
		if (sd_attrs & REISERFS_NOTAIL_FL)
			REISERFS_I(ip)->i_flags |= i_nopack_mask;
		else
			REISERFS_I(ip)->i_flags &= ~i_nopack_mask;
#endif
	}
}

void
i_attrs_to_sd_attrs(struct reiserfs_node *ip, uint16_t *sd_attrs)
{

	if (reiserfs_attrs(ip->i_reiserfs)) {
#if 0
		if (ip->i_flags & S_SYNC)
			*sd_attrs |= REISERFS_SYNC_FL;
		else
			*sd_attrs &= ~REISERFS_SYNC_FL;
#endif
		if (ip->i_flags & IMMUTABLE)
			*sd_attrs |= REISERFS_IMMUTABLE_FL;
		else
			*sd_attrs &= ~REISERFS_IMMUTABLE_FL;
		if (ip->i_flags & APPEND)
			*sd_attrs |= REISERFS_APPEND_FL;
		else
			*sd_attrs &= ~REISERFS_APPEND_FL;
#if 0
		if (ip->i_flags & S_NOATIME)
			*sd_attrs |= REISERFS_NOATIME_FL;
		else
			*sd_attrs &= ~REISERFS_NOATIME_FL;
		if (REISERFS_I(ip)->i_flags & i_nopack_mask)
			*sd_attrs |= REISERFS_NOTAIL_FL;
		else
			*sd_attrs &= ~REISERFS_NOTAIL_FL;
#endif
	}
}
