]> git.saurik.com Git - apple/xnu.git/blame - bsd/miscfs/nullfs/null_vfsops.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / miscfs / nullfs / null_vfsops.c
CommitLineData
39037602
A
1/*
2 * Copyright (c) 2016 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
85static int
86nullfs_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 */
106static int
107nullfs_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 * Update is a no-op
135 */
136 if (vfs_isupdate(mp)) {
137 return ENOTSUP;
138 }
139
140 /* check entitlement */
141 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) {
142 return EPERM;
143 }
144
145 /*
146 * Get argument
147 */
148 error = copyinstr(user_data, data, MAXPATHLEN - 1, &count);
149 if (error) {
150 NULLFSDEBUG("nullfs: error copying data form user %d\n", error);
151 goto error;
152 }
153
154 /* This could happen if the system is configured for 32 bit inodes instead of
155 * 64 bit */
156 if (count > MAX_MNT_FROM_LENGTH) {
157 error = EINVAL;
158 NULLFSDEBUG("nullfs: path to translocate too large for this system %d vs %d\n", count, MAX_MNT_FROM_LENGTH);
159 goto error;
160 }
161
162 error = vnode_lookup(data, 0, &lowerrootvp, ctx);
163 if (error) {
164 NULLFSDEBUG("lookup %s -> %d\n", data, error);
165 goto error;
166 }
167
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);
173 if (error)
174 {
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
290error:
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 */
313static int
314nullfs_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 {
344 vnode_put(vp);
345 return (error);
346 }
347
348 if (vnode_isinuse(vp,1) && flags == 0)
349 {
350 vnode_put(vp);
351 return EBUSY;
352 }
353
354 vnode_rele(vp); // Drop reference taken by nullfs_mount
355 vnode_put(vp); // Drop ref taken above
356
357 //Force close to get rid of the last vnode
358 (void)vflush(mp, NULL, FORCECLOSE);
359
360 /* no more vnodes, so tear down the mountpoint */
361
362 lck_mtx_lock(&mntdata->nullm_lock);
363
364 vfs_setfsprivate(mp, NULL);
365
366 vnode_getalways(mntdata->nullm_lowerrootvp);
367 vnode_rele(mntdata->nullm_lowerrootvp);
368 vnode_put(mntdata->nullm_lowerrootvp);
369
370 lck_mtx_unlock(&mntdata->nullm_lock);
371
372 nullfs_destroy_lck(&mntdata->nullm_lock);
373
374 FREE(mntdata, M_TEMP);
375
376 uint64_t vflags = vfs_flags(mp);
377 vfs_setflags(mp, vflags & ~MNT_LOCAL);
378
379 return (0);
380}
381
382static int
383nullfs_root(struct mount * mp, struct vnode ** vpp, __unused vfs_context_t ctx)
384{
385 struct vnode * vp;
386 int error;
387
388 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp);
389
390 /*
391 * Return locked reference to root.
392 */
393 vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
394
395 error = vnode_get(vp);
396 if (error)
397 return error;
398
399 *vpp = vp;
400 return 0;
401}
402
403static int
404nullfs_vfs_getattr(struct mount * mp, struct vfs_attr * vfap, vfs_context_t ctx)
405{
406 struct vnode * coveredvp = NULL;
407 struct vfs_attr vfa;
408 struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
409 vol_capabilities_attr_t capabilities;
410 struct vfsstatfs * sp = vfs_statfs(mp);
411
412 struct timespec tzero = {0, 0};
413
414 NULLFSDEBUG("%s\n", __FUNCTION__);
415
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;
420
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);
426
427 capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
428
429 capabilities.valid[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID);
430
431 capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
432
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;
436
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);
440 }
441 }
442
443 if (VFSATTR_IS_ACTIVE(vfap, f_create_time))
444 VFSATTR_RETURN(vfap, f_create_time, tzero);
445
446 if (VFSATTR_IS_ACTIVE(vfap, f_modify_time))
447 VFSATTR_RETURN(vfap, f_modify_time, tzero);
448
449 if (VFSATTR_IS_ACTIVE(vfap, f_access_time))
450 VFSATTR_RETURN(vfap, f_access_time, tzero);
451
452 if (VFSATTR_IS_ACTIVE(vfap, f_bsize))
453 VFSATTR_RETURN(vfap, f_bsize, sp->f_bsize);
454
455 if (VFSATTR_IS_ACTIVE(vfap, f_iosize))
456 VFSATTR_RETURN(vfap, f_iosize, sp->f_iosize);
457
458 if (VFSATTR_IS_ACTIVE(vfap, f_owner))
459 VFSATTR_RETURN(vfap, f_owner, 0);
460
461 if (VFSATTR_IS_ACTIVE(vfap, f_blocks))
462 VFSATTR_RETURN(vfap, f_blocks, sp->f_blocks);
463
464 if (VFSATTR_IS_ACTIVE(vfap, f_bfree))
465 VFSATTR_RETURN(vfap, f_bfree, sp->f_bfree);
466
467 if (VFSATTR_IS_ACTIVE(vfap, f_bavail))
468 VFSATTR_RETURN(vfap, f_bavail, sp->f_bavail);
469
470 if (VFSATTR_IS_ACTIVE(vfap, f_bused))
471 VFSATTR_RETURN(vfap, f_bused, sp->f_bused);
472
473 if (VFSATTR_IS_ACTIVE(vfap, f_files))
474 VFSATTR_RETURN(vfap, f_files, sp->f_files);
475
476 if (VFSATTR_IS_ACTIVE(vfap, f_ffree))
477 VFSATTR_RETURN(vfap, f_ffree, sp->f_ffree);
478
479 if (VFSATTR_IS_ACTIVE(vfap, f_fssubtype))
480 VFSATTR_RETURN(vfap, f_fssubtype, 0);
481
482 if (VFSATTR_IS_ACTIVE(vfap, f_capabilities)) {
483 memcpy(&vfap->f_capabilities, &capabilities, sizeof(vol_capabilities_attr_t));
484
485 VFSATTR_SET_SUPPORTED(vfap, f_capabilities);
486 }
487
488 if (VFSATTR_IS_ACTIVE(vfap, f_attributes)) {
489 vol_attributes_attr_t * volattr = &vfap->f_attributes;
490
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;
496
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;
502
503 VFSATTR_SET_SUPPORTED(vfap, f_attributes);
504 }
505
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);
509 if (coveredvp) {
510 const char * name = vnode_getname_printable(coveredvp);
511 strlcpy(vfap->f_vol_name, name, MAXPATHLEN);
512 vnode_putname_printable(name);
513
514 VFSATTR_SET_SUPPORTED(vfap, f_vol_name);
515 vnode_put(coveredvp);
516 }
517 }
518
519 return 0;
520}
521
522static int
523nullfs_sync(__unused struct mount * mp, __unused int waitfor, __unused vfs_context_t ctx)
524{
525 /*
526 * XXX - Assumes no data cached at null layer.
527 */
528 return (0);
529}
530
531
532
533static int
534nullfs_vfs_start(__unused struct mount * mp, __unused int flags, __unused vfs_context_t ctx)
535{
536 NULLFSDEBUG("%s\n", __FUNCTION__);
537 return 0;
538}
539
540extern struct vnodeopv_desc nullfs_vnodeop_opv_desc;
541
542struct vnodeopv_desc * nullfs_vnodeopv_descs[] = {
543 &nullfs_vnodeop_opv_desc,
544};
545
546struct 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,
554 .vfs_sysctl = NULL,
555 .vfs_setattr = NULL,
556};
557