diff options
author | dumbbell <dumbbell@FreeBSD.org> | 2005-06-18 17:10:50 +0000 |
---|---|---|
committer | dumbbell <dumbbell@FreeBSD.org> | 2005-06-18 17:10:50 +0000 |
commit | 21b06667dd613612697e2117f4d8e7a301dacd72 (patch) | |
tree | c098f3ed0a96017bac6283248e8dcc6e9dd9f895 /sys/gnu/fs/reiserfs/reiserfs_vfsops.c | |
parent | c99c5450e2d73a20a1b2386daced6928b617a5d8 (diff) | |
download | FreeBSD-src-21b06667dd613612697e2117f4d8e7a301dacd72.zip FreeBSD-src-21b06667dd613612697e2117f4d8e7a301dacd72.tar.gz |
Moving reiserfs from sys/gnu to sys/gnu/fs. This was discussed on arch@.
Reviewed by: mux (mentor)
Approved by: re (scottl)
Diffstat (limited to 'sys/gnu/fs/reiserfs/reiserfs_vfsops.c')
-rw-r--r-- | sys/gnu/fs/reiserfs/reiserfs_vfsops.c | 1186 |
1 files changed, 1186 insertions, 0 deletions
diff --git a/sys/gnu/fs/reiserfs/reiserfs_vfsops.c b/sys/gnu/fs/reiserfs/reiserfs_vfsops.c new file mode 100644 index 0000000..4bb425a --- /dev/null +++ b/sys/gnu/fs/reiserfs/reiserfs_vfsops.c @@ -0,0 +1,1186 @@ +/*- + * 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$ + */ + +#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, 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)) { + vrele(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; + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); + if ((error = VOP_ACCESS(devvp, + accessmode, td->td_ucred, td)) != 0) { + vput(devvp); + return (error); + } + VOP_UNLOCK(devvp, 0, td); + } + + if ((mp->mnt_flag & MNT_UPDATE) == 0) { + error = reiserfs_mountfs(devvp, mp, td); + } else { + /* TODO Handle MNT_UPDATE */ + error = (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); + + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); + 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); |