1 /* $NetBSD: adlookup.c,v 1.7 2005/12/11 12:24:25 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1994 Christian E. Hopps 5 * Copyright (c) 1996 Matthias Scheler 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Christian E. Hopps. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: adlookup.c,v 1.7 2005/12/11 12:24:25 christos Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/vnode.h> 40 #include <sys/namei.h> 41 #include <sys/mount.h> 42 #include <sys/time.h> 43 #include <sys/queue.h> 44 #include <fs/adosfs/adosfs.h> 45 46 #ifdef ADOSFS_EXACTMATCH 47 #define strmatch(s1, l1, s2, l2, i) \ 48 ((l1) == (l2) && memcmp((s1), (s2), (l1)) == 0) 49 #else 50 #define strmatch(s1, l1, s2, l2, i) \ 51 ((l1) == (l2) && adoscaseequ((s1), (s2), (l1), (i))) 52 #endif 53 54 /* 55 * adosfs lookup. enters with: 56 * pvp (parent vnode) referenced and locked. 57 * exit with: 58 * target vp referenced and locked. 59 * unlock pvp unless LOCKPARENT and at end of search. 60 * special cases: 61 * pvp == vp, just ref pvp, pvp already holds a ref and lock from 62 * caller, this will not occur with RENAME or CREATE. 63 * LOOKUP always unlocks parent if last element. (not now!?!?) 64 */ 65 int 66 adosfs_lookup(v) 67 void *v; 68 { 69 struct vop_lookup_args /* { 70 struct vnode *a_dvp; 71 struct vnode **a_vpp; 72 struct componentname *a_cnp; 73 } */ *sp = v; 74 int nameiop, last, lockp, wantp, flags, error, nocache, i; 75 struct componentname *cnp; 76 struct vnode **vpp; /* place to store result */ 77 struct anode *ap; /* anode to find */ 78 struct vnode *vdp; /* vnode of search dir */ 79 struct anode *adp; /* anode of search dir */ 80 struct ucred *ucp; /* lookup credentials */ 81 u_long bn, plen, hval; 82 const u_char *pelt; 83 84 #ifdef ADOSFS_DIAGNOSTIC 85 advopprint(sp); 86 #endif 87 cnp = sp->a_cnp; 88 vdp = sp->a_dvp; 89 adp = VTOA(vdp); 90 vpp = sp->a_vpp; 91 *vpp = NULL; 92 ucp = cnp->cn_cred; 93 nameiop = cnp->cn_nameiop; 94 cnp->cn_flags &= ~PDIRUNLOCK; 95 flags = cnp->cn_flags; 96 last = flags & ISLASTCN; 97 lockp = flags & LOCKPARENT; 98 wantp = flags & (LOCKPARENT | WANTPARENT); 99 pelt = (const u_char *)cnp->cn_nameptr; 100 plen = cnp->cn_namelen; 101 nocache = 0; 102 103 /* 104 * Check accessiblity of directory. 105 */ 106 if ((error = VOP_ACCESS(vdp, VEXEC, ucp, cnp->cn_lwp)) != 0) 107 return (error); 108 109 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 110 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 111 return (EROFS); 112 113 /* 114 * Before tediously performing a linear scan of the directory, 115 * check the name cache to see if the directory/name pair 116 * we are looking for is known already. 117 */ 118 if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) 119 return (error); 120 121 /* 122 * fake a '.' 123 */ 124 if (plen == 1 && pelt[0] == '.') { 125 /* see special cases in prologue. */ 126 *vpp = vdp; 127 goto found; 128 } 129 /* 130 * fake a ".." 131 */ 132 if (flags & ISDOTDOT) { 133 if (vdp->v_type == VDIR && (vdp->v_flag & VROOT)) 134 panic("adosfs .. attempted through root"); 135 /* 136 * cannot get `..' while `vdp' is locked 137 * e.g. procA holds lock on `..' and waits for `vdp' 138 * we wait for `..' and hold lock on `vdp'. deadlock. 139 * because `vdp' may have been achieved through symlink 140 * fancy detection code that decreases the race 141 * window size is not reasonably possible. 142 * 143 * basically unlock the parent, try and lock the child (..) 144 * if that fails relock the parent (ignoring error) and 145 * fail. Otherwise we have the child (..) if this is the 146 * last and the caller requested LOCKPARENT, attempt to 147 * relock the parent. If that fails unlock the child (..) 148 * and fail. Otherwise we have succeded. 149 * 150 */ 151 VOP_UNLOCK(vdp, 0); /* race */ 152 cnp->cn_flags |= PDIRUNLOCK; 153 if ((error = VFS_VGET(vdp->v_mount, 154 (ino_t)adp->pblock, vpp)) != 0) { 155 if (vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY) == 0) 156 cnp->cn_flags &= ~PDIRUNLOCK; 157 } else if (last && lockp ) { 158 if ((error = vn_lock(vdp, LK_EXCLUSIVE))) { 159 vput(*vpp); 160 } else { 161 cnp->cn_flags &= ~PDIRUNLOCK; 162 } 163 } 164 if (error) { 165 *vpp = NULL; 166 return (error); 167 } 168 goto found_lockdone; 169 } 170 171 /* 172 * hash the name and grab the first block in chain 173 * then walk the chain. if chain has not been fully 174 * walked before, track the count in `tabi' 175 */ 176 hval = adoshash(pelt, plen, adp->ntabent, IS_INTER(adp->amp)); 177 bn = adp->tab[hval]; 178 i = min(adp->tabi[hval], 0); 179 while (bn != 0) { 180 if ((error = VFS_VGET(vdp->v_mount, (ino_t)bn, vpp 181 )) != 0) { 182 #ifdef ADOSFS_DIAGNOSTIC 183 printf("[aget] %d)", error); 184 #endif 185 /* XXX check to unlock parent possibly? */ 186 return(error); 187 } 188 ap = VTOA(*vpp); 189 if (i <= 0) { 190 if (--i < adp->tabi[hval]) 191 adp->tabi[hval] = i; 192 /* 193 * last header in chain lock count down by 194 * negating it to positive 195 */ 196 if (ap->hashf == 0) { 197 #ifdef DEBUG 198 if (i != adp->tabi[hval]) 199 panic("adlookup: wrong chain count"); 200 #endif 201 adp->tabi[hval] = -adp->tabi[hval]; 202 } 203 } 204 if (strmatch(pelt, plen, ap->name, strlen(ap->name), 205 IS_INTER(adp->amp))) 206 goto found; 207 bn = ap->hashf; 208 vput(*vpp); 209 } 210 *vpp = NULL; 211 /* 212 * not found 213 */ 214 if ((nameiop == CREATE || nameiop == RENAME) && last) { 215 216 if (vdp->v_mount->mnt_flag & MNT_RDONLY) 217 return (EROFS); 218 219 if ((error = VOP_ACCESS(vdp, VWRITE, ucp, cnp->cn_lwp)) != 0) { 220 #ifdef ADOSFS_DIAGNOSTIC 221 printf("[VOP_ACCESS] %d)", error); 222 #endif 223 return (error); 224 } 225 if (lockp == 0) { 226 VOP_UNLOCK(vdp, 0); 227 cnp->cn_flags |= PDIRUNLOCK; 228 } 229 cnp->cn_nameiop |= SAVENAME; 230 #ifdef ADOSFS_DIAGNOSTIC 231 printf("EJUSTRETURN)"); 232 #endif 233 return(EJUSTRETURN); 234 } 235 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 236 cache_enter(vdp, NULL, cnp); 237 #ifdef ADOSFS_DIAGNOSTIC 238 printf("ENOENT)"); 239 #endif 240 return(ENOENT); 241 242 found: 243 if (nameiop == DELETE && last) { 244 if ((error = VOP_ACCESS(vdp, VWRITE, ucp, cnp->cn_lwp)) != 0) { 245 if (vdp != *vpp) 246 vput(*vpp); 247 *vpp = NULL; 248 return (error); 249 } 250 nocache = 1; 251 } 252 if (nameiop == RENAME && wantp && last) { 253 if (vdp == *vpp) 254 return(EISDIR); 255 if ((error = VOP_ACCESS(vdp, VWRITE, ucp, cnp->cn_lwp)) != 0) { 256 vput(*vpp); 257 *vpp = NULL; 258 return (error); 259 } 260 cnp->cn_flags |= SAVENAME; 261 nocache = 1; 262 } 263 if (vdp == *vpp) 264 VREF(vdp); 265 else if (lockp == 0 || last == 0) { 266 VOP_UNLOCK(vdp, 0); 267 cnp->cn_flags |= PDIRUNLOCK; 268 } 269 found_lockdone: 270 if ((cnp->cn_flags & MAKEENTRY) && nocache == 0) 271 cache_enter(vdp, *vpp, cnp); 272 273 #ifdef ADOSFS_DIAGNOSTIC 274 printf("0)\n"); 275 #endif 276 return(0); 277 } 278