This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / isofs / isofs_lookup.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91
34 *
35 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
36 * -------------------- ----- ----------------------
37 * CURRENT PATCH LEVEL: 2 00151
38 * -------------------- ----- ----------------------
39 *
40 * 10 Aug 92 Scott Burris Fixed "delete from CD-ROM" bug
41 * 23 Apr 93 Jagane D Sundar support nfs exported isofs
42 */
43
44#include "param.h"
45#include "namei.h"
46#include "buf.h"
47#include "file.h"
48#include "vnode.h"
49#include "mount.h"
50
51#include "iso.h"
52#include "isofs_node.h"
53
54struct nchstats nchstats;
55
56/*
57 * Convert a component of a pathname into a pointer to a locked inode.
58 * This is a very central and rather complicated routine.
59 * If the file system is not maintained in a strict tree hierarchy,
60 * this can result in a deadlock situation (see comments in code below).
61 *
62 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
63 * whether the name is to be looked up, created, renamed, or deleted.
64 * When CREATE, RENAME, or DELETE is specified, information usable in
65 * creating, renaming, or deleting a directory entry may be calculated.
66 * If flag has LOCKPARENT or'ed into it and the target of the pathname
67 * exists, lookup returns both the target and its parent directory locked.
68 * When creating or renaming and LOCKPARENT is specified, the target may
69 * not be ".". When deleting and LOCKPARENT is specified, the target may
70 * be "."., but the caller must check to ensure it does an vrele and iput
71 * instead of two iputs.
72 *
73 * Overall outline of ufs_lookup:
74 *
75 * check accessibility of directory
76 * look for name in cache, if found, then if at end of path
77 * and deleting or creating, drop it, else return name
78 * search for name in directory, to found or notfound
79 * notfound:
80 * if creating, return locked directory, leaving info on available slots
81 * else return error
82 * found:
83 * if at end of path and deleting, return information to allow delete
84 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
85 * inode and return info to allow rewrite
86 * if not at end, add name to cache; if at end and neither creating
87 * nor deleting, add name to cache
88 *
89 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
90 */
91isofs_lookup(vdp, ndp, p)
92 register struct vnode *vdp;
93 register struct nameidata *ndp;
94 struct proc *p;
95{
96 register struct iso_node *dp; /* the directory we are searching */
97 register struct iso_mnt *imp; /* file system that directory is in */
98 struct buf *bp = 0; /* a buffer of directory entries */
99 register struct iso_directory_record *ep;
100 /* the current directory entry */
101 int entryoffsetinblock; /* offset of ep in bp's buffer */
102 enum {NONE, COMPACT, FOUND} slotstatus;
103 int slotoffset = -1; /* offset of area with free space */
104 int slotsize; /* size of area at slotoffset */
105 int slotfreespace; /* amount of space free in slot */
106 int slotneeded; /* size of the entry we're seeking */
107 int numdirpasses; /* strategy for directory search */
108 int endsearch; /* offset to end directory search */
109 struct iso_node *pdp; /* saved dp during symlink work */
110 struct iso_node *tdp; /* returned by iget */
111 int flag; /* LOOKUP, CREATE, RENAME, or DELETE */
112 int lockparent; /* 1 => lockparent flag is set */
113 int wantparent; /* 1 => wantparent or lockparent flag */
114 int error;
115
116 int reclen;
117 int namelen;
118
119 ndp->ni_dvp = vdp;
120 ndp->ni_vp = NULL;
121 dp = VTOI(vdp);
122 imp = dp->i_mnt;
123 lockparent = ndp->ni_nameiop & LOCKPARENT;
124 flag = ndp->ni_nameiop & OPMASK;
125 wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
126
127 /*
128 * Check accessiblity of directory.
129 */
130 if ((dp->iso_flags & 2) == 0)
131 return (ENOTDIR);
132
133 /*
134 * We now have a segment name to search for, and a directory to search.
135 *
136 * Before tediously performing a linear scan of the directory,
137 * check the name cache to see if the directory/name pair
138 * we are looking for is known already.
139 */
140 if (error = cache_lookup(ndp)) {
141 int vpid; /* capability number of vnode */
142
143 if (error == ENOENT)
144 return (error);
145#ifdef PARANOID
146 if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
147 panic("ufs_lookup: .. through root");
148#endif
149 /*
150 * Get the next vnode in the path.
151 * See comment below starting `Step through' for
152 * an explaination of the locking protocol.
153 */
154 pdp = dp;
155 dp = VTOI(ndp->ni_vp);
156 vdp = ndp->ni_vp;
157 vpid = vdp->v_id;
158 if (pdp == dp) {
159 VREF(vdp);
160 error = 0;
161 } else if (ndp->ni_isdotdot) {
162 ISO_IUNLOCK(pdp);
163 error = vget(vdp);
164 if (!error && lockparent && *ndp->ni_next == '\0')
165 ISO_ILOCK(pdp);
166 } else {
167 error = vget(vdp);
168 if (!lockparent || error || *ndp->ni_next != '\0')
169 ISO_IUNLOCK(pdp);
170 }
171 /*
172 * Check that the capability number did not change
173 * while we were waiting for the lock.
174 */
175 if (!error) {
176 if (vpid == vdp->v_id)
177 return (0);
178 iso_iput(dp);
179 if (lockparent && pdp != dp && *ndp->ni_next == '\0')
180 ISO_IUNLOCK(pdp);
181 }
182 ISO_ILOCK(pdp);
183 dp = pdp;
184 vdp = ITOV(dp);
185 ndp->ni_vp = NULL;
186 }
187
188 /*
189 * If there is cached information on a previous search of
190 * this directory, pick up where we last left off.
191 * We cache only lookups as these are the most common
192 * and have the greatest payoff. Caching CREATE has little
193 * benefit as it usually must search the entire directory
194 * to determine that the entry does not exist. Caching the
195 * location of the last DELETE or RENAME has not reduced
196 * profiling time and hence has been removed in the interest
197 * of simplicity.
198 */
199 if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
200 ndp->ni_ufs.ufs_offset = 0;
201 numdirpasses = 1;
202 } else {
203 ndp->ni_ufs.ufs_offset = dp->i_diroff;
204 entryoffsetinblock = iso_blkoff(imp, ndp->ni_ufs.ufs_offset);
205 if (entryoffsetinblock != 0) {
206 if (error = iso_blkatoff(dp, ndp->ni_ufs.ufs_offset,
207 (char **)0, &bp))
208 return (error);
209 }
210 numdirpasses = 2;
211 nchstats.ncs_2passes++;
212 }
213 endsearch = roundup(dp->i_size, imp->logical_block_size);
214
215searchloop:
216 while (ndp->ni_ufs.ufs_offset < endsearch) {
217 /*
218 * If offset is on a block boundary,
219 * read the next directory block.
220 * Release previous if it exists.
221 */
222 if (iso_blkoff(imp, ndp->ni_ufs.ufs_offset) == 0) {
223 if (bp != NULL)
224 brelse(bp);
225 if (error = iso_blkatoff(dp, ndp->ni_ufs.ufs_offset,
226 (char **)0, &bp))
227 return (error);
228 entryoffsetinblock = 0;
229 }
230 /*
231 * Get pointer to next entry.
232 */
233
234 ep = (struct iso_directory_record *)
235 (bp->b_un.b_addr + entryoffsetinblock);
236
237 reclen = isonum_711 (ep->length);
238 if (reclen == 0) {
239 /* skip to next block, if any */
240 ndp->ni_ufs.ufs_offset =
241 roundup (ndp->ni_ufs.ufs_offset,
242 imp->logical_block_size);
243 continue;
244 }
245
246 if (reclen < sizeof (struct iso_directory_record))
247 /* illegal entry, stop */
248 break;
249
250/* 10 Aug 92*/ if (entryoffsetinblock + reclen -1 >= imp->logical_block_size)
251 /* entries are not allowed to cross boundaries */
252 break;
253
254 /*
255 * Check for a name match.
256 */
257 namelen = isonum_711 (ep->name_len);
258
259 if (reclen < sizeof (struct iso_directory_record) + namelen)
260 /* illegal entry, stop */
261 break;
262
263 if ((namelen == 1
264 && ((ndp->ni_namelen == 1
265 && ndp->ni_ptr[0] == '.'
266 && ep->name[0] == 0)
267 || (ndp->ni_isdotdot && ep->name[0] == 1)))
268 || (namelen >= ndp->ni_namelen
269 && isofncmp(ndp->ni_ptr, ndp->ni_namelen, ep->name,
270 namelen))) {
271 /*
272 * Save directory entry's inode number and
273 * reclen in ndp->ni_ufs area, and release
274 * directory buffer.
275 */
276 ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent);
277 brelse(bp);
278 goto found;
279 }
280 ndp->ni_ufs.ufs_offset += reclen;
281 entryoffsetinblock += reclen;
282 }
283/* notfound: */
284 /*
285 * If we started in the middle of the directory and failed
286 * to find our target, we must check the beginning as well.
287 */
288 if (numdirpasses == 2) {
289 numdirpasses--;
290 ndp->ni_ufs.ufs_offset = 0;
291 endsearch = dp->i_diroff;
292 goto searchloop;
293 }
294 if (bp != NULL)
295 brelse(bp);
296 /*
297 * Insert name into cache (as non-existent) if appropriate.
298 */
299 if (ndp->ni_makeentry)
300 cache_enter(ndp);
301 return (ENOENT);
302
303found:
304 if (numdirpasses == 2)
305 nchstats.ncs_pass2++;
306
307 /*
308 * Found component in pathname.
309 * If the final component of path name, save information
310 * in the cache as to where the entry was found.
311 */
312 if (*ndp->ni_next == '\0' && flag == LOOKUP)
313 dp->i_diroff = ndp->ni_ufs.ufs_offset;
314 /* &~ (imp->logical_block_size - 1); */
315
316 /*
317 * Step through the translation in the name. We do not `iput' the
318 * directory because we may need it again if a symbolic link
319 * is relative to the current directory. Instead we save it
320 * unlocked as "pdp". We must get the target inode before unlocking
321 * the directory to insure that the inode will not be removed
322 * before we get it. We prevent deadlock by always fetching
323 * inodes from the root, moving down the directory tree. Thus
324 * when following backward pointers ".." we must unlock the
325 * parent directory before getting the requested directory.
326 * There is a potential race condition here if both the current
327 * and parent directories are removed before the `iget' for the
328 * inode associated with ".." returns. We hope that this occurs
329 * infrequently since we cannot avoid this race condition without
330 * implementing a sophisticated deadlock detection algorithm.
331 * Note also that this simple deadlock detection scheme will not
332 * work if the file system has any hard links other than ".."
333 * that point backwards in the directory structure.
334 */
335 pdp = dp;
336 if (ndp->ni_isdotdot) {
337 ISO_IUNLOCK(pdp); /* race to get the inode */
338 if (error = iso_iget(dp, ndp->ni_ufs.ufs_ino, &tdp, ep)) {
339 ISO_ILOCK(pdp);
340 return (error);
341 }
342 if (lockparent && *ndp->ni_next == '\0')
343 ISO_ILOCK(pdp);
344 ndp->ni_vp = ITOV(tdp);
345 } else if (dp->i_number == ndp->ni_ufs.ufs_ino) {
346 VREF(vdp); /* we want ourself, ie "." */
347 ndp->ni_vp = vdp;
348 } else {
349 if (error = iso_iget(dp, ndp->ni_ufs.ufs_ino, &tdp, ep))
350 return (error);
351 if (!lockparent || *ndp->ni_next != '\0')
352 ISO_IUNLOCK(pdp);
353 ndp->ni_vp = ITOV(tdp);
354 }
355
356 /*
357 * Insert name into cache if appropriate.
358 */
359 if (ndp->ni_makeentry)
360 cache_enter(ndp);
361 return (0);
362}
363
364
365/*
366 * Return buffer with contents of block "offset"
367 * from the beginning of directory "ip". If "res"
368 * is non-zero, fill it in with a pointer to the
369 * remaining space in the directory.
370 */
371iso_blkatoff(ip, offset, res, bpp)
372 struct iso_node *ip;
373 off_t offset;
374 char **res;
375 struct buf **bpp;
376{
377 register struct iso_mnt *imp = ip->i_mnt;
378 daddr_t lbn = iso_lblkno (imp, offset);
379 int bsize = iso_blksize (imp, ip, lbn);
380 struct buf *bp;
381 int error;
382
383 *bpp = 0;
384 if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) {
385 brelse(bp);
386 return (error);
387 }
388 if (res)
389 *res = bp->b_un.b_addr + iso_blkoff(imp, offset);
390 *bpp = bp;
391
392 return (0);
393}
394
395