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