]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/nullfs/null_vfsops.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / bsd / miscfs / nullfs / null_vfsops.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, 1995
26 * The Regents of the University of California. All rights reserved.
27 *
28 * This code is derived from software donated to Berkeley by
29 * Jan-Simon Pendry.
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_vfsops.c 8.2 (Berkeley) 1/21/94
56 *
57 * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
58 * $FreeBSD$
59 */
60
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/fcntl.h>
64 #include <sys/kernel.h>
65 #include <sys/lock.h>
66 #include <sys/malloc.h>
67 #include <sys/mount.h>
68 #include <sys/namei.h>
69 #include <sys/proc.h>
70 #include <sys/vnode.h>
71 #include <sys/vnode_internal.h>
72 #include <security/mac_internal.h>
73
74 #include <sys/param.h>
75
76 #include <IOKit/IOBSD.h>
77
78 #include "nullfs.h"
79
80 #define NULLFS_ENTITLEMENT "com.apple.private.nullfs_allow"
81
82 #define SIZEOF_MEMBER(type, member) (sizeof(((type *)0)->member))
83 #define MAX_MNT_FROM_LENGTH (SIZEOF_MEMBER(struct vfsstatfs, f_mntfromname))
84
85 static int
86 nullfs_vfs_getlowerattr(mount_t mp, struct vfs_attr * vfap, vfs_context_t ctx)
87 {
88 memset(vfap, 0, sizeof(*vfap));
89 VFSATTR_INIT(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);
99
100 return vfs_getattr(mp, vfap, ctx);
101 }
102
103 /*
104 * Mount null layer
105 */
106 static int
107 nullfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, vfs_context_t ctx)
108 {
109 int error = 0;
110 struct vnode *lowerrootvp = NULL, *vp = NULL;
111 struct vfsstatfs * sp = NULL;
112 struct null_mount * xmp = NULL;
113 char data[MAXPATHLEN];
114 size_t count;
115 struct vfs_attr vfa;
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;
120 uint64_t bfree = 0;
121 uint64_t bavail = 0;
122 uint64_t bused = 4711;
123 uint64_t files = 4711;
124 uint64_t ffree = 0;
125
126 kauth_cred_t cred = vfs_context_ucred(ctx);
127
128 NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp, vfs_flags(mp));
129
130 if (vfs_flags(mp) & MNT_ROOTFS) {
131 return EOPNOTSUPP;
132 }
133
134 /*
135 * Update is a no-op
136 */
137 if (vfs_isupdate(mp)) {
138 return ENOTSUP;
139 }
140
141 /* check entitlement */
142 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) {
143 return EPERM;
144 }
145
146 /*
147 * Get argument
148 */
149 error = copyinstr(user_data, data, MAXPATHLEN - 1, &count);
150 if (error) {
151 NULLFSDEBUG("nullfs: error copying data form user %d\n", error);
152 goto error;
153 }
154
155 /* This could happen if the system is configured for 32 bit inodes instead of
156 * 64 bit */
157 if (count > MAX_MNT_FROM_LENGTH) {
158 error = EINVAL;
159 NULLFSDEBUG("nullfs: path to translocate too large for this system %d vs %d\n", count, MAX_MNT_FROM_LENGTH);
160 goto error;
161 }
162
163 error = vnode_lookup(data, 0, &lowerrootvp, ctx);
164 if (error) {
165 NULLFSDEBUG("lookup %s -> %d\n", data, error);
166 goto error;
167 }
168
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);
174 if (error) {
175 // If vnode_ref failed, then null it out so it can't be used anymore in cleanup.
176 lowerrootvp = NULL;
177 goto error;
178 }
179
180 NULLFSDEBUG("mount %s\n", data);
181
182 MALLOC(xmp, struct null_mount *, sizeof(*xmp), M_TEMP, M_WAITOK | M_ZERO);
183 if (xmp == NULL) {
184 error = ENOMEM;
185 goto error;
186 }
187
188 /*
189 * Save reference to underlying FS
190 */
191 xmp->nullm_lowerrootvp = lowerrootvp;
192 xmp->nullm_lowerrootvid = vnode_vid(lowerrootvp);
193
194 error = null_getnewvnode(mp, NULL, NULL, &vp, NULL, 1);
195 if (error) {
196 goto error;
197 }
198
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
201 *
202 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
203 vnode_ref(vp);
204 vnode_put(vp);
205
206 error = nullfs_init_lck(&xmp->nullm_lock);
207 if (error) {
208 goto error;
209 }
210
211 xmp->nullm_rootvp = vp;
212
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);
217
218 if (lowerflags) {
219 flags |= lowerflags;
220 }
221
222 /* force these flags */
223 flags |= (MNT_DONTBROWSE | MNT_MULTILABEL | MNT_NOSUID | MNT_RDONLY);
224 vfs_setflags(mp, flags);
225
226 vfs_setfsprivate(mp, xmp);
227 vfs_getnewfsid(mp);
228 vfs_setlocklocal(mp);
229
230 /* fill in the stat block */
231 sp = vfs_statfs(mp);
232 strlcpy(sp->f_mntfromname, data, MAX_MNT_FROM_LENGTH);
233
234 sp->f_flags = flags;
235
236 xmp->nullm_flags = NULLM_CASEINSENSITIVE; /* default to case insensitive */
237
238 error = nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx);
239 if (error == 0) {
240 if (VFSATTR_IS_SUPPORTED(&vfa, f_bsize)) {
241 bsize = vfa.f_bsize;
242 }
243 if (VFSATTR_IS_SUPPORTED(&vfa, f_iosize)) {
244 iosize = vfa.f_iosize;
245 }
246 if (VFSATTR_IS_SUPPORTED(&vfa, f_blocks)) {
247 blocks = vfa.f_blocks;
248 }
249 if (VFSATTR_IS_SUPPORTED(&vfa, f_bfree)) {
250 bfree = vfa.f_bfree;
251 }
252 if (VFSATTR_IS_SUPPORTED(&vfa, f_bavail)) {
253 bavail = vfa.f_bavail;
254 }
255 if (VFSATTR_IS_SUPPORTED(&vfa, f_bused)) {
256 bused = vfa.f_bused;
257 }
258 if (VFSATTR_IS_SUPPORTED(&vfa, f_files)) {
259 files = vfa.f_files;
260 }
261 if (VFSATTR_IS_SUPPORTED(&vfa, f_ffree)) {
262 ffree = vfa.f_ffree;
263 }
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;
268 }
269 }
270 } else {
271 goto error;
272 }
273
274 sp->f_bsize = bsize;
275 sp->f_iosize = iosize;
276 sp->f_blocks = blocks;
277 sp->f_bfree = bfree;
278 sp->f_bavail = bavail;
279 sp->f_bused = bused;
280 sp->f_files = files;
281 sp->f_ffree = ffree;
282
283 /* Associate the mac label information from the mirrored filesystem with the
284 * mirror */
285 MAC_PERFORM(mount_label_associate, cred, vnode_mount(lowerrootvp), vfs_mntlabel(mp));
286
287 NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp->f_mntfromname, sp->f_mntonname);
288 return 0;
289
290 error:
291 if (xmp) {
292 FREE(xmp, M_TEMP);
293 }
294 if (lowerrootvp) {
295 vnode_getwithref(lowerrootvp);
296 vnode_rele(lowerrootvp);
297 vnode_put(lowerrootvp);
298 }
299 if (vp) {
300 /* we made the root vnode but the mount is failed, so clean it up */
301 vnode_getwithref(vp);
302 vnode_rele(vp);
303 /* give vp back */
304 vnode_recycle(vp);
305 vnode_put(vp);
306 }
307 return error;
308 }
309
310 /*
311 * Free reference to null layer
312 */
313 static int
314 nullfs_unmount(struct mount * mp, int mntflags, __unused vfs_context_t ctx)
315 {
316 struct null_mount * mntdata;
317 struct vnode * vp;
318 int error, flags;
319
320 NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
321
322 /* check entitlement or superuser*/
323 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT) &&
324 vfs_context_suser(ctx) != 0) {
325 return EPERM;
326 }
327
328 if (mntflags & MNT_FORCE) {
329 flags = FORCECLOSE;
330 } else {
331 flags = 0;
332 }
333
334 mntdata = MOUNTTONULLMOUNT(mp);
335 vp = mntdata->nullm_rootvp;
336
337 // release our reference on the root before flushing.
338 // it will get pulled out of the mount structure by reclaim
339 vnode_getalways(vp);
340
341 error = vflush(mp, vp, flags);
342 if (error) {
343 vnode_put(vp);
344 return error;
345 }
346
347 if (vnode_isinuse(vp, 1) && flags == 0) {
348 vnode_put(vp);
349 return EBUSY;
350 }
351
352 vnode_rele(vp); // Drop reference taken by nullfs_mount
353 vnode_put(vp); // Drop ref taken above
354
355 //Force close to get rid of the last vnode
356 (void)vflush(mp, NULL, FORCECLOSE);
357
358 /* no more vnodes, so tear down the mountpoint */
359
360 lck_mtx_lock(&mntdata->nullm_lock);
361
362 vfs_setfsprivate(mp, NULL);
363
364 vnode_getalways(mntdata->nullm_lowerrootvp);
365 vnode_rele(mntdata->nullm_lowerrootvp);
366 vnode_put(mntdata->nullm_lowerrootvp);
367
368 lck_mtx_unlock(&mntdata->nullm_lock);
369
370 nullfs_destroy_lck(&mntdata->nullm_lock);
371
372 FREE(mntdata, M_TEMP);
373
374 uint64_t vflags = vfs_flags(mp);
375 vfs_setflags(mp, vflags & ~MNT_LOCAL);
376
377 return 0;
378 }
379
380 static int
381 nullfs_root(struct mount * mp, struct vnode ** vpp, __unused vfs_context_t ctx)
382 {
383 struct vnode * vp;
384 int error;
385
386 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp);
387
388 /*
389 * Return locked reference to root.
390 */
391 vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
392
393 error = vnode_get(vp);
394 if (error) {
395 return error;
396 }
397
398 *vpp = vp;
399 return 0;
400 }
401
402 static int
403 nullfs_vfs_getattr(struct mount * mp, struct vfs_attr * vfap, vfs_context_t ctx)
404 {
405 struct vnode * coveredvp = NULL;
406 struct vfs_attr vfa;
407 struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
408 vol_capabilities_attr_t capabilities;
409 struct vfsstatfs * sp = vfs_statfs(mp);
410
411 struct timespec tzero = {.tv_sec = 0, .tv_nsec = 0};
412
413 NULLFSDEBUG("%s\n", __FUNCTION__);
414
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;
419
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);
425
426 capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
427
428 capabilities.valid[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID);
429
430 capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
431
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;
435
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);
439 }
440 }
441
442 if (VFSATTR_IS_ACTIVE(vfap, f_create_time)) {
443 VFSATTR_RETURN(vfap, f_create_time, tzero);
444 }
445
446 if (VFSATTR_IS_ACTIVE(vfap, f_modify_time)) {
447 VFSATTR_RETURN(vfap, f_modify_time, tzero);
448 }
449
450 if (VFSATTR_IS_ACTIVE(vfap, f_access_time)) {
451 VFSATTR_RETURN(vfap, f_access_time, tzero);
452 }
453
454 if (VFSATTR_IS_ACTIVE(vfap, f_bsize)) {
455 VFSATTR_RETURN(vfap, f_bsize, sp->f_bsize);
456 }
457
458 if (VFSATTR_IS_ACTIVE(vfap, f_iosize)) {
459 VFSATTR_RETURN(vfap, f_iosize, sp->f_iosize);
460 }
461
462 if (VFSATTR_IS_ACTIVE(vfap, f_owner)) {
463 VFSATTR_RETURN(vfap, f_owner, 0);
464 }
465
466 if (VFSATTR_IS_ACTIVE(vfap, f_blocks)) {
467 VFSATTR_RETURN(vfap, f_blocks, sp->f_blocks);
468 }
469
470 if (VFSATTR_IS_ACTIVE(vfap, f_bfree)) {
471 VFSATTR_RETURN(vfap, f_bfree, sp->f_bfree);
472 }
473
474 if (VFSATTR_IS_ACTIVE(vfap, f_bavail)) {
475 VFSATTR_RETURN(vfap, f_bavail, sp->f_bavail);
476 }
477
478 if (VFSATTR_IS_ACTIVE(vfap, f_bused)) {
479 VFSATTR_RETURN(vfap, f_bused, sp->f_bused);
480 }
481
482 if (VFSATTR_IS_ACTIVE(vfap, f_files)) {
483 VFSATTR_RETURN(vfap, f_files, sp->f_files);
484 }
485
486 if (VFSATTR_IS_ACTIVE(vfap, f_ffree)) {
487 VFSATTR_RETURN(vfap, f_ffree, sp->f_ffree);
488 }
489
490 if (VFSATTR_IS_ACTIVE(vfap, f_fssubtype)) {
491 VFSATTR_RETURN(vfap, f_fssubtype, 0);
492 }
493
494 if (VFSATTR_IS_ACTIVE(vfap, f_capabilities)) {
495 memcpy(&vfap->f_capabilities, &capabilities, sizeof(vol_capabilities_attr_t));
496
497 VFSATTR_SET_SUPPORTED(vfap, f_capabilities);
498 }
499
500 if (VFSATTR_IS_ACTIVE(vfap, f_attributes)) {
501 vol_attributes_attr_t * volattr = &vfap->f_attributes;
502
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;
508
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;
514
515 VFSATTR_SET_SUPPORTED(vfap, f_attributes);
516 }
517
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);
521 if (coveredvp) {
522 const char * name = vnode_getname_printable(coveredvp);
523 strlcpy(vfap->f_vol_name, name, MAXPATHLEN);
524 vnode_putname_printable(name);
525
526 VFSATTR_SET_SUPPORTED(vfap, f_vol_name);
527 vnode_put(coveredvp);
528 }
529 }
530
531 return 0;
532 }
533
534 static int
535 nullfs_sync(__unused struct mount * mp, __unused int waitfor, __unused vfs_context_t ctx)
536 {
537 /*
538 * XXX - Assumes no data cached at null layer.
539 */
540 return 0;
541 }
542
543
544
545 static int
546 nullfs_vfs_start(__unused struct mount * mp, __unused int flags, __unused vfs_context_t ctx)
547 {
548 NULLFSDEBUG("%s\n", __FUNCTION__);
549 return 0;
550 }
551
552 extern const struct vnodeopv_desc nullfs_vnodeop_opv_desc;
553
554 const struct vnodeopv_desc * nullfs_vnodeopv_descs[] = {
555 &nullfs_vnodeop_opv_desc,
556 };
557
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,
566 .vfs_sysctl = NULL,
567 .vfs_setattr = NULL,
568 };