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>
74 #include <sys/param.h>
76 #include <IOKit/IOBSD.h>
80 #define NULLFS_ENTITLEMENT "com.apple.private.nullfs_allow"
82 #define SIZEOF_MEMBER(type, member) (sizeof(((type *)0)->member))
83 #define MAX_MNT_FROM_LENGTH (SIZEOF_MEMBER(struct vfsstatfs, f_mntfromname))
86 nullfs_vfs_getlowerattr(mount_t mp
, struct vfs_attr
* vfap
, vfs_context_t ctx
)
88 memset(vfap
, 0, sizeof(*vfap
));
90 VFSATTR_WANTED(vfap
, f_bsize
);
91 VFSATTR_WANTED(vfap
, f_iosize
);
92 VFSATTR_WANTED(vfap
, f_blocks
);
93 VFSATTR_WANTED(vfap
, f_bfree
);
94 VFSATTR_WANTED(vfap
, f_bavail
);
95 VFSATTR_WANTED(vfap
, f_bused
);
96 VFSATTR_WANTED(vfap
, f_files
);
97 VFSATTR_WANTED(vfap
, f_ffree
);
98 VFSATTR_WANTED(vfap
, f_capabilities
);
100 return vfs_getattr(mp
, vfap
, ctx
);
107 nullfs_mount(struct mount
* mp
, __unused vnode_t devvp
, user_addr_t user_data
, vfs_context_t ctx
)
110 struct vnode
*lowerrootvp
= NULL
, *vp
= NULL
;
111 struct vfsstatfs
* sp
= NULL
;
112 struct null_mount
* xmp
= NULL
;
113 char data
[MAXPATHLEN
];
116 /* set defaults (arbitrary since this file system is readonly) */
117 uint32_t bsize
= BLKDEV_IOSIZE
;
118 size_t iosize
= BLKDEV_IOSIZE
;
119 uint64_t blocks
= 4711 * 4711;
122 uint64_t bused
= 4711;
123 uint64_t files
= 4711;
126 kauth_cred_t cred
= vfs_context_ucred(ctx
);
128 NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp
, vfs_flags(mp
));
130 if (vfs_flags(mp
) & MNT_ROOTFS
) {
137 if (vfs_isupdate(mp
)) {
141 /* check entitlement */
142 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT
)) {
149 error
= copyinstr(user_data
, data
, MAXPATHLEN
- 1, &count
);
151 NULLFSDEBUG("nullfs: error copying data form user %d\n", error
);
155 /* This could happen if the system is configured for 32 bit inodes instead of
157 if (count
> MAX_MNT_FROM_LENGTH
) {
159 NULLFSDEBUG("nullfs: path to translocate too large for this system %d vs %d\n", count
, MAX_MNT_FROM_LENGTH
);
163 error
= vnode_lookup(data
, 0, &lowerrootvp
, ctx
);
165 NULLFSDEBUG("lookup %s -> %d\n", data
, error
);
169 /* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount.
170 * Keep this to signal what we want to keep around the thing we are mirroring.
171 * Drop it in unmount.*/
172 error
= vnode_ref(lowerrootvp
);
173 vnode_put(lowerrootvp
);
175 // If vnode_ref failed, then null it out so it can't be used anymore in cleanup.
180 NULLFSDEBUG("mount %s\n", data
);
182 MALLOC(xmp
, struct null_mount
*, sizeof(*xmp
), M_TEMP
, M_WAITOK
| M_ZERO
);
189 * Save reference to underlying FS
191 xmp
->nullm_lowerrootvp
= lowerrootvp
;
192 xmp
->nullm_lowerrootvid
= vnode_vid(lowerrootvp
);
194 error
= null_getnewvnode(mp
, NULL
, NULL
, &vp
, NULL
, 1);
199 /* vp has an iocount on it from vnode_create. drop that for a usecount. This
200 * is our root vnode so we drop the ref in unmount
202 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
206 error
= nullfs_init_lck(&xmp
->nullm_lock
);
211 xmp
->nullm_rootvp
= vp
;
213 /* read the flags the user set, but then ignore some of them, we will only
214 * allow them if they are set on the lower file system */
215 uint64_t flags
= vfs_flags(mp
) & (~(MNT_IGNORE_OWNERSHIP
| MNT_LOCAL
));
216 uint64_t lowerflags
= vfs_flags(vnode_mount(lowerrootvp
)) & (MNT_LOCAL
| MNT_QUARANTINE
| MNT_IGNORE_OWNERSHIP
| MNT_NOEXEC
);
222 /* force these flags */
223 flags
|= (MNT_DONTBROWSE
| MNT_MULTILABEL
| MNT_NOSUID
| MNT_RDONLY
);
224 vfs_setflags(mp
, flags
);
226 vfs_setfsprivate(mp
, xmp
);
228 vfs_setlocklocal(mp
);
230 /* fill in the stat block */
232 strlcpy(sp
->f_mntfromname
, data
, MAX_MNT_FROM_LENGTH
);
236 xmp
->nullm_flags
= NULLM_CASEINSENSITIVE
; /* default to case insensitive */
238 error
= nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp
), &vfa
, ctx
);
240 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bsize
)) {
243 if (VFSATTR_IS_SUPPORTED(&vfa
, f_iosize
)) {
244 iosize
= vfa
.f_iosize
;
246 if (VFSATTR_IS_SUPPORTED(&vfa
, f_blocks
)) {
247 blocks
= vfa
.f_blocks
;
249 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bfree
)) {
252 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bavail
)) {
253 bavail
= vfa
.f_bavail
;
255 if (VFSATTR_IS_SUPPORTED(&vfa
, f_bused
)) {
258 if (VFSATTR_IS_SUPPORTED(&vfa
, f_files
)) {
261 if (VFSATTR_IS_SUPPORTED(&vfa
, f_ffree
)) {
264 if (VFSATTR_IS_SUPPORTED(&vfa
, f_capabilities
)) {
265 if ((vfa
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] & (VOL_CAP_FMT_CASE_SENSITIVE
)) &&
266 (vfa
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] & (VOL_CAP_FMT_CASE_SENSITIVE
))) {
267 xmp
->nullm_flags
&= ~NULLM_CASEINSENSITIVE
;
275 sp
->f_iosize
= iosize
;
276 sp
->f_blocks
= blocks
;
278 sp
->f_bavail
= bavail
;
283 /* Associate the mac label information from the mirrored filesystem with the
285 MAC_PERFORM(mount_label_associate
, cred
, vnode_mount(lowerrootvp
), vfs_mntlabel(mp
));
287 NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp
->f_mntfromname
, sp
->f_mntonname
);
295 vnode_getwithref(lowerrootvp
);
296 vnode_rele(lowerrootvp
);
297 vnode_put(lowerrootvp
);
300 /* we made the root vnode but the mount is failed, so clean it up */
301 vnode_getwithref(vp
);
311 * Free reference to null layer
314 nullfs_unmount(struct mount
* mp
, int mntflags
, __unused vfs_context_t ctx
)
316 struct null_mount
* mntdata
;
320 NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp
);
322 /* check entitlement or superuser*/
323 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT
) &&
324 vfs_context_suser(ctx
) != 0) {
328 if (mntflags
& MNT_FORCE
) {
334 mntdata
= MOUNTTONULLMOUNT(mp
);
335 vp
= mntdata
->nullm_rootvp
;
337 // release our reference on the root before flushing.
338 // it will get pulled out of the mount structure by reclaim
341 error
= vflush(mp
, vp
, flags
);
347 if (vnode_isinuse(vp
, 1) && flags
== 0) {
352 vnode_rele(vp
); // Drop reference taken by nullfs_mount
353 vnode_put(vp
); // Drop ref taken above
355 //Force close to get rid of the last vnode
356 (void)vflush(mp
, NULL
, FORCECLOSE
);
358 /* no more vnodes, so tear down the mountpoint */
360 lck_mtx_lock(&mntdata
->nullm_lock
);
362 vfs_setfsprivate(mp
, NULL
);
364 vnode_getalways(mntdata
->nullm_lowerrootvp
);
365 vnode_rele(mntdata
->nullm_lowerrootvp
);
366 vnode_put(mntdata
->nullm_lowerrootvp
);
368 lck_mtx_unlock(&mntdata
->nullm_lock
);
370 nullfs_destroy_lck(&mntdata
->nullm_lock
);
372 FREE(mntdata
, M_TEMP
);
374 uint64_t vflags
= vfs_flags(mp
);
375 vfs_setflags(mp
, vflags
& ~MNT_LOCAL
);
381 nullfs_root(struct mount
* mp
, struct vnode
** vpp
, __unused vfs_context_t ctx
)
386 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp
, (void *)MOUNTTONULLMOUNT(mp
)->nullm_rootvp
);
389 * Return locked reference to root.
391 vp
= MOUNTTONULLMOUNT(mp
)->nullm_rootvp
;
393 error
= vnode_get(vp
);
403 nullfs_vfs_getattr(struct mount
* mp
, struct vfs_attr
* vfap
, vfs_context_t ctx
)
405 struct vnode
* coveredvp
= NULL
;
407 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
408 vol_capabilities_attr_t capabilities
;
409 struct vfsstatfs
* sp
= vfs_statfs(mp
);
411 struct timespec tzero
= {.tv_sec
= 0, .tv_nsec
= 0};
413 NULLFSDEBUG("%s\n", __FUNCTION__
);
415 /* Set default capabilities in case the lower file system is gone */
416 memset(&capabilities
, 0, sizeof(capabilities
));
417 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] = VOL_CAP_FMT_FAST_STATFS
| VOL_CAP_FMT_HIDDEN_FILES
;
418 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] = VOL_CAP_FMT_FAST_STATFS
| VOL_CAP_FMT_HIDDEN_FILES
;
420 if (nullfs_vfs_getlowerattr(vnode_mount(null_mp
->nullm_lowerrootvp
), &vfa
, ctx
) == 0) {
421 if (VFSATTR_IS_SUPPORTED(&vfa
, f_capabilities
)) {
422 memcpy(&capabilities
, &vfa
.f_capabilities
, sizeof(capabilities
));
423 /* don't support vget */
424 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS
| VOL_CAP_FMT_PATH_FROM_ID
);
426 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_HIDDEN_FILES
; /* Always support UF_HIDDEN */
428 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS
| VOL_CAP_FMT_PATH_FROM_ID
);
430 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_HIDDEN_FILES
; /* Always support UF_HIDDEN */
432 /* dont' support interfaces that only make sense on a writable file system
433 * or one with specific vnops implemented */
434 capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] = 0;
436 capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] &=
437 ~(VOL_CAP_INT_SEARCHFS
| VOL_CAP_INT_ATTRLIST
| VOL_CAP_INT_READDIRATTR
| VOL_CAP_INT_EXCHANGEDATA
|
438 VOL_CAP_INT_COPYFILE
| VOL_CAP_INT_ALLOCATE
| VOL_CAP_INT_VOL_RENAME
| VOL_CAP_INT_ADVLOCK
| VOL_CAP_INT_FLOCK
);
442 if (VFSATTR_IS_ACTIVE(vfap
, f_create_time
)) {
443 VFSATTR_RETURN(vfap
, f_create_time
, tzero
);
446 if (VFSATTR_IS_ACTIVE(vfap
, f_modify_time
)) {
447 VFSATTR_RETURN(vfap
, f_modify_time
, tzero
);
450 if (VFSATTR_IS_ACTIVE(vfap
, f_access_time
)) {
451 VFSATTR_RETURN(vfap
, f_access_time
, tzero
);
454 if (VFSATTR_IS_ACTIVE(vfap
, f_bsize
)) {
455 VFSATTR_RETURN(vfap
, f_bsize
, sp
->f_bsize
);
458 if (VFSATTR_IS_ACTIVE(vfap
, f_iosize
)) {
459 VFSATTR_RETURN(vfap
, f_iosize
, sp
->f_iosize
);
462 if (VFSATTR_IS_ACTIVE(vfap
, f_owner
)) {
463 VFSATTR_RETURN(vfap
, f_owner
, 0);
466 if (VFSATTR_IS_ACTIVE(vfap
, f_blocks
)) {
467 VFSATTR_RETURN(vfap
, f_blocks
, sp
->f_blocks
);
470 if (VFSATTR_IS_ACTIVE(vfap
, f_bfree
)) {
471 VFSATTR_RETURN(vfap
, f_bfree
, sp
->f_bfree
);
474 if (VFSATTR_IS_ACTIVE(vfap
, f_bavail
)) {
475 VFSATTR_RETURN(vfap
, f_bavail
, sp
->f_bavail
);
478 if (VFSATTR_IS_ACTIVE(vfap
, f_bused
)) {
479 VFSATTR_RETURN(vfap
, f_bused
, sp
->f_bused
);
482 if (VFSATTR_IS_ACTIVE(vfap
, f_files
)) {
483 VFSATTR_RETURN(vfap
, f_files
, sp
->f_files
);
486 if (VFSATTR_IS_ACTIVE(vfap
, f_ffree
)) {
487 VFSATTR_RETURN(vfap
, f_ffree
, sp
->f_ffree
);
490 if (VFSATTR_IS_ACTIVE(vfap
, f_fssubtype
)) {
491 VFSATTR_RETURN(vfap
, f_fssubtype
, 0);
494 if (VFSATTR_IS_ACTIVE(vfap
, f_capabilities
)) {
495 memcpy(&vfap
->f_capabilities
, &capabilities
, sizeof(vol_capabilities_attr_t
));
497 VFSATTR_SET_SUPPORTED(vfap
, f_capabilities
);
500 if (VFSATTR_IS_ACTIVE(vfap
, f_attributes
)) {
501 vol_attributes_attr_t
* volattr
= &vfap
->f_attributes
;
503 volattr
->validattr
.commonattr
= 0;
504 volattr
->validattr
.volattr
= ATTR_VOL_NAME
| ATTR_VOL_CAPABILITIES
| ATTR_VOL_ATTRIBUTES
;
505 volattr
->validattr
.dirattr
= 0;
506 volattr
->validattr
.fileattr
= 0;
507 volattr
->validattr
.forkattr
= 0;
509 volattr
->nativeattr
.commonattr
= 0;
510 volattr
->nativeattr
.volattr
= ATTR_VOL_NAME
| ATTR_VOL_CAPABILITIES
| ATTR_VOL_ATTRIBUTES
;
511 volattr
->nativeattr
.dirattr
= 0;
512 volattr
->nativeattr
.fileattr
= 0;
513 volattr
->nativeattr
.forkattr
= 0;
515 VFSATTR_SET_SUPPORTED(vfap
, f_attributes
);
518 if (VFSATTR_IS_ACTIVE(vfap
, f_vol_name
)) {
519 /* The name of the volume is the same as the directory we mounted on */
520 coveredvp
= vfs_vnodecovered(mp
);
522 const char * name
= vnode_getname_printable(coveredvp
);
523 strlcpy(vfap
->f_vol_name
, name
, MAXPATHLEN
);
524 vnode_putname_printable(name
);
526 VFSATTR_SET_SUPPORTED(vfap
, f_vol_name
);
527 vnode_put(coveredvp
);
535 nullfs_sync(__unused
struct mount
* mp
, __unused
int waitfor
, __unused vfs_context_t ctx
)
538 * XXX - Assumes no data cached at null layer.
546 nullfs_vfs_start(__unused
struct mount
* mp
, __unused
int flags
, __unused vfs_context_t ctx
)
548 NULLFSDEBUG("%s\n", __FUNCTION__
);
552 extern const struct vnodeopv_desc nullfs_vnodeop_opv_desc
;
554 const struct vnodeopv_desc
* nullfs_vnodeopv_descs
[] = {
555 &nullfs_vnodeop_opv_desc
,
558 struct vfsops nullfs_vfsops
= {
559 .vfs_mount
= nullfs_mount
,
560 .vfs_unmount
= nullfs_unmount
,
561 .vfs_start
= nullfs_vfs_start
,
562 .vfs_root
= nullfs_root
,
563 .vfs_getattr
= nullfs_vfs_getattr
,
564 .vfs_sync
= nullfs_sync
,
565 .vfs_init
= nullfs_init
,