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