/*-
 * Copyright 2000 Hans Reiser
 * See README for licensing and copyright details
 * 
 * Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
 * 
 * $FreeBSD: src/sys/gnu/fs/reiserfs/reiserfs_vfsops.c,v 1.4 2005/10/31 15:41:24 rwatson Exp $
 */

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

const char reiserfs_3_5_magic_string[] = REISERFS_SUPER_MAGIC_STRING;
const char reiserfs_3_6_magic_string[] = REISER2FS_SUPER_MAGIC_STRING;
const char reiserfs_jr_magic_string[]  = REISER2FS_JR_SUPER_MAGIC_STRING;

/*
 * Default recommended I/O size is 128k. There might be broken
 * applications that are confused by this. Use nolargeio mount option to
 * get usual i/o size = PAGE_SIZE.
 */
int reiserfs_default_io_size = 128 * 1024;

static vfs_cmount_t	reiserfs_cmount;
static vfs_fhtovp_t	reiserfs_fhtovp;
static vfs_mount_t	reiserfs_mount;
static vfs_root_t	reiserfs_root;
static vfs_statfs_t	reiserfs_statfs;
static vfs_unmount_t	reiserfs_unmount;
static vfs_vptofh_t	reiserfs_vptofh;

static int	reiserfs_mountfs(struct vnode *devvp, struct mount *mp,
		    struct thread *td);
static void	load_bitmap_info_data(struct reiserfs_sb_info *sbi,
		    struct reiserfs_bitmap_info *bi);
static int	read_bitmaps(struct reiserfs_mount *rmp);
static int	read_old_bitmaps(struct reiserfs_mount *rmp);
static int	read_super_block(struct reiserfs_mount *rmp, int offset);
static hashf_t	hash_function(struct reiserfs_mount *rmp);

static int	get_root_node(struct reiserfs_mount *rmp,
		    struct reiserfs_node **root);
uint32_t	find_hash_out(struct reiserfs_mount *rmp);

MALLOC_DEFINE(M_REISERFSMNT, "reiserfs_mount", "ReiserFS mount structure");
MALLOC_DEFINE(M_REISERFSPATH, "reiserfs_path", "ReiserFS path structure");
MALLOC_DEFINE(M_REISERFSNODE, "reiserfs_node", "ReiserFS vnode private part");

/* -------------------------------------------------------------------
 * VFS operations
 * -------------------------------------------------------------------*/

static int
reiserfs_cmount(struct mntarg *ma, void *data, int flags, struct thread *td)
{
	struct reiserfs_args args;
	int error;

	error = copyin(data, &args, sizeof(args));
	if (error)
		return (error);

	ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN);
	ma = mount_arg(ma, "export", &args.export, sizeof args.export);

	error = kernel_mount(ma, flags);

	return (error);
}

/*
 * Mount system call
 */
static int
reiserfs_mount(struct mount *mp, struct thread *td)
{
	size_t size;
	int error, len;
	mode_t accessmode;
	char *path, *fspec;
	struct vnode *devvp;
	struct vfsoptlist *opts;
	struct export_args *export;
	struct reiserfs_mount *rmp;
	struct reiserfs_sb_info *sbi;
	struct nameidata nd, *ndp = &nd;

	if (!(mp->mnt_flag & MNT_RDONLY))
		return EROFS;

	/* Get the new options passed to mount */
	opts = mp->mnt_optnew;

	/* `fspath' contains the mount point (eg. /mnt/linux); REQUIRED */
	vfs_getopt(opts, "fspath", (void **)&path, NULL);
	reiserfs_log(LOG_INFO, "mount point is `%s'\n", path);

	/* `from' contains the device name (eg. /dev/ad0s1); REQUIRED */
	fspec = NULL;
	error = vfs_getopt(opts, "from", (void **)&fspec, &len);
	if (!error && fspec[len - 1] != '\0')
		return (EINVAL);
	reiserfs_log(LOG_INFO, "device is `%s'\n", fspec);

	/* Handle MNT_UPDATE (mp->mnt_flag) */
	if (mp->mnt_flag & MNT_UPDATE) {
		/* For now, only NFS export is supported. */
		error = vfs_getopt(opts, "export", (void **)&export, &len);
		if (error == 0 && len == sizeof(*export) && export->ex_flags)
			return (vfs_export(mp, export));
	}

	/* Not an update, or updating the name: look up the name
	 * and verify that it refers to a sensible disk device. */
	if (fspec == NULL)
		return (EINVAL);

	NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td);
	if ((error = namei(ndp)) != 0)
		return (error);
	NDFREE(ndp, NDF_ONLY_PNBUF);
	devvp = ndp->ni_vp;

	if (!vn_isdisk(devvp, &error)) {
		vput(devvp);
		return (error);
	}

	/* If mount by non-root, then verify that user has necessary
	 * permissions on the device. */
	if (suser(td)) {
		accessmode = VREAD;
		if ((mp->mnt_flag & MNT_RDONLY) == 0)
			accessmode |= VWRITE;
		if ((error = VOP_ACCESS(devvp,
		    accessmode, td->td_ucred, td)) != 0) {
			vput(devvp);
			return (error);
		}
	}

	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
		error = reiserfs_mountfs(devvp, mp, td);
	} else {
		/* TODO Handle MNT_UPDATE */
		vput(devvp);
		return (EOPNOTSUPP);
	}

	if (error) {
		vrele(devvp);
		return (error);
	}

	rmp = VFSTOREISERFS(mp);
	sbi = rmp->rm_reiserfs;

	/*
	 * Note that this strncpy() is ok because of a check at the start
	 * of reiserfs_mount().
	 */
	reiserfs_log(LOG_DEBUG, "prepare statfs data\n");
	(void)copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
	(void)reiserfs_statfs(mp, &mp->mnt_stat, td);

	reiserfs_log(LOG_DEBUG, "done\n");
	return (0);
}

/*
 * Unmount system call
 */
static int
reiserfs_unmount(struct mount *mp, int mntflags, struct thread *td)
{
	int error, flags = 0;
	struct reiserfs_mount *rmp;
	struct reiserfs_sb_info *sbi;

	reiserfs_log(LOG_DEBUG, "get private data\n");
	rmp = VFSTOREISERFS(mp);
	sbi = rmp->rm_reiserfs;

	/* Flangs handling */
	reiserfs_log(LOG_DEBUG, "handle mntflags\n");
	if (mntflags & MNT_FORCE)
		flags |= FORCECLOSE;

	/* Flush files -> vflush */
	reiserfs_log(LOG_DEBUG, "flush vnodes\n");
	if ((error = vflush(mp, 0, flags, td)))
		return (error);

	/* XXX Super block update */

	if (sbi) {
		if (SB_AP_BITMAP(sbi)) {
			int i;
			reiserfs_log(LOG_DEBUG,
			    "release bitmap buffers (total: %d)\n",
			    SB_BMAP_NR(sbi));
			for (i = 0; i < SB_BMAP_NR(sbi); i++) {
				if (SB_AP_BITMAP(sbi)[i].bp_data) {
					free(SB_AP_BITMAP(sbi)[i].bp_data,
					    M_REISERFSMNT);
					SB_AP_BITMAP(sbi)[i].bp_data = NULL;
				}
			}

			reiserfs_log(LOG_DEBUG, "free bitmaps structure\n");
			free(SB_AP_BITMAP(sbi), M_REISERFSMNT);
			SB_AP_BITMAP(sbi) = NULL;
		}

		if (sbi->s_rs) {
			reiserfs_log(LOG_DEBUG, "free super block data\n");
			free(sbi->s_rs, M_REISERFSMNT);
			sbi->s_rs = NULL;
		}
	}

	reiserfs_log(LOG_DEBUG, "close device\n");
#if defined(si_mountpoint)
	rmp->rm_devvp->v_rdev->si_mountpoint = NULL;
#endif

	DROP_GIANT();
	g_topology_lock();
	g_wither_geom_close(rmp->rm_cp->geom, ENXIO);
	g_topology_unlock();
	PICKUP_GIANT();
	vrele(rmp->rm_devvp);

	if (sbi) {
		reiserfs_log(LOG_DEBUG, "free sbi\n");
		free(sbi, M_REISERFSMNT);
		sbi = rmp->rm_reiserfs = NULL;
	}
	if (rmp) {
		reiserfs_log(LOG_DEBUG, "free rmp\n");
		free(rmp, M_REISERFSMNT);
		rmp = NULL;
	}

	mp->mnt_data  = (qaddr_t)0;
	mp->mnt_flag &= ~MNT_LOCAL;

	reiserfs_log(LOG_DEBUG, "done\n");
	return (error);
}

/*
 * Return the root of a filesystem.
 */ 
static int
reiserfs_root(struct mount *mp, int flags, struct vnode **vpp,
    struct thread *td)
{
	int error;
	struct vnode *vp;
	struct cpu_key rootkey;

	rootkey.on_disk_key.k_dir_id = REISERFS_ROOT_PARENT_OBJECTID;
	rootkey.on_disk_key.k_objectid = REISERFS_ROOT_OBJECTID;

	error = reiserfs_iget(mp, &rootkey, &vp, td);

	if (error == 0)
		*vpp = vp;
	return (error);
}

/*
 * The statfs syscall
 */
static int
reiserfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
{
	struct reiserfs_mount *rmp;
	struct reiserfs_sb_info *sbi;
	struct reiserfs_super_block *rs;

	reiserfs_log(LOG_DEBUG, "get private data\n");
	rmp = VFSTOREISERFS(mp);
	sbi = rmp->rm_reiserfs;
	rs  = sbi->s_rs;

	reiserfs_log(LOG_DEBUG, "fill statfs structure\n");
	sbp->f_bsize  = sbi->s_blocksize;
	sbp->f_iosize = sbp->f_bsize;
	sbp->f_blocks = sb_block_count(rs) - sb_bmap_nr(rs) - 1;
	sbp->f_bfree  = sb_free_blocks(rs);
	sbp->f_bavail = sbp->f_bfree;
	sbp->f_files  = 0;
	sbp->f_ffree  = 0;
	reiserfs_log(LOG_DEBUG, "  block size   = %ju\n",
	    (intmax_t)sbp->f_bsize);
	reiserfs_log(LOG_DEBUG, "  IO size      = %ju\n",
	    (intmax_t)sbp->f_iosize);
	reiserfs_log(LOG_DEBUG, "  block count  = %ju\n",
	    (intmax_t)sbp->f_blocks);
	reiserfs_log(LOG_DEBUG, "  free blocks  = %ju\n",
	    (intmax_t)sbp->f_bfree);
	reiserfs_log(LOG_DEBUG, "  avail blocks = %ju\n",
	    (intmax_t)sbp->f_bavail);
	reiserfs_log(LOG_DEBUG, "...done\n");

	if (sbp != &mp->mnt_stat) {
		reiserfs_log(LOG_DEBUG, "copying monut point info\n");
		sbp->f_type = mp->mnt_vfc->vfc_typenum;
		bcopy((caddr_t)mp->mnt_stat.f_mntonname,
		    (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
		bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
		    (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
		reiserfs_log(LOG_DEBUG, "  mount from: %s\n",
		    sbp->f_mntfromname);
		reiserfs_log(LOG_DEBUG, "  mount on:   %s\n",
		    sbp->f_mntonname);
		reiserfs_log(LOG_DEBUG, "...done\n");
	}

	return (0);
}

/*
 * File handle to vnode
 *
 * Have to be really careful about stale file handles:
 * - check that the inode key is valid
 * - call ffs_vget() to get the locked inode
 * - check for an unallocated inode (i_mode == 0)
 * - check that the given client host has export rights and return
 *   those rights via. exflagsp and credanonp
 */
static int
reiserfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
	int error;
	struct rfid *rfhp;
	struct vnode *nvp;
	struct cpu_key key;
	struct reiserfs_node *ip;
	struct reiserfs_sb_info *sbi;
	struct thread *td = curthread;

	rfhp = (struct rfid *)fhp;
	sbi  = VFSTOREISERFS(mp)->rm_reiserfs;

	/* Check that the key is valid */
	if (rfhp->rfid_dirid < REISERFS_ROOT_PARENT_OBJECTID &&
	    rfhp->rfid_objectid < REISERFS_ROOT_OBJECTID)
		return (ESTALE);

	reiserfs_log(LOG_DEBUG,
	    "file handle key is (dirid=%d, objectid=%d)\n",
	    rfhp->rfid_dirid, rfhp->rfid_objectid);
	key.on_disk_key.k_dir_id   = rfhp->rfid_dirid;
	key.on_disk_key.k_objectid = rfhp->rfid_objectid;

	reiserfs_log(LOG_DEBUG, "read this inode\n");
	error = reiserfs_iget(mp, &key, &nvp, td);
	if (error) {
		*vpp = NULLVP;
		return (error);
	}

	reiserfs_log(LOG_DEBUG, "check validity\n");
	ip = VTOI(nvp);
	if (ip->i_mode == 0 || ip->i_generation != rfhp->rfid_gen) {
		vput(nvp);
		*vpp = NULLVP;
		return (ESTALE);
	}

	reiserfs_log(LOG_DEBUG, "return it\n");
	*vpp = nvp;
	return (0);
}

/*
 * Vnode pointer to File handle
 */
static int
reiserfs_vptofh(struct vnode *vp, struct fid *fhp)
{
	struct rfid *rfhp;
	struct reiserfs_node *ip;

	ip = VTOI(vp);
	reiserfs_log(LOG_DEBUG,
	    "fill *fhp with inode (dirid=%d, objectid=%d)\n",
	    ip->i_ino, ip->i_number);

	rfhp = (struct rfid *)fhp;
	rfhp->rfid_len      = sizeof(struct rfid);
	rfhp->rfid_dirid    = ip->i_ino;
	rfhp->rfid_objectid = ip->i_number;
	rfhp->rfid_gen      = ip->i_generation;

	reiserfs_log(LOG_DEBUG, "return it\n");
	return (0);
}

/* -------------------------------------------------------------------
 * Functions for the journal
 * -------------------------------------------------------------------*/

int
is_reiserfs_3_5(struct reiserfs_super_block *rs)
{

	return (!strncmp(rs->s_v1.s_magic, reiserfs_3_5_magic_string,
	    strlen(reiserfs_3_5_magic_string)));
}

int
is_reiserfs_3_6(struct reiserfs_super_block *rs)
{

	return (!strncmp(rs->s_v1.s_magic, reiserfs_3_6_magic_string,
	    strlen(reiserfs_3_6_magic_string)));
}

int
is_reiserfs_jr(struct reiserfs_super_block *rs)
{

	return (!strncmp(rs->s_v1.s_magic, reiserfs_jr_magic_string,
	    strlen(reiserfs_jr_magic_string)));
}

static int
is_any_reiserfs_magic_string(struct reiserfs_super_block *rs)
{

	return ((is_reiserfs_3_5(rs) || is_reiserfs_3_6(rs) ||
	    is_reiserfs_jr(rs)));
}

/* -------------------------------------------------------------------
 * Internal functions
 * -------------------------------------------------------------------*/

/*
 * Common code for mount and mountroot
 */ 
static int
reiserfs_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td)
{
	int error, old_format = 0;
	struct reiserfs_mount *rmp;
	struct reiserfs_sb_info *sbi;
	struct reiserfs_super_block *rs;
	struct cdev *dev = devvp->v_rdev;

#if (__FreeBSD_version >= 600000)
	struct g_consumer *cp;
	struct bufobj *bo;
#endif

	//ronly = (mp->mnt_flag & MNT_RDONLY) != 0;

#if (__FreeBSD_version < 600000)
	/*
	 * Disallow multiple mounts of the same device.
	 * Disallow mounting of a device that is currently in use
	 * (except for root, which might share swap device for miniroot).
	 * Flush out any old buffers remaining from a previous use.
	 */
	if ((error = vfs_mountedon(devvp)) != 0)
		return (error);
	if (vcount(devvp) > 1)
		return (EBUSY);

	error = vinvalbuf(devvp, V_SAVE, td->td_ucred, td, 0, 0);
	if (error) {
		VOP_UNLOCK(devvp, 0, td);
		return (error);
	}

	/*
	 * Open the device in read-only, 'cause we don't support write
	 * for now
	 */
	error = VOP_OPEN(devvp, FREAD, FSCRED, td, -1);
	VOP_UNLOCK(devvp, 0, td);
	if (error)
		return (error);
#else
	DROP_GIANT();
	g_topology_lock();
	error = g_vfs_open(devvp, &cp, "reiserfs", /* read-only */ 0);
	g_topology_unlock();
	PICKUP_GIANT();
	VOP_UNLOCK(devvp, 0, td);
	if (error)
		return (error);

	bo = &devvp->v_bufobj;
	bo->bo_private = cp;
	bo->bo_ops = g_vfs_bufops;
#endif

	if (devvp->v_rdev->si_iosize_max != 0)
		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
	if (mp->mnt_iosize_max > MAXPHYS)
		mp->mnt_iosize_max = MAXPHYS;

	rmp = NULL;
	sbi = NULL;

	/* rmp contains any information about this specific mount */
	rmp = malloc(sizeof *rmp, M_REISERFSMNT, M_WAITOK | M_ZERO);
	if (!rmp) {
		error = (ENOMEM);
		goto out;
	}
	sbi = malloc(sizeof *sbi, M_REISERFSMNT, M_WAITOK | M_ZERO);
	if (!sbi) {
		error = (ENOMEM);
		goto out;
	}
	rmp->rm_reiserfs = sbi;
	rmp->rm_mountp   = mp;
	rmp->rm_devvp    = devvp;
	rmp->rm_dev      = dev;
#if (__FreeBSD_version >= 600000)
	rmp->rm_bo       = &devvp->v_bufobj;
	rmp->rm_cp       = cp;
#endif

	/* Set default values for options: non-aggressive tails */
	REISERFS_SB(sbi)->s_mount_opt = (1 << REISERFS_SMALLTAIL);
	REISERFS_SB(sbi)->s_rd_only   = 1;
	REISERFS_SB(sbi)->s_devvp     = devvp;

	/* Read the super block */
	if ((error = read_super_block(rmp, REISERFS_OLD_DISK_OFFSET)) == 0) {
		/* The read process succeeded, it's an old format */
		old_format = 1;
	} else if ((error = read_super_block(rmp, REISERFS_DISK_OFFSET)) != 0) {
		reiserfs_log(LOG_ERR, "can not find a ReiserFS filesystem\n");
		goto out;
	}

	rs = SB_DISK_SUPER_BLOCK(sbi);

	/*
	 * Let's do basic sanity check to verify that underlying device is
	 * not smaller than the filesystem. If the check fails then abort and
	 * scream, because bad stuff will happen otherwise.
	 */
#if 0
	if (s->s_bdev && s->s_bdev->bd_inode &&
	    i_size_read(s->s_bdev->bd_inode) <
	    sb_block_count(rs) * sb_blocksize(rs)) {
		reiserfs_log(LOG_ERR,
		    "reiserfs: filesystem cannot be mounted because it is "
		    "bigger than the device.\n");
		reiserfs_log(LOG_ERR, "reiserfs: you may need to run fsck "
		    "rr may be you forgot to reboot after fdisk when it "
		    "told you to.\n");
		goto out;
	}
#endif

	/*
	 * XXX This is from the original Linux code, but why affecting 2 values
	 * to the same variable?
	 */
	sbi->s_mount_state = SB_REISERFS_STATE(sbi);
	sbi->s_mount_state = REISERFS_VALID_FS;

	if ((error = (old_format ?
	    read_old_bitmaps(rmp) : read_bitmaps(rmp)))) {
		reiserfs_log(LOG_ERR, "unable to read bitmap\n");
		goto out;
	}

	/* Make data=ordered the default */
	if (!reiserfs_data_log(sbi) && !reiserfs_data_ordered(sbi) &&
	    !reiserfs_data_writeback(sbi)) {
		REISERFS_SB(sbi)->s_mount_opt |= (1 << REISERFS_DATA_ORDERED);
	}

	if (reiserfs_data_log(sbi)) {
		reiserfs_log(LOG_INFO, "using journaled data mode\n");
	} else if (reiserfs_data_ordered(sbi)) {
		reiserfs_log(LOG_INFO, "using ordered data mode\n");
	} else {
		reiserfs_log(LOG_INFO, "using writeback data mode\n");
	}

	/* TODO Not yet supported */
#if 0
	if(journal_init(sbi, jdev_name, old_format, commit_max_age)) {
		reiserfs_log(LOG_ERR, "unable to initialize journal space\n");
		goto out;
	} else {
		jinit_done = 1 ; /* once this is set, journal_release must
				    be called if we error out of the mount */
	}

	if (reread_meta_blocks(sbi)) {
		reiserfs_log(LOG_ERR,
		    "unable to reread meta blocks after journal init\n");
		goto out;
	}
#endif

	/* Define and initialize hash function */
	sbi->s_hash_function = hash_function(rmp);

	if (sbi->s_hash_function == NULL) {
		reiserfs_log(LOG_ERR, "couldn't determined hash function\n");
		error = (EINVAL);
		goto out;
	}

	if (is_reiserfs_3_5(rs) ||
	    (is_reiserfs_jr(rs) && SB_VERSION(sbi) == REISERFS_VERSION_1))
		bit_set(&(sbi->s_properties), REISERFS_3_5);
	else
		bit_set(&(sbi->s_properties), REISERFS_3_6);

	mp->mnt_data = (qaddr_t)rmp;
	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
	mp->mnt_flag |= MNT_LOCAL;
#if defined(si_mountpoint)
	devvp->v_rdev->si_mountpoint = mp;
#endif

	return (0);

out:
	reiserfs_log(LOG_INFO, "*** error during mount ***\n");
	if (sbi) {
		if (SB_AP_BITMAP(sbi)) {
			int i;
			for (i = 0; i < SB_BMAP_NR(sbi); i++) {
				if (!SB_AP_BITMAP(sbi)[i].bp_data)
					break;
				free(SB_AP_BITMAP(sbi)[i].bp_data, M_REISERFSMNT);
			}
			free(SB_AP_BITMAP(sbi), M_REISERFSMNT);
		}

		if (sbi->s_rs) {
			free(sbi->s_rs, M_REISERFSMNT);
			sbi->s_rs = NULL;
		}
	}

#if (__FreeBSD_version < 600000)
	(void)VOP_CLOSE(devvp, FREAD, NOCRED, td);
#else
	if (cp != NULL) {
		DROP_GIANT();
		g_topology_lock();
		g_wither_geom_close(cp->geom, ENXIO);
		g_topology_unlock();
		PICKUP_GIANT();
	}
#endif

	if (sbi)
		free(sbi, M_REISERFSMNT);
	if (rmp)
		free(rmp, M_REISERFSMNT);
	return (error);
}

/*
 * Read the super block
 */
static int
read_super_block(struct reiserfs_mount *rmp, int offset)
{
	struct buf *bp;
	int error, bits;
	struct reiserfs_super_block *rs;
	struct reiserfs_sb_info *sbi;
	uint16_t fs_blocksize;

	if (offset == REISERFS_OLD_DISK_OFFSET) {
		reiserfs_log(LOG_DEBUG,
		    "reiserfs/super: read old format super block\n");
	} else {
		reiserfs_log(LOG_DEBUG,
		    "reiserfs/super: read new format super block\n");
	}

	/* Read the super block */
	if ((error = bread(rmp->rm_devvp, offset * btodb(REISERFS_BSIZE),
	    REISERFS_BSIZE, NOCRED, &bp)) != 0) {
		reiserfs_log(LOG_ERR, "can't read device\n");
		return (error);
	}

	/* Get it from the buffer data */
	rs = (struct reiserfs_super_block *)bp->b_data;
	if (!is_any_reiserfs_magic_string(rs)) {
		brelse(bp);
		return (EINVAL);
	}

	fs_blocksize = sb_blocksize(rs);
	brelse(bp);
	bp = NULL;

	if (fs_blocksize <= 0) {
		reiserfs_log(LOG_ERR, "unexpected null block size");
		return (EINVAL);
	}

	/* Read the super block (for double check)
	 * We can't read the same blkno with a different size: it causes
	 * panic() if INVARIANTS is set. So we keep REISERFS_BSIZE */
	if ((error = bread(rmp->rm_devvp,
	    offset * REISERFS_BSIZE / fs_blocksize * btodb(fs_blocksize),
	    REISERFS_BSIZE, NOCRED, &bp)) != 0) {
		reiserfs_log(LOG_ERR, "can't reread the super block\n");
		return (error);
	}

	rs = (struct reiserfs_super_block *)bp->b_data;
	if (sb_blocksize(rs) != fs_blocksize) {
		reiserfs_log(LOG_ERR, "unexpected block size "
		    "(found=%u, expected=%u)\n",
		    sb_blocksize(rs), fs_blocksize);
		brelse(bp);
		return (EINVAL);
	}

	reiserfs_log(LOG_DEBUG, "magic: `%s'\n", rs->s_v1.s_magic);
	reiserfs_log(LOG_DEBUG, "label: `%s'\n", rs->s_label);
	reiserfs_log(LOG_DEBUG, "block size:     %6d\n", sb_blocksize(rs));
	reiserfs_log(LOG_DEBUG, "block count:    %6u\n",
	    rs->s_v1.s_block_count);
	reiserfs_log(LOG_DEBUG, "bitmaps number: %6u\n",
	    rs->s_v1.s_bmap_nr);

	if (rs->s_v1.s_root_block == -1) {
		log(LOG_ERR,
		    "reiserfs: Unfinished reiserfsck --rebuild-tree run "
		    "detected. Please\n"
		    "run reiserfsck --rebuild-tree and wait for a "
		    "completion. If that\n"
		    "fails, get newer reiserfsprogs package");
		brelse(bp);
		return (EINVAL);
	}

	sbi = rmp->rm_reiserfs;
	sbi->s_blocksize = fs_blocksize;

	for (bits = 9, fs_blocksize >>= 9; fs_blocksize >>= 1; bits++)
		;
	sbi->s_blocksize_bits = bits;

	/* Copy the buffer and release it */
	sbi->s_rs = malloc(sizeof *rs, M_REISERFSMNT, M_WAITOK | M_ZERO);
	if (!sbi->s_rs) {
		reiserfs_log(LOG_ERR, "can not read the super block\n");
		brelse(bp);
		return (ENOMEM);
	}
	bcopy(rs, sbi->s_rs, sizeof(struct reiserfs_super_block));
	brelse(bp);

	if (is_reiserfs_jr(rs)) {
		if (sb_version(rs) == REISERFS_VERSION_2)
			reiserfs_log(LOG_INFO, "found reiserfs format \"3.6\""
			    " with non-standard journal");
		else if (sb_version(rs) == REISERFS_VERSION_1)
			reiserfs_log(LOG_INFO, "found reiserfs format \"3.5\""
			    " with non-standard journal");
		else {
			reiserfs_log(LOG_ERR, "found unknown "
			    "format \"%u\" of reiserfs with non-standard magic",
			    sb_version(rs));
			return (EINVAL);
		}
	} else {
		/*
		 * s_version of standard format may contain incorrect
		 * information, so we just look at the magic string
		 */
		reiserfs_log(LOG_INFO,
		    "found reiserfs format \"%s\" with standard journal\n",
		    is_reiserfs_3_5(rs) ? "3.5" : "3.6");
	}

	return (0);
}

/*
 * load_bitmap_info_data - Sets up the reiserfs_bitmap_info structure
 * from disk.
 * @sbi - superblock info for this filesystem
 * @bi  - the bitmap info to be loaded. Requires that bi->bp is valid.
 *
 * This routine counts how many free bits there are, finding the first
 * zero as a side effect. Could also be implemented as a loop of
 * test_bit() calls, or a loop of find_first_zero_bit() calls. This
 * implementation is similar to find_first_zero_bit(), but doesn't
 * return after it finds the first bit. Should only be called on fs
 * mount, but should be fairly efficient anyways.
 *
 * bi->first_zero_hint is considered unset if it == 0, since the bitmap
 * itself will invariably occupt block 0 represented in the bitmap. The
 * only exception to this is when free_count also == 0, since there will
 * be no free blocks at all.
 */
static void
load_bitmap_info_data(struct reiserfs_sb_info *sbi,
    struct reiserfs_bitmap_info *bi)
{
	unsigned long *cur;

	cur = (unsigned long *)bi->bp_data;
	while ((char *)cur < (bi->bp_data + sbi->s_blocksize)) {
		/*
		 * No need to scan if all 0's or all 1's.
		 * Since we're only counting 0's, we can simply ignore
		 * all 1's
		 */
		if (*cur == 0) {
			if (bi->first_zero_hint == 0) {
				bi->first_zero_hint =
				    ((char *)cur - bi->bp_data) << 3;
			}
			bi->free_count += sizeof(unsigned long) * 8;
		} else if (*cur != ~0L) {
			int b;

			for (b = 0; b < sizeof(unsigned long) * 8; b++) {
				if (!reiserfs_test_le_bit(b, cur)) {
					bi->free_count++;
					if (bi->first_zero_hint == 0)
						bi->first_zero_hint =
						    (((char *)cur -
						      bi->bp_data) << 3) + b;
				}
			}
		}
		cur++;
	}
}

/*
 * Read the bitmaps
 */
static int
read_bitmaps(struct reiserfs_mount *rmp)
{
	int i, bmap_nr;
	struct buf *bp = NULL;
	struct reiserfs_sb_info *sbi = rmp->rm_reiserfs;

	/* Allocate memory for the table of bitmaps */
	SB_AP_BITMAP(sbi) =
	    malloc(sizeof(struct reiserfs_bitmap_info) * SB_BMAP_NR(sbi),
		M_REISERFSMNT, M_WAITOK | M_ZERO);
	if (!SB_AP_BITMAP(sbi))
		return (ENOMEM);

	/* Read all the bitmaps */
	for (i = 0,
	    bmap_nr = (REISERFS_DISK_OFFSET_IN_BYTES / sbi->s_blocksize + 1) *
	    btodb(sbi->s_blocksize);
	    i < SB_BMAP_NR(sbi); i++, bmap_nr = sbi->s_blocksize * 8 * i) {
		SB_AP_BITMAP(sbi)[i].bp_data = malloc(sbi->s_blocksize,
		    M_REISERFSMNT, M_WAITOK | M_ZERO);
		if (!SB_AP_BITMAP(sbi)[i].bp_data)
			return (ENOMEM);
		bread(rmp->rm_devvp, bmap_nr, sbi->s_blocksize, NOCRED, &bp);
		bcopy(bp->b_data, SB_AP_BITMAP(sbi)[i].bp_data,
		    sbi->s_blocksize);
		brelse(bp);
		bp = NULL;

		/*if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh))
			ll_rw_block(READ, 1, &SB_AP_BITMAP(s)[i].bh);*/
	}

	for (i = 0; i < SB_BMAP_NR(sbi); i++) {
		/*if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh)) {
		  reiserfs_warning(s,"sh-2029: reiserfs read_bitmaps: "
		  "bitmap block (#%lu) reading failed",
		  SB_AP_BITMAP(s)[i].bh->b_blocknr);
		  for (i = 0; i < SB_BMAP_NR(s); i++)
		  brelse(SB_AP_BITMAP(s)[i].bh);
		  vfree(SB_AP_BITMAP(s));
		  SB_AP_BITMAP(s) = NULL;
		  return 1;
		  }*/
		load_bitmap_info_data(sbi, SB_AP_BITMAP(sbi) + i);
		reiserfs_log(LOG_DEBUG,
		    "%d free blocks (starting at block %ld)\n",
		    SB_AP_BITMAP(sbi)[i].free_count,
		    (long)SB_AP_BITMAP(sbi)[i].first_zero_hint);
	}

	return (0);
}

// TODO Not supported
static int
read_old_bitmaps(struct reiserfs_mount *rmp)
{

	return (EOPNOTSUPP);
#if 0
	int i;
	struct reiserfs_sb_info *sbi = rmp->rm_reiserfs;
	struct reiserfs_super_block *rs = SB_DISK_SUPER_BLOCK(sbi);

	/* First of bitmap blocks */
	int bmp1 = (REISERFS_OLD_DISK_OFFSET / sbi->s_blocksize) *
	    btodb(sbi->s_blocksize);

	/* Read true bitmap */
	SB_AP_BITMAP(sbi) =
	    malloc(sizeof (struct reiserfs_buffer_info *) * sb_bmap_nr(rs),
		M_REISERFSMNT, M_WAITOK | M_ZERO);
	if (!SB_AP_BITMAP(sbi))
		return 1;

	for (i = 0; i < sb_bmap_nr(rs); i ++) {
		SB_AP_BITMAP(sbi)[i].bp = getblk(rmp->rm_devvp,
		    (bmp1 + i) * btodb(sbi->s_blocksize), sbi->s_blocksize, 0, 0, 0);
		if (!SB_AP_BITMAP(sbi)[i].bp)
			return 1;
		load_bitmap_info_data(sbi, SB_AP_BITMAP(sbi) + i);
	}

	return 0;
#endif
}

/* -------------------------------------------------------------------
 * Hash detection stuff
 * -------------------------------------------------------------------*/

static int
get_root_node(struct reiserfs_mount *rmp, struct reiserfs_node **root)
{
	struct reiserfs_node *ip;
	struct reiserfs_iget_args args;

	/* Allocate the node structure */
	reiserfs_log(LOG_DEBUG, "malloc(struct reiserfs_node)\n");
	MALLOC(ip, struct reiserfs_node *, sizeof(struct reiserfs_node),
	    M_REISERFSNODE, M_WAITOK | M_ZERO);

	/* Fill the structure */
	reiserfs_log(LOG_DEBUG, "filling *ip\n");
	ip->i_dev      = rmp->rm_dev;
	ip->i_number   = REISERFS_ROOT_OBJECTID;
	ip->i_ino      = REISERFS_ROOT_PARENT_OBJECTID;
	ip->i_reiserfs = rmp->rm_reiserfs;

	/* Read the inode */
	args.objectid = ip->i_number;
	args.dirid    = ip->i_ino;
	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;
	//XXX VREF(ip->i_devvp); Is it necessary ?

	*root = ip;
	return (0);
}

/*
 * If root directory is empty - we set default - Yura's - hash and warn
 * about it.
 * FIXME: we look for only one name in a directory. If tea and yura both
 * have the same value - we ask user to send report to the mailing list
 */
uint32_t find_hash_out(struct reiserfs_mount *rmp)
{
	int retval;
	struct cpu_key key;
	INITIALIZE_PATH(path);
	struct reiserfs_node *ip;
	struct reiserfs_sb_info *sbi;
	struct reiserfs_dir_entry de;
	uint32_t hash = DEFAULT_HASH;

	get_root_node(rmp, &ip);
	if (!ip)
		return (UNSET_HASH);

	sbi = rmp->rm_reiserfs;

	do {
		uint32_t teahash, r5hash, yurahash;

		reiserfs_log(LOG_DEBUG, "make_cpu_key\n");
		make_cpu_key(&key, ip, ~0, TYPE_DIRENTRY, 3);
		reiserfs_log(LOG_DEBUG, "search_by_entry_key for "
		    "key(objectid=%d,dirid=%d)\n",
		    key.on_disk_key.k_objectid, key.on_disk_key.k_dir_id);
		retval = search_by_entry_key(sbi, &key, &path, &de);
		if (retval == IO_ERROR) {
			pathrelse(&path);
			return (UNSET_HASH);
		}
		if (retval == NAME_NOT_FOUND)
			de.de_entry_num--;

		reiserfs_log(LOG_DEBUG, "name found\n");

		set_de_name_and_namelen(&de);

		if (deh_offset(&(de.de_deh[de.de_entry_num])) == DOT_DOT_OFFSET) {
			/* Allow override in this case */
			if (reiserfs_rupasov_hash(sbi)) {
				hash = YURA_HASH;
			}
			reiserfs_log(LOG_DEBUG,
			    "FS seems to be empty, autodetect "
			    "is using the default hash");
			break;
		}

		r5hash   = GET_HASH_VALUE(r5_hash(de.de_name, de.de_namelen));
		teahash  = GET_HASH_VALUE(keyed_hash(de.de_name,
		    de.de_namelen));
		yurahash = GET_HASH_VALUE(yura_hash(de.de_name, de.de_namelen));
		if (((teahash == r5hash) &&
		    (GET_HASH_VALUE(
		     deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash)) ||
		    ((teahash == yurahash) &&
		     (yurahash ==
		      GET_HASH_VALUE(
		      deh_offset(&(de.de_deh[de.de_entry_num]))))) ||
		    ((r5hash == yurahash) &&
		     (yurahash ==
		      GET_HASH_VALUE(
		      deh_offset(&(de.de_deh[de.de_entry_num])))))) {
			reiserfs_log(LOG_ERR,
			    "unable to automatically detect hash "
			    "function. Please mount with -o "
			    "hash={tea,rupasov,r5}");
			hash = UNSET_HASH;
			break;
		}

		if (GET_HASH_VALUE(
		    deh_offset(&(de.de_deh[de.de_entry_num]))) == yurahash) {
			reiserfs_log(LOG_DEBUG, "detected YURA hash\n");
			hash = YURA_HASH;
		} else if (GET_HASH_VALUE(
		    deh_offset(&(de.de_deh[de.de_entry_num]))) == teahash) {
			reiserfs_log(LOG_DEBUG, "detected TEA hash\n");
			hash = TEA_HASH;
		} else if (GET_HASH_VALUE(
		    deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash) {
			reiserfs_log(LOG_DEBUG, "detected R5 hash\n");
			hash = R5_HASH;
		} else {
			reiserfs_log(LOG_WARNING, "unrecognised hash function");
			hash = UNSET_HASH;
		}
	} while (0);

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

/* Finds out which hash names are sorted with */
static int
what_hash(struct reiserfs_mount *rmp)
{
	uint32_t code;
	struct reiserfs_sb_info *sbi = rmp->rm_reiserfs;

	find_hash_out(rmp);
	code = sb_hash_function_code(SB_DISK_SUPER_BLOCK(sbi));

	/*
	 * reiserfs_hash_detect() == true if any of the hash mount options
	 * were used. We must check them to make sure the user isn't using a
	 * bad hash value
	 */
	if (code == UNSET_HASH || reiserfs_hash_detect(sbi))
		code = find_hash_out(rmp);

	if (code != UNSET_HASH && reiserfs_hash_detect(sbi)) {
		/*
		 * Detection has found the hash, and we must check against
		 * the mount options
		 */
		if (reiserfs_rupasov_hash(sbi) && code != YURA_HASH) {
			reiserfs_log(LOG_ERR, "error, %s hash detected, "
			    "unable to force rupasov hash",
			    reiserfs_hashname(code));
			code = UNSET_HASH;
		} else if (reiserfs_tea_hash(sbi) && code != TEA_HASH) {
			reiserfs_log(LOG_ERR, "error, %s hash detected, "
			    "unable to force tea hash",
			    reiserfs_hashname(code));
			code = UNSET_HASH;
		} else if (reiserfs_r5_hash(sbi) && code != R5_HASH) {
			reiserfs_log(LOG_ERR, "error, %s hash detected, "
			    "unable to force r5 hash",
			    reiserfs_hashname(code));
			code = UNSET_HASH;
		}
	} else {
		/*
		 * Find_hash_out was not called or could not determine
		 * the hash
		 */
		if (reiserfs_rupasov_hash(sbi)) {
			code = YURA_HASH;
		} else if (reiserfs_tea_hash(sbi)) {
			code = TEA_HASH;
		} else if (reiserfs_r5_hash(sbi)) {
			code = R5_HASH;
		}
	}

	/* TODO Not supported yet */
#if 0
	/* If we are mounted RW, and we have a new valid hash code, update
	 * the super */
	if (code != UNSET_HASH &&
	    !(s->s_flags & MS_RDONLY) &&
	    code != sb_hash_function_code(SB_DISK_SUPER_BLOCK(s))) {
		set_sb_hash_function_code(SB_DISK_SUPER_BLOCK(s), code);
	}
#endif

	return (code);
}

/* Return pointer to appropriate function */
static hashf_t
hash_function(struct reiserfs_mount *rmp)
{

	switch (what_hash(rmp)) {
	case TEA_HASH:
		reiserfs_log(LOG_INFO, "using tea hash to sort names\n");
		return (keyed_hash);
	case YURA_HASH:
		reiserfs_log(LOG_INFO, "using rupasov hash to sort names\n");
		return (yura_hash);
	case R5_HASH:
		reiserfs_log(LOG_INFO, "using r5 hash to sort names\n");
		return (r5_hash);
	}

	return (NULL);
}

/* -------------------------------------------------------------------
 * VFS registration
 * -------------------------------------------------------------------*/

static struct vfsops reiser_vfsops = {
	.vfs_cmount	= reiserfs_cmount,
	.vfs_mount	= reiserfs_mount,
	.vfs_unmount	= reiserfs_unmount,
	//.vfs_checkexp	= reiserfs_checkexp,
	//.vfs_extattrctl = reiserfs_extattrctl,
	.vfs_fhtovp	= reiserfs_fhtovp,
	//.vfs_quotactl	= reiserfs_quotactl,
	.vfs_root	= reiserfs_root,
	//.vfs_start	= reiserfs_start,
	.vfs_statfs	= reiserfs_statfs,
	//.vfs_sync	= reiserfs_sync,
	//.vfs_vget	= reiserfs_vget,
	.vfs_vptofh	= reiserfs_vptofh,
};

VFS_SET(reiser_vfsops, reiserfs, VFCF_READONLY);
