Index: head/sys/gnu/fs/reiserfs/reiserfs_vfsops.c =================================================================== --- head/sys/gnu/fs/reiserfs/reiserfs_vfsops.c (revision 286887) +++ head/sys/gnu/fs/reiserfs/reiserfs_vfsops.c (revision 286888) @@ -1,1135 +1,1135 @@ /*- * Copyright 2000 Hans Reiser * See README for licensing and copyright details * * Ported to FreeBSD by Jean-Sébastien Pédron * * $FreeBSD$ */ #include 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 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, uint64_t flags) { struct reiserfs_args args; struct export_args exp; int error; error = copyin(data, &args, sizeof(args)); if (error) return (error); vfs_oexport_conv(&args.export, &exp); ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); ma = mount_arg(ma, "export", &exp, sizeof(exp)); error = kernel_mount(ma, flags); return (error); } /* * Mount system call */ static int reiserfs_mount(struct mount *mp) { size_t size; int error, len; accmode_t accmode; char *path, *fspec; struct vnode *devvp; struct vfsoptlist *opts; struct reiserfs_mount *rmp; struct reiserfs_sb_info *sbi; struct nameidata nd, *ndp = &nd; struct thread *td; td = curthread; 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. */ if (vfs_flagopt(opts, "export", NULL, 0)) return (0); } /* 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. */ accmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accmode |= VWRITE; error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { 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); reiserfs_log(LOG_DEBUG, "done\n"); return (0); } /* * Unmount system call */ static int reiserfs_unmount(struct mount *mp, int mntflags) { 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, curthread))) 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_vfs_close(rmp->rm_cp); g_topology_unlock(); PICKUP_GIANT(); vrele(rmp->rm_devvp); dev_rel(rmp->rm_dev); 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 = 0; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_LOCAL; MNT_IUNLOCK(mp); 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) { 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, curthread); if (error == 0) *vpp = vp; return (error); } /* * The statfs syscall */ static int reiserfs_statfs(struct mount *mp, struct statfs *sbp) { 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, int flags, 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); } /* ------------------------------------------------------------------- * 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; struct g_consumer *cp; struct bufobj *bo; //ronly = (mp->mnt_flag & MNT_RDONLY) != 0; dev = devvp->v_rdev; dev_ref(dev); DROP_GIANT(); g_topology_lock(); error = g_vfs_open(devvp, &cp, "reiserfs", /* read-only */ 0); g_topology_unlock(); PICKUP_GIANT(); VOP_UNLOCK(devvp, 0); if (error) { dev_rel(dev); return (error); } bo = &devvp->v_bufobj; bo->bo_private = cp; bo->bo_ops = g_vfs_bufops; 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; rmp->rm_bo = &devvp->v_bufobj; rmp->rm_cp = cp; /* 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 = rmp; mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; MNT_IUNLOCK(mp); #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 (cp != NULL) { DROP_GIANT(); g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); PICKUP_GIANT(); } if (sbi) free(sbi, M_REISERFSMNT); if (rmp) free(rmp, M_REISERFSMNT); dev_rel(dev); 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"); ip = malloc(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); + hash = UNSET_HASH; + break; } 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_SET(reiser_vfsops, reiserfs, VFCF_READONLY);