1 /*-
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley
6 * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
7 * Support code is derived from software contributed to Berkeley
8 * by Atsushi Murai (amurai@spec.co.jp).
9 *
10 * %sccs.include.redist.c%
11 *
12 * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91
13 *
14 * @(#)cd9660_lookup.c 8.7 (Berkeley) 05/27/95
15 */
16
17 #include <sys/param.h>
18 #include <sys/namei.h>
19 #include <sys/buf.h>
20 #include <sys/file.h>
21 #include <sys/vnode.h>
22 #include <sys/mount.h>
23
24 #include <isofs/cd9660/iso.h>
25 #include <isofs/cd9660/cd9660_node.h>
26 #include <isofs/cd9660/iso_rrip.h>
27 #include <isofs/cd9660/cd9660_rrip.h>
28
29 struct nchstats iso_nchstats;
30
31 /*
32 * Convert a component of a pathname into a pointer to a locked inode.
33 * This is a very central and rather complicated routine.
34 * If the file system is not maintained in a strict tree hierarchy,
35 * this can result in a deadlock situation (see comments in code below).
36 *
37 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
38 * whether the name is to be looked up, created, renamed, or deleted.
39 * When CREATE, RENAME, or DELETE is specified, information usable in
40 * creating, renaming, or deleting a directory entry may be calculated.
41 * If flag has LOCKPARENT or'ed into it and the target of the pathname
42 * exists, lookup returns both the target and its parent directory locked.
43 * When creating or renaming and LOCKPARENT is specified, the target may
44 * not be ".". When deleting and LOCKPARENT is specified, the target may
45 * be "."., but the caller must check to ensure it does an vrele and iput
46 * instead of two iputs.
47 *
48 * Overall outline of ufs_lookup:
49 *
50 * check accessibility of directory
51 * look for name in cache, if found, then if at end of path
52 * and deleting or creating, drop it, else return name
53 * search for name in directory, to found or notfound
54 * notfound:
55 * if creating, return locked directory, leaving info on available slots
56 * else return error
57 * found:
58 * if at end of path and deleting, return information to allow delete
59 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
60 * inode and return info to allow rewrite
61 * if not at end, add name to cache; if at end and neither creating
62 * nor deleting, add name to cache
63 *
64 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
65 */
66 cd9660_lookup(ap)
67 struct vop_lookup_args /* {
68 struct vnode *a_dvp;
69 struct vnode **a_vpp;
70 struct componentname *a_cnp;
71 } */ *ap;
72 {
73 register struct vnode *vdp; /* vnode for directory being searched */
74 register struct iso_node *dp; /* inode for directory being searched */
75 register struct iso_mnt *imp; /* file system that directory is in */
76 struct buf *bp; /* a buffer of directory entries */
77 struct iso_directory_record *ep;/* the current directory entry */
78 int entryoffsetinblock; /* offset of ep in bp's buffer */
79 int saveoffset; /* offset of last directory entry in dir */
80 int numdirpasses; /* strategy for directory search */
81 doff_t endsearch; /* offset to end directory search */
82 struct vnode *pdp; /* saved dp during symlink work */
83 struct vnode *tdp; /* returned by cd9660_vget_internal */
84 u_long bmask; /* block offset mask */
85 int lockparent; /* 1 => lockparent flag is set */
86 int wantparent; /* 1 => wantparent or lockparent flag */
87 int error;
88 ino_t ino = 0;
89 int reclen;
90 u_short namelen;
91 char altname[NAME_MAX];
92 int res;
93 int assoc, len;
94 char *name;
95 struct vnode **vpp = ap->a_vpp;
96 struct componentname *cnp = ap->a_cnp;
97 struct ucred *cred = cnp->cn_cred;
98 int flags = cnp->cn_flags;
99 int nameiop = cnp->cn_nameiop;
100 struct proc *p = cnp->cn_proc;
101
102 bp = NULL;
103 *vpp = NULL;
104 vdp = ap->a_dvp;
105 dp = VTOI(vdp);
106 imp = dp->i_mnt;
107 lockparent = flags & LOCKPARENT;
108 wantparent = flags & (LOCKPARENT|WANTPARENT);
109
110 /*
111 * Check accessiblity of directory.
112 */
113 if (vdp->v_type != VDIR)
114 return (ENOTDIR);
115 if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))
116 return (error);
117 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
118 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
119 return (EROFS);
120
121 /*
122 * We now have a segment name to search for, and a directory to search.
123 *
124 * Before tediously performing a linear scan of the directory,
125 * check the name cache to see if the directory/name pair
126 * we are looking for is known already.
127 */
128 if (error = cache_lookup(vdp, vpp, cnp)) {
129 int vpid; /* capability number of vnode */
130
131 if (error == ENOENT)
132 return (error);
133 #ifdef PARANOID
134 if ((vdp->v_flag & VROOT) && (flags & ISDOTDOT))
135 panic("cd9660_lookup: .. through root");
136 #endif
137 /*
138 * Get the next vnode in the path.
139 * See comment below starting `Step through' for
140 * an explaination of the locking protocol.
141 */
142 pdp = vdp;
143 dp = VTOI(*vpp);
144 vdp = *vpp;
145 vpid = vdp->v_id;
146 if (pdp == vdp) {
147 VREF(vdp);
148 error = 0;
149 } else if (flags & ISDOTDOT) {
150 VOP_UNLOCK(pdp, 0, p);
151 error = vget(vdp, LK_EXCLUSIVE, p);
152 if (!error && lockparent && (flags & ISLASTCN))
153 error = vn_lock(pdp, LK_EXCLUSIVE, p);
154 } else {
155 error = vget(vdp, LK_EXCLUSIVE, p);
156 if (!lockparent || error || !(flags & ISLASTCN))
157 VOP_UNLOCK(pdp, 0, p);
158 }
159 /*
160 * Check that the capability number did not change
161 * while we were waiting for the lock.
162 */
163 if (!error) {
164 if (vpid == vdp->v_id)
165 return (0);
166 vput(vdp);
167 if (lockparent && pdp != vdp && (flags & ISLASTCN))
168 VOP_UNLOCK(pdp, 0, p);
169 }
170 if (error = vn_lock(pdp, LK_EXCLUSIVE, p))
171 return (error);
172 vdp = pdp;
173 dp = VTOI(pdp);
174 *vpp = NULL;
175 }
176
177 len = cnp->cn_namelen;
178 name = cnp->cn_nameptr;
179 /*
180 * A leading `=' means, we are looking for an associated file
181 */
182 if (assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR)) {
183 len--;
184 name++;
185 }
186
187 /*
188 * If there is cached information on a previous search of
189 * this directory, pick up where we last left off.
190 * We cache only lookups as these are the most common
191 * and have the greatest payoff. Caching CREATE has little
192 * benefit as it usually must search the entire directory
193 * to determine that the entry does not exist. Caching the
194 * location of the last DELETE or RENAME has not reduced
195 * profiling time and hence has been removed in the interest
196 * of simplicity.
197 */
198 bmask = imp->im_bmask;
199 if (nameiop != LOOKUP || dp->i_diroff == 0 ||
200 dp->i_diroff > dp->i_size) {
201 entryoffsetinblock = 0;
202 dp->i_offset = 0;
203 numdirpasses = 1;
204 } else {
205 dp->i_offset = dp->i_diroff;
206 if ((entryoffsetinblock = dp->i_offset & bmask) &&
207 (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
208 return (error);
209 numdirpasses = 2;
210 iso_nchstats.ncs_2passes++;
211 }
212 endsearch = dp->i_size;
213
214 searchloop:
215 while (dp->i_offset < endsearch) {
216 /*
217 * If offset is on a block boundary,
218 * read the next directory block.
219 * Release previous if it exists.
220 */
221 if ((dp->i_offset & bmask) == 0) {
222 if (bp != NULL)
223 brelse(bp);
224 if (error =
225 VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))
226 return (error);
227 entryoffsetinblock = 0;
228 }
229 /*
230 * Get pointer to next entry.
231 */
232 ep = (struct iso_directory_record *)
233 ((char *)bp->b_data + entryoffsetinblock);
234
235 reclen = isonum_711(ep->length);
236 if (reclen == 0) {
237 /* skip to next block, if any */
238 dp->i_offset =
239 (dp->i_offset & ~bmask) + imp->logical_block_size;
240 continue;
241 }
242
243 if (reclen < ISO_DIRECTORY_RECORD_SIZE)
244 /* illegal entry, stop */
245 break;
246
247 if (entryoffsetinblock + reclen > imp->logical_block_size)
248 /* entries are not allowed to cross boundaries */
249 break;
250
251 namelen = isonum_711(ep->name_len);
252
253 if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen)
254 /* illegal entry, stop */
255 break;
256
257 /*
258 * Check for a name match.
259 */
260 switch (imp->iso_ftype) {
261 default:
262 if ((!(isonum_711(ep->flags)&4)) == !assoc) {
263 if ((len == 1
264 && *name == '.')
265 || (flags & ISDOTDOT)) {
266 if (namelen == 1
267 && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) {
268 /*
269 * Save directory entry's inode number and
270 * release directory buffer.
271 */
272 dp->i_ino = isodirino(ep, imp);
273 goto found;
274 }
275 if (namelen != 1
276 || ep->name[0] != 0)
277 goto notfound;
278 } else if (!(res = isofncmp(name,len,
279 ep->name,namelen))) {
280 if (isonum_711(ep->flags)&2)
281 ino = isodirino(ep, imp);
282 else
283 ino = dbtob(bp->b_blkno)
284 + entryoffsetinblock;
285 saveoffset = dp->i_offset;
286 } else if (ino)
287 goto foundino;
288 #ifdef NOSORTBUG /* On some CDs directory entries are not sorted correctly */
289 else if (res < 0)
290 goto notfound;
291 else if (res > 0 && numdirpasses == 2)
292 numdirpasses++;
293 #endif
294 }
295 break;
296 case ISO_FTYPE_RRIP:
297 if (isonum_711(ep->flags)&2)
298 ino = isodirino(ep, imp);
299 else
300 ino = dbtob(bp->b_blkno) + entryoffsetinblock;
301 dp->i_ino = ino;
302 cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
303 if (namelen == cnp->cn_namelen
304 && !bcmp(name,altname,namelen))
305 goto found;
306 ino = 0;
307 break;
308 }
309 dp->i_offset += reclen;
310 entryoffsetinblock += reclen;
311 }
312 if (ino) {
313 foundino:
314 dp->i_ino = ino;
315 if (saveoffset != dp->i_offset) {
316 if (lblkno(imp, dp->i_offset) !=
317 lblkno(imp, saveoffset)) {
318 if (bp != NULL)
319 brelse(bp);
320 if (error = VOP_BLKATOFF(vdp,
321 (off_t)saveoffset, NULL, &bp))
322 return (error);
323 }
324 entryoffsetinblock = saveoffset & bmask;
325 ep = (struct iso_directory_record *)
326 ((char *)bp->b_data + entryoffsetinblock);
327 dp->i_offset = saveoffset;
328 }
329 goto found;
330 }
331 notfound:
332 /*
333 * If we started in the middle of the directory and failed
334 * to find our target, we must check the beginning as well.
335 */
336 if (numdirpasses == 2) {
337 numdirpasses--;
338 dp->i_offset = 0;
339 endsearch = dp->i_diroff;
340 goto searchloop;
341 }
342 if (bp != NULL)
343 brelse(bp);
344
345 /*
346 * Insert name into cache (as non-existent) if appropriate.
347 */
348 if (cnp->cn_flags & MAKEENTRY)
349 cache_enter(vdp, *vpp, cnp);
350 if (nameiop == CREATE || nameiop == RENAME)
351 return (EROFS);
352 return (ENOENT);
353
354 found:
355 if (numdirpasses == 2)
356 iso_nchstats.ncs_pass2++;
357
358 /*
359 * Found component in pathname.
360 * If the final component of path name, save information
361 * in the cache as to where the entry was found.
362 */
363 if ((flags & ISLASTCN) && nameiop == LOOKUP)
364 dp->i_diroff = dp->i_offset;
365
366 /*
367 * Step through the translation in the name. We do not `iput' the
368 * directory because we may need it again if a symbolic link
369 * is relative to the current directory. Instead we save it
370 * unlocked as "pdp". We must get the target inode before unlocking
371 * the directory to insure that the inode will not be removed
372 * before we get it. We prevent deadlock by always fetching
373 * inodes from the root, moving down the directory tree. Thus
374 * when following backward pointers ".." we must unlock the
375 * parent directory before getting the requested directory.
376 * There is a potential race condition here if both the current
377 * and parent directories are removed before the `iget' for the
378 * inode associated with ".." returns. We hope that this occurs
379 * infrequently since we cannot avoid this race condition without
380 * implementing a sophisticated deadlock detection algorithm.
381 * Note also that this simple deadlock detection scheme will not
382 * work if the file system has any hard links other than ".."
383 * that point backwards in the directory structure.
384 */
385 pdp = vdp;
386 /*
387 * If ino is different from dp->i_ino,
388 * it's a relocated directory.
389 */
390 if (flags & ISDOTDOT) {
391 VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
392 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
393 dp->i_ino != ino, ep);
394 brelse(bp);
395 if (error) {
396 vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
397 return (error);
398 }
399 if (lockparent && (flags & ISLASTCN) &&
400 (error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
401 vput(tdp);
402 return (error);
403 }
404 *vpp = tdp;
405 } else if (dp->i_number == dp->i_ino) {
406 brelse(bp);
407 VREF(vdp); /* we want ourself, ie "." */
408 *vpp = vdp;
409 } else {
410 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
411 dp->i_ino != ino, ep);
412 brelse(bp);
413 if (error)
414 return (error);
415 if (!lockparent || !(flags & ISLASTCN))
416 VOP_UNLOCK(pdp, 0, p);
417 *vpp = tdp;
418 }
419
420 /*
421 * Insert name into cache if appropriate.
422 */
423 if (cnp->cn_flags & MAKEENTRY)
424 cache_enter(vdp, *vpp, cnp);
425 return (0);
426 }
427
428 /*
429 * Return buffer with the contents of block "offset" from the beginning of
430 * directory "ip". If "res" is non-zero, fill it in with a pointer to the
431 * remaining space in the directory.
432 */
433 int
cd9660_blkatoff(ap)434 cd9660_blkatoff(ap)
435 struct vop_blkatoff_args /* {
436 struct vnode *a_vp;
437 off_t a_offset;
438 char **a_res;
439 struct buf **a_bpp;
440 } */ *ap;
441 {
442 struct iso_node *ip;
443 register struct iso_mnt *imp;
444 struct buf *bp;
445 daddr_t lbn;
446 int bsize, error;
447
448 ip = VTOI(ap->a_vp);
449 imp = ip->i_mnt;
450 lbn = lblkno(imp, ap->a_offset);
451 bsize = blksize(imp, ip, lbn);
452
453 if (error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) {
454 brelse(bp);
455 *ap->a_bpp = NULL;
456 return (error);
457 }
458 if (ap->a_res)
459 *ap->a_res = (char *)bp->b_data + blkoff(imp, ap->a_offset);
460 *ap->a_bpp = bp;
461 return (0);
462 }
463