]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
b0d623f7 | 2 | * Copyright (c) 2000-2008 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 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 License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ | |
29 | /* | |
30 | * Copyright (c) 1994 Jan-Simon Pendry | |
31 | * Copyright (c) 1994 | |
32 | * The Regents of the University of California. All rights reserved. | |
33 | * | |
34 | * This code is derived from software contributed to Berkeley by | |
35 | * Jan-Simon Pendry. | |
36 | * | |
37 | * Redistribution and use in source and binary forms, with or without | |
38 | * modification, are permitted provided that the following conditions | |
39 | * are met: | |
40 | * 1. Redistributions of source code must retain the above copyright | |
41 | * notice, this list of conditions and the following disclaimer. | |
42 | * 2. Redistributions in binary form must reproduce the above copyright | |
43 | * notice, this list of conditions and the following disclaimer in the | |
44 | * documentation and/or other materials provided with the distribution. | |
45 | * 3. All advertising materials mentioning features or use of this software | |
46 | * must display the following acknowledgement: | |
47 | * This product includes software developed by the University of | |
48 | * California, Berkeley and its contributors. | |
49 | * 4. Neither the name of the University nor the names of its contributors | |
50 | * may be used to endorse or promote products derived from this software | |
51 | * without specific prior written permission. | |
52 | * | |
53 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
54 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
55 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
56 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
57 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
58 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
59 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
60 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
61 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
62 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
63 | * SUCH DAMAGE. | |
64 | * | |
65 | * @(#)union_subr.c 8.20 (Berkeley) 5/20/95 | |
66 | */ | |
2d21ac55 A |
67 | /* |
68 | * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce | |
69 | * support for mandatory and extensible security protections. This notice | |
70 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
71 | * Version 2.0. | |
72 | */ | |
1c79356b A |
73 | |
74 | #include <sys/param.h> | |
75 | #include <sys/systm.h> | |
91447636 A |
76 | #include <sys/proc_internal.h> |
77 | #include <sys/kauth.h> | |
1c79356b A |
78 | #include <sys/time.h> |
79 | #include <sys/kernel.h> | |
91447636 | 80 | #include <sys/vnode_internal.h> |
1c79356b A |
81 | #include <sys/namei.h> |
82 | #include <sys/malloc.h> | |
2d21ac55 | 83 | #include <sys/file_internal.h> |
1c79356b A |
84 | #include <sys/filedesc.h> |
85 | #include <sys/queue.h> | |
91447636 | 86 | #include <sys/mount_internal.h> |
1c79356b A |
87 | #include <sys/stat.h> |
88 | #include <sys/ubc.h> | |
91447636 | 89 | #include <sys/uio_internal.h> |
1c79356b | 90 | #include <miscfs/union/union.h> |
2d21ac55 A |
91 | #include <sys/lock.h> |
92 | #include <sys/kdebug.h> | |
93 | #if CONFIG_MACF | |
94 | #include <security/mac_framework.h> | |
1c79356b A |
95 | #endif |
96 | ||
2d21ac55 A |
97 | |
98 | static int union_vn_close(struct vnode *vp, int fmode, vfs_context_t ctx); | |
99 | ||
1c79356b A |
100 | /* must be power of two, otherwise change UNION_HASH() */ |
101 | #define NHASH 32 | |
102 | ||
103 | /* unsigned int ... */ | |
104 | #define UNION_HASH(u, l) \ | |
b0d623f7 | 105 | (((((uintptr_t) (u)) + ((uintptr_t) l)) >> 8) & (NHASH-1)) |
1c79356b A |
106 | |
107 | static LIST_HEAD(unhead, union_node) unhead[NHASH]; | |
108 | static int unvplock[NHASH]; | |
109 | ||
2d21ac55 A |
110 | static lck_grp_t * union_lck_grp; |
111 | static lck_grp_attr_t * union_lck_grp_attr; | |
112 | static lck_attr_t * union_lck_attr; | |
113 | static lck_mtx_t * union_mtxp; | |
114 | ||
115 | static int union_dircheck(struct vnode **, struct fileproc *, vfs_context_t ctx); | |
116 | static void union_newlower(struct union_node *, struct vnode *); | |
117 | static void union_newupper(struct union_node *, struct vnode *); | |
118 | ||
119 | ||
1c79356b | 120 | int |
2d21ac55 | 121 | union_init(__unused struct vfsconf *vfsp) |
1c79356b A |
122 | { |
123 | int i; | |
124 | ||
2d21ac55 A |
125 | union_lck_grp_attr= lck_grp_attr_alloc_init(); |
126 | #if DIAGNOSTIC | |
127 | lck_grp_attr_setstat(union_lck_grp_attr); | |
128 | #endif | |
129 | union_lck_grp = lck_grp_alloc_init("union", union_lck_grp_attr); | |
130 | union_lck_attr = lck_attr_alloc_init(); | |
131 | #if DIAGNOSTIC | |
132 | lck_attr_setdebug(union_lck_attr); | |
133 | #endif | |
134 | union_mtxp = lck_mtx_alloc_init(union_lck_grp, union_lck_attr); | |
135 | ||
1c79356b A |
136 | for (i = 0; i < NHASH; i++) |
137 | LIST_INIT(&unhead[i]); | |
138 | bzero((caddr_t) unvplock, sizeof(unvplock)); | |
2d21ac55 A |
139 | /* add the hook for getdirentries */ |
140 | union_dircheckp = union_dircheck; | |
141 | ||
142 | return (0); | |
1c79356b A |
143 | } |
144 | ||
2d21ac55 A |
145 | void |
146 | union_lock() | |
147 | { | |
148 | lck_mtx_lock(union_mtxp); | |
149 | } | |
150 | ||
151 | void | |
152 | union_unlock() | |
153 | { | |
154 | lck_mtx_unlock(union_mtxp); | |
155 | } | |
156 | ||
157 | ||
1c79356b | 158 | static int |
2d21ac55 | 159 | union_list_lock(int ix) |
1c79356b A |
160 | { |
161 | ||
2d21ac55 A |
162 | if (unvplock[ix] & UNVP_LOCKED) { |
163 | unvplock[ix] |= UNVP_WANT; | |
164 | msleep((caddr_t) &unvplock[ix], union_mtxp, PINOD, "union_list_lock", NULL); | |
1c79356b A |
165 | return (1); |
166 | } | |
167 | ||
2d21ac55 | 168 | unvplock[ix] |= UNVP_LOCKED; |
1c79356b A |
169 | |
170 | return (0); | |
171 | } | |
172 | ||
173 | static void | |
2d21ac55 | 174 | union_list_unlock(int ix) |
1c79356b A |
175 | { |
176 | ||
2d21ac55 | 177 | unvplock[ix] &= ~UNVP_LOCKED; |
1c79356b | 178 | |
2d21ac55 A |
179 | if (unvplock[ix] & UNVP_WANT) { |
180 | unvplock[ix] &= ~UNVP_WANT; | |
1c79356b A |
181 | wakeup((caddr_t) &unvplock[ix]); |
182 | } | |
183 | } | |
184 | ||
2d21ac55 A |
185 | /* |
186 | * union_updatevp: | |
187 | * | |
188 | * The uppervp, if not NULL, must be referenced and not locked by us | |
189 | * The lowervp, if not NULL, must be referenced. | |
190 | * | |
191 | * If uppervp and lowervp match pointers already installed, then | |
192 | * nothing happens. The passed vp's (when matching) are not adjusted. | |
193 | * | |
194 | * This routine may only be called by union_newupper() and | |
195 | * union_newlower(). | |
196 | */ | |
197 | ||
198 | /* always called with union lock held */ | |
1c79356b | 199 | void |
2d21ac55 A |
200 | union_updatevp(struct union_node *un, struct vnode *uppervp, |
201 | struct vnode *lowervp) | |
1c79356b A |
202 | { |
203 | int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp); | |
204 | int nhash = UNION_HASH(uppervp, lowervp); | |
205 | int docache = (lowervp != NULLVP || uppervp != NULLVP); | |
91447636 | 206 | int lhash, uhash; |
2d21ac55 A |
207 | vnode_t freevp; |
208 | vnode_t freedirvp; | |
209 | caddr_t freepath; | |
1c79356b A |
210 | |
211 | /* | |
212 | * Ensure locking is ordered from lower to higher | |
213 | * to avoid deadlocks. | |
214 | */ | |
215 | if (nhash < ohash) { | |
216 | lhash = nhash; | |
217 | uhash = ohash; | |
218 | } else { | |
219 | lhash = ohash; | |
220 | uhash = nhash; | |
221 | } | |
222 | ||
2d21ac55 | 223 | if (lhash != uhash) { |
1c79356b A |
224 | while (union_list_lock(lhash)) |
225 | continue; | |
2d21ac55 | 226 | } |
1c79356b A |
227 | |
228 | while (union_list_lock(uhash)) | |
229 | continue; | |
230 | ||
231 | if (ohash != nhash || !docache) { | |
232 | if (un->un_flags & UN_CACHED) { | |
233 | un->un_flags &= ~UN_CACHED; | |
234 | LIST_REMOVE(un, un_cache); | |
235 | } | |
236 | } | |
237 | ||
238 | if (ohash != nhash) | |
239 | union_list_unlock(ohash); | |
240 | ||
241 | if (un->un_lowervp != lowervp) { | |
2d21ac55 A |
242 | freevp = freedirvp = NULLVP; |
243 | freepath = (caddr_t)0; | |
1c79356b | 244 | if (un->un_lowervp) { |
2d21ac55 A |
245 | freevp = un->un_lowervp; |
246 | un->un_lowervp = lowervp; | |
1c79356b | 247 | if (un->un_path) { |
2d21ac55 | 248 | freepath = un->un_path; |
1c79356b A |
249 | un->un_path = 0; |
250 | } | |
251 | if (un->un_dirvp) { | |
2d21ac55 | 252 | freedirvp = un->un_dirvp; |
1c79356b A |
253 | un->un_dirvp = NULLVP; |
254 | } | |
2d21ac55 A |
255 | union_unlock(); |
256 | if (freevp) | |
257 | vnode_put(freevp); | |
258 | if (freedirvp) | |
259 | vnode_put(freedirvp); | |
260 | if (freepath) | |
261 | _FREE(un->un_path, M_TEMP); | |
262 | union_lock(); | |
263 | } else | |
264 | un->un_lowervp = lowervp; | |
265 | if (lowervp != NULLVP) | |
266 | un->un_lowervid = vnode_vid(lowervp); | |
1c79356b A |
267 | un->un_lowersz = VNOVAL; |
268 | } | |
269 | ||
270 | if (un->un_uppervp != uppervp) { | |
2d21ac55 A |
271 | freevp = NULLVP; |
272 | if (un->un_uppervp) { | |
273 | freevp = un->un_uppervp; | |
274 | } | |
1c79356b | 275 | un->un_uppervp = uppervp; |
2d21ac55 A |
276 | if (uppervp != NULLVP) |
277 | un->un_uppervid = vnode_vid(uppervp); | |
1c79356b | 278 | un->un_uppersz = VNOVAL; |
2d21ac55 A |
279 | union_unlock(); |
280 | if (freevp) | |
281 | vnode_put(freevp); | |
282 | union_lock(); | |
1c79356b A |
283 | } |
284 | ||
285 | if (docache && (ohash != nhash)) { | |
286 | LIST_INSERT_HEAD(&unhead[nhash], un, un_cache); | |
287 | un->un_flags |= UN_CACHED; | |
288 | } | |
289 | ||
290 | union_list_unlock(nhash); | |
291 | } | |
292 | ||
2d21ac55 A |
293 | /* |
294 | * Set a new lowervp. The passed lowervp must be referenced and will be | |
295 | * stored in the vp in a referenced state. | |
296 | */ | |
297 | /* always called with union lock held */ | |
298 | ||
299 | static void | |
1c79356b A |
300 | union_newlower(un, lowervp) |
301 | struct union_node *un; | |
302 | struct vnode *lowervp; | |
303 | { | |
1c79356b A |
304 | union_updatevp(un, un->un_uppervp, lowervp); |
305 | } | |
306 | ||
2d21ac55 A |
307 | /* |
308 | * Set a new uppervp. The passed uppervp must be locked and will be | |
309 | * stored in the vp in a locked state. The caller should not unlock | |
310 | * uppervp. | |
311 | */ | |
312 | ||
313 | /* always called with union lock held */ | |
314 | static void | |
1c79356b A |
315 | union_newupper(un, uppervp) |
316 | struct union_node *un; | |
317 | struct vnode *uppervp; | |
318 | { | |
1c79356b A |
319 | union_updatevp(un, uppervp, un->un_lowervp); |
320 | } | |
321 | ||
322 | /* | |
323 | * Keep track of size changes in the underlying vnodes. | |
324 | * If the size changes, then callback to the vm layer | |
325 | * giving priority to the upper layer size. | |
326 | */ | |
2d21ac55 | 327 | /* always called with union lock held */ |
1c79356b A |
328 | void |
329 | union_newsize(vp, uppersz, lowersz) | |
330 | struct vnode *vp; | |
331 | off_t uppersz, lowersz; | |
332 | { | |
333 | struct union_node *un; | |
334 | off_t sz; | |
335 | ||
336 | /* only interested in regular files */ | |
337 | if (vp->v_type != VREG) | |
338 | return; | |
339 | ||
340 | un = VTOUNION(vp); | |
341 | sz = VNOVAL; | |
342 | ||
343 | if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) { | |
344 | un->un_uppersz = uppersz; | |
345 | if (sz == VNOVAL) | |
346 | sz = un->un_uppersz; | |
347 | } | |
348 | ||
349 | if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) { | |
350 | un->un_lowersz = lowersz; | |
351 | if (sz == VNOVAL) | |
352 | sz = un->un_lowersz; | |
353 | } | |
354 | ||
355 | if (sz != VNOVAL) { | |
356 | #ifdef UNION_DIAGNOSTIC | |
357 | printf("union: %s size now %ld\n", | |
358 | uppersz != VNOVAL ? "upper" : "lower", (long) sz); | |
359 | #endif | |
2d21ac55 | 360 | union_unlock(); |
91447636 | 361 | ubc_setsize(vp, sz); |
2d21ac55 | 362 | union_lock(); |
1c79356b A |
363 | } |
364 | } | |
365 | ||
366 | /* | |
2d21ac55 A |
367 | * union_allocvp: allocate a union_node and associate it with a |
368 | * parent union_node and one or two vnodes. | |
369 | * | |
370 | * vpp Holds the returned vnode locked and referenced if no | |
371 | * error occurs. | |
372 | * | |
373 | * mp Holds the mount point. mp may or may not be busied. | |
374 | * allocvp() makes no changes to mp. | |
375 | * | |
376 | * dvp Holds the parent union_node to the one we wish to create. | |
377 | * XXX may only be used to traverse an uncopied lowervp-based | |
378 | * tree? XXX | |
1c79356b | 379 | * |
2d21ac55 A |
380 | * dvp may or may not be locked. allocvp() makes no changes |
381 | * to dvp. | |
382 | * | |
383 | * upperdvp Holds the parent vnode to uppervp, generally used along | |
384 | * with path component information to create a shadow of | |
385 | * lowervp when uppervp does not exist. | |
386 | * | |
387 | * upperdvp is referenced but unlocked on entry, and will be | |
388 | * dereferenced on return. | |
389 | * | |
390 | * uppervp Holds the new uppervp vnode to be stored in the | |
391 | * union_node we are allocating. uppervp is referenced but | |
392 | * not locked, and will be dereferenced on return. | |
393 | * | |
394 | * lowervp Holds the new lowervp vnode to be stored in the | |
395 | * union_node we are allocating. lowervp is referenced but | |
396 | * not locked, and will be dereferenced on return. | |
397 | * | |
398 | * cnp Holds path component information to be coupled with | |
399 | * lowervp and upperdvp to allow unionfs to create an uppervp | |
400 | * later on. Only used if lowervp is valid. The contents | |
401 | * of cnp is only valid for the duration of the call. | |
402 | * | |
403 | * docache Determine whether this node should be entered in the | |
404 | * cache or whether it should be destroyed as soon as possible. | |
405 | * | |
406 | * All union_nodes are maintained on a singly-linked | |
407 | * list. New nodes are only allocated when they cannot | |
408 | * be found on this list. Entries on the list are | |
1c79356b A |
409 | * removed when the vfs reclaim entry is called. |
410 | * | |
2d21ac55 | 411 | * A single lock is kept for the entire list. This is |
1c79356b A |
412 | * needed because the getnewvnode() function can block |
413 | * waiting for a vnode to become free, in which case there | |
414 | * may be more than one process trying to get the same | |
2d21ac55 A |
415 | * vnode. This lock is only taken if we are going to |
416 | * call getnewvnode(), since the kernel itself is single-threaded. | |
1c79356b | 417 | * |
2d21ac55 A |
418 | * If an entry is found on the list, then call vget() to |
419 | * take a reference. This is done because there may be | |
1c79356b A |
420 | * zero references to it and so it needs to removed from |
421 | * the vnode free list. | |
422 | */ | |
2d21ac55 A |
423 | |
424 | /* always called with union lock held */ | |
425 | ||
1c79356b | 426 | int |
2d21ac55 A |
427 | union_allocvp(struct vnode **vpp, |
428 | struct mount *mp, | |
429 | struct vnode *undvp, | |
430 | struct vnode *dvp, | |
431 | struct componentname *cnp, | |
432 | struct vnode *uppervp, | |
433 | struct vnode *lowervp, | |
434 | int docache) | |
1c79356b A |
435 | { |
436 | int error; | |
2d21ac55 A |
437 | struct union_node *un = NULL; |
438 | struct union_node *unp; | |
1c79356b A |
439 | struct vnode *xlowervp = NULLVP; |
440 | struct union_mount *um = MOUNTTOUNIONMOUNT(mp); | |
2d21ac55 | 441 | int hash = 0; /* protected by docache */ |
91447636 | 442 | int markroot; |
1c79356b | 443 | int try; |
91447636 A |
444 | struct vnode_fsparam vfsp; |
445 | enum vtype vtype; | |
1c79356b A |
446 | |
447 | if (uppervp == NULLVP && lowervp == NULLVP) | |
448 | panic("union: unidentifiable allocation"); | |
449 | ||
2d21ac55 A |
450 | /* |
451 | * if both upper and lower vp are provided and are off different type | |
452 | * consider lowervp as NULL | |
453 | */ | |
1c79356b A |
454 | if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) { |
455 | xlowervp = lowervp; | |
456 | lowervp = NULLVP; | |
457 | } | |
458 | ||
459 | /* detect the root vnode (and aliases) */ | |
91447636 | 460 | markroot = 0; |
1c79356b A |
461 | if ((uppervp == um->um_uppervp) && |
462 | ((lowervp == NULLVP) || lowervp == um->um_lowervp)) { | |
463 | if (lowervp == NULLVP) { | |
464 | lowervp = um->um_lowervp; | |
2d21ac55 A |
465 | if (lowervp != NULLVP) { |
466 | union_unlock(); | |
91447636 | 467 | vnode_get(lowervp); |
2d21ac55 A |
468 | union_lock(); |
469 | } | |
1c79356b | 470 | } |
91447636 | 471 | markroot = VROOT; |
1c79356b A |
472 | } |
473 | ||
474 | loop: | |
475 | if (!docache) { | |
2d21ac55 | 476 | un = NULL; |
1c79356b A |
477 | } else for (try = 0; try < 3; try++) { |
478 | switch (try) { | |
479 | case 0: | |
480 | if (lowervp == NULLVP) | |
481 | continue; | |
482 | hash = UNION_HASH(uppervp, lowervp); | |
483 | break; | |
484 | ||
485 | case 1: | |
486 | if (uppervp == NULLVP) | |
487 | continue; | |
488 | hash = UNION_HASH(uppervp, NULLVP); | |
489 | break; | |
490 | ||
491 | case 2: | |
492 | if (lowervp == NULLVP) | |
493 | continue; | |
2d21ac55 | 494 | /* Not sure how this path gets exercised ? */ |
1c79356b A |
495 | hash = UNION_HASH(NULLVP, lowervp); |
496 | break; | |
497 | } | |
498 | ||
499 | while (union_list_lock(hash)) | |
500 | continue; | |
501 | ||
502 | for (un = unhead[hash].lh_first; un != 0; | |
503 | un = un->un_cache.le_next) { | |
504 | if ((un->un_lowervp == lowervp || | |
505 | un->un_lowervp == NULLVP) && | |
506 | (un->un_uppervp == uppervp || | |
507 | un->un_uppervp == NULLVP) && | |
2d21ac55 | 508 | (un->un_mount == mp)) { |
1c79356b A |
509 | break; |
510 | } | |
511 | } | |
512 | ||
513 | union_list_unlock(hash); | |
514 | ||
515 | if (un) | |
516 | break; | |
517 | } | |
518 | ||
519 | if (un) { | |
520 | /* | |
521 | * Obtain a lock on the union_node. | |
522 | * uppervp is locked, though un->un_uppervp | |
523 | * may not be. this doesn't break the locking | |
524 | * hierarchy since in the case that un->un_uppervp | |
91447636 | 525 | * is not yet locked it will be vnode_put'd and replaced |
1c79356b A |
526 | * with uppervp. |
527 | */ | |
528 | ||
2d21ac55 A |
529 | if (un->un_flags & UN_LOCKED) { |
530 | un->un_flags |= UN_WANT; | |
531 | msleep((caddr_t) &un->un_flags, union_mtxp, PINOD, "union node locked", 0); | |
532 | goto loop; | |
533 | } | |
534 | un->un_flags |= UN_LOCKED; | |
535 | ||
536 | union_unlock(); | |
537 | if (UNIONTOV(un) == NULLVP) | |
538 | panic("null vnode in union node\n"); | |
539 | if (vnode_get(UNIONTOV(un))) { | |
540 | union_lock(); | |
541 | un->un_flags &= ~UN_LOCKED; | |
542 | if ((un->un_flags & UN_WANT) == UN_WANT) { | |
543 | un->un_flags &= ~UN_LOCKED; | |
544 | wakeup(&un->un_flags); | |
1c79356b | 545 | } |
2d21ac55 | 546 | goto loop; |
1c79356b | 547 | } |
2d21ac55 | 548 | union_lock(); |
1c79356b A |
549 | |
550 | /* | |
551 | * At this point, the union_node is locked, | |
552 | * un->un_uppervp may not be locked, and uppervp | |
553 | * is locked or nil. | |
554 | */ | |
555 | ||
556 | /* | |
557 | * Save information about the upper layer. | |
558 | */ | |
559 | if (uppervp != un->un_uppervp) { | |
560 | union_newupper(un, uppervp); | |
561 | } else if (uppervp) { | |
2d21ac55 | 562 | union_unlock(); |
91447636 | 563 | vnode_put(uppervp); |
2d21ac55 | 564 | union_lock(); |
1c79356b A |
565 | } |
566 | ||
567 | /* | |
568 | * Save information about the lower layer. | |
569 | * This needs to keep track of pathname | |
570 | * and directory information which union_vn_create | |
571 | * might need. | |
572 | */ | |
573 | if (lowervp != un->un_lowervp) { | |
574 | union_newlower(un, lowervp); | |
575 | if (cnp && (lowervp != NULLVP)) { | |
576 | un->un_hash = cnp->cn_hash; | |
2d21ac55 | 577 | union_unlock(); |
1c79356b A |
578 | MALLOC(un->un_path, caddr_t, cnp->cn_namelen+1, |
579 | M_TEMP, M_WAITOK); | |
580 | bcopy(cnp->cn_nameptr, un->un_path, | |
581 | cnp->cn_namelen); | |
91447636 | 582 | vnode_get(dvp); |
2d21ac55 A |
583 | union_lock(); |
584 | un->un_path[cnp->cn_namelen] = '\0'; | |
1c79356b A |
585 | un->un_dirvp = dvp; |
586 | } | |
587 | } else if (lowervp) { | |
2d21ac55 | 588 | union_unlock(); |
91447636 | 589 | vnode_put(lowervp); |
2d21ac55 | 590 | union_lock(); |
1c79356b A |
591 | } |
592 | *vpp = UNIONTOV(un); | |
2d21ac55 A |
593 | un->un_flags &= ~UN_LOCKED; |
594 | if ((un->un_flags & UN_WANT) == UN_WANT) { | |
595 | un->un_flags &= ~UN_WANT; | |
596 | wakeup(&un->un_flags); | |
597 | } | |
1c79356b A |
598 | return (0); |
599 | } | |
600 | ||
601 | if (docache) { | |
602 | /* | |
603 | * otherwise lock the vp list while we call getnewvnode | |
604 | * since that can block. | |
605 | */ | |
606 | hash = UNION_HASH(uppervp, lowervp); | |
607 | ||
608 | if (union_list_lock(hash)) | |
609 | goto loop; | |
610 | } | |
611 | ||
2d21ac55 | 612 | union_unlock(); |
1c79356b | 613 | MALLOC(unp, void *, sizeof(struct union_node), M_TEMP, M_WAITOK); |
2d21ac55 A |
614 | union_lock(); |
615 | ||
616 | bzero(unp, sizeof(struct union_node)); | |
617 | un = unp; | |
618 | un->un_uppervp = uppervp; | |
619 | if (uppervp != NULLVP) | |
620 | un->un_uppervid = vnode_vid(uppervp); | |
621 | un->un_uppersz = VNOVAL; | |
622 | un->un_lowervp = lowervp; | |
623 | if (lowervp != NULLVP) | |
624 | un->un_lowervid = vnode_vid(lowervp); | |
625 | un->un_lowersz = VNOVAL; | |
626 | un->un_pvp = undvp; | |
627 | if (undvp != NULLVP) | |
628 | vnode_get(undvp); | |
629 | un->un_dircache = 0; | |
630 | un->un_openl = 0; | |
631 | un->un_mount = mp; | |
632 | un->un_flags = UN_LOCKED; | |
633 | #ifdef FAULTFS | |
634 | if (UNION_FAULTIN(um)) | |
635 | un->un_flags |= UN_FAULTFS; | |
636 | #endif | |
637 | ||
638 | if (docache) { | |
639 | /* Insert with lock held */ | |
640 | LIST_INSERT_HEAD(&unhead[hash], un, un_cache); | |
641 | un->un_flags |= UN_CACHED; | |
642 | union_list_unlock(hash); | |
643 | } | |
644 | ||
645 | union_unlock(); | |
91447636 A |
646 | |
647 | if (uppervp) | |
648 | vtype = uppervp->v_type; | |
649 | else | |
650 | vtype = lowervp->v_type; | |
2d21ac55 A |
651 | |
652 | bzero(&vfsp, sizeof(struct vnode_fsparam)); | |
91447636 A |
653 | vfsp.vnfs_mp = mp; |
654 | vfsp.vnfs_vtype = vtype; | |
655 | vfsp.vnfs_str = "unionfs"; | |
2d21ac55 | 656 | vfsp.vnfs_dvp = undvp; |
91447636 A |
657 | vfsp.vnfs_fsnode = unp; |
658 | vfsp.vnfs_cnp = cnp; | |
659 | vfsp.vnfs_vops = union_vnodeop_p; | |
660 | vfsp.vnfs_rdev = 0; | |
661 | vfsp.vnfs_filesize = 0; | |
662 | vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE; | |
663 | vfsp.vnfs_marksystem = 0; | |
664 | vfsp.vnfs_markroot = markroot; | |
665 | ||
666 | error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, vpp); | |
1c79356b | 667 | if (error) { |
2d21ac55 | 668 | /* XXXXX Is this right ???? XXXXXXX */ |
1c79356b | 669 | if (uppervp) { |
91447636 | 670 | vnode_put(uppervp); |
1c79356b A |
671 | } |
672 | if (lowervp) | |
91447636 | 673 | vnode_put(lowervp); |
1c79356b | 674 | |
2d21ac55 A |
675 | union_lock(); |
676 | if (un->un_flags & UN_CACHED) { | |
677 | un->un_flags &= ~UN_CACHED; | |
678 | LIST_REMOVE(un, un_cache); | |
679 | } | |
680 | if (docache) | |
681 | union_list_unlock(hash); | |
682 | ||
683 | FREE(unp, M_TEMP); | |
684 | ||
685 | return (error); | |
1c79356b A |
686 | } |
687 | ||
1c79356b A |
688 | if (cnp && (lowervp != NULLVP)) { |
689 | un->un_hash = cnp->cn_hash; | |
690 | un->un_path = _MALLOC(cnp->cn_namelen+1, M_TEMP, M_WAITOK); | |
691 | bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen); | |
692 | un->un_path[cnp->cn_namelen] = '\0'; | |
91447636 | 693 | vnode_get(dvp); |
1c79356b A |
694 | un->un_dirvp = dvp; |
695 | } else { | |
696 | un->un_hash = 0; | |
697 | un->un_path = 0; | |
698 | un->un_dirvp = 0; | |
699 | } | |
700 | ||
1c79356b | 701 | if (xlowervp) |
91447636 | 702 | vnode_put(xlowervp); |
1c79356b | 703 | |
2d21ac55 A |
704 | union_lock(); |
705 | ||
706 | vnode_settag(*vpp, VT_UNION); | |
707 | un->un_vnode = *vpp; | |
708 | if (un->un_vnode->v_type == VDIR) { | |
709 | if (un->un_uppervp == NULLVP) { | |
710 | panic("faulting fs and no upper vp for dir?"); | |
711 | } | |
712 | ||
713 | } | |
714 | ||
715 | ||
716 | un->un_flags &= ~UN_LOCKED; | |
717 | if ((un->un_flags & UN_WANT) == UN_WANT) { | |
718 | un->un_flags &= ~UN_WANT; | |
719 | wakeup(&un->un_flags); | |
720 | } | |
721 | ||
722 | return(error); | |
1c79356b | 723 | |
1c79356b A |
724 | } |
725 | ||
2d21ac55 | 726 | /* always called with union lock held */ |
1c79356b | 727 | int |
2d21ac55 | 728 | union_freevp(struct vnode *vp) |
1c79356b A |
729 | { |
730 | struct union_node *un = VTOUNION(vp); | |
731 | ||
732 | if (un->un_flags & UN_CACHED) { | |
733 | un->un_flags &= ~UN_CACHED; | |
734 | LIST_REMOVE(un, un_cache); | |
735 | } | |
736 | ||
2d21ac55 | 737 | union_unlock(); |
1c79356b | 738 | if (un->un_pvp != NULLVP) |
91447636 | 739 | vnode_put(un->un_pvp); |
1c79356b | 740 | if (un->un_uppervp != NULLVP) |
91447636 | 741 | vnode_put(un->un_uppervp); |
1c79356b | 742 | if (un->un_lowervp != NULLVP) |
91447636 | 743 | vnode_put(un->un_lowervp); |
1c79356b | 744 | if (un->un_dirvp != NULLVP) |
91447636 | 745 | vnode_put(un->un_dirvp); |
1c79356b A |
746 | if (un->un_path) |
747 | _FREE(un->un_path, M_TEMP); | |
748 | ||
749 | FREE(vp->v_data, M_TEMP); | |
750 | vp->v_data = 0; | |
2d21ac55 | 751 | union_lock(); |
1c79356b A |
752 | |
753 | return (0); | |
754 | } | |
755 | ||
756 | /* | |
757 | * copyfile. copy the vnode (fvp) to the vnode (tvp) | |
758 | * using a sequence of reads and writes. both (fvp) | |
759 | * and (tvp) are locked on entry and exit. | |
760 | */ | |
2d21ac55 | 761 | /* called with no union lock held */ |
1c79356b | 762 | int |
2d21ac55 | 763 | union_copyfile(struct vnode *fvp, struct vnode *tvp, vfs_context_t context) |
1c79356b | 764 | { |
91447636 | 765 | char *bufp; |
b0d623f7 A |
766 | struct uio *auio; |
767 | char uio_buf [ UIO_SIZEOF(1) ]; | |
1c79356b A |
768 | int error = 0; |
769 | ||
770 | /* | |
771 | * strategy: | |
772 | * allocate a buffer of size MAXPHYSIO. | |
773 | * loop doing reads and writes, keeping track | |
774 | * of the current uio offset. | |
775 | * give up at the first sign of trouble. | |
776 | */ | |
777 | ||
b0d623f7 A |
778 | auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, |
779 | UIO_READ /* will change */, &uio_buf, sizeof(uio_buf)); | |
1c79356b | 780 | |
91447636 | 781 | bufp = _MALLOC(MAXPHYSIO, M_TEMP, M_WAITOK); |
b0d623f7 A |
782 | if (bufp == NULL) { |
783 | return ENOMEM; | |
784 | } | |
1c79356b A |
785 | |
786 | /* ugly loop follows... */ | |
787 | do { | |
b0d623f7 | 788 | off_t offset = uio_offset(auio); |
1c79356b | 789 | |
b0d623f7 A |
790 | uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ); |
791 | uio_addiov(auio, (uintptr_t)bufp, MAXPHYSIO); | |
792 | error = VNOP_READ(fvp, auio, 0, context); | |
1c79356b A |
793 | |
794 | if (error == 0) { | |
b0d623f7 A |
795 | user_ssize_t resid = uio_resid(auio); |
796 | ||
797 | uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE); | |
798 | uio_addiov(auio, (uintptr_t)bufp, MAXPHYSIO - resid); | |
799 | ||
800 | if (uio_resid(auio) == 0) | |
1c79356b A |
801 | break; |
802 | ||
803 | do { | |
b0d623f7 A |
804 | error = VNOP_WRITE(tvp, auio, 0, context); |
805 | } while ((uio_resid(auio) > 0) && (error == 0)); | |
1c79356b A |
806 | } |
807 | ||
808 | } while (error == 0); | |
809 | ||
91447636 | 810 | _FREE(bufp, M_TEMP); |
1c79356b A |
811 | return (error); |
812 | } | |
813 | ||
814 | /* | |
815 | * (un) is assumed to be locked on entry and remains | |
816 | * locked on exit. | |
817 | */ | |
2d21ac55 | 818 | /* always called with union lock held */ |
1c79356b | 819 | int |
2d21ac55 | 820 | union_copyup(struct union_node *un, int docopy, vfs_context_t context) |
1c79356b A |
821 | { |
822 | int error; | |
823 | struct vnode *lvp, *uvp; | |
2d21ac55 A |
824 | struct vnode_attr vattr; |
825 | mode_t cmode = 0; | |
1c79356b | 826 | |
2d21ac55 A |
827 | |
828 | lvp = un->un_lowervp; | |
1c79356b | 829 | |
2d21ac55 | 830 | union_unlock(); |
91447636 | 831 | |
2d21ac55 A |
832 | if (UNNODE_FAULTIN(un)) { |
833 | /* Need to inherit exec mode in faulting fs */ | |
834 | VATTR_INIT(&vattr); | |
835 | VATTR_WANTED(&vattr, va_flags); | |
836 | if (vnode_getattr(lvp, &vattr, context) == 0 ) | |
837 | cmode = vattr.va_mode; | |
838 | ||
839 | } | |
840 | error = union_vn_create(&uvp, un, cmode, context); | |
841 | if (error) { | |
842 | union_lock(); | |
843 | if (error == EEXIST) { | |
844 | if (uvp != NULLVP) { | |
845 | union_newupper(un, uvp); | |
846 | error = 0; | |
847 | } | |
848 | } | |
849 | return (error); | |
850 | } | |
851 | ||
852 | union_lock(); | |
1c79356b A |
853 | /* at this point, uppervp is locked */ |
854 | union_newupper(un, uvp); | |
2d21ac55 | 855 | union_unlock(); |
1c79356b | 856 | |
1c79356b A |
857 | |
858 | if (docopy) { | |
859 | /* | |
860 | * XX - should not ignore errors | |
91447636 | 861 | * from vnop_close |
1c79356b | 862 | */ |
2d21ac55 | 863 | error = VNOP_OPEN(lvp, FREAD, context); |
1c79356b | 864 | if (error == 0) { |
2d21ac55 A |
865 | error = union_copyfile(lvp, uvp, context); |
866 | (void) VNOP_CLOSE(lvp, FREAD, context); | |
1c79356b A |
867 | } |
868 | #ifdef UNION_DIAGNOSTIC | |
869 | if (error == 0) | |
870 | uprintf("union: copied up %s\n", un->un_path); | |
871 | #endif | |
872 | ||
873 | } | |
2d21ac55 | 874 | union_vn_close(uvp, FWRITE, context); |
1c79356b A |
875 | |
876 | /* | |
877 | * Subsequent IOs will go to the top layer, so | |
878 | * call close on the lower vnode and open on the | |
879 | * upper vnode to ensure that the filesystem keeps | |
880 | * its references counts right. This doesn't do | |
881 | * the right thing with (cred) and (FREAD) though. | |
882 | * Ignoring error returns is not right, either. | |
883 | */ | |
2d21ac55 A |
884 | |
885 | /* No need to hold the lock as the union node should be locked for this(it is in faultin mode) */ | |
1c79356b A |
886 | if (error == 0) { |
887 | int i; | |
888 | ||
889 | for (i = 0; i < un->un_openl; i++) { | |
2d21ac55 A |
890 | (void) VNOP_CLOSE(lvp, FREAD, context); |
891 | (void) VNOP_OPEN(uvp, FREAD, context); | |
1c79356b A |
892 | } |
893 | un->un_openl = 0; | |
894 | } | |
895 | ||
2d21ac55 A |
896 | union_lock(); |
897 | ||
1c79356b A |
898 | return (error); |
899 | ||
900 | } | |
901 | ||
2d21ac55 A |
902 | |
903 | int | |
904 | union_faultin_copyup(struct vnode **vpp, vnode_t udvp, vnode_t lvp, struct componentname * cnp, vfs_context_t context) | |
905 | { | |
906 | int error; | |
907 | struct vnode *uvp; | |
908 | struct vnode_attr vattr; | |
909 | struct vnode_attr *vap; | |
910 | mode_t cmode = 0; | |
911 | int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL); | |
912 | struct proc * p = vfs_context_proc(context); | |
913 | struct componentname cn; | |
914 | ||
915 | ||
916 | vap = &vattr; | |
917 | VATTR_INIT(vap); | |
918 | VATTR_WANTED(vap, va_flags); | |
919 | if (vnode_getattr(lvp, vap, context) == 0 ) | |
920 | cmode = vattr.va_mode; | |
921 | ||
922 | *vpp = NULLVP; | |
923 | ||
924 | ||
925 | if (cmode == (mode_t)0) | |
926 | cmode = UN_FILEMODE & ~p->p_fd->fd_cmask; | |
927 | else | |
928 | cmode = cmode & ~p->p_fd->fd_cmask; | |
929 | ||
930 | ||
931 | /* | |
932 | * Build a new componentname structure (for the same | |
933 | * reasons outlines in union_mkshadow()). | |
934 | * The difference here is that the file is owned by | |
935 | * the current user, rather than by the person who | |
936 | * did the mount, since the current user needs to be | |
937 | * able to write the file (that's why it is being | |
938 | * copied in the first place). | |
939 | */ | |
940 | bzero(&cn, sizeof(struct componentname)); | |
941 | ||
942 | cn.cn_namelen = cnp->cn_namelen; | |
943 | cn.cn_pnbuf = (caddr_t) _MALLOC_ZONE(cn.cn_namelen+1, | |
944 | M_NAMEI, M_WAITOK); | |
945 | cn.cn_pnlen = cn.cn_namelen+1; | |
946 | bcopy(cnp->cn_nameptr, cn.cn_pnbuf, cn.cn_namelen+1); | |
947 | cn.cn_nameiop = CREATE; | |
948 | cn.cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN|UNIONCREATED); | |
949 | cn.cn_context = context; | |
950 | cn.cn_nameptr = cn.cn_pnbuf; | |
951 | cn.cn_hash = 0; | |
952 | cn.cn_consume = 0; | |
953 | ||
954 | /* | |
955 | * Pass dvp unlocked and referenced on call to relookup(). | |
956 | * | |
957 | * If an error occurs, dvp will be returned unlocked and dereferenced. | |
958 | */ | |
959 | if ((error = relookup(udvp, &uvp, &cn)) != 0) { | |
960 | goto out; | |
961 | } | |
962 | ||
963 | /* | |
964 | * If no error occurs, dvp will be returned locked with the reference | |
965 | * left as before, and vpp will be returned referenced and locked. | |
966 | */ | |
967 | if (uvp) { | |
968 | *vpp = uvp; | |
969 | error = EEXIST; | |
970 | goto out; | |
971 | } | |
972 | ||
973 | /* | |
974 | * Good - there was no race to create the file | |
975 | * so go ahead and create it. The permissions | |
976 | * on the file will be 0666 modified by the | |
977 | * current user's umask. Access to the file, while | |
978 | * it is unioned, will require access to the top *and* | |
979 | * bottom files. Access when not unioned will simply | |
980 | * require access to the top-level file. | |
981 | * | |
982 | * TODO: confirm choice of access permissions. | |
983 | * decide on authorisation behaviour | |
984 | */ | |
985 | ||
986 | VATTR_INIT(vap); | |
987 | VATTR_SET(vap, va_type, VREG); | |
988 | VATTR_SET(vap, va_mode, cmode); | |
989 | ||
990 | cn.cn_flags |= (UNIONCREATED); | |
991 | if ((error = vn_create(udvp, &uvp, &cn, vap, 0, context)) != 0) { | |
992 | goto out; | |
993 | } | |
994 | ||
995 | ||
996 | if ((error = VNOP_OPEN(uvp, fmode, context)) != 0) { | |
997 | vn_clearunionwait(uvp, 0); | |
998 | vnode_recycle(uvp); | |
999 | vnode_put(uvp); | |
1000 | goto out; | |
1001 | } | |
1002 | ||
1003 | error = vnode_ref_ext(uvp, fmode); | |
1004 | if (error ) { | |
1005 | vn_clearunionwait(uvp, 0); | |
1006 | VNOP_CLOSE(uvp, fmode, context); | |
1007 | vnode_recycle(uvp); | |
1008 | vnode_put(uvp); | |
1009 | goto out; | |
1010 | } | |
1011 | ||
1012 | ||
1013 | /* | |
1014 | * XX - should not ignore errors | |
1015 | * from vnop_close | |
1016 | */ | |
1017 | error = VNOP_OPEN(lvp, FREAD, context); | |
1018 | if (error == 0) { | |
1019 | error = union_copyfile(lvp, uvp, context); | |
1020 | (void) VNOP_CLOSE(lvp, FREAD, context); | |
1021 | } | |
1022 | ||
1023 | VNOP_CLOSE(uvp, fmode, context); | |
1024 | vnode_rele_ext(uvp, fmode, 0); | |
1025 | vn_clearunionwait(uvp, 0); | |
1026 | ||
1027 | *vpp = uvp; | |
1028 | out: | |
1029 | if ((cn.cn_flags & HASBUF) == HASBUF) { | |
1030 | FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI); | |
1031 | cn.cn_flags &= ~HASBUF; | |
1032 | } | |
1033 | return (error); | |
1034 | } | |
1035 | ||
1036 | ||
1037 | /* | |
1038 | * union_relookup: | |
1039 | * | |
1040 | * dvp should be locked on entry and will be locked on return. No | |
1041 | * net change in the ref count will occur. | |
1042 | * | |
1043 | * If an error is returned, *vpp will be invalid, otherwise it | |
1044 | * will hold a locked, referenced vnode. If *vpp == dvp then | |
1045 | * remember that only one exclusive lock is held. | |
1046 | */ | |
1047 | ||
1048 | /* No union lock held for this call */ | |
1c79356b | 1049 | static int |
2d21ac55 A |
1050 | union_relookup( |
1051 | #ifdef XXX_HELP_ME | |
1052 | struct union_mount *um, | |
1053 | #else /* !XXX_HELP_ME */ | |
1054 | __unused struct union_mount *um, | |
1055 | #endif /* !XXX_HELP_ME */ | |
1056 | struct vnode *dvp, | |
1057 | struct vnode **vpp, | |
1058 | struct componentname *cnp, | |
1059 | struct componentname *cn, | |
1060 | char *path, | |
1061 | int pathlen) | |
1c79356b A |
1062 | { |
1063 | int error; | |
1064 | ||
1065 | /* | |
1066 | * A new componentname structure must be faked up because | |
1067 | * there is no way to know where the upper level cnp came | |
1068 | * from or what it is being used for. This must duplicate | |
1069 | * some of the work done by NDINIT, some of the work done | |
1070 | * by namei, some of the work done by lookup and some of | |
91447636 | 1071 | * the work done by vnop_lookup when given a CREATE flag. |
1c79356b | 1072 | * Conclusion: Horrible. |
1c79356b A |
1073 | */ |
1074 | cn->cn_namelen = pathlen; | |
1075 | cn->cn_pnbuf = _MALLOC_ZONE(cn->cn_namelen+1, M_NAMEI, M_WAITOK); | |
1076 | cn->cn_pnlen = cn->cn_namelen+1; | |
1077 | bcopy(path, cn->cn_pnbuf, cn->cn_namelen); | |
1078 | cn->cn_pnbuf[cn->cn_namelen] = '\0'; | |
1079 | ||
1080 | cn->cn_nameiop = CREATE; | |
2d21ac55 | 1081 | cn->cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN ); |
91447636 | 1082 | #ifdef XXX_HELP_ME |
1c79356b A |
1083 | cn->cn_proc = cnp->cn_proc; |
1084 | if (um->um_op == UNMNT_ABOVE) | |
1085 | cn->cn_cred = cnp->cn_cred; | |
1086 | else | |
1087 | cn->cn_cred = um->um_cred; | |
91447636 A |
1088 | #endif |
1089 | cn->cn_context = cnp->cn_context; /* XXX !UNMNT_ABOVE case ??? */ | |
1c79356b | 1090 | cn->cn_nameptr = cn->cn_pnbuf; |
2d21ac55 | 1091 | cn->cn_hash = 0; |
1c79356b A |
1092 | cn->cn_consume = cnp->cn_consume; |
1093 | ||
91447636 | 1094 | vnode_get(dvp); |
1c79356b | 1095 | error = relookup(dvp, vpp, cn); |
2d21ac55 | 1096 | vnode_put(dvp); |
1c79356b A |
1097 | |
1098 | return (error); | |
1099 | } | |
1100 | ||
1101 | /* | |
1102 | * Create a shadow directory in the upper layer. | |
1103 | * The new vnode is returned locked. | |
1104 | * | |
1105 | * (um) points to the union mount structure for access to the | |
1106 | * the mounting process's credentials. | |
2d21ac55 A |
1107 | * (dvp) is the directory in which to create the shadow directory, |
1108 | * It is locked (but not ref'd) on entry and return. | |
1109 | * (cnp) is the component name to be created. | |
1c79356b | 1110 | * (vpp) is the returned newly created shadow directory, which |
2d21ac55 | 1111 | * is returned locked and ref'd |
1c79356b | 1112 | */ |
2d21ac55 | 1113 | /* No union lock held for this call */ |
1c79356b A |
1114 | int |
1115 | union_mkshadow(um, dvp, cnp, vpp) | |
1116 | struct union_mount *um; | |
1117 | struct vnode *dvp; | |
1118 | struct componentname *cnp; | |
1119 | struct vnode **vpp; | |
1120 | { | |
1121 | int error; | |
91447636 | 1122 | struct vnode_attr va; |
1c79356b A |
1123 | struct componentname cn; |
1124 | ||
2d21ac55 A |
1125 | bzero(&cn, sizeof(struct componentname)); |
1126 | ||
1127 | ||
1c79356b A |
1128 | error = union_relookup(um, dvp, vpp, cnp, &cn, |
1129 | cnp->cn_nameptr, cnp->cn_namelen); | |
2d21ac55 A |
1130 | if (error) |
1131 | goto out; | |
1c79356b A |
1132 | |
1133 | if (*vpp) { | |
2d21ac55 A |
1134 | error = EEXIST; |
1135 | goto out; | |
1c79356b A |
1136 | } |
1137 | ||
1138 | /* | |
2d21ac55 | 1139 | * Policy: when creating the shadow directory in the |
1c79356b A |
1140 | * upper layer, create it owned by the user who did |
1141 | * the mount, group from parent directory, and mode | |
1142 | * 777 modified by umask (ie mostly identical to the | |
1143 | * mkdir syscall). (jsp, kb) | |
1144 | */ | |
2d21ac55 | 1145 | |
91447636 A |
1146 | VATTR_INIT(&va); |
1147 | VATTR_SET(&va, va_type, VDIR); | |
1148 | VATTR_SET(&va, va_mode, um->um_cmode); | |
1c79356b | 1149 | |
91447636 | 1150 | error = vn_create(dvp, vpp, &cn, &va, 0, cnp->cn_context); |
2d21ac55 A |
1151 | out: |
1152 | if ((cn.cn_flags & HASBUF) == HASBUF) { | |
1153 | FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI); | |
1154 | cn.cn_flags &= ~HASBUF; | |
1155 | } | |
1c79356b A |
1156 | return (error); |
1157 | } | |
1158 | ||
1159 | /* | |
1160 | * Create a whiteout entry in the upper layer. | |
1161 | * | |
1162 | * (um) points to the union mount structure for access to the | |
1163 | * the mounting process's credentials. | |
1164 | * (dvp) is the directory in which to create the whiteout. | |
1165 | * it is locked on entry and exit. | |
1166 | * (cnp) is the componentname to be created. | |
1167 | */ | |
2d21ac55 | 1168 | /* No union lock held for this call */ |
1c79356b A |
1169 | int |
1170 | union_mkwhiteout(um, dvp, cnp, path) | |
1171 | struct union_mount *um; | |
1172 | struct vnode *dvp; | |
1173 | struct componentname *cnp; | |
1174 | char *path; | |
1175 | { | |
1176 | int error; | |
1c79356b A |
1177 | struct vnode *wvp; |
1178 | struct componentname cn; | |
1179 | ||
2d21ac55 A |
1180 | bzero(&cn, sizeof(struct componentname)); |
1181 | ||
1c79356b A |
1182 | error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path)); |
1183 | if (error) { | |
2d21ac55 | 1184 | goto out; |
1c79356b | 1185 | } |
1c79356b | 1186 | if (wvp) { |
2d21ac55 A |
1187 | error = EEXIST; |
1188 | goto out; | |
1c79356b A |
1189 | } |
1190 | ||
91447636 | 1191 | error = VNOP_WHITEOUT(dvp, &cn, CREATE, cnp->cn_context); |
1c79356b | 1192 | |
2d21ac55 A |
1193 | out: |
1194 | if ((cn.cn_flags & HASBUF) == HASBUF) { | |
1195 | FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI); | |
1196 | cn.cn_flags &= ~HASBUF; | |
1197 | } | |
1c79356b A |
1198 | return (error); |
1199 | } | |
1200 | ||
2d21ac55 | 1201 | |
1c79356b A |
1202 | /* |
1203 | * union_vn_create: creates and opens a new shadow file | |
2d21ac55 A |
1204 | * on the upper union layer. This function is similar |
1205 | * in spirit to calling vn_open() but it avoids calling namei(). | |
1206 | * The problem with calling namei() is that a) it locks too many | |
1c79356b | 1207 | * things, and b) it doesn't start at the "right" directory, |
2d21ac55 A |
1208 | * whereas relookup() is told where to start. |
1209 | * | |
1210 | * On entry, the vnode associated with un is locked. It remains locked | |
1211 | * on return. | |
1212 | * | |
1213 | * If no error occurs, *vpp contains a locked referenced vnode for your | |
1214 | * use. If an error occurs *vpp iis undefined. | |
1c79356b | 1215 | */ |
2d21ac55 | 1216 | /* called with no union lock held */ |
1c79356b | 1217 | int |
2d21ac55 | 1218 | union_vn_create(struct vnode **vpp, struct union_node *un, mode_t cmode, vfs_context_t context) |
1c79356b A |
1219 | { |
1220 | struct vnode *vp; | |
91447636 A |
1221 | struct vnode_attr vat; |
1222 | struct vnode_attr *vap = &vat; | |
1c79356b A |
1223 | int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL); |
1224 | int error; | |
2d21ac55 | 1225 | struct proc * p = vfs_context_proc(context); |
1c79356b A |
1226 | struct componentname cn; |
1227 | ||
2d21ac55 | 1228 | bzero(&cn, sizeof(struct componentname)); |
1c79356b A |
1229 | *vpp = NULLVP; |
1230 | ||
2d21ac55 A |
1231 | if (cmode == (mode_t)0) |
1232 | cmode = UN_FILEMODE & ~p->p_fd->fd_cmask; | |
1233 | else | |
1234 | cmode = cmode & ~p->p_fd->fd_cmask; | |
1235 | ||
91447636 | 1236 | |
1c79356b A |
1237 | /* |
1238 | * Build a new componentname structure (for the same | |
2d21ac55 | 1239 | * reasons outlines in union_mkshadow()). |
1c79356b A |
1240 | * The difference here is that the file is owned by |
1241 | * the current user, rather than by the person who | |
1242 | * did the mount, since the current user needs to be | |
1243 | * able to write the file (that's why it is being | |
1244 | * copied in the first place). | |
1245 | */ | |
1246 | cn.cn_namelen = strlen(un->un_path); | |
1247 | cn.cn_pnbuf = (caddr_t) _MALLOC_ZONE(cn.cn_namelen+1, | |
1248 | M_NAMEI, M_WAITOK); | |
1249 | cn.cn_pnlen = cn.cn_namelen+1; | |
1250 | bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1); | |
1251 | cn.cn_nameiop = CREATE; | |
2d21ac55 A |
1252 | if (UNNODE_FAULTIN(un)) |
1253 | cn.cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN|UNIONCREATED); | |
1254 | else | |
1255 | cn.cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN); | |
1256 | cn.cn_context = context; | |
1c79356b A |
1257 | cn.cn_nameptr = cn.cn_pnbuf; |
1258 | cn.cn_hash = un->un_hash; | |
1259 | cn.cn_consume = 0; | |
1260 | ||
2d21ac55 A |
1261 | /* |
1262 | * Pass dvp unlocked and referenced on call to relookup(). | |
1263 | * | |
1264 | * If an error occurs, dvp will be returned unlocked and dereferenced. | |
1265 | */ | |
91447636 | 1266 | vnode_get(un->un_dirvp); |
2d21ac55 A |
1267 | if ((error = relookup(un->un_dirvp, &vp, &cn)) != 0) { |
1268 | vnode_put(un->un_dirvp); | |
1269 | goto out; | |
0c530ab8 | 1270 | } |
91447636 | 1271 | vnode_put(un->un_dirvp); |
1c79356b | 1272 | |
2d21ac55 A |
1273 | /* |
1274 | * If no error occurs, dvp will be returned locked with the reference | |
1275 | * left as before, and vpp will be returned referenced and locked. | |
1276 | */ | |
1c79356b | 1277 | if (vp) { |
2d21ac55 A |
1278 | *vpp = vp; |
1279 | error = EEXIST; | |
1280 | goto out; | |
1c79356b A |
1281 | } |
1282 | ||
1283 | /* | |
1284 | * Good - there was no race to create the file | |
1285 | * so go ahead and create it. The permissions | |
1286 | * on the file will be 0666 modified by the | |
1287 | * current user's umask. Access to the file, while | |
1288 | * it is unioned, will require access to the top *and* | |
1289 | * bottom files. Access when not unioned will simply | |
1290 | * require access to the top-level file. | |
91447636 | 1291 | * |
1c79356b | 1292 | * TODO: confirm choice of access permissions. |
91447636 | 1293 | * decide on authorisation behaviour |
1c79356b | 1294 | */ |
91447636 A |
1295 | |
1296 | VATTR_INIT(vap); | |
1297 | VATTR_SET(vap, va_type, VREG); | |
1298 | VATTR_SET(vap, va_mode, cmode); | |
1299 | ||
2d21ac55 A |
1300 | if ((error = vn_create(un->un_dirvp, &vp, &cn, vap, 0, context)) != 0) { |
1301 | goto out; | |
0c530ab8 | 1302 | } |
1c79356b | 1303 | |
2d21ac55 | 1304 | if ((error = VNOP_OPEN(vp, fmode, context)) != 0) { |
91447636 | 1305 | vnode_put(vp); |
2d21ac55 | 1306 | goto out; |
1c79356b A |
1307 | } |
1308 | ||
91447636 | 1309 | vnode_lock(vp); |
1c79356b A |
1310 | if (++vp->v_writecount <= 0) |
1311 | panic("union: v_writecount"); | |
91447636 | 1312 | vnode_unlock(vp); |
1c79356b | 1313 | *vpp = vp; |
2d21ac55 A |
1314 | error = 0; |
1315 | ||
1316 | out: | |
1317 | if ((cn.cn_flags & HASBUF) == HASBUF) { | |
1318 | FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI); | |
1319 | cn.cn_flags &= ~HASBUF; | |
1320 | } | |
1321 | return(error); | |
1c79356b A |
1322 | } |
1323 | ||
2d21ac55 A |
1324 | /* called with no union lock held */ |
1325 | static int | |
1326 | union_vn_close(struct vnode *vp, int fmode, vfs_context_t context) | |
1c79356b A |
1327 | { |
1328 | ||
91447636 A |
1329 | if (fmode & FWRITE) { |
1330 | vnode_lock(vp); | |
1c79356b | 1331 | --vp->v_writecount; |
91447636 A |
1332 | vnode_unlock(vp); |
1333 | } | |
2d21ac55 | 1334 | return (VNOP_CLOSE(vp, fmode, context)); |
1c79356b A |
1335 | } |
1336 | ||
2d21ac55 A |
1337 | /* |
1338 | * union_removed_upper: | |
1339 | * | |
1340 | * An upper-only file/directory has been removed; un-cache it so | |
1341 | * that unionfs vnode gets reclaimed and the last uppervp reference | |
1342 | * disappears. | |
1343 | * | |
1344 | * Called with union_node unlocked. | |
1345 | */ | |
1346 | /* always called with union lock held */ | |
1c79356b A |
1347 | void |
1348 | union_removed_upper(un) | |
1349 | struct union_node *un; | |
1350 | { | |
1c79356b A |
1351 | union_newupper(un, NULLVP); |
1352 | if (un->un_flags & UN_CACHED) { | |
1353 | un->un_flags &= ~UN_CACHED; | |
1354 | LIST_REMOVE(un, un_cache); | |
1355 | } | |
1356 | ||
1c79356b A |
1357 | } |
1358 | ||
1359 | #if 0 | |
1360 | struct vnode * | |
1361 | union_lowervp(vp) | |
1362 | struct vnode *vp; | |
1363 | { | |
1364 | struct union_node *un = VTOUNION(vp); | |
1365 | ||
1366 | if ((un->un_lowervp != NULLVP) && | |
1367 | (vp->v_type == un->un_lowervp->v_type)) { | |
91447636 | 1368 | if (vnode_get(un->un_lowervp) == 0) |
1c79356b A |
1369 | return (un->un_lowervp); |
1370 | } | |
1371 | ||
1372 | return (NULLVP); | |
1373 | } | |
1374 | #endif | |
1375 | ||
1376 | /* | |
2d21ac55 | 1377 | * Determine whether a whiteout is needed |
1c79356b A |
1378 | * during a remove/rmdir operation. |
1379 | */ | |
2d21ac55 | 1380 | /* called with no union lock held */ |
1c79356b | 1381 | int |
91447636 | 1382 | union_dowhiteout(struct union_node *un, vfs_context_t ctx) |
1c79356b | 1383 | { |
91447636 | 1384 | struct vnode_attr va; |
1c79356b | 1385 | |
2d21ac55 A |
1386 | if (UNNODE_FAULTIN(un)) |
1387 | return(0); | |
1388 | ||
1389 | if ((un->un_lowervp != NULLVP) ) | |
1c79356b A |
1390 | return (1); |
1391 | ||
91447636 A |
1392 | VATTR_INIT(&va); |
1393 | VATTR_WANTED(&va, va_flags); | |
1394 | if (vnode_getattr(un->un_uppervp, &va, ctx) == 0 && | |
1c79356b A |
1395 | (va.va_flags & OPAQUE)) |
1396 | return (1); | |
1397 | ||
1398 | return (0); | |
1399 | } | |
1400 | ||
2d21ac55 | 1401 | /* called with no union lock held */ |
1c79356b | 1402 | static void |
2d21ac55 | 1403 | union_dircache_r(struct vnode *vp, struct vnode ***vppp, int *cntp) |
1c79356b A |
1404 | { |
1405 | struct union_node *un; | |
1406 | ||
1407 | if (vp->v_op != union_vnodeop_p) { | |
1408 | if (vppp) { | |
91447636 | 1409 | vnode_get(vp); |
1c79356b A |
1410 | *(*vppp)++ = vp; |
1411 | if (--(*cntp) == 0) | |
1412 | panic("union: dircache table too small"); | |
1413 | } else { | |
1414 | (*cntp)++; | |
1415 | } | |
1416 | ||
1417 | return; | |
1418 | } | |
1419 | ||
1420 | un = VTOUNION(vp); | |
1421 | if (un->un_uppervp != NULLVP) | |
1422 | union_dircache_r(un->un_uppervp, vppp, cntp); | |
1423 | if (un->un_lowervp != NULLVP) | |
1424 | union_dircache_r(un->un_lowervp, vppp, cntp); | |
1425 | } | |
1426 | ||
2d21ac55 | 1427 | /* called with no union lock held */ |
1c79356b | 1428 | struct vnode * |
2d21ac55 | 1429 | union_dircache(struct vnode *vp, __unused vfs_context_t context) |
1c79356b | 1430 | { |
91447636 | 1431 | int count; |
2d21ac55 | 1432 | struct vnode *nvp, *lvp; |
1c79356b | 1433 | struct vnode **vpp; |
2d21ac55 | 1434 | struct vnode **dircache, **newdircache; |
1c79356b A |
1435 | struct union_node *un; |
1436 | int error; | |
2d21ac55 | 1437 | int alloced = 0; |
1c79356b | 1438 | |
2d21ac55 A |
1439 | union_lock(); |
1440 | newdircache = NULL; | |
1c79356b A |
1441 | |
1442 | nvp = NULLVP; | |
2d21ac55 | 1443 | un = VTOUNION(vp); |
1c79356b | 1444 | |
2d21ac55 | 1445 | dircache = un->un_dircache; |
1c79356b | 1446 | if (dircache == 0) { |
2d21ac55 | 1447 | union_unlock(); |
91447636 A |
1448 | count = 0; |
1449 | union_dircache_r(vp, 0, &count); | |
1450 | count++; | |
2d21ac55 A |
1451 | #if 0 |
1452 | /* too bad; we need Union now! */ | |
1453 | #if MAC_XXX | |
1454 | panic("MAC Framework doesn't support unionfs (yet)\n"); | |
1455 | #endif /* MAC */ | |
1456 | #endif | |
1457 | ||
1c79356b | 1458 | dircache = (struct vnode **) |
91447636 | 1459 | _MALLOC(count * sizeof(struct vnode *), |
1c79356b | 1460 | M_TEMP, M_WAITOK); |
b0d623f7 A |
1461 | if (dircache == NULL) { |
1462 | goto out; | |
1463 | } | |
2d21ac55 A |
1464 | newdircache = dircache; |
1465 | alloced = 1; | |
1c79356b | 1466 | vpp = dircache; |
91447636 | 1467 | union_dircache_r(vp, &vpp, &count); |
1c79356b A |
1468 | *vpp = NULLVP; |
1469 | vpp = dircache + 1; | |
2d21ac55 | 1470 | union_lock(); |
1c79356b A |
1471 | } else { |
1472 | vpp = dircache; | |
1473 | do { | |
2d21ac55 | 1474 | if (*vpp++ == un->un_uppervp) |
1c79356b A |
1475 | break; |
1476 | } while (*vpp != NULLVP); | |
1477 | } | |
1478 | ||
2d21ac55 A |
1479 | lvp = *vpp; |
1480 | union_unlock(); | |
1481 | if (lvp == NULLVP) { | |
1c79356b | 1482 | goto out; |
2d21ac55 | 1483 | } |
1c79356b | 1484 | |
2d21ac55 A |
1485 | vnode_get(lvp); |
1486 | union_lock(); | |
1487 | ||
1488 | error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, lvp, NULLVP, 0); | |
1489 | if (error) { | |
1490 | union_unlock(); | |
1491 | vnode_put(lvp); | |
1c79356b | 1492 | goto out; |
2d21ac55 | 1493 | } |
1c79356b | 1494 | |
2d21ac55 | 1495 | un->un_dircache = 0; |
1c79356b | 1496 | un = VTOUNION(nvp); |
2d21ac55 A |
1497 | #if 0 |
1498 | if ((alloced != 0) && (un->un_dircache != 0)) { | |
1499 | union_unlock(); | |
1500 | for (vpp = newdircache; *vpp != NULLVP; vpp++) | |
1501 | vnode_put(*vpp); | |
1502 | _FREE(newdircache, M_TEMP); | |
1503 | newdircache = NULL; | |
1504 | union_lock(); | |
1505 | if (nvp != NULLVP) | |
1506 | union_freevp(nvp); | |
1507 | goto loop; | |
1508 | } | |
1509 | #endif | |
1c79356b | 1510 | un->un_dircache = dircache; |
2d21ac55 A |
1511 | un->un_flags |= UN_DIRENVN; |
1512 | ||
1513 | newdircache = NULL; | |
1514 | union_unlock(); | |
1515 | return (nvp); | |
1c79356b A |
1516 | |
1517 | out: | |
2d21ac55 A |
1518 | /* |
1519 | * If we allocated a new dircache and couldn't attach | |
1520 | * it to a new vp, free the resources we allocated. | |
1521 | */ | |
1522 | if (newdircache) { | |
1523 | for (vpp = newdircache; *vpp != NULLVP; vpp++) | |
1524 | vnode_put(*vpp); | |
1525 | _FREE(newdircache, M_TEMP); | |
1526 | } | |
1527 | return (NULLVP); | |
1528 | } | |
1529 | ||
1530 | /* | |
1531 | * Module glue to remove #ifdef UNION from vfs_syscalls.c | |
1532 | */ | |
1533 | /* Called with no union lock, the union_dircache takes locks when necessary */ | |
1534 | static int | |
1535 | union_dircheck(struct vnode **vpp, struct fileproc *fp, vfs_context_t ctx) | |
1536 | { | |
1537 | int error = 0; | |
1538 | vnode_t vp = *vpp; | |
1539 | ||
1540 | if (vp->v_op == union_vnodeop_p) { | |
1541 | struct vnode *lvp; | |
1542 | ||
1543 | lvp = union_dircache(vp, ctx); | |
1544 | if (lvp != NULLVP) { | |
1545 | struct vnode_attr va; | |
1546 | /* | |
1547 | * If the directory is opaque, | |
1548 | * then don't show lower entries | |
1549 | */ | |
1550 | VATTR_INIT(&va); | |
1551 | VATTR_WANTED(&va, va_flags); | |
1552 | error = vnode_getattr(vp, &va, ctx); | |
1553 | if (va.va_flags & OPAQUE) { | |
1554 | vnode_put(lvp); | |
1555 | lvp = NULL; | |
1556 | } | |
1557 | } | |
1558 | ||
1559 | if (lvp != NULLVP) { | |
1560 | #if CONFIG_MACF | |
1561 | error = mac_vnode_check_open(ctx, lvp, FREAD); | |
1562 | if (error) { | |
1563 | vnode_put(lvp); | |
1564 | return(error); | |
1565 | } | |
1566 | #endif /* MAC */ | |
1567 | error = VNOP_OPEN(lvp, FREAD, ctx); | |
1568 | if (error) { | |
1569 | vnode_put(lvp); | |
1570 | return(error); | |
1571 | } | |
1572 | vnode_ref(lvp); | |
1573 | fp->f_fglob->fg_data = (caddr_t) lvp; | |
1574 | fp->f_fglob->fg_offset = 0; | |
1575 | ||
1576 | error = VNOP_CLOSE(vp, FREAD, ctx); | |
1577 | vnode_rele(vp); | |
1578 | vnode_put(vp); | |
1579 | if (error) | |
1580 | return(error); | |
1581 | ||
1582 | *vpp = lvp; | |
1583 | return -1; /* goto unionread */ | |
1584 | } | |
1585 | } | |
1586 | return error; | |
1c79356b | 1587 | } |
2d21ac55 A |
1588 | |
1589 | /* called from inactive with union lock held */ | |
1590 | void | |
1591 | union_dircache_free(struct union_node *un) | |
1592 | { | |
1593 | struct vnode **vpp; | |
1594 | ||
1595 | vpp = un->un_dircache; | |
1596 | un->un_dircache = NULL; | |
1597 | union_unlock(); | |
1598 | ||
1599 | for (; *vpp != NULLVP; vpp++) | |
1600 | vnode_put(*vpp); | |
1601 | _FREE(un->un_dircache, M_TEMP); | |
1602 | union_lock(); | |
1603 | } | |
1604 |