2 * Copyright (c) 2019 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Portions Copyright (c) 1992, 1993, 1995
26 * The Regents of the University of California. All rights reserved.
28 * This code is derived from software donated to Berkeley by
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94
57 * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/fcntl.h>
64 #include <sys/kernel.h>
66 #include <sys/malloc.h>
67 #include <sys/mount.h>
68 #include <sys/namei.h>
70 #include <sys/vnode.h>
71 #include <sys/vnode_internal.h>
72 #include <security/mac_internal.h>
73 #include <sys/kauth.h>
75 #include <sys/param.h>
77 #include <IOKit/IOBSD.h>
81 #define NULLFS_ENTITLEMENT "com.apple.private.nullfs_allow"
83 #define SIZEOF_MEMBER(type, member) (sizeof(((type *)0)->member))
84 #define MAX_MNT_FROM_LENGTH (SIZEOF_MEMBER(struct vfsstatfs, f_mntfromname))
87 nullfs_vfs_getlowerattr(mount_t mp
, struct vfs_attr
* vfap
, vfs_context_t ctx
)
89 memset(vfap
, 0, sizeof(*vfap
));
91 VFSATTR_WANTED(vfap
, f_bsize
);
92 VFSATTR_WANTED(vfap
, f_iosize
);
93 VFSATTR_WANTED(vfap
, f_blocks
);
94 VFSATTR_WANTED(vfap
, f_bfree
);
95 VFSATTR_WANTED(vfap
, f_bavail
);
96 VFSATTR_WANTED(vfap
, f_bused
);
97 VFSATTR_WANTED(vfap
, f_files
);
98 VFSATTR_WANTED(vfap
, f_ffree
);
99 VFSATTR_WANTED(vfap
, f_capabilities
);
101 return vfs_getattr(mp
, vfap
, ctx
);
108 nullfs_mount(struct mount
* mp
, __unused vnode_t devvp
, user_addr_t user_data
, vfs_context_t ctx
)
111 struct vnode
*lowerrootvp
= NULL
, *vp
= NULL
;
112 struct vfsstatfs
* sp
= NULL
;
113 struct null_mount
* xmp
= NULL
;
114 struct null_mount_conf conf
= {0};
115 char path
[MAXPATHLEN
];
119 /* set defaults (arbitrary since this file system is readonly) */
120 uint32_t bsize
= BLKDEV_IOSIZE
;
121 size_t iosize
= BLKDEV_IOSIZE
;
122 uint64_t blocks
= 4711 * 4711;
125 uint64_t bused
= 4711;
126 uint64_t files
= 4711;
129 kauth_cred_t cred
= vfs_context_ucred(ctx
);
131 NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp
, vfs_flags(mp
));
133 if (vfs_flags(mp
) & MNT_ROOTFS
) {
140 if (vfs_isupdate(mp
)) {
144 /* check entitlement */
145 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT
)) {
152 error
= copyin(user_data
, &conf
, sizeof(conf
));
154 NULLFSDEBUG("nullfs: error copying configuration form user %d\n", error
);
161 error
= copyinstr(user_data
+ sizeof(conf
), path
, MAXPATHLEN
- 1, &count
);
163 NULLFSDEBUG("nullfs: error copying data form user %d\n", error
);
167 /* This could happen if the system is configured for 32 bit inodes instead of
169 if (count
> MAX_MNT_FROM_LENGTH
) {
171 NULLFSDEBUG("nullfs: path to translocate too large for this system %ld vs %ld\n", count
, MAX_MNT_FROM_LENGTH
);
175 error
= vnode_lookup(path
, 0, &lowerrootvp
, ctx
);
177 NULLFSDEBUG("lookup %s -> %d\n", path
, error
);
181 /* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount.
182 * Keep this to signal what we want to keep around the thing we are mirroring.
183 * Drop it in unmount.*/
184 error
= vnode_ref(lowerrootvp
);
185 vnode_put(lowerrootvp
);
187 // If vnode_ref failed, then null it out so it can't be used anymore in cleanup.
192 NULLFSDEBUG("mount %s\n", path
);
194 MALLOC(xmp
, struct null_mount
*, sizeof(*xmp
), M_TEMP
, M_WAITOK
| M_ZERO
);
201 * Grab the uid/gid of the caller, which may be used for unveil later
203 xmp
->uid
= kauth_cred_getuid(cred
);
204 xmp
->gid
= kauth_cred_getgid(cred
);
207 * Save reference to underlying FS
209 xmp
->nullm_lowerrootvp
= lowerrootvp
;
210 xmp
->nullm_lowerrootvid
= vnode_vid(lowerrootvp
);
212 error
= null_getnewvnode(mp
, NULL
, NULL
, &vp
, NULL
, 1);
217 /* vp has an iocount on it from vnode_create. drop that for a usecount. This
218 * is our root vnode so we drop the ref in unmount
220 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
224 error
= nullfs_init_lck(&xmp
->nullm_lock
);
229 xmp
->nullm_rootvp
= vp
;
231 /* read the flags the user set, but then ignore some of them, we will only
232 * allow them if they are set on the lower file system */
233 uint64_t flags
= vfs_flags(mp
) & (~(MNT_IGNORE_OWNERSHIP
| MNT_LOCAL
));
234 uint64_t lowerflags
= vfs_flags(vnode_mount(lowerrootvp
)) & (MNT_LOCAL
| MNT_QUARANTINE
| MNT_IGNORE_OWNERSHIP
| MNT_NOEXEC
);
240 /* force these flags */
241 flags
|= (MNT_DONTBROWSE
| MNT_MULTILABEL
| MNT_NOSUID
| MNT_RDONLY
);
242 vfs_setflags(mp
, flags
);
244 vfs_setfsprivate(mp
, xmp
);
246 vfs_setlocklocal(mp
);
248 /* fill in the stat block */
250 strlcpy(sp
->f_mntfromname
, path
, MAX_MNT_FROM_LENGTH
);
254 xmp
->nullm_flags
= NULLM_CASEINSENSITIVE
; /* default to case insensitive */
256 // Set the flags that are requested
257 xmp
->nullm_flags
|= conf
.flags
& NULLM_UNVEIL
;
259 error
= nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp
), &vfa
, ctx
);
261 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bsize
)) {
264 if (VFSATTR_IS_SUPPORTED(&vfa
, f_iosize
)) {
265 iosize
= vfa
.f_iosize
;
267 if (VFSATTR_IS_SUPPORTED(&vfa
, f_blocks
)) {
268 blocks
= vfa
.f_blocks
;
270 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bfree
)) {
273 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bavail
)) {
274 bavail
= vfa
.f_bavail
;
276 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bused
)) {
279 if (VFSATTR_IS_SUPPORTED(&vfa
, f_files
)) {
282 if (VFSATTR_IS_SUPPORTED(&vfa
, f_ffree
)) {
285 if (VFSATTR_IS_SUPPORTED(&vfa
, f_capabilities
)) {
286 if ((vfa
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] & (VOL_CAP_FMT_CASE_SENSITIVE
)) &&
287 (vfa
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] & (VOL_CAP_FMT_CASE_SENSITIVE
))) {
288 xmp
->nullm_flags
&= ~NULLM_CASEINSENSITIVE
;
296 sp
->f_iosize
= iosize
;
297 sp
->f_blocks
= blocks
;
299 sp
->f_bavail
= bavail
;
304 /* Associate the mac label information from the mirrored filesystem with the
306 MAC_PERFORM(mount_label_associate
, cred
, vnode_mount(lowerrootvp
), vfs_mntlabel(mp
));
308 NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp
->f_mntfromname
, sp
->f_mntonname
);
316 vnode_getwithref(lowerrootvp
);
317 vnode_rele(lowerrootvp
);
318 vnode_put(lowerrootvp
);
321 /* we made the root vnode but the mount is failed, so clean it up */
322 vnode_getwithref(vp
);
332 * Free reference to null layer
335 nullfs_unmount(struct mount
* mp
, int mntflags
, __unused vfs_context_t ctx
)
337 struct null_mount
* mntdata
;
341 NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp
);
343 /* check entitlement or superuser*/
344 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT
) &&
345 vfs_context_suser(ctx
) != 0) {
349 if (mntflags
& MNT_FORCE
) {
355 mntdata
= MOUNTTONULLMOUNT(mp
);
356 vp
= mntdata
->nullm_rootvp
;
358 // release our reference on the root before flushing.
359 // it will get pulled out of the mount structure by reclaim
362 error
= vflush(mp
, vp
, flags
);
368 if (vnode_isinuse(vp
, 1) && flags
== 0) {
373 vnode_rele(vp
); // Drop reference taken by nullfs_mount
374 vnode_put(vp
); // Drop ref taken above
376 //Force close to get rid of the last vnode
377 (void)vflush(mp
, NULL
, FORCECLOSE
);
379 /* no more vnodes, so tear down the mountpoint */
381 lck_mtx_lock(&mntdata
->nullm_lock
);
383 vfs_setfsprivate(mp
, NULL
);
385 vnode_getalways(mntdata
->nullm_lowerrootvp
);
386 vnode_rele(mntdata
->nullm_lowerrootvp
);
387 vnode_put(mntdata
->nullm_lowerrootvp
);
389 lck_mtx_unlock(&mntdata
->nullm_lock
);
391 nullfs_destroy_lck(&mntdata
->nullm_lock
);
393 FREE(mntdata
, M_TEMP
);
395 uint64_t vflags
= vfs_flags(mp
);
396 vfs_setflags(mp
, vflags
& ~MNT_LOCAL
);
402 nullfs_root(struct mount
* mp
, struct vnode
** vpp
, __unused vfs_context_t ctx
)
407 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp
, (void *)MOUNTTONULLMOUNT(mp
)->nullm_rootvp
);
410 * Return locked reference to root.
412 vp
= MOUNTTONULLMOUNT(mp
)->nullm_rootvp
;
414 error
= vnode_get(vp
);
424 nullfs_vfs_getattr(struct mount
* mp
, struct vfs_attr
* vfap
, vfs_context_t ctx
)
426 struct vnode
* coveredvp
= NULL
;
428 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
429 vol_capabilities_attr_t capabilities
;
430 struct vfsstatfs
* sp
= vfs_statfs(mp
);
431 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, ctx
);
433 struct timespec tzero
= {.tv_sec
= 0, .tv_nsec
= 0};
435 NULLFSDEBUG("%s\n", __FUNCTION__
);
437 /* Set default capabilities in case the lower file system is gone */
438 memset(&capabilities
, 0, sizeof(capabilities
));
439 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] = VOL_CAP_FMT_FAST_STATFS
| VOL_CAP_FMT_HIDDEN_FILES
;
440 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] = VOL_CAP_FMT_FAST_STATFS
| VOL_CAP_FMT_HIDDEN_FILES
;
442 if (nullfs_vfs_getlowerattr(vnode_mount(null_mp
->nullm_lowerrootvp
), &vfa
, ectx
) == 0) {
443 if (VFSATTR_IS_SUPPORTED(&vfa
, f_capabilities
)) {
444 memcpy(&capabilities
, &vfa
.f_capabilities
, sizeof(capabilities
));
445 /* don't support vget */
446 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS
| VOL_CAP_FMT_PATH_FROM_ID
);
448 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_HIDDEN_FILES
; /* Always support UF_HIDDEN */
450 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS
| VOL_CAP_FMT_PATH_FROM_ID
);
452 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_HIDDEN_FILES
; /* Always support UF_HIDDEN */
454 /* dont' support interfaces that only make sense on a writable file system
455 * or one with specific vnops implemented */
456 capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] = 0;
458 capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] &=
459 ~(VOL_CAP_INT_SEARCHFS
| VOL_CAP_INT_ATTRLIST
| VOL_CAP_INT_READDIRATTR
| VOL_CAP_INT_EXCHANGEDATA
|
460 VOL_CAP_INT_COPYFILE
| VOL_CAP_INT_ALLOCATE
| VOL_CAP_INT_VOL_RENAME
| VOL_CAP_INT_ADVLOCK
| VOL_CAP_INT_FLOCK
);
464 if (VFSATTR_IS_ACTIVE(vfap
, f_create_time
)) {
465 VFSATTR_RETURN(vfap
, f_create_time
, tzero
);
468 if (VFSATTR_IS_ACTIVE(vfap
, f_modify_time
)) {
469 VFSATTR_RETURN(vfap
, f_modify_time
, tzero
);
472 if (VFSATTR_IS_ACTIVE(vfap
, f_access_time
)) {
473 VFSATTR_RETURN(vfap
, f_access_time
, tzero
);
476 if (VFSATTR_IS_ACTIVE(vfap
, f_bsize
)) {
477 VFSATTR_RETURN(vfap
, f_bsize
, sp
->f_bsize
);
480 if (VFSATTR_IS_ACTIVE(vfap
, f_iosize
)) {
481 VFSATTR_RETURN(vfap
, f_iosize
, sp
->f_iosize
);
484 if (VFSATTR_IS_ACTIVE(vfap
, f_owner
)) {
485 VFSATTR_RETURN(vfap
, f_owner
, 0);
488 if (VFSATTR_IS_ACTIVE(vfap
, f_blocks
)) {
489 VFSATTR_RETURN(vfap
, f_blocks
, sp
->f_blocks
);
492 if (VFSATTR_IS_ACTIVE(vfap
, f_bfree
)) {
493 VFSATTR_RETURN(vfap
, f_bfree
, sp
->f_bfree
);
496 if (VFSATTR_IS_ACTIVE(vfap
, f_bavail
)) {
497 VFSATTR_RETURN(vfap
, f_bavail
, sp
->f_bavail
);
500 if (VFSATTR_IS_ACTIVE(vfap
, f_bused
)) {
501 VFSATTR_RETURN(vfap
, f_bused
, sp
->f_bused
);
504 if (VFSATTR_IS_ACTIVE(vfap
, f_files
)) {
505 VFSATTR_RETURN(vfap
, f_files
, sp
->f_files
);
508 if (VFSATTR_IS_ACTIVE(vfap
, f_ffree
)) {
509 VFSATTR_RETURN(vfap
, f_ffree
, sp
->f_ffree
);
512 if (VFSATTR_IS_ACTIVE(vfap
, f_fssubtype
)) {
513 VFSATTR_RETURN(vfap
, f_fssubtype
, 0);
516 if (VFSATTR_IS_ACTIVE(vfap
, f_capabilities
)) {
517 memcpy(&vfap
->f_capabilities
, &capabilities
, sizeof(vol_capabilities_attr_t
));
519 VFSATTR_SET_SUPPORTED(vfap
, f_capabilities
);
522 if (VFSATTR_IS_ACTIVE(vfap
, f_attributes
)) {
523 vol_attributes_attr_t
* volattr
= &vfap
->f_attributes
;
525 volattr
->validattr
.commonattr
= 0;
526 volattr
->validattr
.volattr
= ATTR_VOL_NAME
| ATTR_VOL_CAPABILITIES
| ATTR_VOL_ATTRIBUTES
;
527 volattr
->validattr
.dirattr
= 0;
528 volattr
->validattr
.fileattr
= 0;
529 volattr
->validattr
.forkattr
= 0;
531 volattr
->nativeattr
.commonattr
= 0;
532 volattr
->nativeattr
.volattr
= ATTR_VOL_NAME
| ATTR_VOL_CAPABILITIES
| ATTR_VOL_ATTRIBUTES
;
533 volattr
->nativeattr
.dirattr
= 0;
534 volattr
->nativeattr
.fileattr
= 0;
535 volattr
->nativeattr
.forkattr
= 0;
537 VFSATTR_SET_SUPPORTED(vfap
, f_attributes
);
540 if (VFSATTR_IS_ACTIVE(vfap
, f_vol_name
)) {
541 /* The name of the volume is the same as the directory we mounted on */
542 coveredvp
= vfs_vnodecovered(mp
);
544 const char * name
= vnode_getname_printable(coveredvp
);
545 strlcpy(vfap
->f_vol_name
, name
, MAXPATHLEN
);
546 vnode_putname_printable(name
);
548 VFSATTR_SET_SUPPORTED(vfap
, f_vol_name
);
549 vnode_put(coveredvp
);
553 nullfs_cleanup_patched_context(null_mp
, ectx
);
559 nullfs_sync(__unused
struct mount
* mp
, __unused
int waitfor
, __unused vfs_context_t ctx
)
562 * XXX - Assumes no data cached at null layer.
570 nullfs_vfs_start(__unused
struct mount
* mp
, __unused
int flags
, __unused vfs_context_t ctx
)
572 NULLFSDEBUG("%s\n", __FUNCTION__
);
576 extern const struct vnodeopv_desc nullfs_vnodeop_opv_desc
;
578 const struct vnodeopv_desc
* nullfs_vnodeopv_descs
[] = {
579 &nullfs_vnodeop_opv_desc
,
582 struct vfsops nullfs_vfsops
= {
583 .vfs_mount
= nullfs_mount
,
584 .vfs_unmount
= nullfs_unmount
,
585 .vfs_start
= nullfs_vfs_start
,
586 .vfs_root
= nullfs_root
,
587 .vfs_getattr
= nullfs_vfs_getattr
,
588 .vfs_sync
= nullfs_sync
,
589 .vfs_init
= nullfs_init
,