1 /* $NetBSD: adlookup.c,v 1.22 2022/08/06 18:26:41 andvar 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.22 2022/08/06 18:26:41 andvar 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 * parent pvp locked. 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 */ 64 int 65 adosfs_lookup(void *v) 66 { 67 struct vop_lookup_v2_args /* { 68 struct vnode *a_dvp; 69 struct vnode **a_vpp; 70 struct componentname *a_cnp; 71 } */ *sp = v; 72 int nameiop, last, flags, error, nocache, i; 73 struct componentname *cnp; 74 struct vnode **vpp; /* place to store result */ 75 struct anode *ap; /* anode to find */ 76 struct vnode *vdp; /* vnode of search dir */ 77 struct anode *adp; /* anode of search dir */ 78 kauth_cred_t ucp; /* lookup credentials */ 79 u_long bn, plen, hval; 80 const u_char *pelt; 81 82 #ifdef ADOSFS_DIAGNOSTIC 83 advopprint(sp); 84 #endif 85 cnp = sp->a_cnp; 86 vdp = sp->a_dvp; 87 adp = VTOA(vdp); 88 vpp = sp->a_vpp; 89 *vpp = NULL; 90 ucp = cnp->cn_cred; 91 nameiop = cnp->cn_nameiop; 92 flags = cnp->cn_flags; 93 last = flags & ISLASTCN; 94 pelt = (const u_char *)cnp->cn_nameptr; 95 plen = cnp->cn_namelen; 96 nocache = 0; 97 98 /* 99 * Check accessibility of directory. 100 */ 101 if ((error = VOP_ACCESS(vdp, VEXEC, ucp)) != 0) 102 return (error); 103 104 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 105 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 106 return (EROFS); 107 108 /* 109 * Before tediously performing a linear scan of the directory, 110 * check the name cache to see if the directory/name pair 111 * we are looking for is known already. 112 */ 113 if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen, 114 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) { 115 return *vpp == NULLVP ? ENOENT : 0; 116 } 117 118 /* 119 * fake a '.' 120 */ 121 if (plen == 1 && pelt[0] == '.') { 122 /* see special cases in prologue. */ 123 *vpp = vdp; 124 goto found; 125 } 126 /* 127 * fake a ".." 128 */ 129 if (flags & ISDOTDOT) { 130 if (vdp->v_type == VDIR && (vdp->v_vflag & VV_ROOT)) 131 panic("adosfs .. attempted through root"); 132 /* 133 * cannot get `..' while `vdp' is locked 134 * e.g. procA holds lock on `..' and waits for `vdp' 135 * we wait for `..' and hold lock on `vdp'. deadlock. 136 * because `vdp' may have been achieved through symlink 137 * fancy detection code that decreases the race 138 * window size is not reasonably possible. 139 * 140 * basically unlock the parent, try and lock the child (..) 141 * if that fails relock the parent (ignoring error) and 142 * fail. Otherwise we have the child (..), attempt to 143 * relock the parent. If that fails unlock the child (..) 144 * and fail. Otherwise we have succeded. 145 * 146 */ 147 VOP_UNLOCK(vdp); /* race */ 148 error = VFS_VGET(vdp->v_mount, (ino_t)adp->pblock, 149 LK_EXCLUSIVE, vpp); 150 vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY); 151 if (error) { 152 *vpp = NULL; 153 return (error); 154 } 155 goto found_lockdone; 156 } 157 158 /* 159 * hash the name and grab the first block in chain 160 * then walk the chain. if chain has not been fully 161 * walked before, track the count in `tabi' 162 */ 163 hval = adoshash(pelt, plen, adp->ntabent, IS_INTER(adp->amp)); 164 bn = adp->tab[hval]; 165 i = uimin(adp->tabi[hval], 0); 166 while (bn != 0) { 167 if ((error = VFS_VGET(vdp->v_mount, (ino_t)bn, 168 LK_EXCLUSIVE, vpp)) != 0) { 169 #ifdef ADOSFS_DIAGNOSTIC 170 printf("[aget] %d)", error); 171 #endif 172 return(error); 173 } 174 ap = VTOA(*vpp); 175 if (i <= 0) { 176 if (--i < adp->tabi[hval]) 177 adp->tabi[hval] = i; 178 /* 179 * last header in chain lock count down by 180 * negating it to positive 181 */ 182 if (ap->hashf == 0) { 183 #ifdef DEBUG 184 if (i != adp->tabi[hval]) 185 panic("adlookup: wrong chain count"); 186 #endif 187 adp->tabi[hval] = -adp->tabi[hval]; 188 } 189 } 190 if (strmatch(pelt, plen, ap->name, strlen(ap->name), 191 IS_INTER(adp->amp))) 192 goto found; 193 bn = ap->hashf; 194 vput(*vpp); 195 } 196 *vpp = NULL; 197 /* 198 * not found 199 */ 200 if ((nameiop == CREATE || nameiop == RENAME) && last) { 201 202 if (vdp->v_mount->mnt_flag & MNT_RDONLY) 203 return (EROFS); 204 205 if ((error = VOP_ACCESS(vdp, VWRITE, ucp)) != 0) { 206 #ifdef ADOSFS_DIAGNOSTIC 207 printf("[VOP_ACCESS] %d)", error); 208 #endif 209 return (error); 210 } 211 #ifdef ADOSFS_DIAGNOSTIC 212 printf("EJUSTRETURN)"); 213 #endif 214 return(EJUSTRETURN); 215 } 216 if (nameiop != CREATE) 217 cache_enter(vdp, NULL, cnp->cn_nameptr, cnp->cn_namelen, 218 cnp->cn_flags); 219 #ifdef ADOSFS_DIAGNOSTIC 220 printf("ENOENT)"); 221 #endif 222 return(ENOENT); 223 224 found: 225 if (nameiop == DELETE && last) { 226 if ((error = VOP_ACCESS(vdp, VWRITE, ucp)) != 0) { 227 if (vdp != *vpp) 228 vput(*vpp); 229 *vpp = NULL; 230 return (error); 231 } 232 nocache = 1; 233 } 234 if (nameiop == RENAME && last) { 235 if (vdp == *vpp) 236 return(EISDIR); 237 if ((error = VOP_ACCESS(vdp, VWRITE, ucp)) != 0) { 238 vput(*vpp); 239 *vpp = NULL; 240 return (error); 241 } 242 nocache = 1; 243 } 244 if (vdp == *vpp) 245 vref(vdp); 246 found_lockdone: 247 if (*vpp != vdp) 248 VOP_UNLOCK(*vpp); 249 if (nocache == 0) 250 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, 251 cnp->cn_flags); 252 253 #ifdef ADOSFS_DIAGNOSTIC 254 printf("0)\n"); 255 #endif 256 return(0); 257 } 258