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