|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /* $NetBSD: cd9660_lookup.c,v 1.13 1994/12/24 15:30:03 cgd Exp $ */
26:
27: /*-
28: * Copyright (c) 1989, 1993, 1994
29: * The Regents of the University of California. All rights reserved.
30: *
31: * This code is derived from software contributed to Berkeley
32: * by Pace Willisson ([email protected]). The Rock Ridge Extension
33: * Support code is derived from software contributed to Berkeley
34: * by Atsushi Murai ([email protected]).
35: *
36: * Redistribution and use in source and binary forms, with or without
37: * modification, are permitted provided that the following conditions
38: * are met:
39: * 1. Redistributions of source code must retain the above copyright
40: * notice, this list of conditions and the following disclaimer.
41: * 2. Redistributions in binary form must reproduce the above copyright
42: * notice, this list of conditions and the following disclaimer in the
43: * documentation and/or other materials provided with the distribution.
44: * 3. All advertising materials mentioning features or use of this software
45: * must display the following acknowledgement:
46: * This product includes software developed by the University of
47: * California, Berkeley and its contributors.
48: * 4. Neither the name of the University nor the names of its contributors
49: * may be used to endorse or promote products derived from this software
50: * without specific prior written permission.
51: *
52: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62: * SUCH DAMAGE.
63: *
64: * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91
65: *
66: * @(#)cd9660_lookup.c 8.5 (Berkeley) 12/5/94
67: */
68:
69: #include <sys/param.h>
70: #include <sys/namei.h>
71: #include <sys/buf.h>
72: #include <sys/file.h>
73: #include <sys/vnode.h>
74: #include <sys/mount.h>
75:
76: #include <isofs/cd9660/iso.h>
77: #include <isofs/cd9660/cd9660_node.h>
78: #include <isofs/cd9660/iso_rrip.h>
79: #include <isofs/cd9660/cd9660_rrip.h>
80:
81: struct nchstats iso_nchstats;
82:
83: /*
84: * Convert a component of a pathname into a pointer to a locked inode.
85: * This is a very central and rather complicated routine.
86: * If the file system is not maintained in a strict tree hierarchy,
87: * this can result in a deadlock situation (see comments in code below).
88: *
89: * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
90: * whether the name is to be looked up, created, renamed, or deleted.
91: * When CREATE, RENAME, or DELETE is specified, information usable in
92: * creating, renaming, or deleting a directory entry may be calculated.
93: * If flag has LOCKPARENT or'ed into it and the target of the pathname
94: * exists, lookup returns both the target and its parent directory locked.
95: * When creating or renaming and LOCKPARENT is specified, the target may
96: * not be ".". When deleting and LOCKPARENT is specified, the target may
97: * be "."., but the caller must check to ensure it does an vrele and iput
98: * instead of two iputs.
99: *
100: * Overall outline of ufs_lookup:
101: *
102: * check accessibility of directory
103: * look for name in cache, if found, then if at end of path
104: * and deleting or creating, drop it, else return name
105: * search for name in directory, to found or notfound
106: * notfound:
107: * if creating, return locked directory, leaving info on available slots
108: * else return error
109: * found:
110: * if at end of path and deleting, return information to allow delete
111: * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
112: * inode and return info to allow rewrite
113: * if not at end, add name to cache; if at end and neither creating
114: * nor deleting, add name to cache
115: *
116: * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
117: */
118: int
119: cd9660_lookup(ap)
120: struct vop_lookup_args /* {
121: struct vnode *a_dvp;
122: struct vnode **a_vpp;
123: struct componentname *a_cnp;
124: } */ *ap;
125: {
126: register struct vnode *vdp; /* vnode for directory being searched */
127: register struct iso_node *dp; /* inode for directory being searched */
128: register struct iso_mnt *imp; /* file system that directory is in */
129: struct buf *bp; /* a buffer of directory entries */
130: struct iso_directory_record *ep = NULL;/* the current directory entry */
131: int entryoffsetinblock; /* offset of ep in bp's buffer */
132: int saveoffset = 0; /* offset of last directory entry in dir */
133: int numdirpasses; /* strategy for directory search */
134: doff_t endsearch; /* offset to end directory search */
135: struct vnode *pdp; /* saved dp during symlink work */
136: struct vnode *tdp; /* returned by cd9660_vget_internal */
137: u_long bmask; /* block offset mask */
138: int lockparent; /* 1 => lockparent flag is set */
139: int wantparent; /* 1 => wantparent or lockparent flag */
140: int error;
141: ino_t ino = 0;
142: int reclen;
143: u_short namelen;
144: char altname[NAME_MAX];
145: int res;
146: int assoc, len;
147: char *name;
148: struct vnode **vpp = ap->a_vpp;
149: struct componentname *cnp = ap->a_cnp;
150: struct ucred *cred = cnp->cn_cred;
151: int flags = cnp->cn_flags;
152: int nameiop = cnp->cn_nameiop;
153: struct proc *p = cnp->cn_proc;
154: int devBlockSize=0;
155:
156: bp = NULL;
157: *vpp = NULL;
158: vdp = ap->a_dvp;
159: dp = VTOI(vdp);
160: imp = dp->i_mnt;
161: lockparent = flags & LOCKPARENT;
162: wantparent = flags & (LOCKPARENT|WANTPARENT);
163:
164: /*
165: * Check accessiblity of directory.
166: */
167: if (vdp->v_type != VDIR)
168: return (ENOTDIR);
169: if ( (error = VOP_ACCESS(vdp, VEXEC, cred, p)) )
170: return (error);
171:
172: /*
173: * We now have a segment name to search for, and a directory to search.
174: *
175: * Before tediously performing a linear scan of the directory,
176: * check the name cache to see if the directory/name pair
177: * we are looking for is known already.
178: */
179: if ( (error = cache_lookup(vdp, vpp, cnp)) ) {
180: int vpid; /* capability number of vnode */
181:
182: if (error == ENOENT)
183: return (error);
184: #ifdef PARANOID
185: if ((vdp->v_flag & VROOT) && (flags & ISDOTDOT))
186: panic("cd9660_lookup: .. through root");
187: #endif
188: /*
189: * Get the next vnode in the path.
190: * See comment below starting `Step through' for
191: * an explaination of the locking protocol.
192: */
193: pdp = vdp;
194: dp = VTOI(*vpp);
195: vdp = *vpp;
196: vpid = vdp->v_id;
197: if (pdp == vdp) {
198: VREF(vdp);
199: error = 0;
200: } else if (flags & ISDOTDOT) {
201: VOP_UNLOCK(pdp, 0, p);
202: error = vget(vdp, LK_EXCLUSIVE | LK_RETRY, p);
203: if (!error && lockparent && (flags & ISLASTCN))
204: error = VOP_LOCK(pdp, LK_EXCLUSIVE | LK_RETRY, p);
205: } else {
206: error = vget(vdp, LK_EXCLUSIVE | LK_RETRY, p);
207: if (!lockparent || error || !(flags & ISLASTCN))
208: VOP_UNLOCK(pdp, 0, p);
209: }
210: /*
211: * Check that the capability number did not change
212: * while we were waiting for the lock.
213: */
214: if (!error) {
215: if (vpid == vdp->v_id)
216: return (0);
217: vput(vdp);
218: if (lockparent && pdp != vdp && (flags & ISLASTCN))
219: VOP_UNLOCK(pdp, 0, p);
220: }
221: if ( (error = VOP_LOCK(pdp, LK_EXCLUSIVE | LK_RETRY, p)) )
222: return (error);
223: vdp = pdp;
224: dp = VTOI(pdp);
225: *vpp = NULL;
226: }
227:
228: len = cnp->cn_namelen;
229: name = cnp->cn_nameptr;
230: /*
231: * A leading `=' means, we are looking for an associated file
232: */
233: if ( (assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR)) ) {
234: len--;
235: name++;
236: }
237:
238: /*
239: * If there is cached information on a previous search of
240: * this directory, pick up where we last left off.
241: * We cache only lookups as these are the most common
242: * and have the greatest payoff. Caching CREATE has little
243: * benefit as it usually must search the entire directory
244: * to determine that the entry does not exist. Caching the
245: * location of the last DELETE or RENAME has not reduced
246: * profiling time and hence has been removed in the interest
247: * of simplicity.
248: */
249: bmask = imp->im_bmask;
250: if (nameiop != LOOKUP || dp->i_diroff == 0 ||
251: dp->i_diroff > dp->i_size) {
252: entryoffsetinblock = 0;
253: dp->i_offset = 0;
254: numdirpasses = 1;
255: } else {
256: dp->i_offset = dp->i_diroff;
257: if ((entryoffsetinblock = dp->i_offset & bmask) &&
258: (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
259: return (error);
260: numdirpasses = 2;
261: iso_nchstats.ncs_2passes++;
262: }
263: endsearch = dp->i_size;
264:
265: searchloop:
266: while (dp->i_offset < endsearch) {
267: /*
268: * If offset is on a block boundary,
269: * read the next directory block.
270: * Release previous if it exists.
271: */
272: if ((dp->i_offset & bmask) == 0) {
273: if (bp != NULL)
274: brelse(bp);
275: if ( (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) )
276: return (error);
277: entryoffsetinblock = 0;
278: }
279: /*
280: * Get pointer to next entry.
281: */
282: ep = (struct iso_directory_record *)
283: ((char *)bp->b_data + entryoffsetinblock);
284:
285: reclen = isonum_711(ep->length);
286: if (reclen == 0) {
287: /* skip to next block, if any */
288: dp->i_offset =
289: (dp->i_offset & ~bmask) + imp->logical_block_size;
290: continue;
291: }
292:
293: if (reclen < ISO_DIRECTORY_RECORD_SIZE)
294: /* illegal entry, stop */
295: break;
296:
297: if (entryoffsetinblock + reclen > imp->logical_block_size)
298: /* entries are not allowed to cross boundaries */
299: break;
300:
301: namelen = isonum_711(ep->name_len);
302:
303: if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen)
304: /* illegal entry, stop */
305: break;
306:
307: /*
308: * Check for a name match.
309: */
310: switch (imp->iso_ftype) {
311: default:
312: if ((!(isonum_711(ep->flags) & associatedBit)) == !assoc) {
313: if ((len == 1
314: && *name == '.')
315: || (flags & ISDOTDOT)) {
316: if (namelen == 1
317: && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) {
318: /*
319: * Save directory entry's inode number and
320: * release directory buffer.
321: */
322: dp->i_ino = isodirino(ep, imp);
323: goto found;
324: }
325: if (namelen != 1
326: || ep->name[0] != 0)
327: goto notfound;
328: } else if (!(res = isofncmp(name,len,
329: ep->name,namelen))) {
330: if ( isonum_711(ep->flags) & directoryBit )
331: ino = isodirino(ep, imp);
332: else {
333: ino = (bp->b_blkno << imp->im_bshift) + entryoffsetinblock;
334: }
335: saveoffset = dp->i_offset;
336: } else if (ino)
337: goto foundino;
338: #ifdef NOSORTBUG /* On some CDs directory entries are not sorted correctly */
339: else if (res < 0)
340: goto notfound;
341: else if (res > 0 && numdirpasses == 2)
342: numdirpasses++;
343: #endif
344: }
345: break;
346: case ISO_FTYPE_RRIP:
347: if ( isonum_711(ep->flags) & directoryBit )
348: ino = isodirino(ep, imp);
349: else {
350: ino = (bp->b_blkno << imp->im_bshift) + entryoffsetinblock;
351: }
352: dp->i_ino = ino;
353: cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
354: if (namelen == cnp->cn_namelen
355: && !bcmp(name,altname,namelen))
356: goto found;
357: ino = 0;
358: break;
359: }
360: dp->i_offset += reclen;
361: entryoffsetinblock += reclen;
362: }
363:
364: if (ino) {
365: foundino:
366: dp->i_ino = ino;
367: if (saveoffset != dp->i_offset) {
368: if (lblkno(imp, dp->i_offset) !=
369: lblkno(imp, saveoffset)) {
370: if (bp != NULL)
371: brelse(bp);
372: if ( (error = VOP_BLKATOFF(vdp, (off_t)saveoffset, NULL, &bp)) )
373: return (error);
374: }
375: entryoffsetinblock = saveoffset & bmask;
376: ep = (struct iso_directory_record *)
377: ((char *)bp->b_data + entryoffsetinblock);
378: dp->i_offset = saveoffset;
379: }
380: goto found;
381: }
382: notfound:
383: /*
384: * If we started in the middle of the directory and failed
385: * to find our target, we must check the beginning as well.
386: */
387: if (numdirpasses == 2) {
388: numdirpasses--;
389: dp->i_offset = 0;
390: endsearch = dp->i_diroff;
391: goto searchloop;
392: }
393: if (bp != NULL)
394: brelse(bp);
395:
396: /*
397: * Insert name into cache (as non-existent) if appropriate.
398: */
399: if (cnp->cn_flags & MAKEENTRY)
400: cache_enter(vdp, *vpp, cnp);
401: if (nameiop == CREATE || nameiop == RENAME) {
402: /*
403: * return EROFS (NOT EJUSTRETURN). The caller will then unlock
404: * the parent for us.
405: */
406: return (EROFS);
407: }
408: return (ENOENT);
409:
410: found:
411: if (numdirpasses == 2)
412: iso_nchstats.ncs_pass2++;
413:
414: /*
415: * Found component in pathname.
416: * If the final component of path name, save information
417: * in the cache as to where the entry was found.
418: */
419: if ((flags & ISLASTCN) && nameiop == LOOKUP)
420: dp->i_diroff = dp->i_offset;
421:
422: /*
423: * Step through the translation in the name. We do not `iput' the
424: * directory because we may need it again if a symbolic link
425: * is relative to the current directory. Instead we save it
426: * unlocked as "pdp". We must get the target inode before unlocking
427: * the directory to insure that the inode will not be removed
428: * before we get it. We prevent deadlock by always fetching
429: * inodes from the root, moving down the directory tree. Thus
430: * when following backward pointers ".." we must unlock the
431: * parent directory before getting the requested directory.
432: * There is a potential race condition here if both the current
433: * and parent directories are removed before the `iget' for the
434: * inode associated with ".." returns. We hope that this occurs
435: * infrequently since we cannot avoid this race condition without
436: * implementing a sophisticated deadlock detection algorithm.
437: * Note also that this simple deadlock detection scheme will not
438: * work if the file system has any hard links other than ".."
439: * that point backwards in the directory structure.
440: */
441: pdp = vdp;
442: /*
443: * If ino is different from dp->i_ino,
444: * it's a relocated directory.
445: */
446: if (flags & ISDOTDOT) {
447: VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
448: error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
449: dp->i_ino != ino, ep, p);
450: VTOI(tdp)->i_parent = VTOI(pdp)->i_number;
451: brelse(bp);
452: if (error) {
453: VOP_LOCK(pdp, LK_EXCLUSIVE | LK_RETRY, p);
454: return (error);
455: }
456: if (lockparent && (flags & ISLASTCN) &&
457: (error = VOP_LOCK(pdp, LK_EXCLUSIVE | LK_RETRY, p))) {
458: vput(tdp);
459: return (error);
460: }
461: *vpp = tdp;
462: } else if (dp->i_number == dp->i_ino) {
463: brelse(bp);
464: VREF(vdp); /* we want ourself, ie "." */
465: *vpp = vdp;
466: } else {
467: error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp,
468: dp->i_ino != ino, ep, p);
469: VTOI(tdp)->i_parent = VTOI(pdp)->i_number; /* save parent inode number */
470: brelse(bp);
471: if (error)
472: return (error);
473: if (!lockparent || !(flags & ISLASTCN))
474: VOP_UNLOCK(pdp, 0, p);
475: *vpp = tdp;
476: }
477:
478: /*
479: * Insert name into cache if appropriate.
480: */
481: if (cnp->cn_flags & MAKEENTRY)
482: cache_enter(vdp, *vpp, cnp);
483: return (0);
484: }
485:
486: /*
487: * Return buffer with the contents of block "offset" from the beginning of
488: * directory "ip". If "res" is non-zero, fill it in with a pointer to the
489: * remaining space in the directory.
490: */
491: int
492: cd9660_blkatoff(ap)
493: struct vop_blkatoff_args /* {
494: struct vnode *a_vp;
495: off_t a_offset;
496: char **a_res;
497: struct buf **a_bpp;
498: } */ *ap;
499: {
500: struct iso_node *ip;
501: register struct iso_mnt *imp;
502: struct buf *bp;
503: daddr_t lbn;
504: int bsize, error;
505:
506: ip = VTOI(ap->a_vp);
507: imp = ip->i_mnt;
508: lbn = lblkno(imp, ap->a_offset);
509: bsize = blksize(imp, ip, lbn);
510:
511: if ( (error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) ) {
512: brelse(bp);
513: *ap->a_bpp = NULL;
514: return (error);
515: }
516: if (ap->a_res)
517: *ap->a_res = (char *)bp->b_data + blkoff(imp, ap->a_offset);
518: *ap->a_bpp = bp;
519: return (0);
520: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.