/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
+ *
* 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,
* 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.
* 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.
* 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 <sys/param.h>
#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
#include <sys/proc.h>
-#include <sys/kauth.h>
-#include <sys/time.h>
-#include <sys/types.h>
#include <sys/vnode.h>
-#include <sys/mount_internal.h>
-#include <sys/namei.h>
-#include <sys/malloc.h>
-#include <miscfs/nullfs/null.h>
+#include <sys/vnode_internal.h>
+#include <security/mac_internal.h>
+#include <sys/kauth.h>
+
+#include <sys/param.h>
+
+#include <IOKit/IOBSD.h>
+
+#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
*/
static int
-nullfs_mount(mp, devvp, data, context)
- struct mount *mp;
- vnode_t devvp;
- user_addr_t data;
- vfs_context_t context;
+nullfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, vfs_context_t ctx)
{
- int error = 0;
- struct user_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 (ENOTSUP);
- /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, devvp, data, p);*/
+ if (vfs_isupdate(mp)) {
+ return ENOTSUP;
}
- /*
- * Get argument
- */
- if (vfs_context_is64bit(context)) {
- error = copyin(data, (caddr_t)&args, sizeof (args));
- }
- else {
- struct null_args temp;
- error = copyin(data, (caddr_t)&temp, sizeof (temp));
- args.target = CAST_USER_ADDR_T(temp.target);
+ /* check entitlement */
+ if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) {
+ return EPERM;
}
- if (error)
- return (error);
/*
- * Find lower node
+ * Get configuration
*/
- NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
- UIO_USERSPACE, args.target, context);
- if (error = namei(ndp))
- return (error);
- nameidone(ndp);
+ 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;
+ }
- vnode_put(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.
+ * Grab the uid/gid of the caller, which may be used for unveil later
*/
- error = null_node_create(mp, lowerrootvp, &vp);
+ 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) {
- vnode_put(lowerrootvp);
- FREE(xmp, M_UFSMNT); /* XXX */
- return (error);
+ goto error;
}
- /*
- * Keep a held reference to the root vnode.
- * It is vnode_put'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(args.target, mp->mnt_vfsstat.f_mntfromname, MAXPATHLEN - 1,
- &size);
- bzero(mp->mnt_vfsstat.f_mntfromname + size, MNAMELEN - size);
-#ifdef NULLFS_DIAGNOSTIC
- printf("nullfs_mount: lower %s, alias at %s\n",
- mp->mnt_vfsstat.f_mntfromname, mp->mnt_vfsstat.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.
- */
-static int
-nullfs_start(mp, flags, context)
- struct mount *mp;
- int flags;
- vfs_context_t context;
-{
- return (0);
- /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, context); */
+ /* 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
*/
static int
-nullfs_unmount(mp, mntflags, context)
- struct mount *mp;
- int mntflags;
- vfs_context_t context;
+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
- */
- vnode_put(nullm_rootvp);
- /*
- * And blow it away for future re-use
- */
- vnode_reclaim(nullm_rootvp);
- /*
- * Finally, throw away the null_mount structure
- */
- FREE(mp->mnt_data, M_UFSMNT); /* XXX */
- mp->mnt_data = 0;
return 0;
}
static int
-nullfs_root(mp, vpp, context)
- struct mount *mp;
- struct vnode **vpp;
- vfs_context_t context;
+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;
- vnode_get(vp);
+
+ error = vnode_get(vp);
+ if (error) {
+ return error;
+ }
+
*vpp = vp;
return 0;
}
static int
-nullfs_quotactl(mp, cmd, uid, datap, context)
- struct mount *mp;
- int cmd;
- uid_t uid;
- caddr_t datap;
- vfs_context_t context;
+nullfs_vfs_getattr(struct mount * mp, struct vfs_attr * vfap, vfs_context_t ctx)
{
- return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, datap, context);
-}
+ 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);
-static int
-nullfs_statfs(mp, sbp, context)
- struct mount *mp;
- struct vfsstatfs *sbp;
- vfs_context_t context;
-{
- int error;
- struct vfsstatfs 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, context);
- 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;
- 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;
}
static int
-nullfs_sync(__unused struct mount *mp, __unused int waitfor,
- __unused kauth_cred_t cred, __unused vfs_context_t context)
+nullfs_sync(__unused struct mount * mp, __unused int waitfor, __unused vfs_context_t ctx)
{
/*
* XXX - Assumes no data cached at null layer.
*/
- return (0);
-}
-
-static int
-nullfs_vget(mp, ino, vpp, context)
- struct mount *mp;
- ino64_t ino;
- struct vnode **vpp;
- vfs_context_t context;
-{
-
- return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp, context);
+ return 0;
}
-static int
-nullfs_fhtovp(mp, fhlen, fhp, vpp, context)
- struct mount *mp;
- int fhlen;
- unsigned char *fhp;
- struct vnode **vpp;
- vfs_context_t context;
-{
- return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fhlen, fhp, vpp, context);
-}
static int
-nullfs_vptofh(vp, fhlenp, fhp, context)
- struct vnode *vp;
- int *fhlenp;
- unsigned char *fhp;
- vfs_context_t context;
+nullfs_vfs_start(__unused struct mount * mp, __unused int flags, __unused vfs_context_t ctx)
{
- return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhlenp, fhp, context);
+ NULLFSDEBUG("%s\n", __FUNCTION__);
+ return 0;
}
-int nullfs_init (struct vfsconf *);
-
-#define nullfs_sysctl (int (*) (int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, proc_t))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,
};