]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/union/union_vfsops.c
17dbbb251bed935e63d50e88d6a4a0f5e2d84b84
[apple/xnu.git] / bsd / miscfs / union / union_vfsops.c
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31 /*
32 * Copyright (c) 1994, 1995 The Regents of the University of California.
33 * Copyright (c) 1994, 1995 Jan-Simon Pendry.
34 * All rights reserved.
35 *
36 * This code is derived from software donated to Berkeley by
37 * Jan-Simon Pendry.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the University of
50 * California, Berkeley and its contributors.
51 * 4. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95
68 */
69
70 /*
71 * Union Layer
72 */
73
74 #include <sys/param.h>
75 #include <sys/systm.h>
76 #include <sys/time.h>
77 #include <sys/types.h>
78 #include <sys/proc_internal.h>
79 #include <sys/kauth.h>
80 #include <sys/vnode_internal.h>
81 #include <sys/mount_internal.h>
82 #include <sys/namei.h>
83 #include <sys/malloc.h>
84 #include <sys/filedesc.h>
85 #include <sys/queue.h>
86 #include <miscfs/union/union.h>
87
88 static int union_itercallback(__unused vnode_t, void *);
89
90 /*
91 * Mount union filesystem
92 */
93 int
94 union_mount(mount_t mp, __unused vnode_t devvp, user_addr_t data, vfs_context_t context)
95 {
96 proc_t p = vfs_context_proc(context);
97 int error = 0;
98 struct user_union_args args;
99 struct vnode *lowerrootvp = NULLVP;
100 struct vnode *upperrootvp = NULLVP;
101 struct union_mount *um = 0;
102 kauth_cred_t cred = NOCRED;
103 char *cp;
104 int len;
105 u_int size;
106 struct nameidata nd;
107
108 #ifdef UNION_DIAGNOSTIC
109 printf("union_mount(mp = %x)\n", mp);
110 #endif
111
112 /*
113 * Update is a no-op
114 */
115 if (mp->mnt_flag & MNT_UPDATE) {
116 /*
117 * Need to provide.
118 * 1. a way to convert between rdonly and rdwr mounts.
119 * 2. support for nfs exports.
120 */
121 error = ENOTSUP;
122 goto bad;
123 }
124
125 /*
126 * Get argument
127 */
128 if (vfs_context_is64bit(context)) {
129 error = copyin(data, (caddr_t)&args, sizeof(args));
130 }
131 else {
132 struct union_args temp;
133 error = copyin(data, (caddr_t)&temp, sizeof (temp));
134 args.target = CAST_USER_ADDR_T(temp.target);
135 args.mntflags = temp.mntflags;
136 }
137 if (error)
138 goto bad;
139
140 lowerrootvp = mp->mnt_vnodecovered;
141 vnode_get(lowerrootvp);
142
143 /*
144 * Find upper node.
145 */
146 NDINIT(&nd, LOOKUP, FOLLOW|WANTPARENT,
147 (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32),
148 args.target, context);
149
150 if ((error = namei(&nd)))
151 goto bad;
152
153 nameidone(&nd);
154 upperrootvp = nd.ni_vp;
155 vnode_put(nd.ni_dvp);
156 nd.ni_dvp = NULL;
157
158 if (upperrootvp->v_type != VDIR) {
159 error = EINVAL;
160 goto bad;
161 }
162
163 // um = (struct union_mount *) malloc(sizeof(struct union_mount),
164 // M_UFSMNT, M_WAITOK); /* XXX */
165 MALLOC(um, struct union_mount *, sizeof(struct union_mount),
166 M_UFSMNT, M_WAITOK);
167
168 /*
169 * Keep a held reference to the target vnodes.
170 * They are vnode_put'd in union_unmount.
171 *
172 * Depending on the _BELOW flag, the filesystems are
173 * viewed in a different order. In effect, this is the
174 * same as providing a mount under option to the mount syscall.
175 */
176
177 um->um_op = args.mntflags & UNMNT_OPMASK;
178 switch (um->um_op) {
179 case UNMNT_ABOVE:
180 um->um_lowervp = lowerrootvp;
181 um->um_uppervp = upperrootvp;
182 break;
183
184 case UNMNT_BELOW:
185 um->um_lowervp = upperrootvp;
186 um->um_uppervp = lowerrootvp;
187 break;
188
189 case UNMNT_REPLACE:
190 vnode_put(lowerrootvp);
191 lowerrootvp = NULLVP;
192 um->um_uppervp = upperrootvp;
193 um->um_lowervp = lowerrootvp;
194 break;
195
196 default:
197 error = EINVAL;
198 goto bad;
199 }
200
201 /*
202 * Unless the mount is readonly, ensure that the top layer
203 * supports whiteout operations
204 */
205 if ((mp->mnt_flag & MNT_RDONLY) == 0) {
206 error = VNOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0,
207 LOOKUP, context);
208 if (error)
209 goto bad;
210 }
211
212 um->um_cred = kauth_cred_get_with_ref();
213 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
214
215 /*
216 * Depending on what you think the MNT_LOCAL flag might mean,
217 * you may want the && to be || on the conditional below.
218 * At the moment it has been defined that the filesystem is
219 * only local if it is all local, ie the MNT_LOCAL flag implies
220 * that the entire namespace is local. If you think the MNT_LOCAL
221 * flag implies that some of the files might be stored locally
222 * then you will want to change the conditional.
223 */
224 if (um->um_op == UNMNT_ABOVE) {
225 if (((um->um_lowervp == NULLVP) ||
226 (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
227 (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
228 mp->mnt_flag |= MNT_LOCAL;
229 }
230
231 /*
232 * Copy in the upper layer's RDONLY flag. This is for the benefit
233 * of lookup() which explicitly checks the flag, rather than asking
234 * the filesystem for it's own opinion. This means, that an update
235 * mount of the underlying filesystem to go from rdonly to rdwr
236 * will leave the unioned view as read-only.
237 */
238 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
239
240 mp->mnt_data = (qaddr_t) um;
241 vfs_getnewfsid(mp);
242
243
244 switch (um->um_op) {
245 case UNMNT_ABOVE:
246 cp = "<above>:";
247 break;
248 case UNMNT_BELOW:
249 cp = "<below>:";
250 break;
251 case UNMNT_REPLACE:
252 cp = "";
253 break;
254 }
255 len = strlen(cp);
256 bcopy(cp, mp->mnt_vfsstat.f_mntfromname, len);
257
258 cp = mp->mnt_vfsstat.f_mntfromname + len;
259 len = MNAMELEN - len;
260
261 (void) copyinstr(args.target, cp, len - 1, (size_t *)&size);
262 bzero(cp + size, len - size);
263
264 #ifdef UNION_DIAGNOSTIC
265 printf("union_mount: from %s, on %s\n",
266 mp->mnt_vfsstat.f_mntfromname, mp->mnt_vfsstat.f_mntonname);
267 #endif
268 return (0);
269
270 bad:
271 if (um)
272 _FREE(um, M_UFSMNT);
273 if (cred != NOCRED)
274 kauth_cred_rele(cred);
275 if (upperrootvp)
276 vnode_put(upperrootvp);
277 if (lowerrootvp)
278 vnode_put(lowerrootvp);
279 return (error);
280 }
281
282 /*
283 * VFS start. Nothing needed here - the start routine
284 * on the underlying filesystem(s) will have been called
285 * when that filesystem was mounted.
286 */
287 int
288 union_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t context)
289 {
290
291 return (0);
292 }
293
294 static int
295 union_itercallback(__unused vnode_t vp, void *args)
296 {
297 int num = *(int *)args;
298
299 *(int *)args = num + 1;
300 return(VNODE_RETURNED);
301 }
302
303
304
305 /*
306 * Free reference to union layer
307 */
308 int
309 union_unmount(mount_t mp, int mntflags, __unused vfs_context_t context)
310 {
311 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
312 struct vnode *um_rootvp;
313 int error;
314 int freeing;
315 int flags = 0;
316 kauth_cred_t cred;
317
318 #ifdef UNION_DIAGNOSTIC
319 printf("union_unmount(mp = %x)\n", mp);
320 #endif
321
322 if (mntflags & MNT_FORCE)
323 flags |= FORCECLOSE;
324
325 if ((error = union_root(mp, &um_rootvp)))
326 return (error);
327
328 /*
329 * Keep flushing vnodes from the mount list.
330 * This is needed because of the un_pvp held
331 * reference to the parent vnode.
332 * If more vnodes have been freed on a given pass,
333 * the try again. The loop will iterate at most
334 * (d) times, where (d) is the maximum tree depth
335 * in the filesystem.
336 */
337 for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
338 int n = 0;
339
340 vnode_iterate(mp, VNODE_NOLOCK_INTERNAL, union_itercallback, &n);
341
342 /* if this is unchanged then stop */
343 if (n == freeing)
344 break;
345
346 /* otherwise try once more time */
347 freeing = n;
348 }
349
350 /* At this point the root vnode should have a single reference */
351 if (vnode_isinuse(um_rootvp, 0)) {
352 vnode_put(um_rootvp);
353 return (EBUSY);
354 }
355
356 #ifdef UNION_DIAGNOSTIC
357 vprint("union root", um_rootvp);
358 #endif
359 /*
360 * Discard references to upper and lower target vnodes.
361 */
362 if (um->um_lowervp)
363 vnode_put(um->um_lowervp);
364 vnode_put(um->um_uppervp);
365 cred = um->um_cred;
366 if (cred != NOCRED) {
367 um->um_cred = NOCRED;
368 kauth_cred_rele(cred);
369 }
370 /*
371 * Release reference on underlying root vnode
372 */
373 vnode_put(um_rootvp);
374 /*
375 * And blow it away for future re-use
376 */
377 vnode_reclaim(um_rootvp);
378 /*
379 * Finally, throw away the union_mount structure
380 */
381 _FREE(mp->mnt_data, M_UFSMNT); /* XXX */
382 mp->mnt_data = 0;
383 return (0);
384 }
385
386 int
387 union_root(mount_t mp, vnode_t *vpp, __unused vfs_context_t context)
388 {
389 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
390 int error;
391
392 /*
393 * Return locked reference to root.
394 */
395 vnode_get(um->um_uppervp);
396 if (um->um_lowervp)
397 vnode_get(um->um_lowervp);
398 error = union_allocvp(vpp, mp,
399 (struct vnode *) 0,
400 (struct vnode *) 0,
401 (struct componentname *) 0,
402 um->um_uppervp,
403 um->um_lowervp,
404 1);
405
406 if (error) {
407 vnode_put(um->um_uppervp);
408 if (um->um_lowervp)
409 vnode_put(um->um_lowervp);
410 }
411
412 return (error);
413 }
414
415 static int
416 union_vfs_getattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t context)
417 {
418 int error;
419 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
420 struct vfs_attr attr;
421 uint32_t lbsize = 0;
422
423 #ifdef UNION_DIAGNOSTIC
424 printf("union_vfs_getattr(mp = %x, lvp = %x, uvp = %x)\n", mp,
425 um->um_lowervp,
426 um->um_uppervp);
427 #endif
428
429 /* Get values from lower file system (if any) */
430 if (um->um_lowervp) {
431 VFSATTR_INIT(&attr);
432 VFSATTR_WANTED(&attr, f_bsize);
433 VFSATTR_WANTED(&attr, f_blocks);
434 VFSATTR_WANTED(&attr, f_bused);
435 VFSATTR_WANTED(&attr, f_files);
436 error = vfs_getattr(um->um_lowervp->v_mount, &attr, context);
437 if (error)
438 return (error);
439
440 /* now copy across the "interesting" information and fake the rest */
441 if (VFSATTR_IS_SUPPORTED(&attr, f_bsize))
442 lbsize = attr.f_bsize;
443 else
444 lbsize = um->um_lowervp->v_mount->mnt_devblocksize;
445 fsap->f_blocks = VFSATTR_IS_SUPPORTED(&attr, f_blocks) ? attr.f_blocks : 0;
446 fsap->f_bused = VFSATTR_IS_SUPPORTED(&attr, f_bused) ? attr.f_bused : 0;
447 fsap->f_files = VFSATTR_IS_SUPPORTED(&attr, f_files) ? attr.f_files : 0;
448 } else {
449 fsap->f_blocks = 0;
450 fsap->f_bused = 0;
451 fsap->f_files = 0;
452 }
453
454 VFSATTR_INIT(&attr);
455 VFSATTR_WANTED(&attr, f_bsize);
456 VFSATTR_WANTED(&attr, f_blocks);
457 VFSATTR_WANTED(&attr, f_bfree);
458 VFSATTR_WANTED(&attr, f_bavail);
459 VFSATTR_WANTED(&attr, f_files);
460 VFSATTR_WANTED(&attr, f_ffree);
461 error = vfs_getattr(um->um_uppervp->v_mount, &attr, context);
462 if (error)
463 return (error);
464
465 if (VFSATTR_IS_SUPPORTED(&attr, f_bsize)) {
466 fsap->f_bsize = attr.f_bsize;
467 VFSATTR_SET_SUPPORTED(fsap, f_bsize);
468 }
469 if (VFSATTR_IS_SUPPORTED(&attr, f_iosize)) {
470 fsap->f_iosize = attr.f_iosize;
471 VFSATTR_SET_SUPPORTED(fsap, f_iosize);
472 }
473
474 /*
475 * if the lower and upper blocksizes differ, then frig the
476 * block counts so that the sizes reported by df make some
477 * kind of sense. none of this makes sense though.
478 */
479 if (VFSATTR_IS_SUPPORTED(&attr, f_bsize))
480 fsap->f_bsize = attr.f_bsize;
481 else
482 fsap->f_bsize = um->um_uppervp->v_mount->mnt_devblocksize;
483 VFSATTR_RETURN(fsap, f_bsize, attr.f_bsize);
484 if (fsap->f_bsize != lbsize)
485 fsap->f_blocks = fsap->f_blocks * lbsize / attr.f_bsize;
486
487 /*
488 * The "total" fields count total resources in all layers,
489 * the "free" fields count only those resources which are
490 * free in the upper layer (since only the upper layer
491 * is writeable).
492 */
493 if (VFSATTR_IS_SUPPORTED(&attr, f_blocks))
494 fsap->f_blocks += attr.f_blocks;
495 if (VFSATTR_IS_SUPPORTED(&attr, f_bfree))
496 fsap->f_bfree = attr.f_bfree;
497 if (VFSATTR_IS_SUPPORTED(&attr, f_bavail))
498 fsap->f_bavail = attr.f_bavail;
499 if (VFSATTR_IS_SUPPORTED(&attr, f_bused))
500 fsap->f_bused += attr.f_bused;
501 if (VFSATTR_IS_SUPPORTED(&attr, f_files))
502 fsap->f_files += attr.f_files;
503 if (VFSATTR_IS_SUPPORTED(&attr, f_ffree))
504 fsap->f_ffree = attr.f_ffree;
505
506 VFSATTR_SET_SUPPORTED(fsap, f_bsize);
507 VFSATTR_SET_SUPPORTED(fsap, f_blocks);
508 VFSATTR_SET_SUPPORTED(fsap, f_bfree);
509 VFSATTR_SET_SUPPORTED(fsap, f_bavail);
510 VFSATTR_SET_SUPPORTED(fsap, f_bused);
511 VFSATTR_SET_SUPPORTED(fsap, f_files);
512 VFSATTR_SET_SUPPORTED(fsap, f_ffree);
513
514 return (0);
515 }
516
517 /*
518 * XXX - Assumes no data cached at union layer.
519 */
520 #define union_sync (int (*) (mount_t, int, ucred_t, vfs_context_t))nullop
521
522 #define union_fhtovp (int (*) (mount_t, int, unsigned char *, vnode_t *, vfs_context_t))eopnotsupp
523 int union_init (struct vfsconf *);
524 #define union_sysctl (int (*) (int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, vfs_context_t))eopnotsupp
525 #define union_vget (int (*) (mount_t, ino64_t, vnode_t *, vfs_context_t))eopnotsupp
526 #define union_vptofh (int (*) (vnode_t, int *, unsigned char *, vfs_context_t))eopnotsupp
527
528 struct vfsops union_vfsops = {
529 union_mount,
530 union_start,
531 union_unmount,
532 union_root,
533 NULL, /* quotactl */
534 union_vfs_getattr,
535 union_sync,
536 union_vget,
537 union_fhtovp,
538 union_vptofh,
539 union_init,
540 union_sysctl
541 };