]> git.saurik.com Git - apple/xnu.git/blame - bsd/miscfs/union/union_subr.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / miscfs / union / union_subr.c
CommitLineData
1c79356b 1/*
5d5c5d0d
A
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
8ad349bb 4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
1c79356b 5 *
8ad349bb
A
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@
1c79356b
A
29 */
30/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31/*
32 * Copyright (c) 1994 Jan-Simon Pendry
33 * Copyright (c) 1994
34 * The Regents of the University of California. All rights reserved.
35 *
36 * This code is derived from software contributed 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_subr.c 8.20 (Berkeley) 5/20/95
68 */
69
70#include <sys/param.h>
71#include <sys/systm.h>
91447636
A
72#include <sys/proc_internal.h>
73#include <sys/kauth.h>
1c79356b
A
74#include <sys/time.h>
75#include <sys/kernel.h>
91447636 76#include <sys/vnode_internal.h>
1c79356b
A
77#include <sys/namei.h>
78#include <sys/malloc.h>
79#include <sys/file.h>
80#include <sys/filedesc.h>
81#include <sys/queue.h>
91447636 82#include <sys/mount_internal.h>
1c79356b
A
83#include <sys/stat.h>
84#include <sys/ubc.h>
91447636 85#include <sys/uio_internal.h>
1c79356b
A
86#include <miscfs/union/union.h>
87
88#if DIAGNOSTIC
89#include <sys/proc.h>
90#endif
91
92/* must be power of two, otherwise change UNION_HASH() */
93#define NHASH 32
94
95/* unsigned int ... */
96#define UNION_HASH(u, l) \
97 (((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
98
99static LIST_HEAD(unhead, union_node) unhead[NHASH];
100static int unvplock[NHASH];
101
102int
103union_init()
104{
105 int i;
106
107 for (i = 0; i < NHASH; i++)
108 LIST_INIT(&unhead[i]);
109 bzero((caddr_t) unvplock, sizeof(unvplock));
110}
111
112static int
113union_list_lock(ix)
114 int ix;
115{
116
117 if (unvplock[ix] & UN_LOCKED) {
118 unvplock[ix] |= UN_WANT;
119 sleep((caddr_t) &unvplock[ix], PINOD);
120 return (1);
121 }
122
123 unvplock[ix] |= UN_LOCKED;
124
125 return (0);
126}
127
128static void
129union_list_unlock(ix)
130 int ix;
131{
132
133 unvplock[ix] &= ~UN_LOCKED;
134
135 if (unvplock[ix] & UN_WANT) {
136 unvplock[ix] &= ~UN_WANT;
137 wakeup((caddr_t) &unvplock[ix]);
138 }
139}
140
141void
142union_updatevp(un, uppervp, lowervp)
143 struct union_node *un;
144 struct vnode *uppervp;
145 struct vnode *lowervp;
146{
147 int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
148 int nhash = UNION_HASH(uppervp, lowervp);
149 int docache = (lowervp != NULLVP || uppervp != NULLVP);
91447636 150 int lhash, uhash;
1c79356b
A
151
152 /*
153 * Ensure locking is ordered from lower to higher
154 * to avoid deadlocks.
155 */
156 if (nhash < ohash) {
157 lhash = nhash;
158 uhash = ohash;
159 } else {
160 lhash = ohash;
161 uhash = nhash;
162 }
163
164 if (lhash != uhash)
165 while (union_list_lock(lhash))
166 continue;
167
168 while (union_list_lock(uhash))
169 continue;
170
171 if (ohash != nhash || !docache) {
172 if (un->un_flags & UN_CACHED) {
173 un->un_flags &= ~UN_CACHED;
174 LIST_REMOVE(un, un_cache);
175 }
176 }
177
178 if (ohash != nhash)
179 union_list_unlock(ohash);
180
181 if (un->un_lowervp != lowervp) {
182 if (un->un_lowervp) {
91447636 183 vnode_put(un->un_lowervp);
1c79356b
A
184 if (un->un_path) {
185 _FREE(un->un_path, M_TEMP);
186 un->un_path = 0;
187 }
188 if (un->un_dirvp) {
91447636 189 vnode_put(un->un_dirvp);
1c79356b
A
190 un->un_dirvp = NULLVP;
191 }
192 }
193 un->un_lowervp = lowervp;
194 un->un_lowersz = VNOVAL;
195 }
196
197 if (un->un_uppervp != uppervp) {
198 if (un->un_uppervp)
91447636 199 vnode_put(un->un_uppervp);
1c79356b
A
200
201 un->un_uppervp = uppervp;
202 un->un_uppersz = VNOVAL;
203 }
204
205 if (docache && (ohash != nhash)) {
206 LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
207 un->un_flags |= UN_CACHED;
208 }
209
210 union_list_unlock(nhash);
211}
212
213void
214union_newlower(un, lowervp)
215 struct union_node *un;
216 struct vnode *lowervp;
217{
218
219 union_updatevp(un, un->un_uppervp, lowervp);
220}
221
222void
223union_newupper(un, uppervp)
224 struct union_node *un;
225 struct vnode *uppervp;
226{
227
228 union_updatevp(un, uppervp, un->un_lowervp);
229}
230
231/*
232 * Keep track of size changes in the underlying vnodes.
233 * If the size changes, then callback to the vm layer
234 * giving priority to the upper layer size.
235 */
236void
237union_newsize(vp, uppersz, lowersz)
238 struct vnode *vp;
239 off_t uppersz, lowersz;
240{
241 struct union_node *un;
242 off_t sz;
243
244 /* only interested in regular files */
245 if (vp->v_type != VREG)
246 return;
247
248 un = VTOUNION(vp);
249 sz = VNOVAL;
250
251 if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
252 un->un_uppersz = uppersz;
253 if (sz == VNOVAL)
254 sz = un->un_uppersz;
255 }
256
257 if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) {
258 un->un_lowersz = lowersz;
259 if (sz == VNOVAL)
260 sz = un->un_lowersz;
261 }
262
263 if (sz != VNOVAL) {
264#ifdef UNION_DIAGNOSTIC
265 printf("union: %s size now %ld\n",
266 uppersz != VNOVAL ? "upper" : "lower", (long) sz);
267#endif
91447636 268 ubc_setsize(vp, sz);
1c79356b
A
269 }
270}
271
272/*
273 * allocate a union_node/vnode pair. the vnode is
274 * referenced and locked. the new vnode is returned
275 * via (vpp). (mp) is the mountpoint of the union filesystem,
276 * (dvp) is the parent directory where the upper layer object
277 * should exist (but doesn't) and (cnp) is the componentname
278 * information which is partially copied to allow the upper
279 * layer object to be created at a later time. (uppervp)
280 * and (lowervp) reference the upper and lower layer objects
281 * being mapped. either, but not both, can be nil.
282 * if supplied, (uppervp) is locked.
283 * the reference is either maintained in the new union_node
91447636 284 * object which is allocated, or they are vnode_put'd.
1c79356b
A
285 *
286 * all union_nodes are maintained on a singly-linked
287 * list. new nodes are only allocated when they cannot
288 * be found on this list. entries on the list are
289 * removed when the vfs reclaim entry is called.
290 *
291 * a single lock is kept for the entire list. this is
292 * needed because the getnewvnode() function can block
293 * waiting for a vnode to become free, in which case there
294 * may be more than one process trying to get the same
295 * vnode. this lock is only taken if we are going to
296 * call getnewvnode, since the kernel itself is single-threaded.
297 *
91447636 298 * if an entry is found on the list, then call vnode_get() to
1c79356b
A
299 * take a reference. this is done because there may be
300 * zero references to it and so it needs to removed from
301 * the vnode free list.
302 */
303int
304union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp, docache)
305 struct vnode **vpp;
306 struct mount *mp;
307 struct vnode *undvp; /* parent union vnode */
308 struct vnode *dvp; /* may be null */
309 struct componentname *cnp; /* may be null */
310 struct vnode *uppervp; /* may be null */
311 struct vnode *lowervp; /* may be null */
312 int docache;
313{
314 int error;
315 struct union_node *un;
316 struct union_node **pp;
317 struct vnode *xlowervp = NULLVP;
318 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
319 int hash;
91447636 320 int markroot;
1c79356b
A
321 int try;
322 struct union_node *unp;
91447636
A
323 struct vnode_fsparam vfsp;
324 enum vtype vtype;
1c79356b
A
325
326 if (uppervp == NULLVP && lowervp == NULLVP)
327 panic("union: unidentifiable allocation");
328
329 if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
330 xlowervp = lowervp;
331 lowervp = NULLVP;
332 }
333
334 /* detect the root vnode (and aliases) */
91447636 335 markroot = 0;
1c79356b
A
336 if ((uppervp == um->um_uppervp) &&
337 ((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
338 if (lowervp == NULLVP) {
339 lowervp = um->um_lowervp;
340 if (lowervp != NULLVP)
91447636 341 vnode_get(lowervp);
1c79356b 342 }
91447636 343 markroot = VROOT;
1c79356b
A
344 }
345
346loop:
347 if (!docache) {
348 un = 0;
349 } else for (try = 0; try < 3; try++) {
350 switch (try) {
351 case 0:
352 if (lowervp == NULLVP)
353 continue;
354 hash = UNION_HASH(uppervp, lowervp);
355 break;
356
357 case 1:
358 if (uppervp == NULLVP)
359 continue;
360 hash = UNION_HASH(uppervp, NULLVP);
361 break;
362
363 case 2:
364 if (lowervp == NULLVP)
365 continue;
366 hash = UNION_HASH(NULLVP, lowervp);
367 break;
368 }
369
370 while (union_list_lock(hash))
371 continue;
372
373 for (un = unhead[hash].lh_first; un != 0;
374 un = un->un_cache.le_next) {
375 if ((un->un_lowervp == lowervp ||
376 un->un_lowervp == NULLVP) &&
377 (un->un_uppervp == uppervp ||
378 un->un_uppervp == NULLVP) &&
379 (UNIONTOV(un)->v_mount == mp)) {
91447636 380 if (vnode_get(UNIONTOV(un))) {
1c79356b
A
381 union_list_unlock(hash);
382 goto loop;
383 }
384 break;
385 }
386 }
387
388 union_list_unlock(hash);
389
390 if (un)
391 break;
392 }
393
394 if (un) {
395 /*
396 * Obtain a lock on the union_node.
397 * uppervp is locked, though un->un_uppervp
398 * may not be. this doesn't break the locking
399 * hierarchy since in the case that un->un_uppervp
91447636 400 * is not yet locked it will be vnode_put'd and replaced
1c79356b
A
401 * with uppervp.
402 */
403
404 if ((dvp != NULLVP) && (uppervp == dvp)) {
405 /*
406 * Access ``.'', so (un) will already
407 * be locked. Since this process has
408 * the lock on (uppervp) no other
409 * process can hold the lock on (un).
410 */
411#if DIAGNOSTIC
412 if ((un->un_flags & UN_LOCKED) == 0)
413 panic("union: . not locked");
414 else if (current_proc() && un->un_pid != current_proc()->p_pid &&
415 un->un_pid > -1 && current_proc()->p_pid > -1)
416 panic("union: allocvp not lock owner");
417#endif
418 } else {
419 if (un->un_flags & UN_LOCKED) {
91447636 420 vnode_put(UNIONTOV(un));
1c79356b
A
421 un->un_flags |= UN_WANT;
422 sleep((caddr_t) &un->un_flags, PINOD);
423 goto loop;
424 }
425 un->un_flags |= UN_LOCKED;
426
427#if DIAGNOSTIC
428 if (current_proc())
429 un->un_pid = current_proc()->p_pid;
430 else
431 un->un_pid = -1;
432#endif
433 }
434
435 /*
436 * At this point, the union_node is locked,
437 * un->un_uppervp may not be locked, and uppervp
438 * is locked or nil.
439 */
440
441 /*
442 * Save information about the upper layer.
443 */
444 if (uppervp != un->un_uppervp) {
445 union_newupper(un, uppervp);
446 } else if (uppervp) {
91447636 447 vnode_put(uppervp);
1c79356b
A
448 }
449
450 if (un->un_uppervp) {
451 un->un_flags |= UN_ULOCK;
452 un->un_flags &= ~UN_KLOCK;
453 }
454
455 /*
456 * Save information about the lower layer.
457 * This needs to keep track of pathname
458 * and directory information which union_vn_create
459 * might need.
460 */
461 if (lowervp != un->un_lowervp) {
462 union_newlower(un, lowervp);
463 if (cnp && (lowervp != NULLVP)) {
464 un->un_hash = cnp->cn_hash;
465 MALLOC(un->un_path, caddr_t, cnp->cn_namelen+1,
466 M_TEMP, M_WAITOK);
467 bcopy(cnp->cn_nameptr, un->un_path,
468 cnp->cn_namelen);
469 un->un_path[cnp->cn_namelen] = '\0';
91447636 470 vnode_get(dvp);
1c79356b
A
471 un->un_dirvp = dvp;
472 }
473 } else if (lowervp) {
91447636 474 vnode_put(lowervp);
1c79356b
A
475 }
476 *vpp = UNIONTOV(un);
477 return (0);
478 }
479
480 if (docache) {
481 /*
482 * otherwise lock the vp list while we call getnewvnode
483 * since that can block.
484 */
485 hash = UNION_HASH(uppervp, lowervp);
486
487 if (union_list_lock(hash))
488 goto loop;
489 }
490
491 MALLOC(unp, void *, sizeof(struct union_node), M_TEMP, M_WAITOK);
91447636
A
492
493 if (uppervp)
494 vtype = uppervp->v_type;
495 else
496 vtype = lowervp->v_type;
497 //bzero(&vfsp, sizeof(struct vnode_fsparam));
498 vfsp.vnfs_mp = mp;
499 vfsp.vnfs_vtype = vtype;
500 vfsp.vnfs_str = "unionfs";
501 vfsp.vnfs_dvp = dvp;
502 vfsp.vnfs_fsnode = unp;
503 vfsp.vnfs_cnp = cnp;
504 vfsp.vnfs_vops = union_vnodeop_p;
505 vfsp.vnfs_rdev = 0;
506 vfsp.vnfs_filesize = 0;
507 vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
508 vfsp.vnfs_marksystem = 0;
509 vfsp.vnfs_markroot = markroot;
510
511 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, vpp);
1c79356b
A
512 if (error) {
513 FREE(unp, M_TEMP);
514 if (uppervp) {
91447636 515 vnode_put(uppervp);
1c79356b
A
516 }
517 if (lowervp)
91447636 518 vnode_put(lowervp);
1c79356b
A
519
520 goto out;
521 }
522
91447636 523 (*vpp)->v_tag = VT_UNION;
1c79356b
A
524 un = VTOUNION(*vpp);
525 un->un_vnode = *vpp;
526 un->un_uppervp = uppervp;
527 un->un_uppersz = VNOVAL;
528 un->un_lowervp = lowervp;
529 un->un_lowersz = VNOVAL;
530 un->un_pvp = undvp;
531 if (undvp != NULLVP)
91447636 532 vnode_get(undvp);
1c79356b
A
533 un->un_dircache = 0;
534 un->un_openl = 0;
535 un->un_flags = UN_LOCKED;
536 if (un->un_uppervp)
537 un->un_flags |= UN_ULOCK;
538#if DIAGNOSTIC
539 if (current_proc())
540 un->un_pid = current_proc()->p_pid;
541 else
542 un->un_pid = -1;
543#endif
544 if (cnp && (lowervp != NULLVP)) {
545 un->un_hash = cnp->cn_hash;
546 un->un_path = _MALLOC(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
547 bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
548 un->un_path[cnp->cn_namelen] = '\0';
91447636 549 vnode_get(dvp);
1c79356b
A
550 un->un_dirvp = dvp;
551 } else {
552 un->un_hash = 0;
553 un->un_path = 0;
554 un->un_dirvp = 0;
555 }
556
557 if (docache) {
558 LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
559 un->un_flags |= UN_CACHED;
560 }
561
562 if (xlowervp)
91447636 563 vnode_put(xlowervp);
1c79356b
A
564
565out:
566 if (docache)
567 union_list_unlock(hash);
568
569 return (error);
570}
571
572int
573union_freevp(vp)
574 struct vnode *vp;
575{
576 struct union_node *un = VTOUNION(vp);
577
578 if (un->un_flags & UN_CACHED) {
579 un->un_flags &= ~UN_CACHED;
580 LIST_REMOVE(un, un_cache);
581 }
582
583 if (un->un_pvp != NULLVP)
91447636 584 vnode_put(un->un_pvp);
1c79356b 585 if (un->un_uppervp != NULLVP)
91447636 586 vnode_put(un->un_uppervp);
1c79356b 587 if (un->un_lowervp != NULLVP)
91447636 588 vnode_put(un->un_lowervp);
1c79356b 589 if (un->un_dirvp != NULLVP)
91447636 590 vnode_put(un->un_dirvp);
1c79356b
A
591 if (un->un_path)
592 _FREE(un->un_path, M_TEMP);
593
594 FREE(vp->v_data, M_TEMP);
595 vp->v_data = 0;
596
597 return (0);
598}
599
600/*
601 * copyfile. copy the vnode (fvp) to the vnode (tvp)
602 * using a sequence of reads and writes. both (fvp)
603 * and (tvp) are locked on entry and exit.
604 */
605int
91447636
A
606union_copyfile(struct vnode *fvp, struct vnode *tvp, kauth_cred_t cred,
607 struct proc *p)
1c79356b 608{
91447636 609 char *bufp;
1c79356b 610 struct uio uio;
91447636
A
611 struct iovec_32 iov;
612 struct vfs_context context;
1c79356b
A
613 int error = 0;
614
615 /*
616 * strategy:
617 * allocate a buffer of size MAXPHYSIO.
618 * loop doing reads and writes, keeping track
619 * of the current uio offset.
620 * give up at the first sign of trouble.
621 */
622
91447636
A
623 context.vc_proc = p;
624 context.vc_ucred = cred;
625
626#if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
1c79356b 627 uio.uio_segflg = UIO_SYSSPACE;
91447636
A
628#else
629 uio.uio_segflg = UIO_SYSSPACE32;
630#endif
1c79356b
A
631 uio.uio_offset = 0;
632
91447636 633 bufp = _MALLOC(MAXPHYSIO, M_TEMP, M_WAITOK);
1c79356b
A
634
635 /* ugly loop follows... */
636 do {
637 off_t offset = uio.uio_offset;
638
91447636 639 uio.uio_iovs.iov32p = &iov;
1c79356b 640 uio.uio_iovcnt = 1;
91447636 641 iov.iov_base = (uintptr_t)bufp;
1c79356b 642 iov.iov_len = MAXPHYSIO;
91447636 643 uio_setresid(&uio, iov.iov_len);
1c79356b 644 uio.uio_rw = UIO_READ;
91447636 645 error = VNOP_READ(fvp, &uio, 0, &context);
1c79356b
A
646
647 if (error == 0) {
91447636 648 uio.uio_iovs.iov32p = &iov;
1c79356b 649 uio.uio_iovcnt = 1;
91447636
A
650 iov.iov_base = (uintptr_t)bufp;
651 iov.iov_len = MAXPHYSIO - uio_resid(&uio);
1c79356b
A
652 uio.uio_offset = offset;
653 uio.uio_rw = UIO_WRITE;
91447636 654 uio_setresid(&uio, iov.iov_len);
1c79356b 655
91447636 656 if (uio_resid(&uio) == 0)
1c79356b
A
657 break;
658
659 do {
91447636
A
660 error = VNOP_WRITE(tvp, &uio, 0, &context);
661 } while ((uio_resid(&uio) > 0) && (error == 0));
1c79356b
A
662 }
663
664 } while (error == 0);
665
91447636 666 _FREE(bufp, M_TEMP);
1c79356b
A
667 return (error);
668}
669
670/*
671 * (un) is assumed to be locked on entry and remains
672 * locked on exit.
673 */
674int
91447636
A
675union_copyup(struct union_node *un, int docopy, kauth_cred_t cred,
676 struct proc *p)
1c79356b
A
677{
678 int error;
679 struct vnode *lvp, *uvp;
91447636 680 struct vfs_context context;
1c79356b
A
681
682 error = union_vn_create(&uvp, un, p);
683 if (error)
684 return (error);
685
91447636
A
686 context.vc_proc = p;
687 context.vc_ucred = cred;
688
1c79356b
A
689 /* at this point, uppervp is locked */
690 union_newupper(un, uvp);
691 un->un_flags |= UN_ULOCK;
692
693 lvp = un->un_lowervp;
694
695 if (docopy) {
696 /*
697 * XX - should not ignore errors
91447636 698 * from vnop_close
1c79356b 699 */
91447636 700 error = VNOP_OPEN(lvp, FREAD, &context);
1c79356b
A
701 if (error == 0) {
702 error = union_copyfile(lvp, uvp, cred, p);
91447636 703 (void) VNOP_CLOSE(lvp, FREAD, &context);
1c79356b
A
704 }
705#ifdef UNION_DIAGNOSTIC
706 if (error == 0)
707 uprintf("union: copied up %s\n", un->un_path);
708#endif
709
710 }
711 un->un_flags &= ~UN_ULOCK;
1c79356b 712 union_vn_close(uvp, FWRITE, cred, p);
1c79356b
A
713 un->un_flags |= UN_ULOCK;
714
715 /*
716 * Subsequent IOs will go to the top layer, so
717 * call close on the lower vnode and open on the
718 * upper vnode to ensure that the filesystem keeps
719 * its references counts right. This doesn't do
720 * the right thing with (cred) and (FREAD) though.
721 * Ignoring error returns is not right, either.
722 */
723 if (error == 0) {
724 int i;
725
726 for (i = 0; i < un->un_openl; i++) {
91447636
A
727 (void) VNOP_CLOSE(lvp, FREAD, &context);
728 (void) VNOP_OPEN(uvp, FREAD, &context);
1c79356b
A
729 }
730 un->un_openl = 0;
731 }
732
733 return (error);
734
735}
736
737static int
738union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
739 struct union_mount *um;
740 struct vnode *dvp;
741 struct vnode **vpp;
742 struct componentname *cnp;
743 struct componentname *cn;
744 char *path;
745 int pathlen;
746{
747 int error;
748
749 /*
750 * A new componentname structure must be faked up because
751 * there is no way to know where the upper level cnp came
752 * from or what it is being used for. This must duplicate
753 * some of the work done by NDINIT, some of the work done
754 * by namei, some of the work done by lookup and some of
91447636 755 * the work done by vnop_lookup when given a CREATE flag.
1c79356b 756 * Conclusion: Horrible.
1c79356b
A
757 */
758 cn->cn_namelen = pathlen;
759 cn->cn_pnbuf = _MALLOC_ZONE(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
760 cn->cn_pnlen = cn->cn_namelen+1;
761 bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
762 cn->cn_pnbuf[cn->cn_namelen] = '\0';
763
764 cn->cn_nameiop = CREATE;
765 cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
91447636 766#ifdef XXX_HELP_ME
1c79356b
A
767 cn->cn_proc = cnp->cn_proc;
768 if (um->um_op == UNMNT_ABOVE)
769 cn->cn_cred = cnp->cn_cred;
770 else
771 cn->cn_cred = um->um_cred;
91447636
A
772#endif
773 cn->cn_context = cnp->cn_context; /* XXX !UNMNT_ABOVE case ??? */
1c79356b
A
774 cn->cn_nameptr = cn->cn_pnbuf;
775 cn->cn_hash = cnp->cn_hash;
776 cn->cn_consume = cnp->cn_consume;
777
91447636 778 vnode_get(dvp);
1c79356b
A
779 error = relookup(dvp, vpp, cn);
780 if (!error)
91447636 781 vnode_put(dvp);
1c79356b
A
782
783 return (error);
784}
785
786/*
787 * Create a shadow directory in the upper layer.
788 * The new vnode is returned locked.
789 *
790 * (um) points to the union mount structure for access to the
791 * the mounting process's credentials.
792 * (dvp) is the directory in which to create the shadow directory.
793 * it is unlocked on entry and exit.
794 * (cnp) is the componentname to be created.
795 * (vpp) is the returned newly created shadow directory, which
796 * is returned locked.
797 */
798int
799union_mkshadow(um, dvp, cnp, vpp)
800 struct union_mount *um;
801 struct vnode *dvp;
802 struct componentname *cnp;
803 struct vnode **vpp;
804{
805 int error;
91447636 806 struct vnode_attr va;
1c79356b
A
807 struct componentname cn;
808
809 error = union_relookup(um, dvp, vpp, cnp, &cn,
810 cnp->cn_nameptr, cnp->cn_namelen);
811 if (error)
812 return (error);
813
814 if (*vpp) {
91447636 815 vnode_put(*vpp);
1c79356b
A
816 *vpp = NULLVP;
817 return (EEXIST);
818 }
819
820 /*
821 * policy: when creating the shadow directory in the
822 * upper layer, create it owned by the user who did
823 * the mount, group from parent directory, and mode
824 * 777 modified by umask (ie mostly identical to the
825 * mkdir syscall). (jsp, kb)
826 */
91447636
A
827 VATTR_INIT(&va);
828 VATTR_SET(&va, va_type, VDIR);
829 VATTR_SET(&va, va_mode, um->um_cmode);
1c79356b 830
91447636 831 error = vn_create(dvp, vpp, &cn, &va, 0, cnp->cn_context);
1c79356b
A
832 return (error);
833}
834
835/*
836 * Create a whiteout entry in the upper layer.
837 *
838 * (um) points to the union mount structure for access to the
839 * the mounting process's credentials.
840 * (dvp) is the directory in which to create the whiteout.
841 * it is locked on entry and exit.
842 * (cnp) is the componentname to be created.
843 */
844int
845union_mkwhiteout(um, dvp, cnp, path)
846 struct union_mount *um;
847 struct vnode *dvp;
848 struct componentname *cnp;
849 char *path;
850{
851 int error;
1c79356b
A
852 struct vnode *wvp;
853 struct componentname cn;
854
1c79356b
A
855 error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
856 if (error) {
1c79356b
A
857 return (error);
858 }
1c79356b 859 if (wvp) {
91447636
A
860 vnode_put(dvp);
861 vnode_put(wvp);
1c79356b
A
862 return (EEXIST);
863 }
864
91447636 865 error = VNOP_WHITEOUT(dvp, &cn, CREATE, cnp->cn_context);
1c79356b 866
91447636 867 vnode_put(dvp);
1c79356b
A
868
869 return (error);
870}
871
872/*
873 * union_vn_create: creates and opens a new shadow file
874 * on the upper union layer. this function is similar
875 * in spirit to calling vn_open but it avoids calling namei().
876 * the problem with calling namei is that a) it locks too many
877 * things, and b) it doesn't start at the "right" directory,
878 * whereas relookup is told where to start.
879 */
880int
881union_vn_create(vpp, un, p)
882 struct vnode **vpp;
883 struct union_node *un;
884 struct proc *p;
885{
886 struct vnode *vp;
91447636
A
887 kauth_cred_t cred = p->p_ucred;
888 struct vnode_attr vat;
889 struct vnode_attr *vap = &vat;
890 struct vfs_context context;
1c79356b
A
891 int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
892 int error;
893 int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
894 char *cp;
895 struct componentname cn;
896
897 *vpp = NULLVP;
898
91447636
A
899 context.vc_proc = p;
900 context.vc_ucred = p->p_ucred;
901
1c79356b
A
902 /*
903 * Build a new componentname structure (for the same
904 * reasons outlines in union_mkshadow).
905 * The difference here is that the file is owned by
906 * the current user, rather than by the person who
907 * did the mount, since the current user needs to be
908 * able to write the file (that's why it is being
909 * copied in the first place).
910 */
911 cn.cn_namelen = strlen(un->un_path);
912 cn.cn_pnbuf = (caddr_t) _MALLOC_ZONE(cn.cn_namelen+1,
913 M_NAMEI, M_WAITOK);
914 cn.cn_pnlen = cn.cn_namelen+1;
915 bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
916 cn.cn_nameiop = CREATE;
917 cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
91447636 918 cn.cn_context = &context;
1c79356b
A
919 cn.cn_nameptr = cn.cn_pnbuf;
920 cn.cn_hash = un->un_hash;
921 cn.cn_consume = 0;
922
91447636 923 vnode_get(un->un_dirvp);
1c79356b
A
924 if (error = relookup(un->un_dirvp, &vp, &cn))
925 return (error);
91447636 926 vnode_put(un->un_dirvp);
1c79356b
A
927
928 if (vp) {
91447636
A
929 vnode_put(un->un_dirvp);
930 vnode_put(vp);
1c79356b
A
931 return (EEXIST);
932 }
933
934 /*
935 * Good - there was no race to create the file
936 * so go ahead and create it. The permissions
937 * on the file will be 0666 modified by the
938 * current user's umask. Access to the file, while
939 * it is unioned, will require access to the top *and*
940 * bottom files. Access when not unioned will simply
941 * require access to the top-level file.
91447636 942 *
1c79356b 943 * TODO: confirm choice of access permissions.
91447636 944 * decide on authorisation behaviour
1c79356b 945 */
91447636
A
946
947 VATTR_INIT(vap);
948 VATTR_SET(vap, va_type, VREG);
949 VATTR_SET(vap, va_mode, cmode);
950
951 if (error = vn_create(un->un_dirvp, &vp, &cn, vap, 0, &context))
1c79356b
A
952 return (error);
953
91447636
A
954 if (error = VNOP_OPEN(vp, fmode, &context)) {
955 vnode_put(vp);
1c79356b
A
956 return (error);
957 }
958
91447636 959 vnode_lock(vp);
1c79356b
A
960 if (++vp->v_writecount <= 0)
961 panic("union: v_writecount");
91447636 962 vnode_unlock(vp);
1c79356b
A
963 *vpp = vp;
964 return (0);
965}
966
967int
91447636
A
968union_vn_close(struct vnode *vp, int fmode, kauth_cred_t cred,
969 struct proc *p)
1c79356b 970{
91447636
A
971 struct vfs_context context;
972
973 context.vc_proc = p;
974 context.vc_ucred = cred;
1c79356b 975
91447636
A
976 if (fmode & FWRITE) {
977 vnode_lock(vp);
1c79356b 978 --vp->v_writecount;
91447636
A
979 vnode_unlock(vp);
980 }
981 return (VNOP_CLOSE(vp, fmode, &context));
1c79356b
A
982}
983
984void
985union_removed_upper(un)
986 struct union_node *un;
987{
988 struct proc *p = current_proc(); /* XXX */
989
990 union_newupper(un, NULLVP);
991 if (un->un_flags & UN_CACHED) {
992 un->un_flags &= ~UN_CACHED;
993 LIST_REMOVE(un, un_cache);
994 }
995
996 if (un->un_flags & UN_ULOCK) {
997 un->un_flags &= ~UN_ULOCK;
1c79356b
A
998 }
999}
1000
1001#if 0
1002struct vnode *
1003union_lowervp(vp)
1004 struct vnode *vp;
1005{
1006 struct union_node *un = VTOUNION(vp);
1007
1008 if ((un->un_lowervp != NULLVP) &&
1009 (vp->v_type == un->un_lowervp->v_type)) {
91447636 1010 if (vnode_get(un->un_lowervp) == 0)
1c79356b
A
1011 return (un->un_lowervp);
1012 }
1013
1014 return (NULLVP);
1015}
1016#endif
1017
1018/*
1019 * determine whether a whiteout is needed
1020 * during a remove/rmdir operation.
1021 */
1022int
91447636 1023union_dowhiteout(struct union_node *un, vfs_context_t ctx)
1c79356b 1024{
91447636 1025 struct vnode_attr va;
1c79356b
A
1026
1027 if (un->un_lowervp != NULLVP)
1028 return (1);
1029
91447636
A
1030 VATTR_INIT(&va);
1031 VATTR_WANTED(&va, va_flags);
1032 if (vnode_getattr(un->un_uppervp, &va, ctx) == 0 &&
1c79356b
A
1033 (va.va_flags & OPAQUE))
1034 return (1);
1035
1036 return (0);
1037}
1038
1039static void
1040union_dircache_r(vp, vppp, cntp)
1041 struct vnode *vp;
1042 struct vnode ***vppp;
1043 int *cntp;
1044{
1045 struct union_node *un;
1046
1047 if (vp->v_op != union_vnodeop_p) {
1048 if (vppp) {
91447636 1049 vnode_get(vp);
1c79356b
A
1050 *(*vppp)++ = vp;
1051 if (--(*cntp) == 0)
1052 panic("union: dircache table too small");
1053 } else {
1054 (*cntp)++;
1055 }
1056
1057 return;
1058 }
1059
1060 un = VTOUNION(vp);
1061 if (un->un_uppervp != NULLVP)
1062 union_dircache_r(un->un_uppervp, vppp, cntp);
1063 if (un->un_lowervp != NULLVP)
1064 union_dircache_r(un->un_lowervp, vppp, cntp);
1065}
1066
1067struct vnode *
1068union_dircache(vp, p)
1069 struct vnode *vp;
1070 struct proc *p;
1071{
91447636 1072 int count;
1c79356b
A
1073 struct vnode *nvp;
1074 struct vnode **vpp;
1075 struct vnode **dircache;
1076 struct union_node *un;
1077 int error;
1078
1c79356b
A
1079 dircache = VTOUNION(vp)->un_dircache;
1080
1081 nvp = NULLVP;
1082
1083 if (dircache == 0) {
91447636
A
1084 count = 0;
1085 union_dircache_r(vp, 0, &count);
1086 count++;
1c79356b 1087 dircache = (struct vnode **)
91447636 1088 _MALLOC(count * sizeof(struct vnode *),
1c79356b
A
1089 M_TEMP, M_WAITOK);
1090 vpp = dircache;
91447636 1091 union_dircache_r(vp, &vpp, &count);
1c79356b
A
1092 *vpp = NULLVP;
1093 vpp = dircache + 1;
1094 } else {
1095 vpp = dircache;
1096 do {
1097 if (*vpp++ == VTOUNION(vp)->un_uppervp)
1098 break;
1099 } while (*vpp != NULLVP);
1100 }
1101
1102 if (*vpp == NULLVP)
1103 goto out;
1104
91447636 1105 vnode_get(*vpp);
1c79356b
A
1106 error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0);
1107 if (error)
1108 goto out;
1109
1110 VTOUNION(vp)->un_dircache = 0;
1111 un = VTOUNION(nvp);
1112 un->un_dircache = dircache;
1113
1114out:
1c79356b
A
1115 return (nvp);
1116}