]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/nullfs/null_vnops.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / bsd / miscfs / nullfs / null_vnops.c
1 /*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*-
25 * Portions Copyright (c) 1992, 1993
26 * The Regents of the University of California. All rights reserved.
27 *
28 * This code is derived from software contributed to Berkeley by
29 * John Heidemann of the UCLA Ficus project.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
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.
42 *
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
53 * SUCH DAMAGE.
54 *
55 * @(#)null_vnops.c 8.6 (Berkeley) 5/27/95
56 *
57 * Ancestors:
58 * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
59 * ...and...
60 * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
61 *
62 * $FreeBSD$
63 */
64
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/conf.h>
68 #include <sys/kernel.h>
69 #include <sys/lock.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>
77 #include <sys/ubc.h>
78 #include <sys/types.h>
79 #include <sys/dirent.h>
80
81 #include "nullfs.h"
82
83 #define NULL_ROOT_INO 2
84 #define NULL_SECOND_INO 3
85 #define NULL_THIRD_INO 4
86
87 vop_t * nullfs_vnodeop_p = NULL;
88
89 /* the mountpoint lock should be held going into this function */
90 static int
91 nullfs_isspecialvp(struct vnode * vp)
92 {
93 struct null_mount * null_mp;
94
95 null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
96
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)) {
100 return 1;
101 }
102 return 0;
103 }
104
105 /* helper function to handle locking where possible */
106 static int
107 nullfs_checkspecialvp(struct vnode* vp)
108 {
109 int result = 0;
110 struct null_mount * null_mp;
111
112 null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
113
114 lck_mtx_lock(&null_mp->nullm_lock);
115 result = (nullfs_isspecialvp(vp));
116 lck_mtx_unlock(&null_mp->nullm_lock);
117
118 return result;
119 }
120
121 static int
122 nullfs_default(__unused struct vnop_generic_args * args)
123 {
124 NULLFSDEBUG("%s (default)\n", ((struct vnodeop_desc_fake *)args->a_desc)->vdesc_name);
125 return ENOTSUP;
126 }
127
128 static int
129 nullfs_special_getattr(struct vnop_getattr_args * args)
130 {
131 mount_t mp = vnode_mount(args->a_vp);
132 struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
133
134 ino_t ino = NULL_ROOT_INO;
135 struct vnode_attr covered_rootattr;
136 vnode_t checkvp = null_mp->nullm_lowerrootvp;
137
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);
144
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) {
150 return EIO;
151 }
152 }
153
154 int error = vnode_getattr(checkvp, &covered_rootattr, args->a_context);
155
156 vnode_put(checkvp);
157 if (error) {
158 /* we should have been able to get attributes fore one of the two choices so
159 * fail if we didn't */
160 return error;
161 }
162
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;
166 }
167
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
172
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 */
183
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
189 * 1.*/
190 }
191
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);
195 }
196 if (VATTR_IS_ACTIVE(args->a_vap, va_uid)) {
197 VATTR_RETURN(args->a_vap, va_uid, covered_rootattr.va_uid);
198 }
199 if (VATTR_IS_ACTIVE(args->a_vap, va_gid)) {
200 VATTR_RETURN(args->a_vap, va_gid, covered_rootattr.va_gid);
201 }
202
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;
207 }
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;
212 }
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;
217 }
218
219 return 0;
220 }
221
222 static int
223 nullfs_getattr(struct vnop_getattr_args * args)
224 {
225 int error;
226 struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(args->a_vp));
227 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
228
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);
233 return error;
234 }
235 lck_mtx_unlock(&null_mp->nullm_lock);
236
237 /* this will return a different inode for third than read dir will */
238 struct vnode * lowervp = NULLVPTOLOWERVP(args->a_vp);
239
240 error = vnode_getwithref(lowervp);
241 if (error == 0) {
242 error = VNOP_GETATTR(lowervp, args->a_vap, args->a_context);
243 vnode_put(lowervp);
244
245 if (error == 0) {
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]);
248 }
249 }
250
251 return error;
252 }
253
254 static int
255 nullfs_open(struct vnop_open_args * args)
256 {
257 int error;
258 struct vnode *vp, *lvp;
259
260 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
261
262 if (nullfs_checkspecialvp(args->a_vp)) {
263 return 0; /* nothing extra needed */
264 }
265
266 vp = args->a_vp;
267 lvp = NULLVPTOLOWERVP(vp);
268 error = vnode_getwithref(lvp);
269 if (error == 0) {
270 error = VNOP_OPEN(lvp, args->a_mode, args->a_context);
271 vnode_put(lvp);
272 }
273
274 return error;
275 }
276
277 static int
278 nullfs_close(struct vnop_close_args * args)
279 {
280 int error;
281 struct vnode *vp, *lvp;
282
283 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
284
285 if (nullfs_checkspecialvp(args->a_vp)) {
286 return 0; /* nothing extra needed */
287 }
288
289 vp = args->a_vp;
290 lvp = NULLVPTOLOWERVP(vp);
291
292 error = vnode_getwithref(lvp);
293 if (error == 0) {
294 error = VNOP_CLOSE(lvp, args->a_fflag, args->a_context);
295 vnode_put(lvp);
296 }
297 return error;
298 }
299
300 /* get lvp's parent, if possible, even if it isn't set.
301 *
302 * lvp is expected to have an iocount before and after this call.
303 *
304 * if a dvpp is populated the returned vnode has an iocount. */
305 static int
306 null_get_lowerparent(vnode_t lvp, vnode_t * dvpp, vfs_context_t ctx)
307 {
308 int error = 0;
309 struct vnode_attr va;
310 mount_t mp = vnode_mount(lvp);
311 vnode_t dvp = vnode_parent(lvp);
312
313 if (dvp) {
314 error = vnode_get(dvp);
315 goto end;
316 }
317
318 error = ENOENT;
319 if (!(mp->mnt_kern_flag & MNTK_PATH_FROM_ID)) {
320 goto end;
321 }
322
323 VATTR_INIT(&va);
324 VATTR_WANTED(&va, va_parentid);
325
326 error = vnode_getattr(lvp, &va, ctx);
327
328 if (error || !VATTR_IS_SUPPORTED(&va, va_parentid)) {
329 goto end;
330 }
331
332 error = VFS_VGET(mp, (ino64_t)va.va_parentid, &dvp, ctx);
333
334 end:
335 if (error == 0) {
336 *dvpp = dvp;
337 }
338 return error;
339 }
340
341 /* the mountpoint lock should be held going into this function */
342 static int
343 null_special_lookup(struct vnop_lookup_args * ap)
344 {
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);
352 int error = ENOENT;
353
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 */
359 vp = dvp;
360 error = vnode_get(vp);
361 goto end;
362 }
363 }
364
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))) {
369 error = 0;
370 if (null_mp->nullm_secondvp == NULL) {
371 error = null_getnewvnode(mp, NULL, dvp, &vp, cnp, 0);
372 if (error) {
373 goto end;
374 }
375
376 null_mp->nullm_secondvp = vp;
377 } else {
378 vp = null_mp->nullm_secondvp;
379 error = vnode_get(vp);
380 }
381 }
382 } else if (dvp == null_mp->nullm_secondvp) {
383 /* handle . and .. */
384 if (cnp->cn_nameptr[0] == '.') {
385 if (cnp->cn_namelen == 1) {
386 vp = dvp;
387 error = vnode_get(vp);
388 goto end;
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);
393 goto end;
394 }
395 }
396 /* nullmp->nullm_lowerrootvp was set at mount time so don't need to lock to
397 * access it */
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);
401 if (error) {
402 goto end;
403 }
404
405 /* We don't want to mess with case insensitivity and unicode, so the plan to
406 * check here is
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
411 * so we got a match
412 * 4. Anything else results in ENOENT.
413 */
414 error = null_get_lowerparent(null_mp->nullm_lowerrootvp, &ldvp, ap->a_context);
415
416 if (error == 0) {
417 error = VNOP_LOOKUP(ldvp, &lvp, cnp, ap->a_context);
418 vnode_put(ldvp);
419
420 if (error == 0) {
421 if (lvp == null_mp->nullm_lowerrootvp) {
422 /* always check the hashmap for a vnode for this, the root of the
423 * mirrored system */
424 error = null_nodeget(mp, lvp, dvp, &vp, cnp, 0);
425
426 if (error == 0 && null_mp->nullm_thirdcovervp == NULL) {
427 /* if nodeget succeeded then vp has an iocount*/
428 null_mp->nullm_thirdcovervp = vp;
429 }
430 } else {
431 error = ENOENT;
432 }
433 vnode_put(lvp);
434 }
435 }
436 vnode_put(null_mp->nullm_lowerrootvp);
437 }
438
439 end:
440 if (error == 0) {
441 *ap->a_vpp = vp;
442 }
443 return error;
444 }
445
446 /*
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.
450 */
451 static int
452 null_lookup(struct vnop_lookup_args * ap)
453 {
454 struct componentname * cnp = ap->a_cnp;
455 struct vnode * dvp = ap->a_dvp;
456 struct vnode *vp, *ldvp, *lvp;
457 struct mount * mp;
458 struct null_mount * null_mp;
459 int error;
460
461 NULLFSDEBUG("%s parent: %p component: %.*s\n", __FUNCTION__, ap->a_dvp, cnp->cn_namelen, cnp->cn_nameptr);
462
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) {
466 return EROFS;
467 }
468 null_mp = MOUNTTONULLMOUNT(mp);
469
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);
474 return error;
475 }
476 lck_mtx_unlock(&null_mp->nullm_lock);
477
478 // . and .. handling
479 if (cnp->cn_nameptr[0] == '.') {
480 if (cnp->cn_namelen == 1) {
481 vp = dvp;
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);
485 } else {
486 goto notdot;
487 }
488
489 error = vp ? vnode_get(vp) : ENOENT;
490
491 if (error == 0) {
492 *ap->a_vpp = vp;
493 }
494
495 return error;
496 }
497
498 notdot:
499 ldvp = NULLVPTOLOWERVP(dvp);
500 vp = lvp = NULL;
501
502 /*
503 * Hold ldvp. The reference on it, owned by dvp, is lost in
504 * case of dvp reclamation.
505 */
506 error = vnode_getwithref(ldvp);
507 if (error) {
508 return error;
509 }
510
511 error = VNOP_LOOKUP(ldvp, &lvp, cnp, ap->a_context);
512
513 vnode_put(ldvp);
514
515 if ((error == 0 || error == EJUSTRETURN) && lvp != NULL) {
516 if (ldvp == lvp) {
517 vp = dvp;
518 error = vnode_get(vp);
519 } else {
520 error = null_nodeget(mp, lvp, dvp, &vp, cnp, 0);
521 }
522 if (error == 0) {
523 *ap->a_vpp = vp;
524 }
525 }
526
527 /* if we got lvp, drop the iocount from VNOP_LOOKUP */
528 if (lvp != NULL) {
529 vnode_put(lvp);
530 }
531
532 return error;
533 }
534
535 /*
536 * Don't think this needs to do anything
537 */
538 static int
539 null_inactive(__unused struct vnop_inactive_args * ap)
540 {
541 NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
542
543 return 0;
544 }
545
546 static int
547 null_reclaim(struct vnop_reclaim_args * ap)
548 {
549 struct vnode * vp;
550 struct null_node * xp;
551 struct vnode * lowervp;
552 struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(ap->a_vp));
553
554 NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
555
556 vp = ap->a_vp;
557
558 xp = VTONULL(vp);
559 lowervp = xp->null_lowervp;
560
561 lck_mtx_lock(&null_mp->nullm_lock);
562
563 vnode_removefsref(vp);
564
565 if (lowervp != NULL) {
566 /* root and second don't have a lowervp, so nothing to release and nothing
567 * got hashed */
568 if (xp->null_flags & NULL_FLAG_HASHED) {
569 /* only call this if we actually made it into the hash list. reclaim gets
570 * called also to
571 * clean up a vnode that got created when it didn't need to under race
572 * conditions */
573 null_hashrem(xp);
574 }
575 vnode_getwithref(lowervp);
576 vnode_rele(lowervp);
577 vnode_put(lowervp);
578 }
579
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;
586 }
587
588 lck_mtx_unlock(&null_mp->nullm_lock);
589
590 cache_purge(vp);
591 vnode_clearfsnode(vp);
592
593 FREE(xp, M_TEMP);
594
595 return 0;
596 }
597
598 #define DIRENT_SZ(dp) ((sizeof(struct dirent) - NAME_MAX) + (((dp)->d_namlen + 1 + 3) & ~3))
599
600 static int
601 store_entry_special(ino_t ino, const char * name, struct uio * uio)
602 {
603 struct dirent e;
604 size_t namelen = strlen(name);
605 int error = EINVAL;
606
607 if (namelen + 1 <= NAME_MAX) {
608 memset(&e, 0, sizeof(e));
609
610 e.d_ino = ino;
611 e.d_type = DT_DIR;
612
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);
618 } else {
619 error = EMSGSIZE;
620 }
621 }
622 return error;
623 }
624
625 static int
626 nullfs_special_readdir(struct vnop_readdir_args * ap)
627 {
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);
632 int error = ERANGE;
633 int items = 0;
634 ino_t ino = 0;
635 const char * name = NULL;
636
637 if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) {
638 return EINVAL;
639 }
640
641 if (offset == 0) {
642 /* . case */
643 if (vp == null_mp->nullm_rootvp) {
644 ino = NULL_ROOT_INO;
645 } else { /* only get here if vp matches nullm_rootvp or nullm_secondvp */
646 ino = NULL_SECOND_INO;
647 }
648 error = store_entry_special(ino, ".", uio);
649 if (error) {
650 goto out;
651 }
652 offset++;
653 items++;
654 }
655 if (offset == 1) {
656 /* .. case */
657 /* only get here if vp matches nullm_rootvp or nullm_secondvp */
658 ino = NULL_ROOT_INO;
659
660 error = store_entry_special(ino, "..", uio);
661 if (error) {
662 goto out;
663 }
664 offset++;
665 items++;
666 }
667 if (offset == 2) {
668 /* the directory case */
669 if (vp == null_mp->nullm_rootvp) {
670 ino = NULL_SECOND_INO;
671 name = "d";
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. */
678 error = 0;
679 goto out;
680 }
681 name = vnode_getname_printable(null_mp->nullm_lowerrootvp);
682 }
683 error = store_entry_special(ino, name, uio);
684
685 if (ino == NULL_THIRD_INO) {
686 vnode_putname_printable(name);
687 vnode_put(null_mp->nullm_lowerrootvp);
688 }
689
690 if (error) {
691 goto out;
692 }
693 offset++;
694 items++;
695 }
696
697 out:
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 */
701 }
702 uio_setoffset(uio, offset);
703 if (ap->a_numdirent) {
704 *ap->a_numdirent = items;
705 }
706 return error;
707 }
708
709 static int
710 nullfs_readdir(struct vnop_readdir_args * ap)
711 {
712 struct vnode *vp, *lvp;
713 int error;
714 struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(ap->a_vp));
715
716 NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
717 /* assumption is that any vp that comes through here had to go through lookup
718 */
719
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);
724 return error;
725 }
726 lck_mtx_unlock(&null_mp->nullm_lock);
727
728 vp = ap->a_vp;
729 lvp = NULLVPTOLOWERVP(vp);
730 error = vnode_getwithref(lvp);
731 if (error == 0) {
732 error = VNOP_READDIR(lvp, ap->a_uio, ap->a_flags, ap->a_eofflag, ap->a_numdirent, ap->a_context);
733 vnode_put(lvp);
734 }
735
736 return error;
737 }
738
739 static int
740 nullfs_readlink(struct vnop_readlink_args * ap)
741 {
742 NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
743 int error;
744 struct vnode *vp, *lvp;
745
746 if (nullfs_checkspecialvp(ap->a_vp)) {
747 return ENOTSUP; /* the special vnodes aren't links */
748 }
749
750 vp = ap->a_vp;
751 lvp = NULLVPTOLOWERVP(vp);
752
753 error = vnode_getwithref(lvp);
754 if (error == 0) {
755 error = VNOP_READLINK(lvp, ap->a_uio, ap->a_context);
756 vnode_put(lvp);
757
758 if (error) {
759 NULLFSDEBUG("readlink failed: %d\n", error);
760 }
761 }
762
763 return error;
764 }
765
766 static int
767 nullfs_pathconf(__unused struct vnop_pathconf_args * args)
768 {
769 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
770 return EINVAL;
771 }
772
773 static int
774 nullfs_fsync(__unused struct vnop_fsync_args * args)
775 {
776 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
777 return 0;
778 }
779
780 static int
781 nullfs_mmap(struct vnop_mmap_args * args)
782 {
783 int error;
784 struct vnode *vp, *lvp;
785
786 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
787
788 if (nullfs_checkspecialvp(args->a_vp)) {
789 return 0; /* nothing extra needed */
790 }
791
792 vp = args->a_vp;
793 lvp = NULLVPTOLOWERVP(vp);
794 error = vnode_getwithref(lvp);
795 if (error == 0) {
796 error = VNOP_MMAP(lvp, args->a_fflags, args->a_context);
797 vnode_put(lvp);
798 }
799
800 return error;
801 }
802
803 static int
804 nullfs_mnomap(struct vnop_mnomap_args * args)
805 {
806 int error;
807 struct vnode *vp, *lvp;
808
809 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
810
811 if (nullfs_checkspecialvp(args->a_vp)) {
812 return 0; /* nothing extra needed */
813 }
814
815 vp = args->a_vp;
816 lvp = NULLVPTOLOWERVP(vp);
817 error = vnode_getwithref(lvp);
818 if (error == 0) {
819 error = VNOP_MNOMAP(lvp, args->a_context);
820 vnode_put(lvp);
821 }
822
823 return error;
824 }
825
826 static int
827 nullfs_getxattr(struct vnop_getxattr_args * args)
828 {
829 int error;
830 struct vnode *vp, *lvp;
831
832 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
833
834 if (nullfs_checkspecialvp(args->a_vp)) {
835 return ENOATTR; /* no xattrs on the special vnodes */
836 }
837
838 vp = args->a_vp;
839 lvp = NULLVPTOLOWERVP(vp);
840 error = vnode_getwithref(lvp);
841 if (error == 0) {
842 error = VNOP_GETXATTR(lvp, args->a_name, args->a_uio, args->a_size, args->a_options, args->a_context);
843 vnode_put(lvp);
844 }
845
846 return error;
847 }
848
849 static int
850 nullfs_listxattr(struct vnop_listxattr_args * args)
851 {
852 int error;
853 struct vnode *vp, *lvp;
854
855 NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
856
857 if (nullfs_checkspecialvp(args->a_vp)) {
858 return 0; /* no xattrs on the special vnodes */
859 }
860
861 vp = args->a_vp;
862 lvp = NULLVPTOLOWERVP(vp);
863 error = vnode_getwithref(lvp);
864 if (error == 0) {
865 error = VNOP_LISTXATTR(lvp, args->a_uio, args->a_size, args->a_options, args->a_context);
866 vnode_put(lvp);
867 }
868
869 return error;
870 }
871
872 /* relies on v1 paging */
873 static int
874 nullfs_pagein(struct vnop_pagein_args * ap)
875 {
876 int error = EIO;
877 struct vnode *vp, *lvp;
878
879 NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
880
881 vp = ap->a_vp;
882 lvp = NULLVPTOLOWERVP(vp);
883
884 if (vnode_vtype(vp) != VREG) {
885 return ENOTSUP;
886 }
887
888 /*
889 * Ask VM/UBC/VFS to do our bidding
890 */
891 if (vnode_getwithvid(lvp, NULLVPTOLOWERVID(vp)) == 0) {
892 vm_offset_t ioaddr;
893 uio_t auio;
894 kern_return_t kret;
895 off_t bytes_to_commit;
896 off_t lowersize;
897 upl_t upl = ap->a_pl;
898 user_ssize_t bytes_remaining = 0;
899
900 auio = uio_create(1, ap->a_f_offset, UIO_SYSSPACE, UIO_READ);
901 if (auio == NULL) {
902 error = EIO;
903 goto exit_no_unmap;
904 }
905
906 kret = ubc_upl_map(upl, &ioaddr);
907 if (KERN_SUCCESS != kret) {
908 panic("nullfs_pagein: ubc_upl_map() failed with (%d)", kret);
909 }
910
911 ioaddr += ap->a_pl_offset;
912
913 error = uio_addiov(auio, (user_addr_t)ioaddr, ap->a_size);
914 if (error) {
915 goto exit;
916 }
917
918 lowersize = ubc_getsize(lvp);
919 if (lowersize != ubc_getsize(vp)) {
920 (void)ubc_setsize(vp, lowersize); /* ignore failures, nothing can be done */
921 }
922
923 error = VNOP_READ(lvp, auio, ((ap->a_flags & UPL_IOSYNC) ? IO_SYNC : 0), ap->a_context);
924
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);
929 }
930
931 exit:
932 kret = ubc_upl_unmap(upl);
933 if (KERN_SUCCESS != kret) {
934 panic("nullfs_pagein: ubc_upl_unmap() failed with (%d)", kret);
935 }
936
937 if (auio != NULL) {
938 uio_free(auio);
939 }
940
941 exit_no_unmap:
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);
951
952 bytes_remaining = ap->a_size - bytes_to_commit;
953 }
954 ubc_upl_commit_range(upl, ap->a_pl_offset, (upl_size_t)bytes_to_commit, UPL_COMMIT_FREE_ON_EMPTY);
955 }
956
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);
960 }
961 } else {
962 ubc_upl_abort_range(upl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
963 }
964 }
965 vnode_put(lvp);
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);
968 }
969 return error;
970 }
971
972 static int
973 nullfs_read(struct vnop_read_args * ap)
974 {
975 int error = EIO;
976
977 struct vnode *vp, *lvp;
978
979 NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
980
981 if (nullfs_checkspecialvp(ap->a_vp)) {
982 return ENOTSUP; /* the special vnodes can't be read */
983 }
984
985 vp = ap->a_vp;
986 lvp = NULLVPTOLOWERVP(vp);
987
988 /*
989 * First some house keeping
990 */
991 if (vnode_getwithvid(lvp, NULLVPTOLOWERVID(vp)) == 0) {
992 if (!vnode_isreg(lvp) && !vnode_islnk(lvp)) {
993 error = EPERM;
994 goto end;
995 }
996
997 if (uio_resid(ap->a_uio) == 0) {
998 error = 0;
999 goto end;
1000 }
1001
1002 /*
1003 * Now ask VM/UBC/VFS to do our bidding
1004 */
1005
1006 error = VNOP_READ(lvp, ap->a_uio, ap->a_ioflag, ap->a_context);
1007 if (error) {
1008 NULLFSDEBUG("VNOP_READ failed: %d\n", error);
1009 }
1010 end:
1011 vnode_put(lvp);
1012 }
1013 return error;
1014 }
1015
1016 /*
1017 * Global vfs data structures
1018 */
1019
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},
1030 };
1031
1032 const struct vnodeopv_desc nullfs_vnodeop_opv_desc = {.opv_desc_vector_p = &nullfs_vnodeop_p, .opv_desc_ops = nullfs_vnodeop_entries};
1033
1034 //NULLFS Specific helper function
1035
1036 int
1037 nullfs_getbackingvnode(vnode_t in_vp, vnode_t* out_vpp)
1038 {
1039 int result = EINVAL;
1040
1041 if (out_vpp == NULL || in_vp == NULL) {
1042 goto end;
1043 }
1044
1045 struct vfsstatfs * sp = NULL;
1046 mount_t mp = vnode_mount(in_vp);
1047
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)) {
1051 *out_vpp = NULLVP;
1052 result = ENOENT;
1053 goto end;
1054 }
1055
1056 vnode_t lvp = NULLVPTOLOWERVP(in_vp);
1057 if ((result = vnode_getwithvid(lvp, NULLVPTOLOWERVID(in_vp)))) {
1058 goto end;
1059 }
1060
1061 *out_vpp = lvp;
1062
1063 end:
1064 return result;
1065 }