X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e5568f75972dfc723778653c11cb6b4dc825716a..c3c9b80d004dbbfdf763edeb97968c6997e3b45b:/bsd/miscfs/nullfs/null_vfsops.c diff --git a/bsd/miscfs/nullfs/null_vfsops.c b/bsd/miscfs/nullfs/null_vfsops.c index 66f61af3d..c0f5ac6e7 100644 --- a/bsd/miscfs/nullfs/null_vfsops.c +++ b/bsd/miscfs/nullfs/null_vfsops.c @@ -1,28 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * * @APPLE_LICENSE_HEADER_END@ */ -/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ -/* - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. + +/*- + * Portions Copyright (c) 1992, 1993, 1995 + * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. @@ -35,10 +36,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -55,327 +52,538 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)null_vfsops.c 8.7 (Berkeley) 5/14/95 + * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 * - * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 - */ - -/* - * Null Layer - * (See null_vnops.c for a description of what this does.) + * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 + * $FreeBSD$ */ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "nullfs.h" + +#define NULLFS_ENTITLEMENT "com.apple.private.nullfs_allow" + +#define SIZEOF_MEMBER(type, member) (sizeof(((type *)0)->member)) +#define MAX_MNT_FROM_LENGTH (SIZEOF_MEMBER(struct vfsstatfs, f_mntfromname)) + +static int +nullfs_vfs_getlowerattr(mount_t mp, struct vfs_attr * vfap, vfs_context_t ctx) +{ + memset(vfap, 0, sizeof(*vfap)); + VFSATTR_INIT(vfap); + VFSATTR_WANTED(vfap, f_bsize); + VFSATTR_WANTED(vfap, f_iosize); + VFSATTR_WANTED(vfap, f_blocks); + VFSATTR_WANTED(vfap, f_bfree); + VFSATTR_WANTED(vfap, f_bavail); + VFSATTR_WANTED(vfap, f_bused); + VFSATTR_WANTED(vfap, f_files); + VFSATTR_WANTED(vfap, f_ffree); + VFSATTR_WANTED(vfap, f_capabilities); + + return vfs_getattr(mp, vfap, ctx); +} /* * Mount null layer */ -int -nullfs_mount(mp, path, data, ndp, p) - struct mount *mp; - char *path; - caddr_t data; - struct nameidata *ndp; - struct proc *p; +static int +nullfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, vfs_context_t ctx) { - int error = 0; - struct null_args args; - struct vnode *lowerrootvp, *vp; - struct vnode *nullm_rootvp; - struct null_mount *xmp; - u_int size; - -#ifdef NULLFS_DIAGNOSTIC - printf("nullfs_mount(mp = %x)\n", mp); -#endif + int error = 0; + struct vnode *lowerrootvp = NULL, *vp = NULL; + struct vfsstatfs * sp = NULL; + struct null_mount * xmp = NULL; + struct null_mount_conf conf = {0}; + char path[MAXPATHLEN]; + + size_t count; + struct vfs_attr vfa; + /* set defaults (arbitrary since this file system is readonly) */ + uint32_t bsize = BLKDEV_IOSIZE; + size_t iosize = BLKDEV_IOSIZE; + uint64_t blocks = 4711 * 4711; + uint64_t bfree = 0; + uint64_t bavail = 0; + uint64_t bused = 4711; + uint64_t files = 4711; + uint64_t ffree = 0; + + kauth_cred_t cred = vfs_context_ucred(ctx); + + NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp, vfs_flags(mp)); + + if (vfs_flags(mp) & MNT_ROOTFS) { + return EOPNOTSUPP; + } /* * Update is a no-op */ - if (mp->mnt_flag & MNT_UPDATE) { - return (EOPNOTSUPP); - /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/ + if (vfs_isupdate(mp)) { + return ENOTSUP; } - /* - * Get argument - */ - if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args))) - return (error); + /* check entitlement */ + if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) { + return EPERM; + } /* - * Find lower node + * Get configuration */ - NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, - UIO_USERSPACE, args.target, p); - if (error = namei(ndp)) - return (error); + error = copyin(user_data, &conf, sizeof(conf)); + if (error) { + NULLFSDEBUG("nullfs: error copying configuration form user %d\n", error); + goto error; + } /* - * Sanity check on lower vnode + * Get argument */ - lowerrootvp = ndp->ni_vp; + error = copyinstr(user_data + sizeof(conf), path, MAXPATHLEN - 1, &count); + if (error) { + NULLFSDEBUG("nullfs: error copying data form user %d\n", error); + goto error; + } - vrele(ndp->ni_dvp); - ndp->ni_dvp = NULL; + /* This could happen if the system is configured for 32 bit inodes instead of + * 64 bit */ + if (count > MAX_MNT_FROM_LENGTH) { + error = EINVAL; + NULLFSDEBUG("nullfs: path to translocate too large for this system %ld vs %ld\n", count, MAX_MNT_FROM_LENGTH); + goto error; + } - xmp = (struct null_mount *) _MALLOC(sizeof(struct null_mount), - M_UFSMNT, M_WAITOK); /* XXX */ + error = vnode_lookup(path, 0, &lowerrootvp, ctx); + if (error) { + NULLFSDEBUG("lookup %s -> %d\n", path, error); + goto error; + } - /* - * Save reference to underlying FS - */ - xmp->nullm_vfs = lowerrootvp->v_mount; + /* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount. + * Keep this to signal what we want to keep around the thing we are mirroring. + * Drop it in unmount.*/ + error = vnode_ref(lowerrootvp); + vnode_put(lowerrootvp); + if (error) { + // If vnode_ref failed, then null it out so it can't be used anymore in cleanup. + lowerrootvp = NULL; + goto error; + } + + NULLFSDEBUG("mount %s\n", path); + + MALLOC(xmp, struct null_mount *, sizeof(*xmp), M_TEMP, M_WAITOK | M_ZERO); + if (xmp == NULL) { + error = ENOMEM; + goto error; + } /* - * Save reference. Each mount also holds - * a reference on the root vnode. - */ - error = null_node_create(mp, lowerrootvp, &vp); - /* - * Unlock the node (either the lower or the alias) + * Grab the uid/gid of the caller, which may be used for unveil later */ - VOP_UNLOCK(vp, 0, p); + xmp->uid = kauth_cred_getuid(cred); + xmp->gid = kauth_cred_getgid(cred); + /* - * Make sure the node alias worked + * Save reference to underlying FS */ + xmp->nullm_lowerrootvp = lowerrootvp; + xmp->nullm_lowerrootvid = vnode_vid(lowerrootvp); + + error = null_getnewvnode(mp, NULL, NULL, &vp, NULL, 1); if (error) { - vrele(lowerrootvp); - FREE(xmp, M_UFSMNT); /* XXX */ - return (error); + goto error; } - /* - * Keep a held reference to the root vnode. - * It is vrele'd in nullfs_unmount. - */ - nullm_rootvp = vp; - nullm_rootvp->v_flag |= VROOT; - xmp->nullm_rootvp = nullm_rootvp; - if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) - mp->mnt_flag |= MNT_LOCAL; - mp->mnt_data = (qaddr_t) xmp; + /* vp has an iocount on it from vnode_create. drop that for a usecount. This + * is our root vnode so we drop the ref in unmount + * + * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/ + vnode_ref(vp); + vnode_put(vp); + + nullfs_init_lck(&xmp->nullm_lock); + + xmp->nullm_rootvp = vp; + + /* read the flags the user set, but then ignore some of them, we will only + * allow them if they are set on the lower file system */ + uint64_t flags = vfs_flags(mp) & (~(MNT_IGNORE_OWNERSHIP | MNT_LOCAL)); + uint64_t lowerflags = vfs_flags(vnode_mount(lowerrootvp)) & (MNT_LOCAL | MNT_QUARANTINE | MNT_IGNORE_OWNERSHIP | MNT_NOEXEC); + + if (lowerflags) { + flags |= lowerflags; + } + + /* force these flags */ + flags |= (MNT_DONTBROWSE | MNT_MULTILABEL | MNT_NOSUID | MNT_RDONLY); + vfs_setflags(mp, flags); + + vfs_setfsprivate(mp, xmp); vfs_getnewfsid(mp); + vfs_setlocklocal(mp); + + /* fill in the stat block */ + sp = vfs_statfs(mp); + strlcpy(sp->f_mntfromname, path, MAX_MNT_FROM_LENGTH); + + sp->f_flags = flags; + + xmp->nullm_flags = NULLM_CASEINSENSITIVE; /* default to case insensitive */ + + // Set the flags that are requested + xmp->nullm_flags |= conf.flags & NULLM_UNVEIL; + + error = nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx); + if (error == 0) { + if (VFSATTR_IS_SUPPORTED(&vfa, f_bsize)) { + bsize = vfa.f_bsize; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_iosize)) { + iosize = vfa.f_iosize; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_blocks)) { + blocks = vfa.f_blocks; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_bfree)) { + bfree = vfa.f_bfree; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_bavail)) { + bavail = vfa.f_bavail; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_bused)) { + bused = vfa.f_bused; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_files)) { + files = vfa.f_files; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_ffree)) { + ffree = vfa.f_ffree; + } + if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) { + if ((vfa.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE)) && + (vfa.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE))) { + xmp->nullm_flags &= ~NULLM_CASEINSENSITIVE; + } + } + } else { + goto error; + } - (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); - bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); - (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, - &size); - bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); -#ifdef NULLFS_DIAGNOSTIC - printf("nullfs_mount: lower %s, alias at %s\n", - mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); -#endif - return (0); -} + sp->f_bsize = bsize; + sp->f_iosize = iosize; + sp->f_blocks = blocks; + sp->f_bfree = bfree; + sp->f_bavail = bavail; + sp->f_bused = bused; + sp->f_files = files; + sp->f_ffree = ffree; -/* - * VFS start. Nothing needed here - the start routine - * on the underlying filesystem will have been called - * when that filesystem was mounted. - */ -int -nullfs_start(mp, flags, p) - struct mount *mp; - int flags; - struct proc *p; -{ - return (0); - /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */ + /* Associate the mac label information from the mirrored filesystem with the + * mirror */ + MAC_PERFORM(mount_label_associate, cred, vnode_mount(lowerrootvp), vfs_mntlabel(mp)); + + NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp->f_mntfromname, sp->f_mntonname); + return 0; + +error: + if (xmp) { + FREE(xmp, M_TEMP); + } + if (lowerrootvp) { + vnode_getwithref(lowerrootvp); + vnode_rele(lowerrootvp); + vnode_put(lowerrootvp); + } + if (vp) { + /* we made the root vnode but the mount is failed, so clean it up */ + vnode_getwithref(vp); + vnode_rele(vp); + /* give vp back */ + vnode_recycle(vp); + vnode_put(vp); + } + return error; } /* * Free reference to null layer */ -int -nullfs_unmount(mp, mntflags, p) - struct mount *mp; - int mntflags; - struct proc *p; +static int +nullfs_unmount(struct mount * mp, int mntflags, __unused vfs_context_t ctx) { - struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; - int error; - int flags = 0; - int force = 0; + struct null_mount * mntdata; + struct vnode * vp; + int error, flags; -#ifdef NULLFS_DIAGNOSTIC - printf("nullfs_unmount(mp = %x)\n", mp); -#endif + NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); + + /* check entitlement or superuser*/ + if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT) && + vfs_context_suser(ctx) != 0) { + return EPERM; + } if (mntflags & MNT_FORCE) { - flags |= FORCECLOSE; - force = 1; + flags = FORCECLOSE; + } else { + flags = 0; } - if ( (nullm_rootvp->v_usecount > 1) && !force ) - return (EBUSY); - if ( (error = vflush(mp, nullm_rootvp, flags)) && !force ) - return (error); + mntdata = MOUNTTONULLMOUNT(mp); + vp = mntdata->nullm_rootvp; + + // release our reference on the root before flushing. + // it will get pulled out of the mount structure by reclaim + vnode_getalways(vp); + + error = vflush(mp, vp, flags); + if (error) { + vnode_put(vp); + return error; + } + + if (vnode_isinuse(vp, 1) && flags == 0) { + vnode_put(vp); + return EBUSY; + } + + vnode_rele(vp); // Drop reference taken by nullfs_mount + vnode_put(vp); // Drop ref taken above + + //Force close to get rid of the last vnode + (void)vflush(mp, NULL, FORCECLOSE); + + /* no more vnodes, so tear down the mountpoint */ + + lck_mtx_lock(&mntdata->nullm_lock); + + vfs_setfsprivate(mp, NULL); + + vnode_getalways(mntdata->nullm_lowerrootvp); + vnode_rele(mntdata->nullm_lowerrootvp); + vnode_put(mntdata->nullm_lowerrootvp); + + lck_mtx_unlock(&mntdata->nullm_lock); + + nullfs_destroy_lck(&mntdata->nullm_lock); + + FREE(mntdata, M_TEMP); + + uint64_t vflags = vfs_flags(mp); + vfs_setflags(mp, vflags & ~MNT_LOCAL); -#ifdef NULLFS_DIAGNOSTIC - vprint("alias root of lower", nullm_rootvp); -#endif - /* - * Release reference on underlying root vnode - */ - vrele(nullm_rootvp); - /* - * And blow it away for future re-use - */ - vgone(nullm_rootvp); - /* - * Finally, throw away the null_mount structure - */ - FREE(mp->mnt_data, M_UFSMNT); /* XXX */ - mp->mnt_data = 0; return 0; } -int -nullfs_root(mp, vpp) - struct mount *mp; - struct vnode **vpp; +static int +nullfs_root(struct mount * mp, struct vnode ** vpp, __unused vfs_context_t ctx) { - struct proc *p = curproc; /* XXX */ - struct vnode *vp; + struct vnode * vp; + int error; -#ifdef NULLFS_DIAGNOSTIC - printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp, - MOUNTTONULLMOUNT(mp)->nullm_rootvp, - NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) - ); -#endif + NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp); /* * Return locked reference to root. */ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; - VREF(vp); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + error = vnode_get(vp); + if (error) { + return error; + } + *vpp = vp; return 0; } -int -nullfs_quotactl(mp, cmd, uid, arg, p) - struct mount *mp; - int cmd; - uid_t uid; - caddr_t arg; - struct proc *p; +static int +nullfs_vfs_getattr(struct mount * mp, struct vfs_attr * vfap, vfs_context_t ctx) { - return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p); -} + struct vnode * coveredvp = NULL; + struct vfs_attr vfa; + struct null_mount * null_mp = MOUNTTONULLMOUNT(mp); + vol_capabilities_attr_t capabilities; + struct vfsstatfs * sp = vfs_statfs(mp); + vfs_context_t ectx = nullfs_get_patched_context(null_mp, ctx); -int -nullfs_statfs(mp, sbp, p) - struct mount *mp; - struct statfs *sbp; - struct proc *p; -{ - int error; - struct statfs mstat; - -#ifdef NULLFS_DIAGNOSTIC - printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp, - MOUNTTONULLMOUNT(mp)->nullm_rootvp, - NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) - ); -#endif - - bzero(&mstat, sizeof(mstat)); - - error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p); - if (error) - return (error); - - /* now copy across the "interesting" information and fake the rest */ - sbp->f_type = mstat.f_type; - sbp->f_flags = mstat.f_flags; - sbp->f_bsize = mstat.f_bsize; - sbp->f_iosize = mstat.f_iosize; - sbp->f_blocks = mstat.f_blocks; - sbp->f_bfree = mstat.f_bfree; - sbp->f_bavail = mstat.f_bavail; - sbp->f_files = mstat.f_files; - sbp->f_ffree = mstat.f_ffree; - if (sbp != &mp->mnt_stat) { - bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); - bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); - bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); - } - return (0); + struct timespec tzero = {.tv_sec = 0, .tv_nsec = 0}; + + NULLFSDEBUG("%s\n", __FUNCTION__); + + /* Set default capabilities in case the lower file system is gone */ + memset(&capabilities, 0, sizeof(capabilities)); + capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_HIDDEN_FILES; + capabilities.valid[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_HIDDEN_FILES; + + if (nullfs_vfs_getlowerattr(vnode_mount(null_mp->nullm_lowerrootvp), &vfa, ectx) == 0) { + if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) { + memcpy(&capabilities, &vfa.f_capabilities, sizeof(capabilities)); + /* don't support vget */ + capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID); + + capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */ + + capabilities.valid[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID); + + capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */ + + /* dont' support interfaces that only make sense on a writable file system + * or one with specific vnops implemented */ + capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = 0; + + capabilities.valid[VOL_CAPABILITIES_INTERFACES] &= + ~(VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA | + VOL_CAP_INT_COPYFILE | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK); + } + } + + if (VFSATTR_IS_ACTIVE(vfap, f_create_time)) { + VFSATTR_RETURN(vfap, f_create_time, tzero); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_modify_time)) { + VFSATTR_RETURN(vfap, f_modify_time, tzero); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_access_time)) { + VFSATTR_RETURN(vfap, f_access_time, tzero); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_bsize)) { + VFSATTR_RETURN(vfap, f_bsize, sp->f_bsize); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_iosize)) { + VFSATTR_RETURN(vfap, f_iosize, sp->f_iosize); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_owner)) { + VFSATTR_RETURN(vfap, f_owner, 0); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_blocks)) { + VFSATTR_RETURN(vfap, f_blocks, sp->f_blocks); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_bfree)) { + VFSATTR_RETURN(vfap, f_bfree, sp->f_bfree); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_bavail)) { + VFSATTR_RETURN(vfap, f_bavail, sp->f_bavail); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_bused)) { + VFSATTR_RETURN(vfap, f_bused, sp->f_bused); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_files)) { + VFSATTR_RETURN(vfap, f_files, sp->f_files); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_ffree)) { + VFSATTR_RETURN(vfap, f_ffree, sp->f_ffree); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_fssubtype)) { + VFSATTR_RETURN(vfap, f_fssubtype, 0); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_capabilities)) { + memcpy(&vfap->f_capabilities, &capabilities, sizeof(vol_capabilities_attr_t)); + + VFSATTR_SET_SUPPORTED(vfap, f_capabilities); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_attributes)) { + vol_attributes_attr_t * volattr = &vfap->f_attributes; + + volattr->validattr.commonattr = 0; + volattr->validattr.volattr = ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES; + volattr->validattr.dirattr = 0; + volattr->validattr.fileattr = 0; + volattr->validattr.forkattr = 0; + + volattr->nativeattr.commonattr = 0; + volattr->nativeattr.volattr = ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES; + volattr->nativeattr.dirattr = 0; + volattr->nativeattr.fileattr = 0; + volattr->nativeattr.forkattr = 0; + + VFSATTR_SET_SUPPORTED(vfap, f_attributes); + } + + if (VFSATTR_IS_ACTIVE(vfap, f_vol_name)) { + /* The name of the volume is the same as the directory we mounted on */ + coveredvp = vfs_vnodecovered(mp); + if (coveredvp) { + const char * name = vnode_getname_printable(coveredvp); + strlcpy(vfap->f_vol_name, name, MAXPATHLEN); + vnode_putname_printable(name); + + VFSATTR_SET_SUPPORTED(vfap, f_vol_name); + vnode_put(coveredvp); + } + } + + nullfs_cleanup_patched_context(null_mp, ectx); + + return 0; } -int -nullfs_sync(mp, waitfor, cred, p) - struct mount *mp; - int waitfor; - struct ucred *cred; - struct proc *p; +static int +nullfs_sync(__unused struct mount * mp, __unused int waitfor, __unused vfs_context_t ctx) { /* * XXX - Assumes no data cached at null layer. */ - return (0); -} - -int -nullfs_vget(mp, ino, vpp) - struct mount *mp; - ino_t ino; - struct vnode **vpp; -{ - - return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp); + return 0; } -int -nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) - struct mount *mp; - struct fid *fidp; - struct mbuf *nam; - struct vnode **vpp; - int *exflagsp; - struct ucred**credanonp; -{ - return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp); -} -int -nullfs_vptofh(vp, fhp) - struct vnode *vp; - struct fid *fhp; +static int +nullfs_vfs_start(__unused struct mount * mp, __unused int flags, __unused vfs_context_t ctx) { - return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp); + NULLFSDEBUG("%s\n", __FUNCTION__); + return 0; } -int nullfs_init __P((struct vfsconf *)); - -#define nullfs_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \ - size_t, struct proc *)))eopnotsupp) - -struct vfsops null_vfsops = { - nullfs_mount, - nullfs_start, - nullfs_unmount, - nullfs_root, - nullfs_quotactl, - nullfs_statfs, - nullfs_sync, - nullfs_vget, - nullfs_fhtovp, - nullfs_vptofh, - nullfs_init, - nullfs_sysctl, +extern const struct vnodeopv_desc nullfs_vnodeop_opv_desc; + +const struct vnodeopv_desc * nullfs_vnodeopv_descs[] = { + &nullfs_vnodeop_opv_desc, +}; + +struct vfsops nullfs_vfsops = { + .vfs_mount = nullfs_mount, + .vfs_unmount = nullfs_unmount, + .vfs_start = nullfs_vfs_start, + .vfs_root = nullfs_root, + .vfs_getattr = nullfs_vfs_getattr, + .vfs_sync = nullfs_sync, + .vfs_init = nullfs_init, + .vfs_sysctl = NULL, + .vfs_setattr = NULL, };