* Applied fixes from Bruce Evans to fix COW bugs, >1MB kernel loading,
[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
dd18dc33 37 * $Id: isofs_lookup.c,v 1.2 1993/07/20 03:27:28 jkh 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 */
90isofs_lookup(vdp, ndp, p)
91 register struct vnode *vdp;
92 register struct nameidata *ndp;
93 struct proc *p;
94{
95 register struct iso_node *dp; /* the directory we are searching */
96 register struct iso_mnt *imp; /* file system that directory is in */
97 struct buf *bp = 0; /* a buffer of directory entries */
98 register struct iso_directory_record *ep;
99 /* the current directory entry */
100 int entryoffsetinblock; /* offset of ep in bp's buffer */
101 enum {NONE, COMPACT, FOUND} slotstatus;
102 int slotoffset = -1; /* offset of area with free space */
103 int slotsize; /* size of area at slotoffset */
104 int slotfreespace; /* amount of space free in slot */
105 int slotneeded; /* size of the entry we're seeking */
106 int numdirpasses; /* strategy for directory search */
107 int endsearch; /* offset to end directory search */
108 struct iso_node *pdp; /* saved dp during symlink work */
109 struct iso_node *tdp; /* returned by iget */
110 int flag; /* LOOKUP, CREATE, RENAME, or DELETE */
111 int lockparent; /* 1 => lockparent flag is set */
112 int wantparent; /* 1 => wantparent or lockparent flag */
113 int error;
114
115 int reclen;
116 int namelen;
584fddaa
JH
117 char altname[251];
118 int i;
15637ed4
RG
119
120 ndp->ni_dvp = vdp;
121 ndp->ni_vp = NULL;
122 dp = VTOI(vdp);
123 imp = dp->i_mnt;
124 lockparent = ndp->ni_nameiop & LOCKPARENT;
125 flag = ndp->ni_nameiop & OPMASK;
126 wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
127
128 /*
129 * Check accessiblity of directory.
130 */
131 if ((dp->iso_flags & 2) == 0)
132 return (ENOTDIR);
133
134 /*
135 * We now have a segment name to search for, and a directory to search.
136 *
137 * Before tediously performing a linear scan of the directory,
138 * check the name cache to see if the directory/name pair
139 * we are looking for is known already.
140 */
141 if (error = cache_lookup(ndp)) {
142 int vpid; /* capability number of vnode */
143
144 if (error == ENOENT)
145 return (error);
146#ifdef PARANOID
147 if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
148 panic("ufs_lookup: .. through root");
149#endif
150 /*
151 * Get the next vnode in the path.
152 * See comment below starting `Step through' for
153 * an explaination of the locking protocol.
154 */
155 pdp = dp;
156 dp = VTOI(ndp->ni_vp);
157 vdp = ndp->ni_vp;
158 vpid = vdp->v_id;
159 if (pdp == dp) {
160 VREF(vdp);
161 error = 0;
162 } else if (ndp->ni_isdotdot) {
163 ISO_IUNLOCK(pdp);
164 error = vget(vdp);
165 if (!error && lockparent && *ndp->ni_next == '\0')
166 ISO_ILOCK(pdp);
167 } else {
168 error = vget(vdp);
169 if (!lockparent || error || *ndp->ni_next != '\0')
170 ISO_IUNLOCK(pdp);
171 }
172 /*
173 * Check that the capability number did not change
174 * while we were waiting for the lock.
175 */
176 if (!error) {
177 if (vpid == vdp->v_id)
178 return (0);
179 iso_iput(dp);
180 if (lockparent && pdp != dp && *ndp->ni_next == '\0')
181 ISO_IUNLOCK(pdp);
182 }
183 ISO_ILOCK(pdp);
184 dp = pdp;
185 vdp = ITOV(dp);
186 ndp->ni_vp = NULL;
187 }
188
189 /*
190 * If there is cached information on a previous search of
191 * this directory, pick up where we last left off.
192 * We cache only lookups as these are the most common
193 * and have the greatest payoff. Caching CREATE has little
194 * benefit as it usually must search the entire directory
195 * to determine that the entry does not exist. Caching the
196 * location of the last DELETE or RENAME has not reduced
197 * profiling time and hence has been removed in the interest
198 * of simplicity.
199 */
200 if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
201 ndp->ni_ufs.ufs_offset = 0;
202 numdirpasses = 1;
203 } else {
204 ndp->ni_ufs.ufs_offset = dp->i_diroff;
205 entryoffsetinblock = iso_blkoff(imp, ndp->ni_ufs.ufs_offset);
206 if (entryoffsetinblock != 0) {
207 if (error = iso_blkatoff(dp, ndp->ni_ufs.ufs_offset,
208 (char **)0, &bp))
209 return (error);
210 }
211 numdirpasses = 2;
212 nchstats.ncs_2passes++;
213 }
214 endsearch = roundup(dp->i_size, imp->logical_block_size);
215
216searchloop:
217 while (ndp->ni_ufs.ufs_offset < endsearch) {
218 /*
219 * If offset is on a block boundary,
220 * read the next directory block.
221 * Release previous if it exists.
222 */
223 if (iso_blkoff(imp, ndp->ni_ufs.ufs_offset) == 0) {
224 if (bp != NULL)
225 brelse(bp);
226 if (error = iso_blkatoff(dp, ndp->ni_ufs.ufs_offset,
227 (char **)0, &bp))
228 return (error);
229 entryoffsetinblock = 0;
230 }
231 /*
232 * Get pointer to next entry.
233 */
234
235 ep = (struct iso_directory_record *)
236 (bp->b_un.b_addr + entryoffsetinblock);
237
238 reclen = isonum_711 (ep->length);
239 if (reclen == 0) {
240 /* skip to next block, if any */
241 ndp->ni_ufs.ufs_offset =
242 roundup (ndp->ni_ufs.ufs_offset,
243 imp->logical_block_size);
244 continue;
245 }
246
247 if (reclen < sizeof (struct iso_directory_record))
248 /* illegal entry, stop */
249 break;
250
251/* 10 Aug 92*/ if (entryoffsetinblock + reclen -1 >= imp->logical_block_size)
252 /* entries are not allowed to cross boundaries */
253 break;
254
255 /*
256 * Check for a name match.
257 */
258 namelen = isonum_711 (ep->name_len);
259
260 if (reclen < sizeof (struct iso_directory_record) + namelen)
261 /* illegal entry, stop */
262 break;
263
584fddaa 264 if (namelen == 1
15637ed4
RG
265 && ((ndp->ni_namelen == 1
266 && ndp->ni_ptr[0] == '.'
267 && ep->name[0] == 0)
584fddaa 268 || (ndp->ni_isdotdot && ep->name[0] == 1))) {
15637ed4
RG
269 /*
270 * Save directory entry's inode number and
271 * reclen in ndp->ni_ufs area, and release
272 * directory buffer.
273 */
274 ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent);
275 brelse(bp);
276 goto found;
584fddaa
JH
277 } else {
278 switch ( imp->iso_ftype ) {
279 case ISO_FTYPE_9660:
280 if( ( namelen >= ndp->ni_namelen ) &&
281 ( isofncmp( ndp->ni_ptr, ndp->ni_namelen, ep->name, namelen ) ) ) {
282 ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent);
283 brelse(bp);
284 goto found;
285 }
286 break;
287 case ISO_FTYPE_RRIP:
288 isofs_rrip_getname( ep, altname, &namelen );
289 if ( ( namelen == ndp->ni_namelen ) &&
290 ( !bcmp( ndp->ni_ptr, altname, ndp->ni_namelen ) ) ) {
291 ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent);
292 brelse(bp);
293 goto found;
294 }
295 break;
296 default:
297 break;
298 }
15637ed4
RG
299 }
300 ndp->ni_ufs.ufs_offset += reclen;
301 entryoffsetinblock += reclen;
302 }
303/* notfound: */
304 /*
305 * If we started in the middle of the directory and failed
306 * to find our target, we must check the beginning as well.
307 */
308 if (numdirpasses == 2) {
309 numdirpasses--;
310 ndp->ni_ufs.ufs_offset = 0;
311 endsearch = dp->i_diroff;
312 goto searchloop;
313 }
314 if (bp != NULL)
315 brelse(bp);
316 /*
317 * Insert name into cache (as non-existent) if appropriate.
318 */
319 if (ndp->ni_makeentry)
320 cache_enter(ndp);
321 return (ENOENT);
322
323found:
324 if (numdirpasses == 2)
325 nchstats.ncs_pass2++;
326
327 /*
328 * Found component in pathname.
329 * If the final component of path name, save information
330 * in the cache as to where the entry was found.
331 */
332 if (*ndp->ni_next == '\0' && flag == LOOKUP)
333 dp->i_diroff = ndp->ni_ufs.ufs_offset;
334 /* &~ (imp->logical_block_size - 1); */
335
336 /*
337 * Step through the translation in the name. We do not `iput' the
338 * directory because we may need it again if a symbolic link
339 * is relative to the current directory. Instead we save it
340 * unlocked as "pdp". We must get the target inode before unlocking
341 * the directory to insure that the inode will not be removed
342 * before we get it. We prevent deadlock by always fetching
343 * inodes from the root, moving down the directory tree. Thus
344 * when following backward pointers ".." we must unlock the
345 * parent directory before getting the requested directory.
346 * There is a potential race condition here if both the current
347 * and parent directories are removed before the `iget' for the
348 * inode associated with ".." returns. We hope that this occurs
349 * infrequently since we cannot avoid this race condition without
350 * implementing a sophisticated deadlock detection algorithm.
351 * Note also that this simple deadlock detection scheme will not
352 * work if the file system has any hard links other than ".."
353 * that point backwards in the directory structure.
354 */
355 pdp = dp;
356 if (ndp->ni_isdotdot) {
357 ISO_IUNLOCK(pdp); /* race to get the inode */
358 if (error = iso_iget(dp, ndp->ni_ufs.ufs_ino, &tdp, ep)) {
359 ISO_ILOCK(pdp);
360 return (error);
361 }
362 if (lockparent && *ndp->ni_next == '\0')
363 ISO_ILOCK(pdp);
364 ndp->ni_vp = ITOV(tdp);
365 } else if (dp->i_number == ndp->ni_ufs.ufs_ino) {
366 VREF(vdp); /* we want ourself, ie "." */
367 ndp->ni_vp = vdp;
368 } else {
369 if (error = iso_iget(dp, ndp->ni_ufs.ufs_ino, &tdp, ep))
370 return (error);
371 if (!lockparent || *ndp->ni_next != '\0')
372 ISO_IUNLOCK(pdp);
373 ndp->ni_vp = ITOV(tdp);
374 }
375
376 /*
377 * Insert name into cache if appropriate.
378 */
379 if (ndp->ni_makeentry)
380 cache_enter(ndp);
381 return (0);
382}
383
384
385/*
386 * Return buffer with contents of block "offset"
387 * from the beginning of directory "ip". If "res"
388 * is non-zero, fill it in with a pointer to the
389 * remaining space in the directory.
390 */
391iso_blkatoff(ip, offset, res, bpp)
392 struct iso_node *ip;
393 off_t offset;
394 char **res;
395 struct buf **bpp;
396{
397 register struct iso_mnt *imp = ip->i_mnt;
398 daddr_t lbn = iso_lblkno (imp, offset);
399 int bsize = iso_blksize (imp, ip, lbn);
400 struct buf *bp;
401 int error;
402
403 *bpp = 0;
404 if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) {
405 brelse(bp);
406 return (error);
407 }
408 if (res)
409 *res = bp->b_un.b_addr + iso_blkoff(imp, offset);
410 *bpp = bp;
411
412 return (0);
413}