Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/ufs/ffs/ffs_vfsops.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/ufs/ffs/ffs_vfsops.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.302 retrieving revision 1.302.2.2 diff -u -p -r1.302 -r1.302.2.2 --- src/sys/ufs/ffs/ffs_vfsops.c 2014/11/14 10:09:50 1.302 +++ src/sys/ufs/ffs/ffs_vfsops.c 2015/06/06 14:40:30 1.302.2.2 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_vfsops.c,v 1.302 2014/11/14 10:09:50 manu Exp $ */ +/* $NetBSD: ffs_vfsops.c,v 1.302.2.2 2015/06/06 14:40:30 skrll Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -61,7 +61,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.302 2014/11/14 10:09:50 manu Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.302.2.2 2015/06/06 14:40:30 skrll Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -111,7 +111,12 @@ __KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c MODULE(MODULE_CLASS_VFS, ffs, NULL); -static int ffs_vfs_fsync(vnode_t *, int); +static int ffs_vfs_fsync(vnode_t *, int); +static int ffs_superblock_validate(struct fs *); +static int ffs_is_appleufs(struct vnode *, struct fs *); + +static int ffs_init_vnode(struct ufsmount *, struct vnode *, ino_t); +static void ffs_deinit_vnode(struct ufsmount *, struct vnode *); static struct sysctllog *ffs_sysctl_log; @@ -120,6 +125,12 @@ static kauth_listener_t ffs_snapshot_lis /* how many times ffs_init() was called */ int ffs_initcount = 0; +#ifdef DEBUG_FFS_MOUNT +#define DPRINTF(_fmt, args...) printf("%s: " _fmt "\n", __func__, ##args) +#else +#define DPRINTF(_fmt, args...) do {} while (/*CONSTCOND*/0) +#endif + extern const struct vnodeopv_desc ffs_vnodeop_opv_desc; extern const struct vnodeopv_desc ffs_specop_opv_desc; extern const struct vnodeopv_desc ffs_fifoop_opv_desc; @@ -143,6 +154,7 @@ struct vfsops ffs_vfsops = { .vfs_sync = ffs_sync, .vfs_vget = ufs_vget, .vfs_loadvnode = ffs_loadvnode, + .vfs_newvnode = ffs_newvnode, .vfs_fhtovp = ffs_fhtovp, .vfs_vptofh = ffs_vptofh, .vfs_init = ffs_init, @@ -169,10 +181,10 @@ static const struct ufs_ops ffs_ufsops = .uo_itimes = ffs_itimes, .uo_update = ffs_update, .uo_truncate = ffs_truncate, - .uo_valloc = ffs_valloc, - .uo_vfree = ffs_vfree, .uo_balloc = ffs_balloc, .uo_snapgone = ffs_snapgone, + .uo_bufrd = ffs_bufrd, + .uo_bufwr = ffs_bufwr, }; static int @@ -256,7 +268,7 @@ ffs_modcmd(modcmd_t cmd, void *arg) "backing file autocreation"), NULL, 0, &ufs_extattr_autocreate, 0, CTL_VFS, 1, FFS_EXTATTR_AUTOCREATE, CTL_EOL); - + #endif /* UFS_EXTATTR */ ffs_snapshot_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, @@ -346,15 +358,21 @@ ffs_mount(struct mount *mp, const char * int error = 0, flags, update; mode_t accessmode; - if (args == NULL) + if (args == NULL) { + DPRINTF("NULL args"); return EINVAL; - if (*data_len < sizeof *args) + } + if (*data_len < sizeof(*args)) { + DPRINTF("bad size args %zu != %zu", *data_len, sizeof(*args)); return EINVAL; + } if (mp->mnt_flag & MNT_GETARGS) { ump = VFSTOUFS(mp); - if (ump == NULL) + if (ump == NULL) { + DPRINTF("no ump"); return EIO; + } args->fspec = NULL; *data_len = sizeof *args; return 0; @@ -368,18 +386,24 @@ ffs_mount(struct mount *mp, const char * * Look up the name and verify that it's sane. */ error = namei_simple_user(args->fspec, - NSM_FOLLOW_NOEMULROOT, &devvp); - if (error != 0) - return (error); + NSM_FOLLOW_NOEMULROOT, &devvp); + if (error != 0) { + DPRINTF("namei_simple_user returned %d", error); + return error; + } if (!update) { /* * Be sure this is a valid block device */ - if (devvp->v_type != VBLK) + if (devvp->v_type != VBLK) { + DPRINTF("non block device %d", devvp->v_type); error = ENOTBLK; - else if (bdevsw_lookup(devvp->v_rdev) == NULL) + } else if (bdevsw_lookup(devvp->v_rdev) == NULL) { + DPRINTF("can't find block device 0x%jx", + devvp->v_rdev); error = ENXIO; + } } else { /* * Be sure we're still naming the same device @@ -387,9 +411,12 @@ ffs_mount(struct mount *mp, const char * */ ump = VFSTOUFS(mp); if (devvp != ump->um_devvp) { - if (devvp->v_rdev != ump->um_devvp->v_rdev) + if (devvp->v_rdev != ump->um_devvp->v_rdev) { + DPRINTF("wrong device 0x%jx != 0x%jx", + (uintmax_t)devvp->v_rdev, + (uintmax_t)ump->um_devvp->v_rdev); error = EINVAL; - else { + } else { vrele(devvp); devvp = ump->um_devvp; vref(devvp); @@ -399,7 +426,8 @@ ffs_mount(struct mount *mp, const char * } else { if (!update) { /* New mounts must have a filename for the device */ - return (EINVAL); + DPRINTF("no filename for mount"); + return EINVAL; } else { /* Use the extant mount */ ump = VFSTOUFS(mp); @@ -426,6 +454,9 @@ ffs_mount(struct mount *mp, const char * error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode)); + if (error) { + DPRINTF("kauth returned %d", error); + } VOP_UNLOCK(devvp); } @@ -453,10 +484,13 @@ ffs_mount(struct mount *mp, const char * vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_OPEN(devvp, xflags, FSCRED); VOP_UNLOCK(devvp); - if (error) + if (error) { + DPRINTF("VOP_OPEN returned %d", error); goto fail; + } error = ffs_mountfs(devvp, mp, l); if (error) { + DPRINTF("ffs_mountfs returned %d", error); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); (void)VOP_CLOSE(devvp, xflags, NOCRED); VOP_UNLOCK(devvp); @@ -497,17 +531,20 @@ ffs_mount(struct mount *mp, const char * fs->fs_clean = FS_ISCLEAN; (void) ffs_sbupdate(ump, MNT_WAIT); } - if (error == 0) - UFS_WAPBL_END(mp); - if (error) - return (error); + if (error) { + DPRINTF("wapbl %d", error); + return error; + } + UFS_WAPBL_END(mp); } #ifdef WAPBL if ((mp->mnt_flag & MNT_LOG) == 0) { error = ffs_wapbl_stop(mp, mp->mnt_flag & MNT_FORCE); - if (error) + if (error) { + DPRINTF("ffs_wapbl_stop returned %d", error); return error; + } } #endif /* WAPBL */ @@ -521,8 +558,10 @@ ffs_mount(struct mount *mp, const char * if (mp->mnt_flag & MNT_RELOAD) { error = ffs_reload(mp, l->l_cred, l); - if (error) - return (error); + if (error) { + DPRINTF("ffs_reload returned %d", error); + return error; + } } if (fs->fs_ronly && (mp->mnt_iflag & IMNT_WANTRDWR)) { @@ -536,6 +575,7 @@ ffs_mount(struct mount *mp, const char * mp->mnt_stat.f_mntonname, (mp->mnt_flag & MNT_FORCE) ? "" : ", not mounting"); + DPRINTF("ffs_quota2 %d", EINVAL); return EINVAL; } #endif @@ -544,12 +584,18 @@ ffs_mount(struct mount *mp, const char * fs->fs_fmod = 1; #ifdef WAPBL if (fs->fs_flags & FS_DOWAPBL) { - printf("%s: replaying log to disk\n", - mp->mnt_stat.f_mntonname); - KDASSERT(mp->mnt_wapbl_replay); + const char *nm = mp->mnt_stat.f_mntonname; + if (!mp->mnt_wapbl_replay) { + printf("%s: log corrupted;" + " replay cancelled\n", nm); + return EFTYPE; + } + printf("%s: replaying log to disk\n", nm); error = wapbl_replay_write(mp->mnt_wapbl_replay, - devvp); + devvp); if (error) { + DPRINTF("%s: wapbl_replay_write %d", + nm, error); return error; } wapbl_replay_stop(mp->mnt_wapbl_replay); @@ -562,14 +608,17 @@ ffs_mount(struct mount *mp, const char * #ifdef WAPBL error = ffs_wapbl_start(mp); - if (error) + if (error) { + DPRINTF("ffs_wapbl_start returned %d", error); return error; + } #endif /* WAPBL */ #ifdef QUOTA2 if (!fs->fs_ronly) { error = ffs_quota2_mount(mp); if (error) { + DPRINTF("ffs_quota2_mount returned %d", error); return error; } } @@ -587,6 +636,9 @@ ffs_mount(struct mount *mp, const char * if (error == 0) (void)strncpy(fs->fs_fsmnt, mp->mnt_stat.f_mntonname, sizeof(fs->fs_fsmnt)); + else { + DPRINTF("set_statvfs_info returned %d", error); + } fs->fs_flags &= ~FS_DOSOFTDEP; if (fs->fs_fmod != 0) { /* XXX */ int err; @@ -642,9 +694,8 @@ ffs_reload(struct mount *mp, kauth_cred_ void *space; struct buf *bp; struct fs *fs, *newfs; - struct dkwedge_info dkw; int i, bsize, blks, error; - int32_t *lp; + int32_t *lp, fs_sbsize; struct ufsmount *ump; daddr_t sblockloc; struct vnode_iterator *marker; @@ -653,6 +704,7 @@ ffs_reload(struct mount *mp, kauth_cred_ return (EINVAL); ump = VFSTOUFS(mp); + /* * Step 1: invalidate all cached meta-data. */ @@ -662,34 +714,53 @@ ffs_reload(struct mount *mp, kauth_cred_ VOP_UNLOCK(devvp); if (error) panic("ffs_reload: dirty1"); + /* - * Step 2: re-read superblock from disk. + * Step 2: re-read superblock from disk. XXX: We don't handle + * possibility that superblock moved. Which implies that we don't + * want its size to change either. */ fs = ump->um_fs; - - /* XXX we don't handle possibility that superblock moved. */ - error = bread(devvp, fs->fs_sblockloc / DEV_BSIZE, fs->fs_sbsize, - NOCRED, 0, &bp); - if (error) { + fs_sbsize = fs->fs_sbsize; + error = bread(devvp, fs->fs_sblockloc / DEV_BSIZE, fs_sbsize, + 0, &bp); + if (error) return (error); - } - newfs = kmem_alloc(fs->fs_sbsize, KM_SLEEP); - memcpy(newfs, bp->b_data, fs->fs_sbsize); + newfs = kmem_alloc(fs_sbsize, KM_SLEEP); + memcpy(newfs, bp->b_data, fs_sbsize); + #ifdef FFS_EI if (ump->um_flags & UFS_NEEDSWAP) { - ffs_sb_swap((struct fs*)bp->b_data, newfs); - fs->fs_flags |= FS_SWAPPED; + ffs_sb_swap((struct fs *)bp->b_data, newfs); + newfs->fs_flags |= FS_SWAPPED; } else #endif - fs->fs_flags &= ~FS_SWAPPED; - if ((newfs->fs_magic != FS_UFS1_MAGIC && - newfs->fs_magic != FS_UFS2_MAGIC)|| - newfs->fs_bsize > MAXBSIZE || - newfs->fs_bsize < sizeof(struct fs)) { - brelse(bp, 0); - kmem_free(newfs, fs->fs_sbsize); + newfs->fs_flags &= ~FS_SWAPPED; + + brelse(bp, 0); + + if ((newfs->fs_magic != FS_UFS1_MAGIC) && + (newfs->fs_magic != FS_UFS2_MAGIC)) { + kmem_free(newfs, fs_sbsize); return (EIO); /* XXX needs translation */ } + if (!ffs_superblock_validate(newfs)) { + kmem_free(newfs, fs_sbsize); + return (EINVAL); + } + + /* + * The current implementation doesn't handle the possibility that + * these values may have changed. + */ + if ((newfs->fs_sbsize != fs_sbsize) || + (newfs->fs_cssize != fs->fs_cssize) || + (newfs->fs_contigsumsize != fs->fs_contigsumsize) || + (newfs->fs_ncg != fs->fs_ncg)) { + kmem_free(newfs, fs_sbsize); + return (EINVAL); + } + /* Store off old fs_sblockloc for fs_oldfscompat_read. */ sblockloc = fs->fs_sblockloc; /* @@ -702,44 +773,21 @@ ffs_reload(struct mount *mp, kauth_cred_ newfs->fs_contigdirs = fs->fs_contigdirs; newfs->fs_ronly = fs->fs_ronly; newfs->fs_active = fs->fs_active; - memcpy(fs, newfs, (u_int)fs->fs_sbsize); - brelse(bp, 0); - kmem_free(newfs, fs->fs_sbsize); + memcpy(fs, newfs, (u_int)fs_sbsize); + kmem_free(newfs, fs_sbsize); - /* Recheck for apple UFS filesystem */ - ump->um_flags &= ~UFS_ISAPPLEUFS; - /* First check to see if this is tagged as an Apple UFS filesystem - * in the disklabel + /* + * Recheck for Apple UFS filesystem. */ - if (getdiskinfo(devvp, &dkw) == 0 && - strcmp(dkw.dkw_ptype, DKW_PTYPE_APPLEUFS) == 0) - ump->um_flags |= UFS_ISAPPLEUFS; + ump->um_flags &= ~UFS_ISAPPLEUFS; + if (ffs_is_appleufs(devvp, fs)) { #ifdef APPLE_UFS - else { - /* Manually look for an apple ufs label, and if a valid one - * is found, then treat it like an Apple UFS filesystem anyway - * - * EINVAL is most probably a blocksize or alignment problem, - * it is unlikely that this is an Apple UFS filesystem then. - */ - error = bread(devvp, (daddr_t)(APPLEUFS_LABEL_OFFSET / DEV_BSIZE), - APPLEUFS_LABEL_SIZE, cred, 0, &bp); - if (error && error != EINVAL) { - return (error); - } - if (error == 0) { - error = ffs_appleufs_validate(fs->fs_fsmnt, - (struct appleufslabel *)bp->b_data, NULL); - if (error == 0) - ump->um_flags |= UFS_ISAPPLEUFS; - brelse(bp, 0); - } - bp = NULL; - } + ump->um_flags |= UFS_ISAPPLEUFS; #else - if (ump->um_flags & UFS_ISAPPLEUFS) - return (EIO); + DPRINTF("AppleUFS not supported"); + return (EIO); /* XXX: really? */ #endif + } if (UFS_MPISAPPLEUFS(ump)) { /* see comment about NeXT below */ @@ -784,7 +832,7 @@ ffs_reload(struct mount *mp, kauth_cred_ if (i + fs->fs_frag > blks) bsize = (blks - i) * fs->fs_fsize; error = bread(devvp, FFS_FSBTODB(fs, fs->fs_csaddr + i), bsize, - NOCRED, 0, &bp); + 0, &bp); if (error) { return (error); } @@ -828,7 +876,7 @@ ffs_reload(struct mount *mp, kauth_cred_ */ ip = VTOI(vp); error = bread(devvp, FFS_FSBTODB(fs, ino_to_fsba(fs, ip->i_number)), - (int)fs->fs_bsize, NOCRED, 0, &bp); + (int)fs->fs_bsize, 0, &bp); if (error) { vput(vp); break; @@ -846,29 +894,174 @@ ffs_reload(struct mount *mp, kauth_cred_ */ static const int sblock_try[] = SBLOCKSEARCH; + +static int +ffs_superblock_validate(struct fs *fs) +{ + int32_t i, fs_bshift = 0, fs_fshift = 0, fs_fragshift = 0, fs_frag; + int32_t fs_inopb, fs_cgsize; + + /* Check the superblock size */ + if (fs->fs_sbsize > SBLOCKSIZE || fs->fs_sbsize < sizeof(struct fs)) + return 0; + + /* Check the file system blocksize */ + if (fs->fs_bsize > MAXBSIZE || fs->fs_bsize < MINBSIZE) + return 0; + if (!powerof2(fs->fs_bsize)) + return 0; + + /* Check the size of frag blocks */ + if (!powerof2(fs->fs_fsize)) + return 0; + if (fs->fs_fsize == 0) + return 0; + + /* + * XXX: these values are just zero-checked to prevent obvious + * bugs. We need more strict checks. + */ + if (fs->fs_size == 0) + return 0; + if (fs->fs_cssize == 0) + return 0; + if (fs->fs_ipg == 0) + return 0; + if (fs->fs_fpg == 0) + return 0; + if (fs->fs_ncg == 0) + return 0; + if (fs->fs_maxbpg == 0) + return 0; + + /* Check the number of inodes per block */ + if (fs->fs_magic == FS_UFS1_MAGIC) + fs_inopb = fs->fs_bsize / sizeof(struct ufs1_dinode); + else /* fs->fs_magic == FS_UFS2_MAGIC */ + fs_inopb = fs->fs_bsize / sizeof(struct ufs2_dinode); + if (fs->fs_inopb != fs_inopb) + return 0; + + /* Block size cannot be smaller than fragment size */ + if (fs->fs_bsize < fs->fs_fsize) + return 0; + + /* Compute fs_bshift and ensure it is consistent */ + for (i = fs->fs_bsize; i > 1; i >>= 1) + fs_bshift++; + if (fs->fs_bshift != fs_bshift) + return 0; + + /* Compute fs_fshift and ensure it is consistent */ + for (i = fs->fs_fsize; i > 1; i >>= 1) + fs_fshift++; + if (fs->fs_fshift != fs_fshift) + return 0; + + /* Compute fs_fragshift and ensure it is consistent */ + for (i = fs->fs_frag; i > 1; i >>= 1) + fs_fragshift++; + if (fs->fs_fragshift != fs_fragshift) + return 0; + + /* Check the masks */ + if (fs->fs_bmask != ~(fs->fs_bsize - 1)) + return 0; + if (fs->fs_fmask != ~(fs->fs_fsize - 1)) + return 0; + + /* + * Now that the shifts and masks are sanitized, we can use the ffs_ API. + */ + + /* Check the number of frag blocks */ + if ((fs_frag = ffs_numfrags(fs, fs->fs_bsize)) > MAXFRAG) + return 0; + if (fs->fs_frag != fs_frag) + return 0; + + /* Check the size of cylinder groups */ + fs_cgsize = ffs_fragroundup(fs, CGSIZE(fs)); + if (fs->fs_cgsize != fs_cgsize) { + if (fs->fs_cgsize+1 == CGSIZE(fs)) { + printf("CGSIZE(fs) miscalculated by one - this file " + "system may have been created by\n" + " an old (buggy) userland, see\n" + " http://www.NetBSD.org/" + "docs/ffsv1badsuperblock.html\n"); + } else { + printf("ERROR: cylinder group size mismatch: " + "fs_cgsize = 0x%zx, " + "fs->fs_cgsize = 0x%zx, CGSIZE(fs) = 0x%zx\n", + (size_t)fs_cgsize, (size_t)fs->fs_cgsize, + (size_t)CGSIZE(fs)); + return 0; + } + } + + return 1; +} + +static int +ffs_is_appleufs(struct vnode *devvp, struct fs *fs) +{ + struct dkwedge_info dkw; + int ret = 0; + + /* + * First check to see if this is tagged as an Apple UFS filesystem + * in the disklabel. + */ + if (getdiskinfo(devvp, &dkw) == 0 && + strcmp(dkw.dkw_ptype, DKW_PTYPE_APPLEUFS) == 0) + ret = 1; +#ifdef APPLE_UFS + else { + struct appleufslabel *applefs; + struct buf *bp; + daddr_t blkno = APPLEUFS_LABEL_OFFSET / DEV_BSIZE; + int error; + + /* + * Manually look for an Apple UFS label, and if a valid one + * is found, then treat it like an Apple UFS filesystem anyway. + */ + error = bread(devvp, blkno, APPLEUFS_LABEL_SIZE, 0, &bp); + if (error) { + DPRINTF("bread@0x%jx returned %d", (intmax_t)blkno, error); + return 0; + } + applefs = (struct appleufslabel *)bp->b_data; + error = ffs_appleufs_validate(fs->fs_fsmnt, applefs, NULL); + if (error == 0) + ret = 1; + brelse(bp, 0); + } +#endif + + return ret; +} + /* * Common code for mount and mountroot */ int ffs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l) { - struct ufsmount *ump; - struct buf *bp; - struct fs *fs; + struct ufsmount *ump = NULL; + struct buf *bp = NULL; + struct fs *fs = NULL; dev_t dev; - struct dkwedge_info dkw; void *space; - daddr_t sblockloc, fsblockloc; - int blks, fstype; + daddr_t sblockloc = 0; + int blks, fstype = 0; int error, i, bsize, ronly, bset = 0; #ifdef FFS_EI int needswap = 0; /* keep gcc happy */ #endif int32_t *lp; kauth_cred_t cred; - u_int32_t sbsize = 8192; /* keep gcc happy*/ - u_int32_t allocsbsize; - int32_t fsbsize; + u_int32_t allocsbsize, fs_sbsize = 0; dev = devvp->v_rdev; cred = l ? l->l_cred : NOCRED; @@ -877,26 +1070,26 @@ ffs_mountfs(struct vnode *devvp, struct vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0); VOP_UNLOCK(devvp); - if (error) - return (error); + if (error) { + DPRINTF("vinvalbuf returned %d", error); + return error; + } ronly = (mp->mnt_flag & MNT_RDONLY) != 0; - bp = NULL; - ump = NULL; - fs = NULL; - sblockloc = 0; - fstype = 0; - error = fstrans_mount(mp); - if (error) + if (error) { + DPRINTF("fstrans_mount returned %d", error); return error; + } ump = kmem_zalloc(sizeof(*ump), KM_SLEEP); mutex_init(&ump->um_lock, MUTEX_DEFAULT, IPL_NONE); error = ffs_snapshot_init(ump); - if (error) + if (error) { + DPRINTF("ffs_snapshot_init returned %d", error); goto out; + } ump->um_ops = &ffs_ufsops; #ifdef WAPBL @@ -906,51 +1099,60 @@ ffs_mountfs(struct vnode *devvp, struct * Try reading the superblock in each of its possible locations. */ for (i = 0; ; i++) { + daddr_t fs_sblockloc; + if (bp != NULL) { brelse(bp, BC_NOCACHE); bp = NULL; } if (sblock_try[i] == -1) { + DPRINTF("no superblock found"); error = EINVAL; fs = NULL; goto out; } - error = bread(devvp, sblock_try[i] / DEV_BSIZE, SBLOCKSIZE, cred, - 0, &bp); + + error = bread(devvp, sblock_try[i] / DEV_BSIZE, SBLOCKSIZE, + 0, &bp); if (error) { + DPRINTF("bread@0x%x returned %d", + sblock_try[i] / DEV_BSIZE, error); fs = NULL; goto out; } - fs = (struct fs*)bp->b_data; - fsblockloc = sblockloc = sblock_try[i]; + fs = (struct fs *)bp->b_data; + + sblockloc = sblock_try[i]; + DPRINTF("fs_magic 0x%x", fs->fs_magic); + + /* + * Swap: here, we swap fs->fs_sbsize in order to get the correct + * size to read the superblock. Once read, we swap the whole + * superblock structure. + */ if (fs->fs_magic == FS_UFS1_MAGIC) { - sbsize = fs->fs_sbsize; + fs_sbsize = fs->fs_sbsize; fstype = UFS1; - fsbsize = fs->fs_bsize; #ifdef FFS_EI needswap = 0; } else if (fs->fs_magic == FS_UFS1_MAGIC_SWAPPED) { - sbsize = bswap32(fs->fs_sbsize); + fs_sbsize = bswap32(fs->fs_sbsize); fstype = UFS1; - fsbsize = bswap32(fs->fs_bsize); needswap = 1; #endif } else if (fs->fs_magic == FS_UFS2_MAGIC) { - sbsize = fs->fs_sbsize; + fs_sbsize = fs->fs_sbsize; fstype = UFS2; - fsbsize = fs->fs_bsize; #ifdef FFS_EI needswap = 0; } else if (fs->fs_magic == FS_UFS2_MAGIC_SWAPPED) { - sbsize = bswap32(fs->fs_sbsize); + fs_sbsize = bswap32(fs->fs_sbsize); fstype = UFS2; - fsbsize = bswap32(fs->fs_bsize); needswap = 1; #endif } else continue; - /* fs->fs_sblockloc isn't defined for old filesystems */ if (fstype == UFS1 && !(fs->fs_old_flags & FS_FLAGS_UPDATED)) { if (sblockloc == SBLOCK_UFS2) @@ -960,27 +1162,40 @@ ffs_mountfs(struct vnode *devvp, struct * Don't use it. */ continue; - fsblockloc = sblockloc; + fs_sblockloc = sblockloc; } else { - fsblockloc = fs->fs_sblockloc; + fs_sblockloc = fs->fs_sblockloc; #ifdef FFS_EI if (needswap) - fsblockloc = bswap64(fsblockloc); + fs_sblockloc = bswap64(fs_sblockloc); #endif } /* Check we haven't found an alternate superblock */ - if (fsblockloc != sblockloc) + if (fs_sblockloc != sblockloc) continue; - /* Validate size of superblock */ - if (sbsize > SBLOCKSIZE || sbsize < sizeof(struct fs)) + /* Check the superblock size */ + if (fs_sbsize > SBLOCKSIZE || fs_sbsize < sizeof(struct fs)) continue; + fs = kmem_alloc((u_long)fs_sbsize, KM_SLEEP); + memcpy(fs, bp->b_data, fs_sbsize); + + /* Swap the whole superblock structure, if necessary. */ +#ifdef FFS_EI + if (needswap) { + ffs_sb_swap((struct fs*)bp->b_data, fs); + fs->fs_flags |= FS_SWAPPED; + } else +#endif + fs->fs_flags &= ~FS_SWAPPED; - /* Check that we can handle the file system blocksize */ - if (fsbsize > MAXBSIZE) { - printf("ffs_mountfs: block size (%d) > MAXBSIZE (%d)\n", - fsbsize, MAXBSIZE); + /* + * Now that everything is swapped, the superblock is ready to + * be sanitized. + */ + if (!ffs_superblock_validate(fs)) { + kmem_free(fs, fs_sbsize); continue; } @@ -988,23 +1203,15 @@ ffs_mountfs(struct vnode *devvp, struct break; } - fs = kmem_alloc((u_long)sbsize, KM_SLEEP); - memcpy(fs, bp->b_data, sbsize); ump->um_fs = fs; -#ifdef FFS_EI - if (needswap) { - ffs_sb_swap((struct fs*)bp->b_data, fs); - fs->fs_flags |= FS_SWAPPED; - } else -#endif - fs->fs_flags &= ~FS_SWAPPED; - #ifdef WAPBL if ((mp->mnt_wapbl_replay == 0) && (fs->fs_flags & FS_DOWAPBL)) { error = ffs_wapbl_replay_start(mp, fs, devvp); - if (error && (mp->mnt_flag & MNT_FORCE) == 0) + if (error && (mp->mnt_flag & MNT_FORCE) == 0) { + DPRINTF("ffs_wapbl_replay_start returned %d", error); goto out; + } if (!error) { if (!ronly) { /* XXX fsmnt may be stale. */ @@ -1012,8 +1219,11 @@ ffs_mountfs(struct vnode *devvp, struct fs->fs_fsmnt); error = wapbl_replay_write(mp->mnt_wapbl_replay, devvp); - if (error) + if (error) { + DPRINTF("wapbl_replay_write returned %d", + error); goto out; + } wapbl_replay_stop(mp->mnt_wapbl_replay); fs->fs_clean = FS_WASCLEAN; } else { @@ -1025,7 +1235,7 @@ ffs_mountfs(struct vnode *devvp, struct /* Force a re-read of the superblock */ brelse(bp, BC_INVAL); bp = NULL; - kmem_free(fs, sbsize); + kmem_free(fs, fs_sbsize); fs = NULL; goto sbagain; } @@ -1033,6 +1243,7 @@ ffs_mountfs(struct vnode *devvp, struct #else /* !WAPBL */ if ((fs->fs_flags & FS_DOWAPBL) && (mp->mnt_flag & MNT_FORCE) == 0) { error = EPERM; + DPRINTF("no force %d", error); goto out; } #endif /* !WAPBL */ @@ -1046,6 +1257,7 @@ ffs_mountfs(struct vnode *devvp, struct (mp->mnt_flag & MNT_FORCE) ? "" : ", not mounting"); if ((mp->mnt_flag & MNT_FORCE) == 0) { error = EINVAL; + DPRINTF("no force %d", error); goto out; } } @@ -1062,35 +1274,15 @@ ffs_mountfs(struct vnode *devvp, struct brelse(bp, 0); bp = NULL; - /* First check to see if this is tagged as an Apple UFS filesystem - * in the disklabel - */ - if (getdiskinfo(devvp, &dkw) == 0 && - strcmp(dkw.dkw_ptype, DKW_PTYPE_APPLEUFS) == 0) - ump->um_flags |= UFS_ISAPPLEUFS; + if (ffs_is_appleufs(devvp, fs)) { #ifdef APPLE_UFS - else { - /* Manually look for an apple ufs label, and if a valid one - * is found, then treat it like an Apple UFS filesystem anyway - */ - error = bread(devvp, (daddr_t)(APPLEUFS_LABEL_OFFSET / DEV_BSIZE), - APPLEUFS_LABEL_SIZE, cred, 0, &bp); - if (error) - goto out; - error = ffs_appleufs_validate(fs->fs_fsmnt, - (struct appleufslabel *)bp->b_data, NULL); - if (error == 0) { - ump->um_flags |= UFS_ISAPPLEUFS; - } - brelse(bp, 0); - bp = NULL; - } + ump->um_flags |= UFS_ISAPPLEUFS; #else - if (ump->um_flags & UFS_ISAPPLEUFS) { + DPRINTF("AppleUFS not supported"); error = EINVAL; goto out; - } #endif + } #if 0 /* @@ -1123,30 +1315,39 @@ ffs_mountfs(struct vnode *devvp, struct #endif /* - * verify that we can access the last block in the fs + * Verify that we can access the last block in the fs * if we're mounting read/write. */ - if (!ronly) { error = bread(devvp, FFS_FSBTODB(fs, fs->fs_size - 1), - fs->fs_fsize, cred, 0, &bp); + fs->fs_fsize, 0, &bp); if (error) { + DPRINTF("bread@0x%jx returned %d", + (intmax_t)FFS_FSBTODB(fs, fs->fs_size - 1), + error); bset = BC_INVAL; goto out; } - if (bp->b_bcount != fs->fs_fsize) + if (bp->b_bcount != fs->fs_fsize) { + DPRINTF("bcount %x != fsize %x", bp->b_bcount, + fs->fs_fsize); error = EINVAL; + bset = BC_INVAL; + goto out; + } brelse(bp, BC_INVAL); bp = NULL; } fs->fs_ronly = ronly; /* Don't bump fs_clean if we're replaying journal */ - if (!((fs->fs_flags & FS_DOWAPBL) && (fs->fs_clean & FS_WASCLEAN))) + if (!((fs->fs_flags & FS_DOWAPBL) && (fs->fs_clean & FS_WASCLEAN))) { if (ronly == 0) { fs->fs_clean <<= 1; fs->fs_fmod = 1; } + } + bsize = fs->fs_cssize; blks = howmany(bsize, fs->fs_fsize); if (fs->fs_contigsumsize > 0) @@ -1155,15 +1356,18 @@ ffs_mountfs(struct vnode *devvp, struct allocsbsize = bsize; space = kmem_alloc((u_long)allocsbsize, KM_SLEEP); fs->fs_csp = space; + for (i = 0; i < blks; i += fs->fs_frag) { bsize = fs->fs_bsize; if (i + fs->fs_frag > blks) bsize = (blks - i) * fs->fs_fsize; error = bread(devvp, FFS_FSBTODB(fs, fs->fs_csaddr + i), bsize, - cred, 0, &bp); + 0, &bp); if (error) { - kmem_free(fs->fs_csp, allocsbsize); - goto out; + DPRINTF("bread@0x%jx %d", + (intmax_t)FFS_FSBTODB(fs, fs->fs_csaddr + i), + error); + goto out1; } #ifdef FFS_EI if (needswap) @@ -1187,12 +1391,14 @@ ffs_mountfs(struct vnode *devvp, struct fs->fs_contigdirs = space; space = (char *)space + bsize; memset(fs->fs_contigdirs, 0, bsize); - /* Compatibility for old filesystems - XXX */ + + /* Compatibility for old filesystems - XXX */ if (fs->fs_avgfilesize <= 0) fs->fs_avgfilesize = AVFILESIZ; if (fs->fs_avgfpdir <= 0) fs->fs_avgfpdir = AFPDIR; fs->fs_active = NULL; + mp->mnt_data = ump; mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev; mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_FFS); @@ -1242,12 +1448,16 @@ ffs_mountfs(struct vnode *devvp, struct * ffs_wapbl_start() needs mp->mnt_stat initialised if it * needs to create a new log file in-filesystem. */ - ffs_statvfs(mp, &mp->mnt_stat); + error = ffs_statvfs(mp, &mp->mnt_stat); + if (error) { + DPRINTF("ffs_statvfs returned %d", error); + goto out1; + } error = ffs_wapbl_start(mp); if (error) { - kmem_free(fs->fs_csp, allocsbsize); - goto out; + DPRINTF("ffs_wapbl_start returned %d", error); + goto out1; } } #endif /* WAPBL */ @@ -1255,8 +1465,8 @@ ffs_mountfs(struct vnode *devvp, struct #ifdef QUOTA2 error = ffs_quota2_mount(mp); if (error) { - kmem_free(fs->fs_csp, allocsbsize); - goto out; + DPRINTF("ffs_quota2_mount returned %d", error); + goto out1; } #else if (fs->fs_flags & FS_DOQUOTA2) { @@ -1266,8 +1476,8 @@ ffs_mountfs(struct vnode *devvp, struct (mp->mnt_flag & MNT_FORCE) ? "" : ", not mounting"); if ((mp->mnt_flag & MNT_FORCE) == 0) { error = EINVAL; - kmem_free(fs->fs_csp, allocsbsize); - goto out; + DPRINTF("quota disabled %d", error); + goto out1; } } #endif @@ -1277,6 +1487,8 @@ ffs_mountfs(struct vnode *devvp, struct ump->um_discarddata = ffs_discard_init(devvp, fs); return (0); +out1: + kmem_free(fs->fs_csp, allocsbsize); out: #ifdef WAPBL if (mp->mnt_wapbl_replay) { @@ -1733,60 +1945,103 @@ ffs_sync(struct mount *mp, int waitfor, } /* - * Read an inode from disk and initialize this vnode / inode pair. - * Caller assures no other thread will try to load this inode. + * Load inode from disk and initialize vnode. */ -int -ffs_loadvnode(struct mount *mp, struct vnode *vp, - const void *key, size_t key_len, const void **new_key) +static int +ffs_init_vnode(struct ufsmount *ump, struct vnode *vp, ino_t ino) { - ino_t ino; struct fs *fs; struct inode *ip; - struct ufsmount *ump; struct buf *bp; - dev_t dev; int error; - KASSERT(key_len == sizeof(ino)); - memcpy(&ino, key, key_len); - ump = VFSTOUFS(mp); - dev = ump->um_dev; fs = ump->um_fs; /* Read in the disk contents for the inode. */ error = bread(ump->um_devvp, FFS_FSBTODB(fs, ino_to_fsba(fs, ino)), - (int)fs->fs_bsize, NOCRED, 0, &bp); + (int)fs->fs_bsize, 0, &bp); if (error) return error; /* Allocate and initialize inode. */ ip = pool_cache_get(ffs_inode_cache, PR_WAITOK); memset(ip, 0, sizeof(struct inode)); - vp->v_tag = VT_UFS; - vp->v_op = ffs_vnodeop_p; - vp->v_vflag |= VV_LOCKSWORK; - vp->v_data = ip; - ip->i_vnode = vp; ip->i_ump = ump; ip->i_fs = fs; - ip->i_dev = dev; + ip->i_dev = ump->um_dev; ip->i_number = ino; + if (ump->um_fstype == UFS1) + ip->i_din.ffs1_din = pool_cache_get(ffs_dinode1_cache, + PR_WAITOK); + else + ip->i_din.ffs2_din = pool_cache_get(ffs_dinode2_cache, + PR_WAITOK); + ffs_load_inode(bp, ip, fs, ino); + brelse(bp, 0); + ip->i_vnode = vp; #if defined(QUOTA) || defined(QUOTA2) ufsquota_init(ip); #endif + /* Initialise vnode with this inode. */ + vp->v_tag = VT_UFS; + vp->v_op = ffs_vnodeop_p; + vp->v_vflag |= VV_LOCKSWORK; + vp->v_data = ip; + /* Initialize genfs node. */ genfs_node_init(vp, &ffs_genfsops); - if (ip->i_ump->um_fstype == UFS1) - ip->i_din.ffs1_din = pool_cache_get(ffs_dinode1_cache, - PR_WAITOK); + return 0; +} + +/* + * Undo ffs_init_vnode(). + */ +static void +ffs_deinit_vnode(struct ufsmount *ump, struct vnode *vp) +{ + struct inode *ip = VTOI(vp); + + if (ump->um_fstype == UFS1) + pool_cache_put(ffs_dinode1_cache, ip->i_din.ffs1_din); else - ip->i_din.ffs2_din = pool_cache_get(ffs_dinode2_cache, - PR_WAITOK); - ffs_load_inode(bp, ip, fs, ino); - brelse(bp, 0); + pool_cache_put(ffs_dinode2_cache, ip->i_din.ffs2_din); + pool_cache_put(ffs_inode_cache, ip); + + genfs_node_destroy(vp); + vp->v_data = NULL; +} + +/* + * Read an inode from disk and initialize this vnode / inode pair. + * Caller assures no other thread will try to load this inode. + */ +int +ffs_loadvnode(struct mount *mp, struct vnode *vp, + const void *key, size_t key_len, const void **new_key) +{ + ino_t ino; + struct fs *fs; + struct inode *ip; + struct ufsmount *ump; + int error; + + KASSERT(key_len == sizeof(ino)); + memcpy(&ino, key, key_len); + ump = VFSTOUFS(mp); + fs = ump->um_fs; + + error = ffs_init_vnode(ump, vp, ino); + if (error) + return error; + + ip = VTOI(vp); + if (ip->i_mode == 0) { + ffs_deinit_vnode(ump, vp); + + return ENOENT; + } /* Initialize the vnode from the inode. */ ufs_vinit(mp, ffs_specop_p, ffs_fifoop_p, &vp); @@ -1810,6 +2065,119 @@ ffs_loadvnode(struct mount *mp, struct v } /* + * Create a new inode on disk and initialize this vnode / inode pair. + */ +int +ffs_newvnode(struct mount *mp, struct vnode *dvp, struct vnode *vp, + struct vattr *vap, kauth_cred_t cred, + size_t *key_len, const void **new_key) +{ + ino_t ino; + struct fs *fs; + struct inode *ip; + struct timespec ts; + struct ufsmount *ump; + int error, mode; + + KASSERT(dvp->v_mount == mp); + KASSERT(vap->va_type != VNON); + + *key_len = sizeof(ino); + ump = VFSTOUFS(mp); + fs = ump->um_fs; + mode = MAKEIMODE(vap->va_type, vap->va_mode); + + /* Allocate fresh inode. */ + error = ffs_valloc(dvp, mode, cred, &ino); + if (error) + return error; + + /* Attach inode to vnode. */ + error = ffs_init_vnode(ump, vp, ino); + if (error) { + if (UFS_WAPBL_BEGIN(mp) == 0) { + ffs_vfree(dvp, ino, mode); + UFS_WAPBL_END(mp); + } + return error; + } + + ip = VTOI(vp); + if (ip->i_mode || DIP(ip, size) || DIP(ip, blocks)) { + printf("free ino %" PRId64 " on %s:\n", ino, fs->fs_fsmnt); + printf("dmode %x mode %x dgen %x gen %x\n", + DIP(ip, mode), ip->i_mode, + DIP(ip, gen), ip->i_gen); + printf("size %" PRIx64 " blocks %" PRIx64 "\n", + DIP(ip, size), DIP(ip, blocks)); + panic("ffs_init_vnode: dup alloc"); + } + + /* Set uid / gid. */ + if (cred == NOCRED || cred == FSCRED) { + ip->i_gid = 0; + ip->i_uid = 0; + } else { + ip->i_gid = VTOI(dvp)->i_gid; + ip->i_uid = kauth_cred_geteuid(cred); + } + DIP_ASSIGN(ip, gid, ip->i_gid); + DIP_ASSIGN(ip, uid, ip->i_uid); + +#if defined(QUOTA) || defined(QUOTA2) + error = UFS_WAPBL_BEGIN(mp); + if (error) { + ffs_deinit_vnode(ump, vp); + + return error; + } + error = chkiq(ip, 1, cred, 0); + if (error) { + ffs_vfree(dvp, ino, mode); + UFS_WAPBL_END(mp); + ffs_deinit_vnode(ump, vp); + + return error; + } + UFS_WAPBL_END(mp); +#endif + + /* Set type and finalize. */ + ip->i_flags = 0; + DIP_ASSIGN(ip, flags, 0); + ip->i_mode = mode; + DIP_ASSIGN(ip, mode, mode); + if (vap->va_rdev != VNOVAL) { + /* + * Want to be able to use this to make badblock + * inodes, so don't truncate the dev number. + */ + if (ump->um_fstype == UFS1) + ip->i_ffs1_rdev = ufs_rw32(vap->va_rdev, + UFS_MPNEEDSWAP(ump)); + else + ip->i_ffs2_rdev = ufs_rw64(vap->va_rdev, + UFS_MPNEEDSWAP(ump)); + } + ufs_vinit(mp, ffs_specop_p, ffs_fifoop_p, &vp); + ip->i_devvp = ump->um_devvp; + vref(ip->i_devvp); + + /* Set up a new generation number for this inode. */ + ip->i_gen++; + DIP_ASSIGN(ip, gen, ip->i_gen); + if (fs->fs_magic == FS_UFS2_MAGIC) { + vfs_timestamp(&ts); + ip->i_ffs2_birthtime = ts.tv_sec; + ip->i_ffs2_birthnsec = ts.tv_nsec; + } + + uvm_vnp_setsize(vp, ip->i_size); + *new_key = &ip->i_number; + return 0; +} + +/* * File handle to vnode * * Have to be really careful about stale file handles: @@ -1878,7 +2246,6 @@ ffs_init(void) void ffs_reinit(void) { - ufs_reinit(); }