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
adosfs_lookup(void * v)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