2 * Copyright (c) 2016 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
)
136 if (vfs_isupdate(mp
)) {
140 /* check entitlement */
141 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT
)) {
148 error
= copyinstr(user_data
, data
, MAXPATHLEN
- 1, &count
);
150 NULLFSDEBUG("nullfs: error copying data form user %d\n", error
);
154 /* This could happen if the system is configured for 32 bit inodes instead of
156 if (count
> MAX_MNT_FROM_LENGTH
) {
158 NULLFSDEBUG("nullfs: path to translocate too large for this system %d vs %d\n", count
, MAX_MNT_FROM_LENGTH
);
162 error
= vnode_lookup(data
, 0, &lowerrootvp
, ctx
);
164 NULLFSDEBUG("lookup %s -> %d\n", data
, error
);
168 /* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount.
169 Keep this to signal what we want to keep around the thing we are mirroring.
170 Drop it in unmount.*/
171 error
= vnode_ref(lowerrootvp
);
172 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
);
348 if (vnode_isinuse(vp
,1) && flags
== 0)
354 vnode_rele(vp
); // Drop reference taken by nullfs_mount
355 vnode_put(vp
); // Drop ref taken above
357 //Force close to get rid of the last vnode
358 (void)vflush(mp
, NULL
, FORCECLOSE
);
360 /* no more vnodes, so tear down the mountpoint */
362 lck_mtx_lock(&mntdata
->nullm_lock
);
364 vfs_setfsprivate(mp
, NULL
);
366 vnode_getalways(mntdata
->nullm_lowerrootvp
);
367 vnode_rele(mntdata
->nullm_lowerrootvp
);
368 vnode_put(mntdata
->nullm_lowerrootvp
);
370 lck_mtx_unlock(&mntdata
->nullm_lock
);
372 nullfs_destroy_lck(&mntdata
->nullm_lock
);
374 FREE(mntdata
, M_TEMP
);
376 uint64_t vflags
= vfs_flags(mp
);
377 vfs_setflags(mp
, vflags
& ~MNT_LOCAL
);
383 nullfs_root(struct mount
* mp
, struct vnode
** vpp
, __unused vfs_context_t ctx
)
388 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp
, (void *)MOUNTTONULLMOUNT(mp
)->nullm_rootvp
);
391 * Return locked reference to root.
393 vp
= MOUNTTONULLMOUNT(mp
)->nullm_rootvp
;
395 error
= vnode_get(vp
);
404 nullfs_vfs_getattr(struct mount
* mp
, struct vfs_attr
* vfap
, vfs_context_t ctx
)
406 struct vnode
* coveredvp
= NULL
;
408 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
409 vol_capabilities_attr_t capabilities
;
410 struct vfsstatfs
* sp
= vfs_statfs(mp
);
412 struct timespec tzero
= {0, 0};
414 NULLFSDEBUG("%s\n", __FUNCTION__
);
416 /* Set default capabilities in case the lower file system is gone */
417 memset(&capabilities
, 0, sizeof(capabilities
));
418 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] = VOL_CAP_FMT_FAST_STATFS
| VOL_CAP_FMT_HIDDEN_FILES
;
419 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] = VOL_CAP_FMT_FAST_STATFS
| VOL_CAP_FMT_HIDDEN_FILES
;
421 if (nullfs_vfs_getlowerattr(vnode_mount(null_mp
->nullm_lowerrootvp
), &vfa
, ctx
) == 0) {
422 if (VFSATTR_IS_SUPPORTED(&vfa
, f_capabilities
)) {
423 memcpy(&capabilities
, &vfa
.f_capabilities
, sizeof(capabilities
));
424 /* don't support vget */
425 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS
| VOL_CAP_FMT_PATH_FROM_ID
);
427 capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_HIDDEN_FILES
; /* Always support UF_HIDDEN */
429 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS
| VOL_CAP_FMT_PATH_FROM_ID
);
431 capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_HIDDEN_FILES
; /* Always support UF_HIDDEN */
433 /* dont' support interfaces that only make sense on a writable file system
434 * or one with specific vnops implemented */
435 capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] = 0;
437 capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] &=
438 ~(VOL_CAP_INT_SEARCHFS
| VOL_CAP_INT_ATTRLIST
| VOL_CAP_INT_READDIRATTR
| VOL_CAP_INT_EXCHANGEDATA
|
439 VOL_CAP_INT_COPYFILE
| VOL_CAP_INT_ALLOCATE
| VOL_CAP_INT_VOL_RENAME
| VOL_CAP_INT_ADVLOCK
| VOL_CAP_INT_FLOCK
);
443 if (VFSATTR_IS_ACTIVE(vfap
, f_create_time
))
444 VFSATTR_RETURN(vfap
, f_create_time
, tzero
);
446 if (VFSATTR_IS_ACTIVE(vfap
, f_modify_time
))
447 VFSATTR_RETURN(vfap
, f_modify_time
, tzero
);
449 if (VFSATTR_IS_ACTIVE(vfap
, f_access_time
))
450 VFSATTR_RETURN(vfap
, f_access_time
, tzero
);
452 if (VFSATTR_IS_ACTIVE(vfap
, f_bsize
))
453 VFSATTR_RETURN(vfap
, f_bsize
, sp
->f_bsize
);
455 if (VFSATTR_IS_ACTIVE(vfap
, f_iosize
))
456 VFSATTR_RETURN(vfap
, f_iosize
, sp
->f_iosize
);
458 if (VFSATTR_IS_ACTIVE(vfap
, f_owner
))
459 VFSATTR_RETURN(vfap
, f_owner
, 0);
461 if (VFSATTR_IS_ACTIVE(vfap
, f_blocks
))
462 VFSATTR_RETURN(vfap
, f_blocks
, sp
->f_blocks
);
464 if (VFSATTR_IS_ACTIVE(vfap
, f_bfree
))
465 VFSATTR_RETURN(vfap
, f_bfree
, sp
->f_bfree
);
467 if (VFSATTR_IS_ACTIVE(vfap
, f_bavail
))
468 VFSATTR_RETURN(vfap
, f_bavail
, sp
->f_bavail
);
470 if (VFSATTR_IS_ACTIVE(vfap
, f_bused
))
471 VFSATTR_RETURN(vfap
, f_bused
, sp
->f_bused
);
473 if (VFSATTR_IS_ACTIVE(vfap
, f_files
))
474 VFSATTR_RETURN(vfap
, f_files
, sp
->f_files
);
476 if (VFSATTR_IS_ACTIVE(vfap
, f_ffree
))
477 VFSATTR_RETURN(vfap
, f_ffree
, sp
->f_ffree
);
479 if (VFSATTR_IS_ACTIVE(vfap
, f_fssubtype
))
480 VFSATTR_RETURN(vfap
, f_fssubtype
, 0);
482 if (VFSATTR_IS_ACTIVE(vfap
, f_capabilities
)) {
483 memcpy(&vfap
->f_capabilities
, &capabilities
, sizeof(vol_capabilities_attr_t
));
485 VFSATTR_SET_SUPPORTED(vfap
, f_capabilities
);
488 if (VFSATTR_IS_ACTIVE(vfap
, f_attributes
)) {
489 vol_attributes_attr_t
* volattr
= &vfap
->f_attributes
;
491 volattr
->validattr
.commonattr
= 0;
492 volattr
->validattr
.volattr
= ATTR_VOL_NAME
| ATTR_VOL_CAPABILITIES
| ATTR_VOL_ATTRIBUTES
;
493 volattr
->validattr
.dirattr
= 0;
494 volattr
->validattr
.fileattr
= 0;
495 volattr
->validattr
.forkattr
= 0;
497 volattr
->nativeattr
.commonattr
= 0;
498 volattr
->nativeattr
.volattr
= ATTR_VOL_NAME
| ATTR_VOL_CAPABILITIES
| ATTR_VOL_ATTRIBUTES
;
499 volattr
->nativeattr
.dirattr
= 0;
500 volattr
->nativeattr
.fileattr
= 0;
501 volattr
->nativeattr
.forkattr
= 0;
503 VFSATTR_SET_SUPPORTED(vfap
, f_attributes
);
506 if (VFSATTR_IS_ACTIVE(vfap
, f_vol_name
)) {
507 /* The name of the volume is the same as the directory we mounted on */
508 coveredvp
= vfs_vnodecovered(mp
);
510 const char * name
= vnode_getname_printable(coveredvp
);
511 strlcpy(vfap
->f_vol_name
, name
, MAXPATHLEN
);
512 vnode_putname_printable(name
);
514 VFSATTR_SET_SUPPORTED(vfap
, f_vol_name
);
515 vnode_put(coveredvp
);
523 nullfs_sync(__unused
struct mount
* mp
, __unused
int waitfor
, __unused vfs_context_t ctx
)
526 * XXX - Assumes no data cached at null layer.
534 nullfs_vfs_start(__unused
struct mount
* mp
, __unused
int flags
, __unused vfs_context_t ctx
)
536 NULLFSDEBUG("%s\n", __FUNCTION__
);
540 extern struct vnodeopv_desc nullfs_vnodeop_opv_desc
;
542 struct vnodeopv_desc
* nullfs_vnodeopv_descs
[] = {
543 &nullfs_vnodeop_opv_desc
,
546 struct vfsops nullfs_vfsops
= {
547 .vfs_mount
= nullfs_mount
,
548 .vfs_unmount
= nullfs_unmount
,
549 .vfs_start
= nullfs_vfs_start
,
550 .vfs_root
= nullfs_root
,
551 .vfs_getattr
= nullfs_vfs_getattr
,
552 .vfs_sync
= nullfs_sync
,
553 .vfs_init
= nullfs_init
,