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
26 * The Regents of the University of California. All rights reserved.
28 * This code is derived from software contributed to Berkeley by
29 * John Heidemann of the UCLA Ficus project.
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_vnops.c 8.6 (Berkeley) 5/27/95
58 * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
60 * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
65 #include <sys/param.h>
66 #include <sys/systm.h>
68 #include <sys/kernel.h>
70 #include <sys/malloc.h>
71 #include <sys/mount.h>
72 #include <sys/mount_internal.h>
73 #include <sys/namei.h>
74 #include <sys/sysctl.h>
75 #include <sys/vnode.h>
76 #include <sys/xattr.h>
78 #include <sys/types.h>
79 #include <sys/dirent.h>
83 #define NULL_ROOT_INO 2
84 #define NULL_SECOND_INO 3
85 #define NULL_THIRD_INO 4
87 vop_t
* nullfs_vnodeop_p
= NULL
;
89 /* the mountpoint lock should be held going into this function */
91 nullfs_isspecialvp(struct vnode
* vp
)
93 struct null_mount
* null_mp
;
95 null_mp
= MOUNTTONULLMOUNT(vnode_mount(vp
));
97 /* only check for root and second here, third is special in a different way,
98 * related only to lookup and readdir */
99 if (vp
&& (vp
== null_mp
->nullm_rootvp
|| vp
== null_mp
->nullm_secondvp
)) {
105 /* helper function to handle locking where possible */
107 nullfs_checkspecialvp(struct vnode
* vp
)
110 struct null_mount
* null_mp
;
112 null_mp
= MOUNTTONULLMOUNT(vnode_mount(vp
));
114 lck_mtx_lock(&null_mp
->nullm_lock
);
115 result
= (nullfs_isspecialvp(vp
));
116 lck_mtx_unlock(&null_mp
->nullm_lock
);
122 nullfs_default(__unused
struct vnop_generic_args
* args
)
124 NULLFSDEBUG("%s (default)\n", ((struct vnodeop_desc_fake
*)args
->a_desc
)->vdesc_name
);
129 nullfs_special_getattr(struct vnop_getattr_args
* args
)
131 mount_t mp
= vnode_mount(args
->a_vp
);
132 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
134 ino_t ino
= NULL_ROOT_INO
;
135 struct vnode_attr covered_rootattr
;
136 vnode_t checkvp
= null_mp
->nullm_lowerrootvp
;
138 VATTR_INIT(&covered_rootattr
);
139 VATTR_WANTED(&covered_rootattr
, va_uid
);
140 VATTR_WANTED(&covered_rootattr
, va_gid
);
141 VATTR_WANTED(&covered_rootattr
, va_create_time
);
142 VATTR_WANTED(&covered_rootattr
, va_modify_time
);
143 VATTR_WANTED(&covered_rootattr
, va_access_time
);
145 /* prefer to get this from the lower root vp, but if not (i.e. forced unmount
146 * of lower fs) try the mount point covered vnode */
147 if (vnode_getwithvid(checkvp
, null_mp
->nullm_lowerrootvid
)) {
148 checkvp
= vfs_vnodecovered(mp
);
149 if (checkvp
== NULL
) {
154 int error
= vnode_getattr(checkvp
, &covered_rootattr
, args
->a_context
);
158 /* we should have been able to get attributes fore one of the two choices so
159 * fail if we didn't */
163 /* we got the attributes of the vnode we cover so plow ahead */
164 if (args
->a_vp
== null_mp
->nullm_secondvp
) {
165 ino
= NULL_SECOND_INO
;
168 VATTR_RETURN(args
->a_vap
, va_type
, vnode_vtype(args
->a_vp
));
169 VATTR_RETURN(args
->a_vap
, va_rdev
, 0);
170 VATTR_RETURN(args
->a_vap
, va_nlink
, 3); /* always just ., .., and the child */
171 VATTR_RETURN(args
->a_vap
, va_total_size
, 0); // hoping this is ok
173 VATTR_RETURN(args
->a_vap
, va_data_size
, 0); // hoping this is ok
174 VATTR_RETURN(args
->a_vap
, va_data_alloc
, 0);
175 VATTR_RETURN(args
->a_vap
, va_iosize
, vfs_statfs(mp
)->f_iosize
);
176 VATTR_RETURN(args
->a_vap
, va_fileid
, ino
);
177 VATTR_RETURN(args
->a_vap
, va_linkid
, ino
);
178 VATTR_RETURN(args
->a_vap
, va_fsid
, vfs_statfs(mp
)->f_fsid
.val
[0]); // return the fsid of the mount point
179 VATTR_RETURN(args
->a_vap
, va_filerev
, 0);
180 VATTR_RETURN(args
->a_vap
, va_gen
, 0);
181 VATTR_RETURN(args
->a_vap
, va_flags
, UF_HIDDEN
); /* mark our fake directories as hidden. People
182 * shouldn't be enocouraged to poke around in them */
184 if (ino
== NULL_SECOND_INO
) {
185 VATTR_RETURN(args
->a_vap
, va_parentid
, NULL_ROOT_INO
); /* no parent at the root, so
186 * the only other vnode that
187 * goes through this path is
188 * second and its parent is
192 if (VATTR_IS_ACTIVE(args
->a_vap
, va_mode
)) {
193 /* force dr_xr_xr_x */
194 VATTR_RETURN(args
->a_vap
, va_mode
, S_IFDIR
| S_IRUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
);
196 if (VATTR_IS_ACTIVE(args
->a_vap
, va_uid
)) {
197 VATTR_RETURN(args
->a_vap
, va_uid
, covered_rootattr
.va_uid
);
199 if (VATTR_IS_ACTIVE(args
->a_vap
, va_gid
)) {
200 VATTR_RETURN(args
->a_vap
, va_gid
, covered_rootattr
.va_gid
);
203 if (VATTR_IS_ACTIVE(args
->a_vap
, va_create_time
)) {
204 VATTR_SET_SUPPORTED(args
->a_vap
, va_create_time
);
205 args
->a_vap
->va_create_time
.tv_sec
= covered_rootattr
.va_create_time
.tv_sec
;
206 args
->a_vap
->va_create_time
.tv_nsec
= covered_rootattr
.va_create_time
.tv_nsec
;
208 if (VATTR_IS_ACTIVE(args
->a_vap
, va_modify_time
)) {
209 VATTR_SET_SUPPORTED(args
->a_vap
, va_modify_time
);
210 args
->a_vap
->va_modify_time
.tv_sec
= covered_rootattr
.va_modify_time
.tv_sec
;
211 args
->a_vap
->va_modify_time
.tv_nsec
= covered_rootattr
.va_modify_time
.tv_nsec
;
213 if (VATTR_IS_ACTIVE(args
->a_vap
, va_access_time
)) {
214 VATTR_SET_SUPPORTED(args
->a_vap
, va_access_time
);
215 args
->a_vap
->va_modify_time
.tv_sec
= covered_rootattr
.va_access_time
.tv_sec
;
216 args
->a_vap
->va_modify_time
.tv_nsec
= covered_rootattr
.va_access_time
.tv_nsec
;
223 nullfs_getattr(struct vnop_getattr_args
* args
)
226 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(args
->a_vp
));
227 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
229 lck_mtx_lock(&null_mp
->nullm_lock
);
230 if (nullfs_isspecialvp(args
->a_vp
)) {
231 error
= nullfs_special_getattr(args
);
232 lck_mtx_unlock(&null_mp
->nullm_lock
);
235 lck_mtx_unlock(&null_mp
->nullm_lock
);
237 /* this will return a different inode for third than read dir will */
238 struct vnode
* lowervp
= NULLVPTOLOWERVP(args
->a_vp
);
240 error
= vnode_getwithref(lowervp
);
242 error
= VNOP_GETATTR(lowervp
, args
->a_vap
, args
->a_context
);
246 /* fix up fsid so it doesn't say the underlying fs*/
247 VATTR_RETURN(args
->a_vap
, va_fsid
, vfs_statfs(vnode_mount(args
->a_vp
))->f_fsid
.val
[0]);
255 nullfs_open(struct vnop_open_args
* args
)
258 struct vnode
*vp
, *lvp
;
260 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
262 if (nullfs_checkspecialvp(args
->a_vp
)) {
263 return 0; /* nothing extra needed */
267 lvp
= NULLVPTOLOWERVP(vp
);
268 error
= vnode_getwithref(lvp
);
270 error
= VNOP_OPEN(lvp
, args
->a_mode
, args
->a_context
);
278 nullfs_close(struct vnop_close_args
* args
)
281 struct vnode
*vp
, *lvp
;
283 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
285 if (nullfs_checkspecialvp(args
->a_vp
)) {
286 return 0; /* nothing extra needed */
290 lvp
= NULLVPTOLOWERVP(vp
);
292 error
= vnode_getwithref(lvp
);
294 error
= VNOP_CLOSE(lvp
, args
->a_fflag
, args
->a_context
);
300 /* get lvp's parent, if possible, even if it isn't set.
302 * lvp is expected to have an iocount before and after this call.
304 * if a dvpp is populated the returned vnode has an iocount. */
306 null_get_lowerparent(vnode_t lvp
, vnode_t
* dvpp
, vfs_context_t ctx
)
309 struct vnode_attr va
;
310 mount_t mp
= vnode_mount(lvp
);
311 vnode_t dvp
= vnode_parent(lvp
);
314 error
= vnode_get(dvp
);
319 if (!(mp
->mnt_kern_flag
& MNTK_PATH_FROM_ID
)) {
324 VATTR_WANTED(&va
, va_parentid
);
326 error
= vnode_getattr(lvp
, &va
, ctx
);
328 if (error
|| !VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
332 error
= VFS_VGET(mp
, (ino64_t
)va
.va_parentid
, &dvp
, ctx
);
341 /* the mountpoint lock should be held going into this function */
343 null_special_lookup(struct vnop_lookup_args
* ap
)
345 struct componentname
* cnp
= ap
->a_cnp
;
346 struct vnode
* dvp
= ap
->a_dvp
;
347 struct vnode
* ldvp
= NULL
;
348 struct vnode
* lvp
= NULL
;
349 struct vnode
* vp
= NULL
;
350 struct mount
* mp
= vnode_mount(dvp
);
351 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
354 if (dvp
== null_mp
->nullm_rootvp
) {
355 /* handle . and .. */
356 if (cnp
->cn_nameptr
[0] == '.') {
357 if (cnp
->cn_namelen
== 1 || (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.')) {
358 /* this is the root so both . and .. give back the root */
360 error
= vnode_get(vp
);
365 /* our virtual wrapper directory should be d but D is acceptable if the
366 * lower file system is case insensitive */
367 if (cnp
->cn_namelen
== 1 &&
368 (cnp
->cn_nameptr
[0] == 'd' || (null_mp
->nullm_flags
& NULLM_CASEINSENSITIVE
? cnp
->cn_nameptr
[0] == 'D' : 0))) {
370 if (null_mp
->nullm_secondvp
== NULL
) {
371 error
= null_getnewvnode(mp
, NULL
, dvp
, &vp
, cnp
, 0);
376 null_mp
->nullm_secondvp
= vp
;
378 vp
= null_mp
->nullm_secondvp
;
379 error
= vnode_get(vp
);
382 } else if (dvp
== null_mp
->nullm_secondvp
) {
383 /* handle . and .. */
384 if (cnp
->cn_nameptr
[0] == '.') {
385 if (cnp
->cn_namelen
== 1) {
387 error
= vnode_get(vp
);
389 } else if (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.') {
390 /* parent here is the root vp */
391 vp
= null_mp
->nullm_rootvp
;
392 error
= vnode_get(vp
);
396 /* nullmp->nullm_lowerrootvp was set at mount time so don't need to lock to
398 /* v_name should be null terminated but cn_nameptr is not necessarily.
399 * cn_namelen is the number of characters before the null in either case */
400 error
= vnode_getwithvid(null_mp
->nullm_lowerrootvp
, null_mp
->nullm_lowerrootvid
);
405 /* We don't want to mess with case insensitivity and unicode, so the plan to
407 * 1. try to get the lower root's parent
408 * 2. If we get a parent, then perform a lookup on the lower file system
409 * using the parent and the passed in cnp
410 * 3. If that worked and we got a vp, then see if the vp is lowerrootvp. If
412 * 4. Anything else results in ENOENT.
414 error
= null_get_lowerparent(null_mp
->nullm_lowerrootvp
, &ldvp
, ap
->a_context
);
417 error
= VNOP_LOOKUP(ldvp
, &lvp
, cnp
, ap
->a_context
);
421 if (lvp
== null_mp
->nullm_lowerrootvp
) {
422 /* always check the hashmap for a vnode for this, the root of the
424 error
= null_nodeget(mp
, lvp
, dvp
, &vp
, cnp
, 0);
426 if (error
== 0 && null_mp
->nullm_thirdcovervp
== NULL
) {
427 /* if nodeget succeeded then vp has an iocount*/
428 null_mp
->nullm_thirdcovervp
= vp
;
436 vnode_put(null_mp
->nullm_lowerrootvp
);
447 * We have to carry on the locking protocol on the null layer vnodes
448 * as we progress through the tree. We also have to enforce read-only
449 * if this layer is mounted read-only.
452 null_lookup(struct vnop_lookup_args
* ap
)
454 struct componentname
* cnp
= ap
->a_cnp
;
455 struct vnode
* dvp
= ap
->a_dvp
;
456 struct vnode
*vp
, *ldvp
, *lvp
;
458 struct null_mount
* null_mp
;
461 NULLFSDEBUG("%s parent: %p component: %.*s\n", __FUNCTION__
, ap
->a_dvp
, cnp
->cn_namelen
, cnp
->cn_nameptr
);
463 mp
= vnode_mount(dvp
);
464 /* rename and delete are not allowed. this is a read only file system */
465 if (cnp
->cn_nameiop
== DELETE
|| cnp
->cn_nameiop
== RENAME
|| cnp
->cn_nameiop
== CREATE
) {
468 null_mp
= MOUNTTONULLMOUNT(mp
);
470 lck_mtx_lock(&null_mp
->nullm_lock
);
471 if (nullfs_isspecialvp(dvp
)) {
472 error
= null_special_lookup(ap
);
473 lck_mtx_unlock(&null_mp
->nullm_lock
);
476 lck_mtx_unlock(&null_mp
->nullm_lock
);
479 if (cnp
->cn_nameptr
[0] == '.') {
480 if (cnp
->cn_namelen
== 1) {
482 } else if (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.') {
483 /* mount point crossing is handled in null_special_lookup */
484 vp
= vnode_parent(dvp
);
489 error
= vp
? vnode_get(vp
) : ENOENT
;
499 ldvp
= NULLVPTOLOWERVP(dvp
);
503 * Hold ldvp. The reference on it, owned by dvp, is lost in
504 * case of dvp reclamation.
506 error
= vnode_getwithref(ldvp
);
511 error
= VNOP_LOOKUP(ldvp
, &lvp
, cnp
, ap
->a_context
);
515 if ((error
== 0 || error
== EJUSTRETURN
) && lvp
!= NULL
) {
518 error
= vnode_get(vp
);
520 error
= null_nodeget(mp
, lvp
, dvp
, &vp
, cnp
, 0);
527 /* if we got lvp, drop the iocount from VNOP_LOOKUP */
536 * Don't think this needs to do anything
539 null_inactive(__unused
struct vnop_inactive_args
* ap
)
541 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
547 null_reclaim(struct vnop_reclaim_args
* ap
)
550 struct null_node
* xp
;
551 struct vnode
* lowervp
;
552 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
554 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
559 lowervp
= xp
->null_lowervp
;
561 lck_mtx_lock(&null_mp
->nullm_lock
);
563 vnode_removefsref(vp
);
565 if (lowervp
!= NULL
) {
566 /* root and second don't have a lowervp, so nothing to release and nothing
568 if (xp
->null_flags
& NULL_FLAG_HASHED
) {
569 /* only call this if we actually made it into the hash list. reclaim gets
571 * clean up a vnode that got created when it didn't need to under race
575 vnode_getwithref(lowervp
);
580 if (vp
== null_mp
->nullm_rootvp
) {
581 null_mp
->nullm_rootvp
= NULL
;
582 } else if (vp
== null_mp
->nullm_secondvp
) {
583 null_mp
->nullm_secondvp
= NULL
;
584 } else if (vp
== null_mp
->nullm_thirdcovervp
) {
585 null_mp
->nullm_thirdcovervp
= NULL
;
588 lck_mtx_unlock(&null_mp
->nullm_lock
);
591 vnode_clearfsnode(vp
);
598 #define DIRENT_SZ(dp) ((sizeof(struct dirent) - NAME_MAX) + (((dp)->d_namlen + 1 + 3) & ~3))
601 store_entry_special(ino_t ino
, const char * name
, struct uio
* uio
)
604 size_t namelen
= strlen(name
);
607 if (namelen
+ 1 <= NAME_MAX
) {
608 memset(&e
, 0, sizeof(e
));
613 e
.d_namlen
= namelen
; /* don't include NUL */
614 e
.d_reclen
= DIRENT_SZ(&e
);
615 if (uio_resid(uio
) >= e
.d_reclen
) {
616 strlcpy(e
.d_name
, name
, NAME_MAX
);
617 error
= uiomove((caddr_t
)&e
, e
.d_reclen
, uio
);
626 nullfs_special_readdir(struct vnop_readdir_args
* ap
)
628 struct vnode
* vp
= ap
->a_vp
;
629 struct uio
* uio
= ap
->a_uio
;
630 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(vp
));
631 off_t offset
= uio_offset(uio
);
635 const char * name
= NULL
;
637 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
)) {
643 if (vp
== null_mp
->nullm_rootvp
) {
645 } else { /* only get here if vp matches nullm_rootvp or nullm_secondvp */
646 ino
= NULL_SECOND_INO
;
648 error
= store_entry_special(ino
, ".", uio
);
657 /* only get here if vp matches nullm_rootvp or nullm_secondvp */
660 error
= store_entry_special(ino
, "..", uio
);
668 /* the directory case */
669 if (vp
== null_mp
->nullm_rootvp
) {
670 ino
= NULL_SECOND_INO
;
672 } else { /* only get here if vp matches nullm_rootvp or nullm_secondvp */
673 ino
= NULL_THIRD_INO
;
674 if (vnode_getwithvid(null_mp
->nullm_lowerrootvp
, null_mp
->nullm_lowerrootvid
)) {
675 /* In this case the lower file system has been ripped out from under us,
676 * but we don't want to error out
677 * Instead we just want d to look empty. */
681 name
= vnode_getname_printable(null_mp
->nullm_lowerrootvp
);
683 error
= store_entry_special(ino
, name
, uio
);
685 if (ino
== NULL_THIRD_INO
) {
686 vnode_putname_printable(name
);
687 vnode_put(null_mp
->nullm_lowerrootvp
);
698 if (error
== EMSGSIZE
) {
699 error
= 0; /* return success if we ran out of space, but we wanted to make
700 * sure that we didn't update offset and items incorrectly */
702 uio_setoffset(uio
, offset
);
703 if (ap
->a_numdirent
) {
704 *ap
->a_numdirent
= items
;
710 nullfs_readdir(struct vnop_readdir_args
* ap
)
712 struct vnode
*vp
, *lvp
;
714 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
716 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
717 /* assumption is that any vp that comes through here had to go through lookup
720 lck_mtx_lock(&null_mp
->nullm_lock
);
721 if (nullfs_isspecialvp(ap
->a_vp
)) {
722 error
= nullfs_special_readdir(ap
);
723 lck_mtx_unlock(&null_mp
->nullm_lock
);
726 lck_mtx_unlock(&null_mp
->nullm_lock
);
729 lvp
= NULLVPTOLOWERVP(vp
);
730 error
= vnode_getwithref(lvp
);
732 error
= VNOP_READDIR(lvp
, ap
->a_uio
, ap
->a_flags
, ap
->a_eofflag
, ap
->a_numdirent
, ap
->a_context
);
740 nullfs_readlink(struct vnop_readlink_args
* ap
)
742 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
744 struct vnode
*vp
, *lvp
;
746 if (nullfs_checkspecialvp(ap
->a_vp
)) {
747 return ENOTSUP
; /* the special vnodes aren't links */
751 lvp
= NULLVPTOLOWERVP(vp
);
753 error
= vnode_getwithref(lvp
);
755 error
= VNOP_READLINK(lvp
, ap
->a_uio
, ap
->a_context
);
759 NULLFSDEBUG("readlink failed: %d\n", error
);
767 nullfs_pathconf(__unused
struct vnop_pathconf_args
* args
)
769 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
774 nullfs_fsync(__unused
struct vnop_fsync_args
* args
)
776 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
781 nullfs_mmap(struct vnop_mmap_args
* args
)
784 struct vnode
*vp
, *lvp
;
786 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
788 if (nullfs_checkspecialvp(args
->a_vp
)) {
789 return 0; /* nothing extra needed */
793 lvp
= NULLVPTOLOWERVP(vp
);
794 error
= vnode_getwithref(lvp
);
796 error
= VNOP_MMAP(lvp
, args
->a_fflags
, args
->a_context
);
804 nullfs_mnomap(struct vnop_mnomap_args
* args
)
807 struct vnode
*vp
, *lvp
;
809 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
811 if (nullfs_checkspecialvp(args
->a_vp
)) {
812 return 0; /* nothing extra needed */
816 lvp
= NULLVPTOLOWERVP(vp
);
817 error
= vnode_getwithref(lvp
);
819 error
= VNOP_MNOMAP(lvp
, args
->a_context
);
827 nullfs_getxattr(struct vnop_getxattr_args
* args
)
830 struct vnode
*vp
, *lvp
;
832 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
834 if (nullfs_checkspecialvp(args
->a_vp
)) {
835 return ENOATTR
; /* no xattrs on the special vnodes */
839 lvp
= NULLVPTOLOWERVP(vp
);
840 error
= vnode_getwithref(lvp
);
842 error
= VNOP_GETXATTR(lvp
, args
->a_name
, args
->a_uio
, args
->a_size
, args
->a_options
, args
->a_context
);
850 nullfs_listxattr(struct vnop_listxattr_args
* args
)
853 struct vnode
*vp
, *lvp
;
855 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
857 if (nullfs_checkspecialvp(args
->a_vp
)) {
858 return 0; /* no xattrs on the special vnodes */
862 lvp
= NULLVPTOLOWERVP(vp
);
863 error
= vnode_getwithref(lvp
);
865 error
= VNOP_LISTXATTR(lvp
, args
->a_uio
, args
->a_size
, args
->a_options
, args
->a_context
);
872 /* relies on v1 paging */
874 nullfs_pagein(struct vnop_pagein_args
* ap
)
877 struct vnode
*vp
, *lvp
;
879 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
882 lvp
= NULLVPTOLOWERVP(vp
);
884 if (vnode_vtype(vp
) != VREG
) {
889 * Ask VM/UBC/VFS to do our bidding
891 if (vnode_getwithvid(lvp
, NULLVPTOLOWERVID(vp
)) == 0) {
895 off_t bytes_to_commit
;
897 upl_t upl
= ap
->a_pl
;
898 user_ssize_t bytes_remaining
= 0;
900 auio
= uio_create(1, ap
->a_f_offset
, UIO_SYSSPACE
, UIO_READ
);
906 kret
= ubc_upl_map(upl
, &ioaddr
);
907 if (KERN_SUCCESS
!= kret
) {
908 panic("nullfs_pagein: ubc_upl_map() failed with (%d)", kret
);
911 ioaddr
+= ap
->a_pl_offset
;
913 error
= uio_addiov(auio
, (user_addr_t
)ioaddr
, ap
->a_size
);
918 lowersize
= ubc_getsize(lvp
);
919 if (lowersize
!= ubc_getsize(vp
)) {
920 (void)ubc_setsize(vp
, lowersize
); /* ignore failures, nothing can be done */
923 error
= VNOP_READ(lvp
, auio
, ((ap
->a_flags
& UPL_IOSYNC
) ? IO_SYNC
: 0), ap
->a_context
);
925 bytes_remaining
= uio_resid(auio
);
926 if (bytes_remaining
> 0 && bytes_remaining
<= (user_ssize_t
)ap
->a_size
) {
927 /* zero bytes that weren't read in to the upl */
928 bzero((void*)((uintptr_t)(ioaddr
+ ap
->a_size
- bytes_remaining
)), (size_t) bytes_remaining
);
932 kret
= ubc_upl_unmap(upl
);
933 if (KERN_SUCCESS
!= kret
) {
934 panic("nullfs_pagein: ubc_upl_unmap() failed with (%d)", kret
);
942 if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
943 if (!error
&& (bytes_remaining
>= 0) && (bytes_remaining
<= (user_ssize_t
)ap
->a_size
)) {
944 /* only commit what was read in (page aligned)*/
945 bytes_to_commit
= ap
->a_size
- bytes_remaining
;
946 if (bytes_to_commit
) {
947 /* need to make sure bytes_to_commit and byte_remaining are page aligned before calling ubc_upl_commit_range*/
948 if (bytes_to_commit
& PAGE_MASK
) {
949 bytes_to_commit
= (bytes_to_commit
& (~PAGE_MASK
)) + (PAGE_MASK
+ 1);
950 assert(bytes_to_commit
<= (off_t
)ap
->a_size
);
952 bytes_remaining
= ap
->a_size
- bytes_to_commit
;
954 ubc_upl_commit_range(upl
, ap
->a_pl_offset
, (upl_size_t
)bytes_to_commit
, UPL_COMMIT_FREE_ON_EMPTY
);
957 /* abort anything thats left */
958 if (bytes_remaining
) {
959 ubc_upl_abort_range(upl
, ap
->a_pl_offset
+ bytes_to_commit
, (upl_size_t
)bytes_remaining
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
962 ubc_upl_abort_range(upl
, ap
->a_pl_offset
, (upl_size_t
)ap
->a_size
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
966 } else if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
967 ubc_upl_abort_range(ap
->a_pl
, ap
->a_pl_offset
, (upl_size_t
)ap
->a_size
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
973 nullfs_read(struct vnop_read_args
* ap
)
977 struct vnode
*vp
, *lvp
;
979 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
981 if (nullfs_checkspecialvp(ap
->a_vp
)) {
982 return ENOTSUP
; /* the special vnodes can't be read */
986 lvp
= NULLVPTOLOWERVP(vp
);
989 * First some house keeping
991 if (vnode_getwithvid(lvp
, NULLVPTOLOWERVID(vp
)) == 0) {
992 if (!vnode_isreg(lvp
) && !vnode_islnk(lvp
)) {
997 if (uio_resid(ap
->a_uio
) == 0) {
1003 * Now ask VM/UBC/VFS to do our bidding
1006 error
= VNOP_READ(lvp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
1008 NULLFSDEBUG("VNOP_READ failed: %d\n", error
);
1017 * Global vfs data structures
1020 static const struct vnodeopv_entry_desc nullfs_vnodeop_entries
[] = {
1021 {.opve_op
= &vnop_default_desc
, .opve_impl
= (vop_t
)nullfs_default
}, {.opve_op
= &vnop_getattr_desc
, .opve_impl
= (vop_t
)nullfs_getattr
},
1022 {.opve_op
= &vnop_open_desc
, .opve_impl
= (vop_t
)nullfs_open
}, {.opve_op
= &vnop_close_desc
, .opve_impl
= (vop_t
)nullfs_close
},
1023 {.opve_op
= &vnop_inactive_desc
, .opve_impl
= (vop_t
)null_inactive
}, {.opve_op
= &vnop_reclaim_desc
, .opve_impl
= (vop_t
)null_reclaim
},
1024 {.opve_op
= &vnop_lookup_desc
, .opve_impl
= (vop_t
)null_lookup
}, {.opve_op
= &vnop_readdir_desc
, .opve_impl
= (vop_t
)nullfs_readdir
},
1025 {.opve_op
= &vnop_readlink_desc
, .opve_impl
= (vop_t
)nullfs_readlink
}, {.opve_op
= &vnop_pathconf_desc
, .opve_impl
= (vop_t
)nullfs_pathconf
},
1026 {.opve_op
= &vnop_fsync_desc
, .opve_impl
= (vop_t
)nullfs_fsync
}, {.opve_op
= &vnop_mmap_desc
, .opve_impl
= (vop_t
)nullfs_mmap
},
1027 {.opve_op
= &vnop_mnomap_desc
, .opve_impl
= (vop_t
)nullfs_mnomap
}, {.opve_op
= &vnop_getxattr_desc
, .opve_impl
= (vop_t
)nullfs_getxattr
},
1028 {.opve_op
= &vnop_pagein_desc
, .opve_impl
= (vop_t
)nullfs_pagein
}, {.opve_op
= &vnop_read_desc
, .opve_impl
= (vop_t
)nullfs_read
},
1029 {.opve_op
= &vnop_listxattr_desc
, .opve_impl
= (vop_t
)nullfs_listxattr
}, {.opve_op
= NULL
, .opve_impl
= NULL
},
1032 const struct vnodeopv_desc nullfs_vnodeop_opv_desc
= {.opv_desc_vector_p
= &nullfs_vnodeop_p
, .opv_desc_ops
= nullfs_vnodeop_entries
};
1034 //NULLFS Specific helper function
1037 nullfs_getbackingvnode(vnode_t in_vp
, vnode_t
* out_vpp
)
1039 int result
= EINVAL
;
1041 if (out_vpp
== NULL
|| in_vp
== NULL
) {
1045 struct vfsstatfs
* sp
= NULL
;
1046 mount_t mp
= vnode_mount(in_vp
);
1048 sp
= vfs_statfs(mp
);
1049 //If this isn't a nullfs vnode or it is but it's a special vnode
1050 if (strcmp(sp
->f_fstypename
, "nullfs") != 0 || nullfs_checkspecialvp(in_vp
)) {
1056 vnode_t lvp
= NULLVPTOLOWERVP(in_vp
);
1057 if ((result
= vnode_getwithvid(lvp
, NULLVPTOLOWERVID(in_vp
)))) {