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