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