1 1.82 thorpej /* $NetBSD: union_subr.c,v 1.82 2022/07/18 04:30:30 thorpej Exp $ */ 2 1.1 jdolecek 3 1.1 jdolecek /* 4 1.1 jdolecek * Copyright (c) 1994 5 1.1 jdolecek * The Regents of the University of California. All rights reserved. 6 1.1 jdolecek * 7 1.1 jdolecek * This code is derived from software contributed to Berkeley by 8 1.1 jdolecek * Jan-Simon Pendry. 9 1.1 jdolecek * 10 1.1 jdolecek * Redistribution and use in source and binary forms, with or without 11 1.1 jdolecek * modification, are permitted provided that the following conditions 12 1.1 jdolecek * are met: 13 1.1 jdolecek * 1. Redistributions of source code must retain the above copyright 14 1.1 jdolecek * notice, this list of conditions and the following disclaimer. 15 1.1 jdolecek * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 jdolecek * notice, this list of conditions and the following disclaimer in the 17 1.1 jdolecek * documentation and/or other materials provided with the distribution. 18 1.7 agc * 3. Neither the name of the University nor the names of its contributors 19 1.7 agc * may be used to endorse or promote products derived from this software 20 1.7 agc * without specific prior written permission. 21 1.7 agc * 22 1.7 agc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.7 agc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.7 agc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.7 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.7 agc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.7 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.7 agc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.7 agc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.7 agc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.7 agc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.7 agc * SUCH DAMAGE. 33 1.7 agc * 34 1.7 agc * @(#)union_subr.c 8.20 (Berkeley) 5/20/95 35 1.7 agc */ 36 1.7 agc 37 1.7 agc /* 38 1.7 agc * Copyright (c) 1994 Jan-Simon Pendry 39 1.7 agc * 40 1.7 agc * This code is derived from software contributed to Berkeley by 41 1.7 agc * Jan-Simon Pendry. 42 1.7 agc * 43 1.7 agc * Redistribution and use in source and binary forms, with or without 44 1.7 agc * modification, are permitted provided that the following conditions 45 1.7 agc * are met: 46 1.7 agc * 1. Redistributions of source code must retain the above copyright 47 1.7 agc * notice, this list of conditions and the following disclaimer. 48 1.7 agc * 2. Redistributions in binary form must reproduce the above copyright 49 1.7 agc * notice, this list of conditions and the following disclaimer in the 50 1.7 agc * documentation and/or other materials provided with the distribution. 51 1.1 jdolecek * 3. All advertising materials mentioning features or use of this software 52 1.1 jdolecek * must display the following acknowledgement: 53 1.1 jdolecek * This product includes software developed by the University of 54 1.1 jdolecek * California, Berkeley and its contributors. 55 1.1 jdolecek * 4. Neither the name of the University nor the names of its contributors 56 1.1 jdolecek * may be used to endorse or promote products derived from this software 57 1.1 jdolecek * without specific prior written permission. 58 1.1 jdolecek * 59 1.1 jdolecek * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 1.1 jdolecek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 1.1 jdolecek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 1.1 jdolecek * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 1.1 jdolecek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 1.1 jdolecek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 1.1 jdolecek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 1.1 jdolecek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 1.1 jdolecek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 1.1 jdolecek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 1.1 jdolecek * SUCH DAMAGE. 70 1.1 jdolecek * 71 1.1 jdolecek * @(#)union_subr.c 8.20 (Berkeley) 5/20/95 72 1.1 jdolecek */ 73 1.1 jdolecek 74 1.1 jdolecek #include <sys/cdefs.h> 75 1.82 thorpej __KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.82 2022/07/18 04:30:30 thorpej Exp $"); 76 1.1 jdolecek 77 1.1 jdolecek #include <sys/param.h> 78 1.1 jdolecek #include <sys/systm.h> 79 1.1 jdolecek #include <sys/proc.h> 80 1.1 jdolecek #include <sys/time.h> 81 1.1 jdolecek #include <sys/kernel.h> 82 1.1 jdolecek #include <sys/vnode.h> 83 1.1 jdolecek #include <sys/namei.h> 84 1.1 jdolecek #include <sys/malloc.h> 85 1.44 hannken #include <sys/dirent.h> 86 1.1 jdolecek #include <sys/file.h> 87 1.1 jdolecek #include <sys/filedesc.h> 88 1.1 jdolecek #include <sys/queue.h> 89 1.1 jdolecek #include <sys/mount.h> 90 1.1 jdolecek #include <sys/stat.h> 91 1.18 elad #include <sys/kauth.h> 92 1.1 jdolecek 93 1.1 jdolecek #include <uvm/uvm_extern.h> 94 1.1 jdolecek 95 1.1 jdolecek #include <fs/union/union.h> 96 1.53 hannken #include <miscfs/genfs/genfs.h> 97 1.47 hannken #include <miscfs/specfs/specdev.h> 98 1.1 jdolecek 99 1.54 hannken static LIST_HEAD(uhashhead, union_node) *uhashtbl; 100 1.54 hannken static u_long uhash_mask; /* size of hash table - 1 */ 101 1.1 jdolecek #define UNION_HASH(u, l) \ 102 1.54 hannken ((((u_long) (u) + (u_long) (l)) >> 8) & uhash_mask) 103 1.54 hannken #define NOHASH ((u_long)-1) 104 1.1 jdolecek 105 1.54 hannken static kmutex_t uhash_lock; 106 1.1 jdolecek 107 1.76 hannken static void union_newupper(struct union_node *, struct vnode *); 108 1.76 hannken static void union_newlower(struct union_node *, struct vnode *); 109 1.69 hannken static void union_ref(struct union_node *); 110 1.69 hannken static void union_rele(struct union_node *); 111 1.56 dholland static int union_do_lookup(struct vnode *, struct componentname *, kauth_cred_t, const char *); 112 1.18 elad int union_vn_close(struct vnode *, int, kauth_cred_t, struct lwp *); 113 1.14 xtraeme static void union_dircache_r(struct vnode *, struct vnode ***, int *); 114 1.15 christos struct vnode *union_dircache(struct vnode *, struct lwp *); 115 1.1 jdolecek 116 1.1 jdolecek void 117 1.32 matt union_init(void) 118 1.1 jdolecek { 119 1.54 hannken 120 1.54 hannken mutex_init(&uhash_lock, MUTEX_DEFAULT, IPL_NONE); 121 1.54 hannken uhashtbl = hashinit(desiredvnodes, HASH_LIST, true, &uhash_mask); 122 1.54 hannken } 123 1.54 hannken 124 1.54 hannken void 125 1.54 hannken union_reinit(void) 126 1.54 hannken { 127 1.54 hannken struct union_node *un; 128 1.54 hannken struct uhashhead *oldhash, *hash; 129 1.54 hannken u_long oldmask, mask, val; 130 1.1 jdolecek int i; 131 1.1 jdolecek 132 1.54 hannken hash = hashinit(desiredvnodes, HASH_LIST, true, &mask); 133 1.54 hannken mutex_enter(&uhash_lock); 134 1.54 hannken oldhash = uhashtbl; 135 1.54 hannken oldmask = uhash_mask; 136 1.54 hannken uhashtbl = hash; 137 1.54 hannken uhash_mask = mask; 138 1.54 hannken for (i = 0; i <= oldmask; i++) { 139 1.54 hannken while ((un = LIST_FIRST(&oldhash[i])) != NULL) { 140 1.54 hannken LIST_REMOVE(un, un_cache); 141 1.54 hannken val = UNION_HASH(un->un_uppervp, un->un_lowervp); 142 1.54 hannken LIST_INSERT_HEAD(&hash[val], un, un_cache); 143 1.54 hannken } 144 1.49 hannken } 145 1.54 hannken mutex_exit(&uhash_lock); 146 1.54 hannken hashdone(oldhash, HASH_LIST, oldmask); 147 1.1 jdolecek } 148 1.1 jdolecek 149 1.1 jdolecek /* 150 1.1 jdolecek * Free global unionfs resources. 151 1.1 jdolecek */ 152 1.1 jdolecek void 153 1.32 matt union_done(void) 154 1.1 jdolecek { 155 1.49 hannken 156 1.54 hannken hashdone(uhashtbl, HASH_LIST, uhash_mask); 157 1.54 hannken mutex_destroy(&uhash_lock); 158 1.12 perry 159 1.2 jdolecek /* Make sure to unset the readdir hook. */ 160 1.2 jdolecek vn_union_readdir_hook = NULL; 161 1.1 jdolecek } 162 1.1 jdolecek 163 1.1 jdolecek void 164 1.76 hannken union_newlower(struct union_node *un, struct vnode *lowervp) 165 1.1 jdolecek { 166 1.1 jdolecek int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp); 167 1.76 hannken int nhash = UNION_HASH(un->un_uppervp, lowervp); 168 1.76 hannken 169 1.76 hannken if (un->un_lowervp == lowervp) 170 1.76 hannken return; 171 1.1 jdolecek 172 1.53 hannken KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE); 173 1.76 hannken KASSERT(un->un_lowervp == NULL); 174 1.1 jdolecek 175 1.54 hannken mutex_enter(&uhash_lock); 176 1.1 jdolecek 177 1.76 hannken if (ohash != nhash && (un->un_cflags & UN_CACHED)) { 178 1.76 hannken un->un_cflags &= ~UN_CACHED; 179 1.76 hannken LIST_REMOVE(un, un_cache); 180 1.1 jdolecek } 181 1.76 hannken mutex_enter(&un->un_lock); 182 1.76 hannken un->un_lowervp = lowervp; 183 1.76 hannken un->un_lowersz = VNOVAL; 184 1.76 hannken mutex_exit(&un->un_lock); 185 1.76 hannken if (ohash != nhash) { 186 1.54 hannken LIST_INSERT_HEAD(&uhashtbl[nhash], un, un_cache); 187 1.53 hannken un->un_cflags |= UN_CACHED; 188 1.1 jdolecek } 189 1.1 jdolecek 190 1.54 hannken mutex_exit(&uhash_lock); 191 1.1 jdolecek } 192 1.1 jdolecek 193 1.1 jdolecek void 194 1.76 hannken union_newupper(struct union_node *un, struct vnode *uppervp) 195 1.1 jdolecek { 196 1.76 hannken int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp); 197 1.76 hannken int nhash = UNION_HASH(uppervp, un->un_lowervp); 198 1.76 hannken struct vop_lock_args lock_ap; 199 1.76 hannken struct vop_unlock_args unlock_ap; 200 1.76 hannken int error __diagused; 201 1.76 hannken 202 1.76 hannken if (un->un_uppervp == uppervp) 203 1.76 hannken return; 204 1.76 hannken 205 1.76 hannken KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE); 206 1.76 hannken KASSERT(un->un_uppervp == NULL); 207 1.76 hannken 208 1.76 hannken /* 209 1.76 hannken * We have to transfer the vnode lock from the union vnode to 210 1.76 hannken * the upper vnode. Lock the upper vnode first. We cannot use 211 1.76 hannken * VOP_LOCK() here as it would break the fstrans state. 212 1.76 hannken */ 213 1.76 hannken lock_ap.a_desc = VDESC(vop_lock); 214 1.76 hannken lock_ap.a_vp = uppervp; 215 1.76 hannken lock_ap.a_flags = LK_EXCLUSIVE; 216 1.76 hannken error = VCALL(lock_ap.a_vp, VOFFSET(vop_lock), &lock_ap); 217 1.76 hannken KASSERT(error == 0); 218 1.1 jdolecek 219 1.76 hannken mutex_enter(&uhash_lock); 220 1.1 jdolecek 221 1.76 hannken if (ohash != nhash && (un->un_cflags & UN_CACHED)) { 222 1.76 hannken un->un_cflags &= ~UN_CACHED; 223 1.76 hannken LIST_REMOVE(un, un_cache); 224 1.76 hannken } 225 1.76 hannken mutex_enter(&un->un_lock); 226 1.76 hannken un->un_uppervp = uppervp; 227 1.76 hannken un->un_uppersz = VNOVAL; 228 1.76 hannken /* 229 1.76 hannken * With the upper vnode in place unlock the union vnode to 230 1.76 hannken * finalize the lock transfer. 231 1.76 hannken */ 232 1.76 hannken unlock_ap.a_desc = VDESC(vop_unlock); 233 1.76 hannken unlock_ap.a_vp = UNIONTOV(un); 234 1.76 hannken genfs_unlock(&unlock_ap); 235 1.82 thorpej /* Update union vnode interlock, vmobjlock, & klist. */ 236 1.78 ad vshareilock(UNIONTOV(un), uppervp); 237 1.78 ad rw_obj_hold(uppervp->v_uobj.vmobjlock); 238 1.78 ad uvm_obj_setlock(&UNIONTOV(un)->v_uobj, uppervp->v_uobj.vmobjlock); 239 1.82 thorpej vshareklist(UNIONTOV(un), uppervp); 240 1.76 hannken mutex_exit(&un->un_lock); 241 1.76 hannken if (ohash != nhash) { 242 1.76 hannken LIST_INSERT_HEAD(&uhashtbl[nhash], un, un_cache); 243 1.76 hannken un->un_cflags |= UN_CACHED; 244 1.76 hannken } 245 1.1 jdolecek 246 1.76 hannken mutex_exit(&uhash_lock); 247 1.1 jdolecek } 248 1.1 jdolecek 249 1.1 jdolecek /* 250 1.1 jdolecek * Keep track of size changes in the underlying vnodes. 251 1.1 jdolecek * If the size changes, then callback to the vm layer 252 1.1 jdolecek * giving priority to the upper layer size. 253 1.53 hannken * 254 1.53 hannken * Mutex un_lock hold on entry and released on return. 255 1.1 jdolecek */ 256 1.1 jdolecek void 257 1.32 matt union_newsize(struct vnode *vp, off_t uppersz, off_t lowersz) 258 1.1 jdolecek { 259 1.53 hannken struct union_node *un = VTOUNION(vp); 260 1.1 jdolecek off_t sz; 261 1.1 jdolecek 262 1.53 hannken KASSERT(mutex_owned(&un->un_lock)); 263 1.1 jdolecek /* only interested in regular files */ 264 1.26 pooka if (vp->v_type != VREG) { 265 1.53 hannken mutex_exit(&un->un_lock); 266 1.26 pooka uvm_vnp_setsize(vp, 0); 267 1.1 jdolecek return; 268 1.26 pooka } 269 1.1 jdolecek 270 1.1 jdolecek sz = VNOVAL; 271 1.1 jdolecek 272 1.1 jdolecek if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) { 273 1.1 jdolecek un->un_uppersz = uppersz; 274 1.1 jdolecek if (sz == VNOVAL) 275 1.1 jdolecek sz = un->un_uppersz; 276 1.1 jdolecek } 277 1.1 jdolecek 278 1.1 jdolecek if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) { 279 1.1 jdolecek un->un_lowersz = lowersz; 280 1.1 jdolecek if (sz == VNOVAL) 281 1.1 jdolecek sz = un->un_lowersz; 282 1.1 jdolecek } 283 1.53 hannken mutex_exit(&un->un_lock); 284 1.1 jdolecek 285 1.1 jdolecek if (sz != VNOVAL) { 286 1.1 jdolecek #ifdef UNION_DIAGNOSTIC 287 1.1 jdolecek printf("union: %s size now %qd\n", 288 1.1 jdolecek uppersz != VNOVAL ? "upper" : "lower", sz); 289 1.1 jdolecek #endif 290 1.1 jdolecek uvm_vnp_setsize(vp, sz); 291 1.1 jdolecek } 292 1.1 jdolecek } 293 1.1 jdolecek 294 1.69 hannken static void 295 1.69 hannken union_ref(struct union_node *un) 296 1.69 hannken { 297 1.69 hannken 298 1.69 hannken KASSERT(mutex_owned(&uhash_lock)); 299 1.69 hannken un->un_refs++; 300 1.69 hannken } 301 1.69 hannken 302 1.69 hannken static void 303 1.69 hannken union_rele(struct union_node *un) 304 1.69 hannken { 305 1.69 hannken 306 1.69 hannken mutex_enter(&uhash_lock); 307 1.69 hannken un->un_refs--; 308 1.69 hannken if (un->un_refs > 0) { 309 1.69 hannken mutex_exit(&uhash_lock); 310 1.69 hannken return; 311 1.69 hannken } 312 1.69 hannken if (un->un_cflags & UN_CACHED) { 313 1.69 hannken un->un_cflags &= ~UN_CACHED; 314 1.69 hannken LIST_REMOVE(un, un_cache); 315 1.69 hannken } 316 1.69 hannken mutex_exit(&uhash_lock); 317 1.69 hannken 318 1.69 hannken if (un->un_pvp != NULLVP) 319 1.69 hannken vrele(un->un_pvp); 320 1.69 hannken if (un->un_uppervp != NULLVP) 321 1.69 hannken vrele(un->un_uppervp); 322 1.69 hannken if (un->un_lowervp != NULLVP) 323 1.69 hannken vrele(un->un_lowervp); 324 1.69 hannken if (un->un_dirvp != NULLVP) 325 1.69 hannken vrele(un->un_dirvp); 326 1.69 hannken if (un->un_path) 327 1.69 hannken free(un->un_path, M_TEMP); 328 1.69 hannken mutex_destroy(&un->un_lock); 329 1.69 hannken 330 1.69 hannken free(un, M_TEMP); 331 1.69 hannken } 332 1.69 hannken 333 1.1 jdolecek /* 334 1.1 jdolecek * allocate a union_node/vnode pair. the vnode is 335 1.63 hannken * referenced and unlocked. the new vnode is returned 336 1.1 jdolecek * via (vpp). (mp) is the mountpoint of the union filesystem, 337 1.1 jdolecek * (dvp) is the parent directory where the upper layer object 338 1.1 jdolecek * should exist (but doesn't) and (cnp) is the componentname 339 1.1 jdolecek * information which is partially copied to allow the upper 340 1.1 jdolecek * layer object to be created at a later time. (uppervp) 341 1.1 jdolecek * and (lowervp) reference the upper and lower layer objects 342 1.1 jdolecek * being mapped. either, but not both, can be nil. 343 1.63 hannken * both, if supplied, are unlocked. 344 1.1 jdolecek * the reference is either maintained in the new union_node 345 1.1 jdolecek * object which is allocated, or they are vrele'd. 346 1.1 jdolecek * 347 1.70 hannken * all union_nodes are maintained on a hash 348 1.1 jdolecek * list. new nodes are only allocated when they cannot 349 1.1 jdolecek * be found on this list. entries on the list are 350 1.1 jdolecek * removed when the vfs reclaim entry is called. 351 1.1 jdolecek * 352 1.70 hannken * the vnode gets attached or referenced with vcache_get(). 353 1.1 jdolecek */ 354 1.1 jdolecek int 355 1.32 matt union_allocvp( 356 1.32 matt struct vnode **vpp, 357 1.32 matt struct mount *mp, 358 1.32 matt struct vnode *undvp, /* parent union vnode */ 359 1.32 matt struct vnode *dvp, /* may be null */ 360 1.32 matt struct componentname *cnp, /* may be null */ 361 1.32 matt struct vnode *uppervp, /* may be null */ 362 1.32 matt struct vnode *lowervp, /* may be null */ 363 1.32 matt int docache) 364 1.1 jdolecek { 365 1.1 jdolecek int error; 366 1.38 hannken struct union_node *un = NULL, *un1; 367 1.38 hannken struct vnode *vp, *xlowervp = NULLVP; 368 1.54 hannken u_long hash[3]; 369 1.1 jdolecek int try; 370 1.63 hannken bool is_dotdot; 371 1.1 jdolecek 372 1.63 hannken is_dotdot = (dvp != NULL && cnp != NULL && (cnp->cn_flags & ISDOTDOT)); 373 1.53 hannken 374 1.1 jdolecek if (uppervp == NULLVP && lowervp == NULLVP) 375 1.1 jdolecek panic("union: unidentifiable allocation"); 376 1.1 jdolecek 377 1.1 jdolecek if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) { 378 1.1 jdolecek xlowervp = lowervp; 379 1.1 jdolecek lowervp = NULLVP; 380 1.1 jdolecek } 381 1.1 jdolecek 382 1.54 hannken /* 383 1.54 hannken * If both uppervp and lowervp are not NULL we have to 384 1.54 hannken * search union nodes with one vnode as NULL too. 385 1.54 hannken */ 386 1.54 hannken hash[0] = UNION_HASH(uppervp, lowervp); 387 1.54 hannken if (uppervp == NULL || lowervp == NULL) { 388 1.54 hannken hash[1] = hash[2] = NOHASH; 389 1.54 hannken } else { 390 1.54 hannken hash[1] = UNION_HASH(uppervp, NULLVP); 391 1.54 hannken hash[2] = UNION_HASH(NULLVP, lowervp); 392 1.54 hannken } 393 1.1 jdolecek 394 1.77 christos if (!docache) { 395 1.77 christos un = NULL; 396 1.77 christos goto found; 397 1.77 christos } 398 1.77 christos 399 1.54 hannken loop: 400 1.54 hannken mutex_enter(&uhash_lock); 401 1.1 jdolecek 402 1.54 hannken for (try = 0; try < 3; try++) { 403 1.54 hannken if (hash[try] == NOHASH) 404 1.54 hannken continue; 405 1.54 hannken LIST_FOREACH(un, &uhashtbl[hash[try]], un_cache) { 406 1.54 hannken if ((un->un_lowervp && un->un_lowervp != lowervp) || 407 1.54 hannken (un->un_uppervp && un->un_uppervp != uppervp) || 408 1.70 hannken un->un_mount != mp) 409 1.1 jdolecek continue; 410 1.1 jdolecek 411 1.69 hannken union_ref(un); 412 1.54 hannken mutex_exit(&uhash_lock); 413 1.70 hannken error = vcache_get(mp, &un, sizeof(un), &vp); 414 1.70 hannken KASSERT(error != 0 || UNIONTOV(un) == vp); 415 1.69 hannken union_rele(un); 416 1.70 hannken if (error == ENOENT) 417 1.54 hannken goto loop; 418 1.70 hannken else if (error) 419 1.70 hannken goto out; 420 1.54 hannken goto found; 421 1.1 jdolecek } 422 1.54 hannken } 423 1.1 jdolecek 424 1.54 hannken mutex_exit(&uhash_lock); 425 1.1 jdolecek 426 1.54 hannken found: 427 1.1 jdolecek if (un) { 428 1.63 hannken if (uppervp != dvp) { 429 1.63 hannken if (is_dotdot) 430 1.63 hannken VOP_UNLOCK(dvp); 431 1.63 hannken vn_lock(UNIONTOV(un), LK_EXCLUSIVE | LK_RETRY); 432 1.63 hannken if (is_dotdot) 433 1.63 hannken vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 434 1.63 hannken } 435 1.1 jdolecek /* 436 1.1 jdolecek * Save information about the upper layer. 437 1.1 jdolecek */ 438 1.1 jdolecek if (uppervp != un->un_uppervp) { 439 1.1 jdolecek union_newupper(un, uppervp); 440 1.1 jdolecek } else if (uppervp) { 441 1.1 jdolecek vrele(uppervp); 442 1.1 jdolecek } 443 1.1 jdolecek 444 1.1 jdolecek /* 445 1.1 jdolecek * Save information about the lower layer. 446 1.1 jdolecek * This needs to keep track of pathname 447 1.1 jdolecek * and directory information which union_vn_create 448 1.1 jdolecek * might need. 449 1.1 jdolecek */ 450 1.1 jdolecek if (lowervp != un->un_lowervp) { 451 1.1 jdolecek union_newlower(un, lowervp); 452 1.1 jdolecek if (cnp && (lowervp != NULLVP)) { 453 1.1 jdolecek un->un_path = malloc(cnp->cn_namelen+1, 454 1.1 jdolecek M_TEMP, M_WAITOK); 455 1.1 jdolecek memcpy(un->un_path, cnp->cn_nameptr, 456 1.1 jdolecek cnp->cn_namelen); 457 1.1 jdolecek un->un_path[cnp->cn_namelen] = '\0'; 458 1.35 pooka vref(dvp); 459 1.1 jdolecek un->un_dirvp = dvp; 460 1.1 jdolecek } 461 1.1 jdolecek } else if (lowervp) { 462 1.1 jdolecek vrele(lowervp); 463 1.1 jdolecek } 464 1.1 jdolecek *vpp = UNIONTOV(un); 465 1.63 hannken if (uppervp != dvp) 466 1.63 hannken VOP_UNLOCK(*vpp); 467 1.70 hannken error = 0; 468 1.70 hannken goto out; 469 1.47 hannken } 470 1.47 hannken 471 1.70 hannken un = malloc(sizeof(struct union_node), M_TEMP, M_WAITOK); 472 1.53 hannken mutex_init(&un->un_lock, MUTEX_DEFAULT, IPL_NONE); 473 1.69 hannken un->un_refs = 1; 474 1.70 hannken un->un_mount = mp; 475 1.70 hannken un->un_vnode = NULL; 476 1.1 jdolecek un->un_uppervp = uppervp; 477 1.1 jdolecek un->un_lowervp = lowervp; 478 1.1 jdolecek un->un_pvp = undvp; 479 1.1 jdolecek if (undvp != NULLVP) 480 1.35 pooka vref(undvp); 481 1.1 jdolecek un->un_dircache = 0; 482 1.1 jdolecek un->un_openl = 0; 483 1.53 hannken un->un_cflags = 0; 484 1.79 hannken un->un_hooknode = false; 485 1.26 pooka 486 1.26 pooka un->un_uppersz = VNOVAL; 487 1.26 pooka un->un_lowersz = VNOVAL; 488 1.26 pooka 489 1.17 christos if (dvp && cnp && (lowervp != NULLVP)) { 490 1.1 jdolecek un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK); 491 1.1 jdolecek memcpy(un->un_path, cnp->cn_nameptr, cnp->cn_namelen); 492 1.1 jdolecek un->un_path[cnp->cn_namelen] = '\0'; 493 1.35 pooka vref(dvp); 494 1.1 jdolecek un->un_dirvp = dvp; 495 1.1 jdolecek } else { 496 1.1 jdolecek un->un_path = 0; 497 1.1 jdolecek un->un_dirvp = 0; 498 1.1 jdolecek } 499 1.1 jdolecek 500 1.1 jdolecek if (docache) { 501 1.70 hannken mutex_enter(&uhash_lock); 502 1.70 hannken LIST_FOREACH(un1, &uhashtbl[hash[0]], un_cache) { 503 1.70 hannken if (un1->un_lowervp == lowervp && 504 1.70 hannken un1->un_uppervp == uppervp && 505 1.70 hannken un1->un_mount == mp) { 506 1.70 hannken /* 507 1.70 hannken * Another thread beat us, push back freshly 508 1.70 hannken * allocated node and retry. 509 1.70 hannken */ 510 1.70 hannken mutex_exit(&uhash_lock); 511 1.70 hannken union_rele(un); 512 1.70 hannken goto loop; 513 1.70 hannken } 514 1.70 hannken } 515 1.54 hannken LIST_INSERT_HEAD(&uhashtbl[hash[0]], un, un_cache); 516 1.53 hannken un->un_cflags |= UN_CACHED; 517 1.70 hannken mutex_exit(&uhash_lock); 518 1.1 jdolecek } 519 1.1 jdolecek 520 1.70 hannken error = vcache_get(mp, &un, sizeof(un), vpp); 521 1.70 hannken KASSERT(error != 0 || UNIONTOV(un) == *vpp); 522 1.70 hannken union_rele(un); 523 1.70 hannken if (error == ENOENT) 524 1.70 hannken goto loop; 525 1.70 hannken 526 1.70 hannken out: 527 1.1 jdolecek if (xlowervp) 528 1.1 jdolecek vrele(xlowervp); 529 1.1 jdolecek 530 1.70 hannken return error; 531 1.1 jdolecek } 532 1.1 jdolecek 533 1.1 jdolecek int 534 1.32 matt union_freevp(struct vnode *vp) 535 1.1 jdolecek { 536 1.1 jdolecek struct union_node *un = VTOUNION(vp); 537 1.1 jdolecek 538 1.71 hannken /* Detach vnode from union node. */ 539 1.71 hannken un->un_vnode = NULL; 540 1.71 hannken un->un_uppersz = VNOVAL; 541 1.71 hannken un->un_lowersz = VNOVAL; 542 1.71 hannken 543 1.71 hannken /* Detach union node from vnode. */ 544 1.70 hannken mutex_enter(vp->v_interlock); 545 1.70 hannken vp->v_data = NULL; 546 1.70 hannken mutex_exit(vp->v_interlock); 547 1.70 hannken 548 1.69 hannken union_rele(un); 549 1.1 jdolecek 550 1.70 hannken return 0; 551 1.70 hannken } 552 1.70 hannken 553 1.70 hannken int 554 1.70 hannken union_loadvnode(struct mount *mp, struct vnode *vp, 555 1.70 hannken const void *key, size_t key_len, const void **new_key) 556 1.70 hannken { 557 1.70 hannken struct vattr va; 558 1.70 hannken struct vnode *svp; 559 1.70 hannken struct union_node *un; 560 1.70 hannken struct union_mount *um; 561 1.70 hannken voff_t uppersz, lowersz; 562 1.70 hannken 563 1.70 hannken KASSERT(key_len == sizeof(un)); 564 1.70 hannken memcpy(&un, key, key_len); 565 1.70 hannken 566 1.70 hannken um = MOUNTTOUNIONMOUNT(mp); 567 1.70 hannken svp = (un->un_uppervp != NULLVP) ? un->un_uppervp : un->un_lowervp; 568 1.70 hannken 569 1.70 hannken vp->v_tag = VT_UNION; 570 1.70 hannken vp->v_op = union_vnodeop_p; 571 1.70 hannken vp->v_data = un; 572 1.70 hannken un->un_vnode = vp; 573 1.70 hannken 574 1.70 hannken vp->v_type = svp->v_type; 575 1.70 hannken if (svp->v_type == VCHR || svp->v_type == VBLK) 576 1.70 hannken spec_node_init(vp, svp->v_rdev); 577 1.70 hannken 578 1.78 ad vshareilock(vp, svp); 579 1.78 ad rw_obj_hold(svp->v_uobj.vmobjlock); 580 1.78 ad uvm_obj_setlock(&vp->v_uobj, svp->v_uobj.vmobjlock); 581 1.82 thorpej vshareklist(vp, svp); 582 1.70 hannken 583 1.70 hannken /* detect the root vnode (and aliases) */ 584 1.70 hannken if ((un->un_uppervp == um->um_uppervp) && 585 1.70 hannken ((un->un_lowervp == NULLVP) || un->un_lowervp == um->um_lowervp)) { 586 1.70 hannken if (un->un_lowervp == NULLVP) { 587 1.70 hannken un->un_lowervp = um->um_lowervp; 588 1.70 hannken if (un->un_lowervp != NULLVP) 589 1.70 hannken vref(un->un_lowervp); 590 1.70 hannken } 591 1.70 hannken vp->v_vflag |= VV_ROOT; 592 1.70 hannken } 593 1.70 hannken 594 1.70 hannken uppersz = lowersz = VNOVAL; 595 1.70 hannken if (un->un_uppervp != NULLVP) { 596 1.70 hannken if (vn_lock(un->un_uppervp, LK_SHARED) == 0) { 597 1.70 hannken if (VOP_GETATTR(un->un_uppervp, &va, FSCRED) == 0) 598 1.70 hannken uppersz = va.va_size; 599 1.70 hannken VOP_UNLOCK(un->un_uppervp); 600 1.70 hannken } 601 1.70 hannken } 602 1.70 hannken if (un->un_lowervp != NULLVP) { 603 1.70 hannken if (vn_lock(un->un_lowervp, LK_SHARED) == 0) { 604 1.70 hannken if (VOP_GETATTR(un->un_lowervp, &va, FSCRED) == 0) 605 1.70 hannken lowersz = va.va_size; 606 1.70 hannken VOP_UNLOCK(un->un_lowervp); 607 1.70 hannken } 608 1.70 hannken } 609 1.70 hannken 610 1.70 hannken mutex_enter(&un->un_lock); 611 1.70 hannken union_newsize(vp, uppersz, lowersz); 612 1.70 hannken 613 1.70 hannken mutex_enter(&uhash_lock); 614 1.70 hannken union_ref(un); 615 1.70 hannken mutex_exit(&uhash_lock); 616 1.70 hannken 617 1.70 hannken *new_key = &vp->v_data; 618 1.1 jdolecek 619 1.69 hannken return 0; 620 1.1 jdolecek } 621 1.1 jdolecek 622 1.1 jdolecek /* 623 1.1 jdolecek * copyfile. copy the vnode (fvp) to the vnode (tvp) 624 1.1 jdolecek * using a sequence of reads and writes. both (fvp) 625 1.1 jdolecek * and (tvp) are locked on entry and exit. 626 1.1 jdolecek */ 627 1.1 jdolecek int 628 1.32 matt union_copyfile(struct vnode *fvp, struct vnode *tvp, kauth_cred_t cred, 629 1.32 matt struct lwp *l) 630 1.1 jdolecek { 631 1.13 christos char *tbuf; 632 1.1 jdolecek struct uio uio; 633 1.1 jdolecek struct iovec iov; 634 1.1 jdolecek int error = 0; 635 1.1 jdolecek 636 1.1 jdolecek /* 637 1.1 jdolecek * strategy: 638 1.1 jdolecek * allocate a buffer of size MAXBSIZE. 639 1.1 jdolecek * loop doing reads and writes, keeping track 640 1.1 jdolecek * of the current uio offset. 641 1.1 jdolecek * give up at the first sign of trouble. 642 1.1 jdolecek */ 643 1.1 jdolecek 644 1.1 jdolecek uio.uio_offset = 0; 645 1.16 yamt UIO_SETUP_SYSSPACE(&uio); 646 1.1 jdolecek 647 1.13 christos tbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 648 1.1 jdolecek 649 1.1 jdolecek /* ugly loop follows... */ 650 1.1 jdolecek do { 651 1.1 jdolecek off_t offset = uio.uio_offset; 652 1.1 jdolecek 653 1.1 jdolecek uio.uio_iov = &iov; 654 1.1 jdolecek uio.uio_iovcnt = 1; 655 1.13 christos iov.iov_base = tbuf; 656 1.1 jdolecek iov.iov_len = MAXBSIZE; 657 1.1 jdolecek uio.uio_resid = iov.iov_len; 658 1.1 jdolecek uio.uio_rw = UIO_READ; 659 1.1 jdolecek error = VOP_READ(fvp, &uio, 0, cred); 660 1.1 jdolecek 661 1.1 jdolecek if (error == 0) { 662 1.1 jdolecek uio.uio_iov = &iov; 663 1.1 jdolecek uio.uio_iovcnt = 1; 664 1.13 christos iov.iov_base = tbuf; 665 1.1 jdolecek iov.iov_len = MAXBSIZE - uio.uio_resid; 666 1.1 jdolecek uio.uio_offset = offset; 667 1.1 jdolecek uio.uio_rw = UIO_WRITE; 668 1.1 jdolecek uio.uio_resid = iov.iov_len; 669 1.1 jdolecek 670 1.1 jdolecek if (uio.uio_resid == 0) 671 1.1 jdolecek break; 672 1.1 jdolecek 673 1.1 jdolecek do { 674 1.1 jdolecek error = VOP_WRITE(tvp, &uio, 0, cred); 675 1.1 jdolecek } while ((uio.uio_resid > 0) && (error == 0)); 676 1.1 jdolecek } 677 1.1 jdolecek 678 1.1 jdolecek } while (error == 0); 679 1.1 jdolecek 680 1.13 christos free(tbuf, M_TEMP); 681 1.1 jdolecek return (error); 682 1.1 jdolecek } 683 1.1 jdolecek 684 1.1 jdolecek /* 685 1.1 jdolecek * (un) is assumed to be locked on entry and remains 686 1.1 jdolecek * locked on exit. 687 1.1 jdolecek */ 688 1.1 jdolecek int 689 1.32 matt union_copyup(struct union_node *un, int docopy, kauth_cred_t cred, 690 1.32 matt struct lwp *l) 691 1.1 jdolecek { 692 1.1 jdolecek int error; 693 1.1 jdolecek struct vnode *lvp, *uvp; 694 1.1 jdolecek struct vattr lvattr, uvattr; 695 1.1 jdolecek 696 1.15 christos error = union_vn_create(&uvp, un, l); 697 1.25 hannken if (error) 698 1.1 jdolecek return (error); 699 1.1 jdolecek 700 1.1 jdolecek union_newupper(un, uvp); 701 1.1 jdolecek 702 1.1 jdolecek lvp = un->un_lowervp; 703 1.1 jdolecek 704 1.1 jdolecek if (docopy) { 705 1.1 jdolecek /* 706 1.1 jdolecek * XX - should not ignore errors 707 1.1 jdolecek * from VOP_CLOSE 708 1.1 jdolecek */ 709 1.1 jdolecek vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 710 1.1 jdolecek 711 1.29 pooka error = VOP_GETATTR(lvp, &lvattr, cred); 712 1.1 jdolecek if (error == 0) 713 1.29 pooka error = VOP_OPEN(lvp, FREAD, cred); 714 1.1 jdolecek if (error == 0) { 715 1.15 christos error = union_copyfile(lvp, uvp, cred, l); 716 1.29 pooka (void) VOP_CLOSE(lvp, FREAD, cred); 717 1.1 jdolecek } 718 1.1 jdolecek if (error == 0) { 719 1.1 jdolecek /* Copy permissions up too */ 720 1.35 pooka vattr_null(&uvattr); 721 1.1 jdolecek uvattr.va_mode = lvattr.va_mode; 722 1.1 jdolecek uvattr.va_flags = lvattr.va_flags; 723 1.29 pooka error = VOP_SETATTR(uvp, &uvattr, cred); 724 1.1 jdolecek } 725 1.37 hannken VOP_UNLOCK(lvp); 726 1.1 jdolecek #ifdef UNION_DIAGNOSTIC 727 1.1 jdolecek if (error == 0) 728 1.1 jdolecek uprintf("union: copied up %s\n", un->un_path); 729 1.1 jdolecek #endif 730 1.1 jdolecek 731 1.1 jdolecek } 732 1.15 christos union_vn_close(uvp, FWRITE, cred, l); 733 1.1 jdolecek 734 1.1 jdolecek /* 735 1.1 jdolecek * Subsequent IOs will go to the top layer, so 736 1.1 jdolecek * call close on the lower vnode and open on the 737 1.1 jdolecek * upper vnode to ensure that the filesystem keeps 738 1.1 jdolecek * its references counts right. This doesn't do 739 1.1 jdolecek * the right thing with (cred) and (FREAD) though. 740 1.1 jdolecek * Ignoring error returns is not right, either. 741 1.1 jdolecek */ 742 1.1 jdolecek if (error == 0) { 743 1.1 jdolecek int i; 744 1.1 jdolecek 745 1.1 jdolecek vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 746 1.1 jdolecek for (i = 0; i < un->un_openl; i++) { 747 1.29 pooka (void) VOP_CLOSE(lvp, FREAD, cred); 748 1.29 pooka (void) VOP_OPEN(uvp, FREAD, cred); 749 1.1 jdolecek } 750 1.1 jdolecek un->un_openl = 0; 751 1.37 hannken VOP_UNLOCK(lvp); 752 1.1 jdolecek } 753 1.1 jdolecek 754 1.1 jdolecek return (error); 755 1.1 jdolecek 756 1.1 jdolecek } 757 1.1 jdolecek 758 1.50 hannken /* 759 1.50 hannken * Prepare the creation of a new node in the upper layer. 760 1.50 hannken * 761 1.50 hannken * (dvp) is the directory in which to create the new node. 762 1.50 hannken * it is locked on entry and exit. 763 1.50 hannken * (cnp) is the componentname to be created. 764 1.50 hannken * (cred, path, hash) are credentials, path and its hash to fill (cnp). 765 1.50 hannken */ 766 1.1 jdolecek static int 767 1.50 hannken union_do_lookup(struct vnode *dvp, struct componentname *cnp, kauth_cred_t cred, 768 1.56 dholland const char *path) 769 1.1 jdolecek { 770 1.1 jdolecek int error; 771 1.50 hannken struct vnode *vp; 772 1.50 hannken 773 1.50 hannken cnp->cn_nameiop = CREATE; 774 1.50 hannken cnp->cn_flags = LOCKPARENT | ISLASTCN; 775 1.50 hannken cnp->cn_cred = cred; 776 1.50 hannken cnp->cn_nameptr = path; 777 1.50 hannken cnp->cn_namelen = strlen(path); 778 1.1 jdolecek 779 1.50 hannken error = VOP_LOOKUP(dvp, &vp, cnp); 780 1.1 jdolecek 781 1.50 hannken if (error == 0) { 782 1.50 hannken KASSERT(vp != NULL); 783 1.50 hannken VOP_ABORTOP(dvp, cnp); 784 1.60 hannken vrele(vp); 785 1.50 hannken error = EEXIST; 786 1.50 hannken } else if (error == EJUSTRETURN) { 787 1.50 hannken error = 0; 788 1.1 jdolecek } 789 1.1 jdolecek 790 1.50 hannken return error; 791 1.1 jdolecek } 792 1.1 jdolecek 793 1.1 jdolecek /* 794 1.1 jdolecek * Create a shadow directory in the upper layer. 795 1.1 jdolecek * The new vnode is returned locked. 796 1.1 jdolecek * 797 1.1 jdolecek * (um) points to the union mount structure for access to the 798 1.1 jdolecek * the mounting process's credentials. 799 1.1 jdolecek * (dvp) is the directory in which to create the shadow directory. 800 1.1 jdolecek * it is unlocked on entry and exit. 801 1.1 jdolecek * (cnp) is the componentname to be created. 802 1.1 jdolecek * (vpp) is the returned newly created shadow directory, which 803 1.1 jdolecek * is returned locked. 804 1.1 jdolecek * 805 1.1 jdolecek * N.B. We still attempt to create shadow directories even if the union 806 1.1 jdolecek * is mounted read-only, which is a little nonintuitive. 807 1.1 jdolecek */ 808 1.1 jdolecek int 809 1.32 matt union_mkshadow(struct union_mount *um, struct vnode *dvp, 810 1.32 matt struct componentname *cnp, struct vnode **vpp) 811 1.1 jdolecek { 812 1.1 jdolecek int error; 813 1.1 jdolecek struct vattr va; 814 1.1 jdolecek struct componentname cn; 815 1.40 dholland char *pnbuf; 816 1.1 jdolecek 817 1.50 hannken if (cnp->cn_namelen + 1 > MAXPATHLEN) 818 1.50 hannken return ENAMETOOLONG; 819 1.50 hannken pnbuf = PNBUF_GET(); 820 1.50 hannken memcpy(pnbuf, cnp->cn_nameptr, cnp->cn_namelen); 821 1.50 hannken pnbuf[cnp->cn_namelen] = '\0'; 822 1.50 hannken 823 1.22 chs vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 824 1.50 hannken 825 1.50 hannken error = union_do_lookup(dvp, &cn, 826 1.56 dholland (um->um_op == UNMNT_ABOVE ? cnp->cn_cred : um->um_cred), pnbuf); 827 1.8 hannken if (error) { 828 1.37 hannken VOP_UNLOCK(dvp); 829 1.40 dholland PNBUF_PUT(pnbuf); 830 1.50 hannken return error; 831 1.1 jdolecek } 832 1.1 jdolecek 833 1.1 jdolecek /* 834 1.1 jdolecek * policy: when creating the shadow directory in the 835 1.1 jdolecek * upper layer, create it owned by the user who did 836 1.1 jdolecek * the mount, group from parent directory, and mode 837 1.1 jdolecek * 777 modified by umask (ie mostly identical to the 838 1.1 jdolecek * mkdir syscall). (jsp, kb) 839 1.1 jdolecek */ 840 1.1 jdolecek 841 1.35 pooka vattr_null(&va); 842 1.1 jdolecek va.va_type = VDIR; 843 1.1 jdolecek va.va_mode = um->um_cmode; 844 1.1 jdolecek 845 1.65 dholland KASSERT(*vpp == NULL); 846 1.1 jdolecek error = VOP_MKDIR(dvp, vpp, &cn, &va); 847 1.58 hannken VOP_UNLOCK(dvp); 848 1.40 dholland PNBUF_PUT(pnbuf); 849 1.50 hannken return error; 850 1.1 jdolecek } 851 1.1 jdolecek 852 1.1 jdolecek /* 853 1.1 jdolecek * Create a whiteout entry in the upper layer. 854 1.1 jdolecek * 855 1.1 jdolecek * (um) points to the union mount structure for access to the 856 1.1 jdolecek * the mounting process's credentials. 857 1.1 jdolecek * (dvp) is the directory in which to create the whiteout. 858 1.1 jdolecek * it is locked on entry and exit. 859 1.1 jdolecek * (cnp) is the componentname to be created. 860 1.50 hannken * (un) holds the path and its hash to be created. 861 1.1 jdolecek */ 862 1.1 jdolecek int 863 1.32 matt union_mkwhiteout(struct union_mount *um, struct vnode *dvp, 864 1.50 hannken struct componentname *cnp, struct union_node *un) 865 1.1 jdolecek { 866 1.1 jdolecek int error; 867 1.1 jdolecek struct componentname cn; 868 1.1 jdolecek 869 1.50 hannken error = union_do_lookup(dvp, &cn, 870 1.50 hannken (um->um_op == UNMNT_ABOVE ? cnp->cn_cred : um->um_cred), 871 1.56 dholland un->un_path); 872 1.25 hannken if (error) 873 1.50 hannken return error; 874 1.1 jdolecek 875 1.1 jdolecek error = VOP_WHITEOUT(dvp, &cn, CREATE); 876 1.50 hannken return error; 877 1.1 jdolecek } 878 1.1 jdolecek 879 1.1 jdolecek /* 880 1.1 jdolecek * union_vn_create: creates and opens a new shadow file 881 1.1 jdolecek * on the upper union layer. this function is similar 882 1.1 jdolecek * in spirit to calling vn_open but it avoids calling namei(). 883 1.1 jdolecek * the problem with calling namei is that a) it locks too many 884 1.1 jdolecek * things, and b) it doesn't start at the "right" directory, 885 1.50 hannken * whereas union_do_lookup is told where to start. 886 1.1 jdolecek */ 887 1.1 jdolecek int 888 1.32 matt union_vn_create(struct vnode **vpp, struct union_node *un, struct lwp *l) 889 1.1 jdolecek { 890 1.1 jdolecek struct vnode *vp; 891 1.19 ad kauth_cred_t cred = l->l_cred; 892 1.1 jdolecek struct vattr vat; 893 1.1 jdolecek struct vattr *vap = &vat; 894 1.1 jdolecek int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL); 895 1.1 jdolecek int error; 896 1.15 christos int cmode = UN_FILEMODE & ~l->l_proc->p_cwdi->cwdi_cmask; 897 1.1 jdolecek struct componentname cn; 898 1.1 jdolecek 899 1.1 jdolecek *vpp = NULLVP; 900 1.1 jdolecek 901 1.50 hannken vn_lock(un->un_dirvp, LK_EXCLUSIVE | LK_RETRY); 902 1.1 jdolecek 903 1.50 hannken error = union_do_lookup(un->un_dirvp, &cn, l->l_cred, 904 1.56 dholland un->un_path); 905 1.22 chs if (error) { 906 1.37 hannken VOP_UNLOCK(un->un_dirvp); 907 1.50 hannken return error; 908 1.1 jdolecek } 909 1.1 jdolecek 910 1.1 jdolecek /* 911 1.1 jdolecek * Good - there was no race to create the file 912 1.1 jdolecek * so go ahead and create it. The permissions 913 1.1 jdolecek * on the file will be 0666 modified by the 914 1.1 jdolecek * current user's umask. Access to the file, while 915 1.1 jdolecek * it is unioned, will require access to the top *and* 916 1.1 jdolecek * bottom files. Access when not unioned will simply 917 1.1 jdolecek * require access to the top-level file. 918 1.1 jdolecek * TODO: confirm choice of access permissions. 919 1.1 jdolecek */ 920 1.35 pooka vattr_null(vap); 921 1.1 jdolecek vap->va_type = VREG; 922 1.1 jdolecek vap->va_mode = cmode; 923 1.64 dholland vp = NULL; 924 1.50 hannken error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap); 925 1.66 hannken if (error) { 926 1.66 hannken VOP_UNLOCK(un->un_dirvp); 927 1.50 hannken return error; 928 1.66 hannken } 929 1.1 jdolecek 930 1.59 hannken vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 931 1.66 hannken VOP_UNLOCK(un->un_dirvp); 932 1.50 hannken error = VOP_OPEN(vp, fmode, cred); 933 1.50 hannken if (error) { 934 1.1 jdolecek vput(vp); 935 1.50 hannken return error; 936 1.1 jdolecek } 937 1.1 jdolecek 938 1.1 jdolecek vp->v_writecount++; 939 1.76 hannken VOP_UNLOCK(vp); 940 1.1 jdolecek *vpp = vp; 941 1.50 hannken return 0; 942 1.1 jdolecek } 943 1.1 jdolecek 944 1.1 jdolecek int 945 1.32 matt union_vn_close(struct vnode *vp, int fmode, kauth_cred_t cred, struct lwp *l) 946 1.1 jdolecek { 947 1.1 jdolecek 948 1.1 jdolecek if (fmode & FWRITE) 949 1.1 jdolecek --vp->v_writecount; 950 1.29 pooka return (VOP_CLOSE(vp, fmode, cred)); 951 1.1 jdolecek } 952 1.1 jdolecek 953 1.1 jdolecek void 954 1.32 matt union_removed_upper(struct union_node *un) 955 1.1 jdolecek { 956 1.53 hannken struct vnode *vp = UNIONTOV(un); 957 1.38 hannken 958 1.53 hannken vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 959 1.1 jdolecek #if 1 960 1.1 jdolecek /* 961 1.1 jdolecek * We do not set the uppervp to NULLVP here, because lowervp 962 1.1 jdolecek * may also be NULLVP, so this routine would end up creating 963 1.1 jdolecek * a bogus union node with no upper or lower VP (that causes 964 1.1 jdolecek * pain in many places that assume at least one VP exists). 965 1.1 jdolecek * Since we've removed this node from the cache hash chains, 966 1.1 jdolecek * it won't be found again. When all current holders 967 1.1 jdolecek * release it, union_inactive() will vgone() it. 968 1.1 jdolecek */ 969 1.1 jdolecek union_diruncache(un); 970 1.1 jdolecek #else 971 1.1 jdolecek union_newupper(un, NULLVP); 972 1.1 jdolecek #endif 973 1.1 jdolecek 974 1.53 hannken VOP_UNLOCK(vp); 975 1.38 hannken 976 1.54 hannken mutex_enter(&uhash_lock); 977 1.53 hannken if (un->un_cflags & UN_CACHED) { 978 1.53 hannken un->un_cflags &= ~UN_CACHED; 979 1.1 jdolecek LIST_REMOVE(un, un_cache); 980 1.1 jdolecek } 981 1.54 hannken mutex_exit(&uhash_lock); 982 1.1 jdolecek } 983 1.1 jdolecek 984 1.1 jdolecek #if 0 985 1.1 jdolecek struct vnode * 986 1.32 matt union_lowervp(struct vnode *vp) 987 1.1 jdolecek { 988 1.1 jdolecek struct union_node *un = VTOUNION(vp); 989 1.1 jdolecek 990 1.1 jdolecek if ((un->un_lowervp != NULLVP) && 991 1.1 jdolecek (vp->v_type == un->un_lowervp->v_type)) { 992 1.72 riastrad if (vget(un->un_lowervp, 0, true /* wait */) == 0) 993 1.1 jdolecek return (un->un_lowervp); 994 1.1 jdolecek } 995 1.1 jdolecek 996 1.1 jdolecek return (NULLVP); 997 1.1 jdolecek } 998 1.1 jdolecek #endif 999 1.1 jdolecek 1000 1.1 jdolecek /* 1001 1.1 jdolecek * determine whether a whiteout is needed 1002 1.1 jdolecek * during a remove/rmdir operation. 1003 1.1 jdolecek */ 1004 1.1 jdolecek int 1005 1.32 matt union_dowhiteout(struct union_node *un, kauth_cred_t cred) 1006 1.1 jdolecek { 1007 1.1 jdolecek struct vattr va; 1008 1.1 jdolecek 1009 1.1 jdolecek if (un->un_lowervp != NULLVP) 1010 1.1 jdolecek return (1); 1011 1.1 jdolecek 1012 1.29 pooka if (VOP_GETATTR(un->un_uppervp, &va, cred) == 0 && 1013 1.1 jdolecek (va.va_flags & OPAQUE)) 1014 1.1 jdolecek return (1); 1015 1.1 jdolecek 1016 1.1 jdolecek return (0); 1017 1.1 jdolecek } 1018 1.1 jdolecek 1019 1.1 jdolecek static void 1020 1.32 matt union_dircache_r(struct vnode *vp, struct vnode ***vppp, int *cntp) 1021 1.1 jdolecek { 1022 1.1 jdolecek struct union_node *un; 1023 1.1 jdolecek 1024 1.1 jdolecek if (vp->v_op != union_vnodeop_p) { 1025 1.1 jdolecek if (vppp) { 1026 1.35 pooka vref(vp); 1027 1.1 jdolecek *(*vppp)++ = vp; 1028 1.1 jdolecek if (--(*cntp) == 0) 1029 1.1 jdolecek panic("union: dircache table too small"); 1030 1.1 jdolecek } else { 1031 1.1 jdolecek (*cntp)++; 1032 1.1 jdolecek } 1033 1.1 jdolecek 1034 1.1 jdolecek return; 1035 1.1 jdolecek } 1036 1.1 jdolecek 1037 1.1 jdolecek un = VTOUNION(vp); 1038 1.1 jdolecek if (un->un_uppervp != NULLVP) 1039 1.1 jdolecek union_dircache_r(un->un_uppervp, vppp, cntp); 1040 1.1 jdolecek if (un->un_lowervp != NULLVP) 1041 1.1 jdolecek union_dircache_r(un->un_lowervp, vppp, cntp); 1042 1.1 jdolecek } 1043 1.1 jdolecek 1044 1.1 jdolecek struct vnode * 1045 1.21 christos union_dircache(struct vnode *vp, struct lwp *l) 1046 1.1 jdolecek { 1047 1.1 jdolecek int cnt; 1048 1.1 jdolecek struct vnode *nvp = NULLVP; 1049 1.1 jdolecek struct vnode **vpp; 1050 1.1 jdolecek struct vnode **dircache; 1051 1.1 jdolecek int error; 1052 1.1 jdolecek 1053 1.1 jdolecek vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 1054 1.1 jdolecek dircache = VTOUNION(vp)->un_dircache; 1055 1.1 jdolecek 1056 1.1 jdolecek nvp = NULLVP; 1057 1.1 jdolecek 1058 1.1 jdolecek if (dircache == 0) { 1059 1.1 jdolecek cnt = 0; 1060 1.1 jdolecek union_dircache_r(vp, 0, &cnt); 1061 1.1 jdolecek cnt++; 1062 1.1 jdolecek dircache = (struct vnode **) 1063 1.1 jdolecek malloc(cnt * sizeof(struct vnode *), 1064 1.1 jdolecek M_TEMP, M_WAITOK); 1065 1.1 jdolecek vpp = dircache; 1066 1.1 jdolecek union_dircache_r(vp, &vpp, &cnt); 1067 1.1 jdolecek VTOUNION(vp)->un_dircache = dircache; 1068 1.1 jdolecek *vpp = NULLVP; 1069 1.1 jdolecek vpp = dircache + 1; 1070 1.1 jdolecek } else { 1071 1.1 jdolecek vpp = dircache; 1072 1.1 jdolecek do { 1073 1.79 hannken if (*vpp++ == VTOUNION(vp)->un_lowervp) 1074 1.1 jdolecek break; 1075 1.1 jdolecek } while (*vpp != NULLVP); 1076 1.1 jdolecek } 1077 1.1 jdolecek 1078 1.1 jdolecek if (*vpp == NULLVP) 1079 1.1 jdolecek goto out; 1080 1.1 jdolecek 1081 1.35 pooka vref(*vpp); 1082 1.79 hannken error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, 1083 1.79 hannken NULLVP, *vpp, 0); 1084 1.1 jdolecek if (!error) { 1085 1.63 hannken vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY); 1086 1.1 jdolecek VTOUNION(vp)->un_dircache = 0; 1087 1.79 hannken VTOUNION(nvp)->un_hooknode = true; 1088 1.1 jdolecek VTOUNION(nvp)->un_dircache = dircache; 1089 1.1 jdolecek } 1090 1.1 jdolecek 1091 1.1 jdolecek out: 1092 1.37 hannken VOP_UNLOCK(vp); 1093 1.1 jdolecek return (nvp); 1094 1.1 jdolecek } 1095 1.1 jdolecek 1096 1.1 jdolecek void 1097 1.32 matt union_diruncache(struct union_node *un) 1098 1.1 jdolecek { 1099 1.1 jdolecek struct vnode **vpp; 1100 1.1 jdolecek 1101 1.53 hannken KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE); 1102 1.1 jdolecek if (un->un_dircache != 0) { 1103 1.1 jdolecek for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) 1104 1.1 jdolecek vrele(*vpp); 1105 1.1 jdolecek free(un->un_dircache, M_TEMP); 1106 1.1 jdolecek un->un_dircache = 0; 1107 1.1 jdolecek } 1108 1.2 jdolecek } 1109 1.2 jdolecek 1110 1.2 jdolecek /* 1111 1.44 hannken * Check whether node can rmdir (check empty). 1112 1.44 hannken */ 1113 1.44 hannken int 1114 1.44 hannken union_check_rmdir(struct union_node *un, kauth_cred_t cred) 1115 1.44 hannken { 1116 1.44 hannken int dirlen, eofflag, error; 1117 1.44 hannken char *dirbuf; 1118 1.44 hannken struct vattr va; 1119 1.44 hannken struct vnode *tvp; 1120 1.44 hannken struct dirent *dp, *edp; 1121 1.44 hannken struct componentname cn; 1122 1.44 hannken struct iovec aiov; 1123 1.44 hannken struct uio auio; 1124 1.44 hannken 1125 1.44 hannken KASSERT(un->un_uppervp != NULL); 1126 1.44 hannken 1127 1.44 hannken /* Check upper for being opaque. */ 1128 1.44 hannken KASSERT(VOP_ISLOCKED(un->un_uppervp)); 1129 1.44 hannken error = VOP_GETATTR(un->un_uppervp, &va, cred); 1130 1.44 hannken if (error || (va.va_flags & OPAQUE)) 1131 1.44 hannken return error; 1132 1.44 hannken 1133 1.44 hannken if (un->un_lowervp == NULL) 1134 1.44 hannken return 0; 1135 1.44 hannken 1136 1.44 hannken /* Check lower for being empty. */ 1137 1.45 hannken vn_lock(un->un_lowervp, LK_SHARED | LK_RETRY); 1138 1.44 hannken error = VOP_GETATTR(un->un_lowervp, &va, cred); 1139 1.44 hannken if (error) { 1140 1.44 hannken VOP_UNLOCK(un->un_lowervp); 1141 1.44 hannken return error; 1142 1.44 hannken } 1143 1.44 hannken dirlen = va.va_blocksize; 1144 1.44 hannken dirbuf = kmem_alloc(dirlen, KM_SLEEP); 1145 1.44 hannken /* error = 0; */ 1146 1.44 hannken eofflag = 0; 1147 1.44 hannken auio.uio_offset = 0; 1148 1.44 hannken do { 1149 1.44 hannken aiov.iov_len = dirlen; 1150 1.44 hannken aiov.iov_base = dirbuf; 1151 1.44 hannken auio.uio_iov = &aiov; 1152 1.44 hannken auio.uio_iovcnt = 1; 1153 1.44 hannken auio.uio_resid = aiov.iov_len; 1154 1.44 hannken auio.uio_rw = UIO_READ; 1155 1.44 hannken UIO_SETUP_SYSSPACE(&auio); 1156 1.44 hannken error = VOP_READDIR(un->un_lowervp, &auio, cred, &eofflag, 1157 1.44 hannken NULL, NULL); 1158 1.44 hannken if (error) 1159 1.44 hannken break; 1160 1.44 hannken edp = (struct dirent *)&dirbuf[dirlen - auio.uio_resid]; 1161 1.44 hannken for (dp = (struct dirent *)dirbuf; 1162 1.44 hannken error == 0 && dp < edp; 1163 1.44 hannken dp = (struct dirent *)((char *)dp + dp->d_reclen)) { 1164 1.44 hannken if (dp->d_reclen == 0) { 1165 1.44 hannken error = ENOTEMPTY; 1166 1.44 hannken break; 1167 1.44 hannken } 1168 1.44 hannken if (dp->d_type == DT_WHT || 1169 1.44 hannken (dp->d_namlen == 1 && dp->d_name[0] == '.') || 1170 1.44 hannken (dp->d_namlen == 2 && !memcmp(dp->d_name, "..", 2))) 1171 1.44 hannken continue; 1172 1.44 hannken /* Check for presence in the upper layer. */ 1173 1.44 hannken cn.cn_nameiop = LOOKUP; 1174 1.44 hannken cn.cn_flags = ISLASTCN | RDONLY; 1175 1.44 hannken cn.cn_cred = cred; 1176 1.44 hannken cn.cn_nameptr = dp->d_name; 1177 1.44 hannken cn.cn_namelen = dp->d_namlen; 1178 1.44 hannken error = VOP_LOOKUP(un->un_uppervp, &tvp, &cn); 1179 1.44 hannken if (error == ENOENT && (cn.cn_flags & ISWHITEOUT)) { 1180 1.44 hannken error = 0; 1181 1.44 hannken continue; 1182 1.44 hannken } 1183 1.44 hannken if (error == 0) 1184 1.60 hannken vrele(tvp); 1185 1.44 hannken error = ENOTEMPTY; 1186 1.44 hannken } 1187 1.44 hannken } while (error == 0 && !eofflag); 1188 1.44 hannken kmem_free(dirbuf, dirlen); 1189 1.44 hannken VOP_UNLOCK(un->un_lowervp); 1190 1.44 hannken 1191 1.44 hannken return error; 1192 1.44 hannken } 1193 1.44 hannken 1194 1.44 hannken /* 1195 1.2 jdolecek * This hook is called from vn_readdir() to switch to lower directory 1196 1.2 jdolecek * entry after the upper directory is read. 1197 1.2 jdolecek */ 1198 1.2 jdolecek int 1199 1.15 christos union_readdirhook(struct vnode **vpp, struct file *fp, struct lwp *l) 1200 1.2 jdolecek { 1201 1.2 jdolecek struct vnode *vp = *vpp, *lvp; 1202 1.2 jdolecek struct vattr va; 1203 1.2 jdolecek int error; 1204 1.2 jdolecek 1205 1.2 jdolecek if (vp->v_op != union_vnodeop_p) 1206 1.2 jdolecek return (0); 1207 1.2 jdolecek 1208 1.2 jdolecek /* 1209 1.2 jdolecek * If the directory is opaque, 1210 1.2 jdolecek * then don't show lower entries 1211 1.2 jdolecek */ 1212 1.53 hannken vn_lock(vp, LK_SHARED | LK_RETRY); 1213 1.29 pooka error = VOP_GETATTR(vp, &va, fp->f_cred); 1214 1.53 hannken VOP_UNLOCK(vp); 1215 1.51 hannken if (error || (va.va_flags & OPAQUE)) 1216 1.51 hannken return error; 1217 1.51 hannken 1218 1.51 hannken if ((lvp = union_dircache(vp, l)) == NULLVP) 1219 1.51 hannken return (0); 1220 1.12 perry 1221 1.29 pooka error = VOP_OPEN(lvp, FREAD, fp->f_cred); 1222 1.2 jdolecek if (error) { 1223 1.2 jdolecek vput(lvp); 1224 1.2 jdolecek return (error); 1225 1.2 jdolecek } 1226 1.37 hannken VOP_UNLOCK(lvp); 1227 1.67 matt fp->f_vnode = lvp; 1228 1.2 jdolecek fp->f_offset = 0; 1229 1.33 ad error = vn_close(vp, FREAD, fp->f_cred); 1230 1.2 jdolecek if (error) 1231 1.2 jdolecek return (error); 1232 1.2 jdolecek *vpp = lvp; 1233 1.2 jdolecek return (0); 1234 1.1 jdolecek } 1235