xref: /illumos-gate/usr/src/uts/common/fs/hsfs/hsfs_node.c (revision fe0e7ec4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Directory operations for High Sierra filesystem
31  */
32 
33 #include <sys/types.h>
34 #include <sys/t_lock.h>
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/cred.h>
38 #include <sys/user.h>
39 #include <sys/vfs.h>
40 #include <sys/stat.h>
41 #include <sys/vnode.h>
42 #include <sys/mode.h>
43 #include <sys/dnlc.h>
44 #include <sys/cmn_err.h>
45 #include <sys/fbuf.h>
46 #include <sys/kmem.h>
47 #include <sys/policy.h>
48 #include <sys/sunddi.h>
49 #include <vm/hat.h>
50 #include <vm/as.h>
51 #include <vm/pvn.h>
52 #include <vm/seg.h>
53 #include <vm/seg_map.h>
54 #include <vm/seg_kmem.h>
55 #include <vm/page.h>
56 
57 #include <sys/fs/hsfs_spec.h>
58 #include <sys/fs/hsfs_isospec.h>
59 #include <sys/fs/hsfs_node.h>
60 #include <sys/fs/hsfs_impl.h>
61 #include <sys/fs/hsfs_susp.h>
62 #include <sys/fs/hsfs_rrip.h>
63 
64 #include <sys/sysinfo.h>
65 #include <sys/sysmacros.h>
66 #include <sys/errno.h>
67 #include <sys/debug.h>
68 #include <fs/fs_subr.h>
69 
70 /*
71  * This macro expects a name that ends in '.' and returns TRUE if the
72  * name is not "." or ".."
73  */
74 #define	CAN_TRUNCATE_DOT(name, namelen)	\
75 		(namelen > 1 && (namelen > 2 || name[0] != '.'))
76 
77 enum dirblock_result { FOUND_ENTRY, WENT_PAST, HIT_END };
78 
79 /*
80  * These values determine whether we will try to read a file or dir;
81  * they may be patched via /etc/system to allow users to read
82  * record-oriented files.
83  */
84 int ide_prohibited = IDE_PROHIBITED;
85 int hde_prohibited = HDE_PROHIBITED;
86 
87 /*
88  * This variable determines if the HSFS code will use the
89  * directory name lookup cache. The default is for the cache to be used.
90  */
91 static int hsfs_use_dnlc = 1;
92 
93 /*
94  * This variable determines whether strict ISO-9660 directory ordering
95  * is to be assumed.  If false (which it is by default), then when
96  * searching a directory of an ISO-9660 disk, we do not expect the
97  * entries to be sorted (as the spec requires), and so cannot terminate
98  * the search early.  Unfortunately, some vendors are producing
99  * non-compliant disks.  This variable exists to revert to the old
100  * behavior in case someone relies on this. This option is expected to be
101  * removed at some point in the future.
102  *
103  * Use "set hsfs:strict_iso9660_ordering = 1" in /etc/system to override.
104  */
105 static int strict_iso9660_ordering = 0;
106 
107 static void hs_hsnode_cache_reclaim(void *unused);
108 static void hs_addfreeb(struct hsfs *fsp, struct hsnode *hp);
109 static int nmcmp(char *a, char *b, int len, int is_rrip);
110 static enum dirblock_result process_dirblock(struct fbuf *fbp, uint_t *offset,
111 	uint_t last_offset, char *nm, int nmlen, struct hsfs *fsp,
112 	struct hsnode *dhp, struct vnode *dvp, struct vnode **vpp,
113 	int *error, int is_rrip);
114 static int strip_trailing(struct hsfs *fsp, char *nm, int len);
115 static int uppercase_cp(char *from, char *to, int size);
116 
117 /*
118  * hs_access
119  * Return 0 if the desired access may be granted.
120  * Otherwise return error code.
121  */
122 int
123 hs_access(struct vnode *vp, mode_t m, struct cred *cred)
124 {
125 	struct hsnode *hp;
126 	int	shift = 0;
127 
128 	/*
129 	 * Write access cannot be granted for a read-only medium
130 	 */
131 	if ((m & VWRITE) && !IS_DEVVP(vp))
132 		return (EROFS);
133 
134 	hp = VTOH(vp);
135 
136 	/*
137 	 * XXX - For now, use volume protections.
138 	 *  Also, always grant EXEC access for directories
139 	 *  if READ access is granted.
140 	 */
141 	if ((vp->v_type == VDIR) && (m & VEXEC)) {
142 		m &= ~VEXEC;
143 		m |= VREAD;
144 	}
145 
146 	if (crgetuid(cred) != hp->hs_dirent.uid) {
147 		shift += 3;
148 		if (!groupmember((uid_t)hp->hs_dirent.gid, cred))
149 			shift += 3;
150 	}
151 	m &= ~(hp->hs_dirent.mode << shift);
152 	if (m != 0)
153 		return (secpolicy_vnode_access(cred, vp, hp->hs_dirent.uid, m));
154 	return (0);
155 }
156 
157 #if ((HS_HASHSIZE & (HS_HASHSIZE - 1)) == 0)
158 #define	HS_HASH(l)	((uint_t)(l) & (HS_HASHSIZE - 1))
159 #else
160 #define	HS_HASH(l)	((uint_t)(l) % HS_HASHSIZE)
161 #endif
162 #define	HS_HPASH(hp)	HS_HASH((hp)->hs_nodeid)
163 
164 /*
165  * The tunable nhsnode is now a threshold for a dynamically allocated
166  * pool of hsnodes, not the size of a statically allocated table.
167  * When the number of hsnodes for a particular file system exceeds
168  * nhsnode, the allocate and free logic will try to reduce the number
169  * of allocated nodes by returning unreferenced nodes to the kmem_cache
170  * instead of putting them on the file system's private free list.
171  */
172 int nhsnode = HS_HSNODESPACE / sizeof (struct hsnode);
173 
174 struct kmem_cache *hsnode_cache;  /* free hsnode cache */
175 
176 /*
177  * Initialize the cache of free hsnodes.
178  */
179 void
180 hs_init_hsnode_cache(void)
181 {
182 	/*
183 	 * A kmem_cache is used for the hsnodes
184 	 * No constructor because hsnodes are initialised by bzeroing.
185 	 */
186 	hsnode_cache = kmem_cache_create("hsfs_hsnode_cache",
187 	    sizeof (struct hsnode), 0, NULL,
188 	    NULL, hs_hsnode_cache_reclaim, NULL, NULL, 0);
189 }
190 
191 /*
192  * System is short on memory, free up as much as possible
193  */
194 /*ARGSUSED*/
195 static void
196 hs_hsnode_cache_reclaim(void *unused)
197 {
198 	struct hsfs *fsp;
199 	struct hsnode *hp;
200 
201 	/*
202 	 * For each vfs in the hs_mounttab list
203 	 */
204 	mutex_enter(&hs_mounttab_lock);
205 	for (fsp = hs_mounttab; fsp != NULL; fsp = fsp->hsfs_next) {
206 		/*
207 		 * Purge the dnlc of all hsfs entries
208 		 */
209 		(void) dnlc_purge_vfsp(fsp->hsfs_vfs, 0);
210 
211 		/*
212 		 * For each entry in the free chain
213 		 */
214 		rw_enter(&fsp->hsfs_hash_lock, RW_WRITER);
215 		mutex_enter(&fsp->hsfs_free_lock);
216 		for (hp = fsp->hsfs_free_f; hp != NULL; hp = fsp->hsfs_free_f) {
217 			/*
218 			 * Remove from chain
219 			 */
220 			fsp->hsfs_free_f = hp->hs_freef;
221 			if (fsp->hsfs_free_f != NULL) {
222 				fsp->hsfs_free_f->hs_freeb = NULL;
223 			} else {
224 				fsp->hsfs_free_b = NULL;
225 			}
226 			/*
227 			 * Free the node. Force it to be fully freed
228 			 * by setting the 3rd arg (nopage) to 1.
229 			 */
230 			hs_freenode(HTOV(hp), fsp, 1);
231 		}
232 		mutex_exit(&fsp->hsfs_free_lock);
233 		rw_exit(&fsp->hsfs_hash_lock);
234 	}
235 	mutex_exit(&hs_mounttab_lock);
236 }
237 
238 /*
239  * Add an hsnode to the end of the free list.
240  */
241 static void
242 hs_addfreeb(struct hsfs *fsp, struct hsnode *hp)
243 {
244 	struct hsnode *ep;
245 
246 	vn_invalid(HTOV(hp));
247 	mutex_enter(&fsp->hsfs_free_lock);
248 	ep = fsp->hsfs_free_b;
249 	fsp->hsfs_free_b = hp;		/* hp is the last entry in free list */
250 	hp->hs_freef = NULL;
251 	hp->hs_freeb = ep;		/* point at previous last entry */
252 	if (ep == NULL)
253 		fsp->hsfs_free_f = hp;	/* hp is only entry in free list */
254 	else
255 		ep->hs_freef = hp;	/* point previous last entry at hp */
256 
257 	mutex_exit(&fsp->hsfs_free_lock);
258 }
259 
260 /*
261  * Get an hsnode from the front of the free list.
262  * Must be called with write hsfs_hash_lock held.
263  */
264 static struct hsnode *
265 hs_getfree(struct hsfs *fsp)
266 {
267 	struct hsnode *hp, **tp;
268 
269 	ASSERT(RW_WRITE_HELD(&fsp->hsfs_hash_lock));
270 
271 	/*
272 	 * If the number of currently-allocated hsnodes is less than
273 	 * the hsnode count threshold (nhsnode), or if there are no
274 	 * nodes on the file system's local free list (which acts as a
275 	 * cache), call kmem_cache_alloc to get a new hsnode from
276 	 * kernel memory.
277 	 */
278 	mutex_enter(&fsp->hsfs_free_lock);
279 	if ((fsp->hsfs_nohsnode < nhsnode) || (fsp->hsfs_free_f == NULL)) {
280 		mutex_exit(&fsp->hsfs_free_lock);
281 		hp = kmem_cache_alloc(hsnode_cache, KM_SLEEP);
282 		fsp->hsfs_nohsnode++;
283 		bzero((caddr_t)hp, sizeof (*hp));
284 		hp->hs_vnode = vn_alloc(KM_SLEEP);
285 		return (hp);
286 	}
287 	hp = fsp->hsfs_free_f;
288 	/* hp cannot be NULL, since we already checked this above */
289 	fsp->hsfs_free_f = hp->hs_freef;
290 	if (fsp->hsfs_free_f != NULL)
291 		fsp->hsfs_free_f->hs_freeb = NULL;
292 	else
293 		fsp->hsfs_free_b = NULL;
294 	mutex_exit(&fsp->hsfs_free_lock);
295 
296 	for (tp = &fsp->hsfs_hash[HS_HPASH(hp)]; *tp != NULL;
297 		tp = &(*tp)->hs_hash) {
298 		if (*tp == hp) {
299 			struct vnode *vp;
300 
301 			vp = HTOV(hp);
302 
303 			/*
304 			 * file is no longer referenced, destroy all old pages
305 			 */
306 			if (vn_has_cached_data(vp))
307 				/*
308 				 * pvn_vplist_dirty will abort all old pages
309 				 */
310 				(void) pvn_vplist_dirty(vp, (u_offset_t)0,
311 				hsfs_putapage, B_INVAL, (struct cred *)NULL);
312 			*tp = hp->hs_hash;
313 			break;
314 		}
315 	}
316 	if (hp->hs_dirent.sym_link != (char *)NULL) {
317 		kmem_free(hp->hs_dirent.sym_link,
318 			(size_t)(hp->hs_dirent.ext_size + 1));
319 	}
320 
321 	mutex_destroy(&hp->hs_contents_lock);
322 	{
323 		vnode_t	*vp;
324 
325 		vp = hp->hs_vnode;
326 		bzero((caddr_t)hp, sizeof (*hp));
327 		hp->hs_vnode = vp;
328 		vn_reinit(vp);
329 	}
330 	return (hp);
331 }
332 
333 /*
334  * Remove an hsnode from the free list.
335  */
336 static void
337 hs_remfree(struct hsfs *fsp, struct hsnode *hp)
338 {
339 	mutex_enter(&fsp->hsfs_free_lock);
340 	if (hp->hs_freef != NULL)
341 		hp->hs_freef->hs_freeb = hp->hs_freeb;
342 	else
343 		fsp->hsfs_free_b = hp->hs_freeb;
344 	if (hp->hs_freeb != NULL)
345 		hp->hs_freeb->hs_freef = hp->hs_freef;
346 	else
347 		fsp->hsfs_free_f = hp->hs_freef;
348 	mutex_exit(&fsp->hsfs_free_lock);
349 }
350 
351 /*
352  * Look for hsnode in hash list.
353  * Check equality of fsid and nodeid.
354  * If found, reactivate it if inactive.
355  * Must be entered with hsfs_hash_lock held.
356  */
357 struct vnode *
358 hs_findhash(ino64_t nodeid, struct vfs *vfsp)
359 {
360 	struct hsnode *tp;
361 	struct hsfs *fsp;
362 
363 	fsp = VFS_TO_HSFS(vfsp);
364 
365 	ASSERT(RW_LOCK_HELD(&fsp->hsfs_hash_lock));
366 
367 	for (tp = fsp->hsfs_hash[HS_HASH(nodeid)]; tp != NULL;
368 	    tp = tp->hs_hash) {
369 		if (tp->hs_nodeid == nodeid) {
370 			struct vnode *vp;
371 
372 			mutex_enter(&tp->hs_contents_lock);
373 			vp = HTOV(tp);
374 			VN_HOLD(vp);
375 			if ((tp->hs_flags & HREF) == 0) {
376 				tp->hs_flags |= HREF;
377 				/*
378 				 * reactivating a free hsnode:
379 				 * remove from free list
380 				 */
381 				hs_remfree(fsp, tp);
382 			}
383 			mutex_exit(&tp->hs_contents_lock);
384 			return (vp);
385 		}
386 	}
387 	return (NULL);
388 }
389 
390 static void
391 hs_addhash(struct hsfs *fsp, struct hsnode *hp)
392 {
393 	ulong_t hashno;
394 
395 	ASSERT(RW_WRITE_HELD(&fsp->hsfs_hash_lock));
396 
397 	hashno = HS_HPASH(hp);
398 	hp->hs_hash = fsp->hsfs_hash[hashno];
399 	fsp->hsfs_hash[hashno] = hp;
400 }
401 
402 /*
403  * Destroy all old pages and free the hsnodes
404  * Return 1 if busy (a hsnode is still referenced).
405  */
406 int
407 hs_synchash(struct vfs *vfsp)
408 {
409 	struct hsfs *fsp;
410 	int i;
411 	struct hsnode *hp, *nhp;
412 	int busy = 0;
413 	struct vnode *vp, *rvp;
414 
415 	fsp = VFS_TO_HSFS(vfsp);
416 	rvp = fsp->hsfs_rootvp;
417 	/* make sure no one can come in */
418 	rw_enter(&fsp->hsfs_hash_lock, RW_WRITER);
419 	for (i = 0; i < HS_HASHSIZE; i++) {
420 		for (hp = fsp->hsfs_hash[i]; hp != NULL; hp = hp->hs_hash) {
421 			vp = HTOV(hp);
422 			if ((hp->hs_flags & HREF) && (vp != rvp ||
423 				(vp == rvp && vp->v_count > 1))) {
424 				busy = 1;
425 				continue;
426 			}
427 			if (vn_has_cached_data(vp))
428 				(void) pvn_vplist_dirty(vp, (u_offset_t)0,
429 				hsfs_putapage, B_INVAL, (struct cred *)NULL);
430 		}
431 	}
432 	if (busy) {
433 		rw_exit(&fsp->hsfs_hash_lock);
434 		return (1);
435 	}
436 
437 	/* now free the hsnodes */
438 	for (i = 0; i < HS_HASHSIZE; i++) {
439 		for (hp = fsp->hsfs_hash[i]; hp != NULL; hp = nhp) {
440 			nhp = hp->hs_hash;
441 			/*
442 			 * We know there are no pages associated with
443 			 * all the hsnodes (they've all been released
444 			 * above). So remove from free list and
445 			 * free the entry with nopage set.
446 			 */
447 			vp = HTOV(hp);
448 			if (vp != rvp) {
449 				hs_remfree(fsp, hp);
450 				hs_freenode(vp, fsp, 1);
451 			}
452 		}
453 	}
454 
455 	ASSERT(fsp->hsfs_nohsnode == 1);
456 	rw_exit(&fsp->hsfs_hash_lock);
457 	/* release the root hsnode, this should free the final hsnode */
458 	VN_RELE(rvp);
459 
460 	return (0);
461 }
462 
463 /*
464  * hs_makenode
465  *
466  * Construct an hsnode.
467  * Caller specifies the directory entry, the block number and offset
468  * of the directory entry, and the vfs pointer.
469  * note: off is the sector offset, not lbn offset
470  * if NULL is returned implies file system hsnode table full
471  */
472 struct vnode *
473 hs_makenode(
474 	struct hs_direntry *dp,
475 	uint_t lbn,
476 	uint_t off,
477 	struct vfs *vfsp)
478 {
479 	struct hsnode *hp;
480 	struct vnode *vp;
481 	struct hs_volume *hvp;
482 	struct vnode *newvp;
483 	struct hsfs *fsp;
484 	ino64_t nodeid;
485 
486 	fsp = VFS_TO_HSFS(vfsp);
487 
488 	/*
489 	 * Construct the nodeid: in the case of a directory
490 	 * entry, this should point to the canonical dirent, the "."
491 	 * directory entry for the directory.  This dirent is pointed
492 	 * to by all directory entries for that dir (including the ".")
493 	 * entry itself.
494 	 * In the case of a file, simply point to the dirent for that
495 	 * file (there are no hard links in Rock Ridge, so there's no
496 	 * need to determine what the canonical dirent is.
497 	 */
498 	if (dp->type == VDIR) {
499 		lbn = dp->ext_lbn;
500 		off = 0;
501 	}
502 
503 	/*
504 	 * Normalize lbn and off before creating a nodeid
505 	 * and before storing them in a hs_node structure
506 	 */
507 	hvp = &fsp->hsfs_vol;
508 	lbn += off >> hvp->lbn_shift;
509 	off &= hvp->lbn_maxoffset;
510 	nodeid = (ino64_t)MAKE_NODEID(lbn, off, vfsp);
511 
512 	/* look for hsnode in cache first */
513 
514 	rw_enter(&fsp->hsfs_hash_lock, RW_READER);
515 
516 	if ((vp = hs_findhash(nodeid, vfsp)) == NULL) {
517 
518 		/*
519 		 * Not in cache.  However, someone else may have come
520 		 * to the same conclusion and just put one in.	Upgrade
521 		 * our lock to a write lock and look again.
522 		 */
523 		rw_exit(&fsp->hsfs_hash_lock);
524 		rw_enter(&fsp->hsfs_hash_lock, RW_WRITER);
525 
526 		if ((vp = hs_findhash(nodeid, vfsp)) == NULL) {
527 			/*
528 			 * Now we are really sure that the hsnode is not
529 			 * in the cache.  Get one off freelist or else
530 			 * allocate one. Either way get a bzeroed hsnode.
531 			 */
532 			hp = hs_getfree(fsp);
533 
534 			bcopy((caddr_t)dp, (caddr_t)&hp->hs_dirent,
535 				sizeof (*dp));
536 			/*
537 			 * We've just copied this pointer into hs_dirent,
538 			 * and don't want 2 references to same symlink.
539 			 */
540 			dp->sym_link = (char *)NULL;
541 
542 			/*
543 			 * No need to hold any lock because hsnode is not
544 			 * yet in the hash chain.
545 			 */
546 			mutex_init(&hp->hs_contents_lock, NULL, MUTEX_DEFAULT,
547 			    NULL);
548 			hp->hs_dir_lbn = lbn;
549 			hp->hs_dir_off = off;
550 			hp->hs_nodeid = nodeid;
551 			hp->hs_seq = 0;
552 			hp->hs_flags = HREF;
553 			if (off > HS_SECTOR_SIZE)
554 				cmn_err(CE_WARN, "hs_makenode: bad offset");
555 
556 			vp = HTOV(hp);
557 			vp->v_vfsp = vfsp;
558 			vp->v_type = dp->type;
559 			vp->v_rdev = dp->r_dev;
560 			vn_setops(vp, hsfs_vnodeops);
561 			vp->v_data = (caddr_t)hp;
562 			vn_exists(vp);
563 			/*
564 			 * if it's a device, call specvp
565 			 */
566 			if (IS_DEVVP(vp)) {
567 				rw_exit(&fsp->hsfs_hash_lock);
568 				newvp = specvp(vp, vp->v_rdev, vp->v_type,
569 						CRED());
570 				if (newvp == NULL)
571 				    cmn_err(CE_NOTE,
572 					"hs_makenode: specvp failed");
573 				VN_RELE(vp);
574 				return (newvp);
575 			}
576 
577 			hs_addhash(fsp, hp);
578 
579 		}
580 	}
581 
582 	if (dp->sym_link != (char *)NULL) {
583 		kmem_free(dp->sym_link, (size_t)(dp->ext_size + 1));
584 		dp->sym_link = (char *)NULL;
585 	}
586 
587 	rw_exit(&fsp->hsfs_hash_lock);
588 	return (vp);
589 }
590 
591 /*
592  * hs_freenode
593  *
594  * Deactivate an hsnode.
595  * Leave it on the hash list but put it on the free list.
596  * If the vnode does not have any pages, release the hsnode to the
597  * kmem_cache using kmem_cache_free, else put in back of the free list.
598  *
599  * This function can be called with the hsfs_free_lock held, but only
600  * when the code is guaranteed to go through the path where the
601  * node is freed entirely, and not the path where the node could go back
602  * on the free list (and where the free lock would need to be acquired).
603  */
604 void
605 hs_freenode(vnode_t *vp, struct hsfs *fsp, int nopage)
606 {
607 	struct hsnode **tp;
608 	struct hsnode *hp = VTOH(vp);
609 
610 	ASSERT(RW_LOCK_HELD(&fsp->hsfs_hash_lock));
611 
612 	if (nopage || (fsp->hsfs_nohsnode >= nhsnode)) {
613 		/* remove this node from the hash list, if it's there */
614 		for (tp = &fsp->hsfs_hash[HS_HPASH(hp)]; *tp != NULL;
615 			tp = &(*tp)->hs_hash) {
616 
617 			if (*tp == hp) {
618 				*tp = hp->hs_hash;
619 				break;
620 			}
621 		}
622 
623 		if (hp->hs_dirent.sym_link != (char *)NULL) {
624 			kmem_free(hp->hs_dirent.sym_link,
625 				(size_t)(hp->hs_dirent.ext_size + 1));
626 			hp->hs_dirent.sym_link = NULL;
627 		}
628 		if (vn_has_cached_data(vp)) {
629 			/* clean all old pages */
630 			(void) pvn_vplist_dirty(vp, (u_offset_t)0,
631 			    hsfs_putapage, B_INVAL, (struct cred *)NULL);
632 			/* XXX - can we remove pages by fiat like this??? */
633 			vp->v_pages = NULL;
634 		}
635 		mutex_destroy(&hp->hs_contents_lock);
636 		vn_invalid(vp);
637 		vn_free(vp);
638 		kmem_cache_free(hsnode_cache, hp);
639 		fsp->hsfs_nohsnode--;
640 		return;
641 	}
642 	hs_addfreeb(fsp, hp); /* add to back of free list */
643 }
644 
645 /*
646  * hs_remakenode
647  *
648  * Reconstruct a vnode given the location of its directory entry.
649  * Caller specifies the the block number and offset
650  * of the directory entry, and the vfs pointer.
651  * Returns an error code or 0.
652  */
653 int
654 hs_remakenode(uint_t lbn, uint_t off, struct vfs *vfsp,
655     struct vnode **vpp)
656 {
657 	struct buf *secbp;
658 	struct hsfs *fsp;
659 	uint_t secno;
660 	uchar_t *dirp;
661 	struct hs_direntry hd;
662 	int error;
663 
664 	/* Convert to sector and offset */
665 	fsp = VFS_TO_HSFS(vfsp);
666 	if (off > HS_SECTOR_SIZE) {
667 		cmn_err(CE_WARN, "hs_remakenode: bad offset");
668 		error = EINVAL;
669 		goto end;
670 	}
671 	secno = LBN_TO_SEC(lbn, vfsp);
672 	secbp = bread(fsp->hsfs_devvp->v_rdev, secno * 4, HS_SECTOR_SIZE);
673 
674 	error = geterror(secbp);
675 	if (error != 0) {
676 		cmn_err(CE_NOTE, "hs_remakenode: bread: error=(%d)", error);
677 		goto end;
678 	}
679 
680 	dirp = (uchar_t *)secbp->b_un.b_addr;
681 	error = hs_parsedir(fsp, &dirp[off], &hd, (char *)NULL, (int *)NULL);
682 	if (!error) {
683 		*vpp = hs_makenode(&hd, lbn, off, vfsp);
684 		if (*vpp == NULL)
685 			error = ENFILE;
686 	}
687 
688 end:
689 	brelse(secbp);
690 	return (error);
691 }
692 
693 
694 /*
695  * hs_dirlook
696  *
697  * Look for a given name in a given directory.
698  * If found, construct an hsnode for it.
699  */
700 int
701 hs_dirlook(
702 	struct vnode	*dvp,
703 	char		*name,
704 	int		namlen,		/* length of 'name' */
705 	struct vnode	**vpp,
706 	struct cred	*cred)
707 {
708 	struct hsnode *dhp;
709 	struct hsfs	*fsp;
710 	int		error = 0;
711 	uint_t		offset;		/* real offset in directory */
712 	uint_t		last_offset;	/* last index into current dir block */
713 	char		*cmpname;	/* case-folded name */
714 	int		cmpname_size;	/* how much memory we allocate for it */
715 	int		cmpnamelen;
716 	int		adhoc_search;	/* did we start at begin of dir? */
717 	int		end;
718 	uint_t		hsoffset;
719 	struct fbuf	*fbp;
720 	int		bytes_wanted;
721 	int		dirsiz;
722 	int		is_rrip;
723 
724 	if (dvp->v_type != VDIR)
725 		return (ENOTDIR);
726 
727 	if (error = hs_access(dvp, (mode_t)VEXEC, cred))
728 		return (error);
729 
730 	if (hsfs_use_dnlc && (*vpp = dnlc_lookup(dvp, name)))
731 		return (0);
732 
733 	dhp = VTOH(dvp);
734 	fsp = VFS_TO_HSFS(dvp->v_vfsp);
735 
736 	cmpname_size = (int)(fsp->hsfs_namemax + 1);
737 	cmpname = kmem_alloc((size_t)cmpname_size, KM_SLEEP);
738 
739 	is_rrip = IS_RRIP_IMPLEMENTED(fsp);
740 
741 	if (namlen >= cmpname_size)
742 		namlen = cmpname_size - 1;
743 	/*
744 	 * For the purposes of comparing the name against dir entries,
745 	 * fold it to upper case.
746 	 */
747 	if (is_rrip) {
748 		(void) strlcpy(cmpname, name, cmpname_size);
749 		cmpnamelen = namlen;
750 	} else {
751 		/*
752 		 * If we don't consider a trailing dot as part of the filename,
753 		 * remove it from the specified name
754 		 */
755 		if ((fsp->hsfs_flags & HSFSMNT_NOTRAILDOT) &&
756 			name[namlen-1] == '.' &&
757 				CAN_TRUNCATE_DOT(name, namlen))
758 			name[--namlen] = '\0';
759 		cmpnamelen = hs_uppercase_copy(name, cmpname, namlen);
760 	}
761 
762 	/* make sure dirent is filled up with all info */
763 	if (dhp->hs_dirent.ext_size == 0)
764 		hs_filldirent(dvp, &dhp->hs_dirent);
765 
766 	/*
767 	 * No lock is needed - hs_offset is used as starting
768 	 * point for searching the directory.
769 	 */
770 	offset = dhp->hs_offset;
771 	hsoffset = offset;
772 	adhoc_search = (offset != 0);
773 
774 	end = dhp->hs_dirent.ext_size;
775 	dirsiz = end;
776 
777 tryagain:
778 
779 	while (offset < end) {
780 		bytes_wanted = MIN(MAXBSIZE, dirsiz - (offset & MAXBMASK));
781 
782 		error = fbread(dvp, (offset_t)(offset & MAXBMASK),
783 			(unsigned int)bytes_wanted, S_READ, &fbp);
784 		if (error)
785 			goto done;
786 
787 		last_offset = (offset & MAXBMASK) + fbp->fb_count;
788 
789 		switch (process_dirblock(fbp, &offset, last_offset,
790 		    cmpname, cmpnamelen, fsp, dhp, dvp, vpp, &error,
791 		    is_rrip)) {
792 		case FOUND_ENTRY:
793 			/* found an entry, either correct or not */
794 			goto done;
795 
796 		case WENT_PAST:
797 			/*
798 			 * If we get here we know we didn't find it on the
799 			 * first pass. If adhoc_search, then we started a
800 			 * bit into the dir, and need to wrap around and
801 			 * search the first entries.  If not, then we started
802 			 * at the beginning and didn't find it.
803 			 */
804 			if (adhoc_search) {
805 				offset = 0;
806 				end = hsoffset;
807 				adhoc_search = 0;
808 				goto tryagain;
809 			}
810 			error = ENOENT;
811 			goto done;
812 
813 		case HIT_END:
814 			goto tryagain;
815 		}
816 	}
817 	/*
818 	 * End of all dir blocks, didn't find entry.
819 	 */
820 	if (adhoc_search) {
821 		offset = 0;
822 		end = hsoffset;
823 		adhoc_search = 0;
824 		goto tryagain;
825 	}
826 	error = ENOENT;
827 done:
828 	/*
829 	 * If we found the entry, add it to the DNLC
830 	 * If the entry is a device file (assuming we support Rock Ridge),
831 	 * we enter the device vnode to the cache since that is what
832 	 * is in *vpp.
833 	 * That is ok since the CD-ROM is read-only, so (dvp,name) will
834 	 * always point to the same device.
835 	 */
836 	if (hsfs_use_dnlc && !error)
837 		dnlc_enter(dvp, name, *vpp);
838 
839 	kmem_free(cmpname, (size_t)cmpname_size);
840 
841 	return (error);
842 }
843 
844 /*
845  * hs_parsedir
846  *
847  * Parse a Directory Record into an hs_direntry structure.
848  * High Sierra and ISO directory are almost the same
849  * except the flag and date
850  */
851 int
852 hs_parsedir(
853 	struct hsfs		*fsp,
854 	uchar_t			*dirp,
855 	struct hs_direntry	*hdp,
856 	char			*dnp,
857 	int			*dnlen)
858 {
859 	char	*on_disk_name;
860 	int	on_disk_namelen;
861 	uchar_t	flags;
862 	int	namelen;
863 	int	error;
864 	int	name_change_flag = 0;	/* set if name was gotten in SUA */
865 
866 	hdp->ext_lbn = HDE_EXT_LBN(dirp);
867 	hdp->ext_size = HDE_EXT_SIZE(dirp);
868 	hdp->xar_len = HDE_XAR_LEN(dirp);
869 	hdp->intlf_sz = HDE_INTRLV_SIZE(dirp);
870 	hdp->intlf_sk = HDE_INTRLV_SKIP(dirp);
871 	hdp->sym_link = (char *)NULL;
872 
873 	if (fsp->hsfs_vol_type == HS_VOL_TYPE_HS) {
874 		flags = HDE_FLAGS(dirp);
875 		hs_parse_dirdate(HDE_cdate(dirp), &hdp->cdate);
876 		hs_parse_dirdate(HDE_cdate(dirp), &hdp->adate);
877 		hs_parse_dirdate(HDE_cdate(dirp), &hdp->mdate);
878 		if ((flags & hde_prohibited) == 0) {
879 			/*
880 			 * Skip files with the associated bit set.
881 			 */
882 			if (flags & HDE_ASSOCIATED)
883 				return (EAGAIN);
884 			hdp->type = VREG;
885 			hdp->mode = HFREG;
886 			hdp->nlink = 1;
887 		} else if ((flags & hde_prohibited) == HDE_DIRECTORY) {
888 			hdp->type = VDIR;
889 			hdp->mode = HFDIR;
890 			hdp->nlink = 2;
891 		} else {
892 			hs_log_bogus_disk_warning(fsp,
893 			    HSFS_ERR_UNSUP_TYPE, flags);
894 			return (EINVAL);
895 		}
896 		hdp->uid = fsp -> hsfs_vol.vol_uid;
897 		hdp->gid = fsp -> hsfs_vol.vol_gid;
898 		hdp->mode = hdp-> mode | (fsp -> hsfs_vol.vol_prot & 0777);
899 	} else if (fsp->hsfs_vol_type == HS_VOL_TYPE_ISO) {
900 		flags = IDE_FLAGS(dirp);
901 		hs_parse_dirdate(IDE_cdate(dirp), &hdp->cdate);
902 		hs_parse_dirdate(IDE_cdate(dirp), &hdp->adate);
903 		hs_parse_dirdate(IDE_cdate(dirp), &hdp->mdate);
904 
905 		if ((flags & ide_prohibited) == 0) {
906 			/*
907 			 * Skip files with the associated bit set.
908 			 */
909 			if (flags & IDE_ASSOCIATED)
910 				return (EAGAIN);
911 			hdp->type = VREG;
912 			hdp->mode = HFREG;
913 			hdp->nlink = 1;
914 		} else if ((flags & ide_prohibited) == IDE_DIRECTORY) {
915 			hdp->type = VDIR;
916 			hdp->mode = HFDIR;
917 			hdp->nlink = 2;
918 		} else {
919 			hs_log_bogus_disk_warning(fsp,
920 			    HSFS_ERR_UNSUP_TYPE, flags);
921 			return (EINVAL);
922 		}
923 		hdp->uid = fsp -> hsfs_vol.vol_uid;
924 		hdp->gid = fsp -> hsfs_vol.vol_gid;
925 		hdp->mode = hdp-> mode | (fsp -> hsfs_vol.vol_prot & 0777);
926 
927 		/*
928 		 * Having this all filled in, let's see if we have any
929 		 * SUA susp to look at.
930 		 */
931 		if (IS_SUSP_IMPLEMENTED(fsp)) {
932 			error = parse_sua((uchar_t *)dnp, dnlen,
933 					&name_change_flag, dirp, hdp, fsp,
934 					(uchar_t *)NULL, NULL);
935 			if (error) {
936 				if (hdp->sym_link) {
937 					kmem_free(hdp->sym_link,
938 						(size_t)(hdp->ext_size + 1));
939 					hdp->sym_link = (char *)NULL;
940 				}
941 				return (error);
942 			}
943 		}
944 	}
945 	hdp->xar_prot = (HDE_PROTECTION & flags) != 0;
946 
947 #if dontskip
948 	if (hdp->xar_len > 0) {
949 		cmn_err(CE_NOTE, "hsfs: extended attributes not supported");
950 		return (EINVAL);
951 	}
952 #endif
953 
954 	/* check interleaf size and skip factor */
955 	/* must both be zero or non-zero */
956 	if (hdp->intlf_sz + hdp->intlf_sk) {
957 		if ((hdp->intlf_sz == 0) || (hdp->intlf_sk == 0)) {
958 			cmn_err(CE_NOTE,
959 				"hsfs: interleaf size or skip factor error");
960 			return (EINVAL);
961 		}
962 		if (hdp->ext_size == 0) {
963 			cmn_err(CE_NOTE,
964 			    "hsfs: interleaving specified on zero length file");
965 			return (EINVAL);
966 		}
967 	}
968 
969 	if (HDE_VOL_SET(dirp) != 1) {
970 		if (fsp->hsfs_vol.vol_set_size != 1 &&
971 		    fsp->hsfs_vol.vol_set_size != HDE_VOL_SET(dirp)) {
972 			cmn_err(CE_NOTE, "hsfs: multivolume file?");
973 			return (EINVAL);
974 		}
975 	}
976 
977 	/*
978 	 * If the name changed, then the NM field for RRIP was hit and
979 	 * we should not copy the name again, just return.
980 	 */
981 	if (NAME_HAS_CHANGED(name_change_flag))
982 		return (0);
983 
984 	/*
985 	 * Fall back to the ISO name. Note that as in process_dirblock,
986 	 * the on-disk filename length must be validated against ISO
987 	 * limits - which, in case of RR present but no RR name found,
988 	 * are NOT identical to fsp->hsfs_namemax on this filesystem.
989 	 */
990 	on_disk_name = (char *)HDE_name(dirp);
991 	on_disk_namelen = (int)HDE_NAME_LEN(dirp);
992 
993 	if (on_disk_namelen > ISO_FILE_NAMELEN) {
994 		hs_log_bogus_disk_warning(fsp, HSFS_ERR_BAD_FILE_LEN, 0);
995 		on_disk_namelen = ISO_FILE_NAMELEN;
996 	}
997 	if (dnp != NULL) {
998 		namelen = hs_namecopy(on_disk_name, dnp, on_disk_namelen,
999 		    fsp->hsfs_flags);
1000 		if ((fsp->hsfs_flags & HSFSMNT_NOTRAILDOT) &&
1001 		    dnp[ namelen-1 ] == '.' && CAN_TRUNCATE_DOT(dnp, namelen))
1002 			dnp[ --namelen ] = '\0';
1003 	} else
1004 		namelen = on_disk_namelen;
1005 	if (dnlen != NULL)
1006 		*dnlen = namelen;
1007 
1008 	return (0);
1009 }
1010 
1011 /*
1012  * hs_namecopy
1013  *
1014  * Parse a file/directory name into UNIX form.
1015  * Delete trailing blanks, upper-to-lower case, add NULL terminator.
1016  * Returns the (possibly new) length.
1017  */
1018 int
1019 hs_namecopy(char *from, char *to, int size, ulong_t flags)
1020 {
1021 	uint_t i;
1022 	uchar_t c;
1023 	int lastspace;
1024 	int maplc;
1025 
1026 	/* special handling for '.' and '..' */
1027 	if (size == 1) {
1028 		if (*from == '\0') {
1029 			*to++ = '.';
1030 			*to = '\0';
1031 			return (1);
1032 		} else if (*from == '\1') {
1033 			*to++ = '.';
1034 			*to++ = '.';
1035 			*to = '\0';
1036 			return (2);
1037 		}
1038 	}
1039 
1040 	maplc = (flags & HSFSMNT_NOMAPLCASE) == 0;
1041 	for (i = 0, lastspace = -1; i < size; i++) {
1042 		c = from[i];
1043 		if (c == ';')
1044 			break;
1045 		if (c <= ' ') {
1046 			if (lastspace == -1)
1047 				lastspace = i;
1048 		} else
1049 			lastspace = -1;
1050 		if (maplc && (c >= 'A') && (c <= 'Z'))
1051 			c += 'a' - 'A';
1052 		to[i] = c;
1053 	}
1054 	if (lastspace != -1)
1055 		i = lastspace;
1056 	to[i] = '\0';
1057 	return (i);
1058 }
1059 
1060 /*
1061  * map a filename to upper case;
1062  * return 1 if found lowercase character
1063  */
1064 static int
1065 uppercase_cp(char *from, char *to, int size)
1066 {
1067 	uint_t i;
1068 	uchar_t c;
1069 	uchar_t had_lc = 0;
1070 
1071 	for (i = 0; i < size; i++) {
1072 		c = *from++;
1073 		if ((c >= 'a') && (c <= 'z')) {
1074 			c -= ('a' - 'A');
1075 			had_lc = 1;
1076 		}
1077 		*to++ = c;
1078 	}
1079 	return (had_lc);
1080 }
1081 
1082 /*
1083  * hs_uppercase_copy
1084  *
1085  * Convert a UNIX-style name into its HSFS equivalent.
1086  * Map to upper case.
1087  * Returns the (possibly new) length.
1088  */
1089 int
1090 hs_uppercase_copy(char *from, char *to, int size)
1091 {
1092 	uint_t i;
1093 	uchar_t c;
1094 
1095 	/* special handling for '.' and '..' */
1096 
1097 	if (size == 1 && *from == '.') {
1098 		*to = '\0';
1099 		return (1);
1100 	} else if (size == 2 && *from == '.' && *(from+1) == '.') {
1101 		*to = '\1';
1102 		return (1);
1103 	}
1104 
1105 	for (i = 0; i < size; i++) {
1106 		c = *from++;
1107 		if ((c >= 'a') && (c <= 'z'))
1108 			c = c - 'a' + 'A';
1109 		*to++ = c;
1110 	}
1111 	return (size);
1112 }
1113 
1114 void
1115 hs_filldirent(struct vnode *vp, struct hs_direntry *hdp)
1116 {
1117 	struct buf *secbp;
1118 	uint_t	secno;
1119 	offset_t secoff;
1120 	struct hsfs *fsp;
1121 	uchar_t *secp;
1122 	int	error;
1123 
1124 	if (vp->v_type != VDIR) {
1125 		cmn_err(CE_WARN, "hsfs_filldirent: vp (0x%p) not a directory",
1126 			(void *)vp);
1127 		return;
1128 	}
1129 
1130 	fsp = VFS_TO_HSFS(vp ->v_vfsp);
1131 	secno = LBN_TO_SEC(hdp->ext_lbn+hdp->xar_len, vp->v_vfsp);
1132 	secoff = LBN_TO_BYTE(hdp->ext_lbn+hdp->xar_len, vp->v_vfsp) &
1133 			MAXHSOFFSET;
1134 	secbp = bread(fsp->hsfs_devvp->v_rdev, secno * 4, HS_SECTOR_SIZE);
1135 	error = geterror(secbp);
1136 	if (error != 0) {
1137 		cmn_err(CE_NOTE, "hs_filldirent: bread: error=(%d)", error);
1138 		goto end;
1139 	}
1140 
1141 	secp = (uchar_t *)secbp->b_un.b_addr;
1142 
1143 	/* quick check */
1144 	if (hdp->ext_lbn != HDE_EXT_LBN(&secp[secoff])) {
1145 		cmn_err(CE_NOTE, "hsfs_filldirent: dirent not match");
1146 		/* keep on going */
1147 	}
1148 	(void) hs_parsedir(fsp, &secp[secoff], hdp, (char *)NULL, (int *)NULL);
1149 
1150 end:
1151 	brelse(secbp);
1152 }
1153 
1154 /*
1155  * Look through a directory block for a matching entry.
1156  * Note: this routine does an fbrelse() on the buffer passed in.
1157  */
1158 static enum dirblock_result
1159 process_dirblock(
1160 	struct fbuf	*fbp,		/* buffer containing dirblk */
1161 	uint_t		*offset,	/* lower index */
1162 	uint_t		last_offset,	/* upper index */
1163 	char		*nm,		/* upcase nm to compare against */
1164 	int		nmlen,		/* length of name */
1165 	struct hsfs	*fsp,
1166 	struct hsnode	*dhp,
1167 	struct vnode	*dvp,
1168 	struct vnode	**vpp,
1169 	int		*error,		/* return value: errno */
1170 	int		is_rrip)	/* 1 if rock ridge is implemented */
1171 {
1172 	uchar_t		*blkp = (uchar_t *)fbp->fb_addr; /* dir block */
1173 	char		*dname;		/* name in directory entry */
1174 	int		dnamelen;	/* length of name */
1175 	struct hs_direntry hd;
1176 	int		hdlen;
1177 	uchar_t		*dirp;	/* the directory entry */
1178 	int		res;
1179 	int		parsedir_res;
1180 	size_t		rrip_name_size;
1181 	int		rr_namelen = 0;
1182 	char		*rrip_name_str = NULL;
1183 	char		*rrip_tmp_name = NULL;
1184 	enum dirblock_result err = 0;
1185 	int 		did_fbrelse = 0;
1186 	char		uppercase_name[ISO_FILE_NAMELEN];
1187 
1188 #define	PD_return(retval)	\
1189 	{ err = retval; goto do_ret; }		/* return after cleanup */
1190 #define	rel_offset(offset)	\
1191 	((offset) & MAXBOFFSET)			/* index into cur blk */
1192 
1193 	if (is_rrip) {
1194 		rrip_name_size = RRIP_FILE_NAMELEN + 1;
1195 		rrip_name_str = kmem_alloc(rrip_name_size, KM_SLEEP);
1196 		rrip_tmp_name = kmem_alloc(rrip_name_size, KM_SLEEP);
1197 		rrip_name_str[0] = '\0';
1198 		rrip_tmp_name[0] = '\0';
1199 	}
1200 
1201 	while (*offset < last_offset) {
1202 
1203 		/*
1204 		 * Directory Entries cannot span sectors.
1205 		 *
1206 		 * Unused bytes at the end of each sector are zeroed
1207 		 * according to ISO9660, but we cannot rely on this
1208 		 * since both media failures and maliciously corrupted
1209 		 * media may return arbitrary values.
1210 		 * We therefore have to check for consistency:
1211 		 * The size of a directory entry must be at least
1212 		 * 34 bytes (the size of the directory entry metadata),
1213 		 * or zero (indicating the end-of-sector condition).
1214 		 * For a non-zero directory entry size of less than
1215 		 * 34 Bytes, log a warning.
1216 		 * In any case, skip the rest of this sector and
1217 		 * continue with the next.
1218 		 */
1219 		hdlen = (int)((uchar_t)
1220 		    HDE_DIR_LEN(&blkp[rel_offset(*offset)]));
1221 
1222 		if (hdlen < HDE_ROOT_DIR_REC_SIZE ||
1223 		    *offset + hdlen > last_offset) {
1224 			/*
1225 			 * Advance to the next sector boundary
1226 			 */
1227 			*offset = roundup(*offset + 1, HS_SECTOR_SIZE);
1228 			if (hdlen)
1229 				hs_log_bogus_disk_warning(fsp,
1230 				    HSFS_ERR_TRAILING_JUNK, 0);
1231 			continue;
1232 		}
1233 
1234 		bzero(&hd, sizeof (hd));
1235 
1236 		/*
1237 		 * Check the filename length in the ISO record for
1238 		 * plausibility and reset it to a safe value, in case
1239 		 * the name length byte is out of range. Since the ISO
1240 		 * name will be used as fallback if the rockridge name
1241 		 * is invalid/nonexistant, we must make sure not to
1242 		 * blow the bounds and initialize dnamelen to a sensible
1243 		 * value within the limits of ISO9660.
1244 		 * In addition to that, the ISO filename is part of the
1245 		 * directory entry. If the filename length is too large
1246 		 * to fit, the record is invalid and we'll advance to
1247 		 * the next.
1248 		 */
1249 		dirp = &blkp[rel_offset(*offset)];
1250 		dname = (char *)HDE_name(dirp);
1251 		dnamelen = (int)((uchar_t)HDE_NAME_LEN(dirp));
1252 		if (dnamelen > hdlen - HDE_FDESIZE) {
1253 			hs_log_bogus_disk_warning(fsp,
1254 			    HSFS_ERR_BAD_FILE_LEN, 0);
1255 			goto skip_rec;
1256 		} else if (dnamelen > ISO_FILE_NAMELEN) {
1257 			hs_log_bogus_disk_warning(fsp,
1258 			    HSFS_ERR_BAD_FILE_LEN, 0);
1259 			dnamelen = ISO_FILE_NAMELEN;
1260 		}
1261 
1262 		/*
1263 		 * If the rock ridge is implemented, then we copy the name
1264 		 * from the SUA area to rrip_name_str. If no Alternate
1265 		 * name is found, then use the uppercase NM in the
1266 		 * rrip_name_str char array.
1267 		 */
1268 		if (is_rrip) {
1269 
1270 			rrip_name_str[0] = '\0';
1271 			rr_namelen = rrip_namecopy(nm, &rrip_name_str[0],
1272 			    &rrip_tmp_name[0], dirp, fsp, &hd);
1273 			if (hd.sym_link) {
1274 				kmem_free(hd.sym_link,
1275 				    (size_t)(hd.ext_size+1));
1276 				hd.sym_link = (char *)NULL;
1277 			}
1278 
1279 			if (rr_namelen != -1) {
1280 				dname = (char *)&rrip_name_str[0];
1281 				dnamelen = rr_namelen;
1282 			}
1283 		}
1284 
1285 		if (!is_rrip || rr_namelen == -1) {
1286 			/* use iso name instead */
1287 
1288 			int i;
1289 			/*
1290 			 * make sure that we get rid of ';' in the dname of
1291 			 * an iso direntry, as we should have no knowledge
1292 			 * of file versions.
1293 			 */
1294 
1295 			for (i = dnamelen - 1;
1296 			    (dname[i] != ';') && (i > 0);
1297 			    i--)
1298 				continue;
1299 
1300 			if (dname[i] == ';')
1301 				dnamelen = i;
1302 			else
1303 				dnamelen = strip_trailing(fsp, dname, dnamelen);
1304 
1305 			ASSERT(dnamelen <= ISO_FILE_NAMELEN);
1306 
1307 			if (uppercase_cp(dname, uppercase_name, dnamelen))
1308 				hs_log_bogus_disk_warning(fsp,
1309 				    HSFS_ERR_LOWER_CASE_NM, 0);
1310 			dname = uppercase_name;
1311 			if (!is_rrip &&
1312 			    (fsp->hsfs_flags & HSFSMNT_NOTRAILDOT) &&
1313 			    dname[dnamelen - 1] == '.' &&
1314 			    CAN_TRUNCATE_DOT(dname, dnamelen))
1315 				dname[--dnamelen] = '\0';
1316 		}
1317 
1318 		/*
1319 		 * Quickly screen for a non-matching entry, but not for RRIP.
1320 		 * This test doesn't work for lowercase vs. uppercase names.
1321 		 */
1322 
1323 		/* if we saw a lower case name we can't do this test either */
1324 		if (strict_iso9660_ordering && !is_rrip &&
1325 		    !HSFS_HAVE_LOWER_CASE(fsp) && *nm < *dname) {
1326 			RESTORE_NM(rrip_tmp_name, nm);
1327 			PD_return(WENT_PAST)
1328 		}
1329 
1330 		if (*nm != *dname || nmlen != dnamelen)
1331 			goto skip_rec;
1332 
1333 		if ((res = nmcmp(dname, nm, nmlen, is_rrip)) == 0) {
1334 			/* name matches */
1335 			parsedir_res = hs_parsedir(fsp, dirp, &hd,
1336 			    (char *)NULL, (int *)NULL);
1337 			if (!parsedir_res) {
1338 				uint_t lbn;	/* logical block number */
1339 
1340 				lbn = dhp->hs_dirent.ext_lbn +
1341 				    dhp->hs_dirent.xar_len;
1342 				/*
1343 				 * Need to do an fbrelse() on the buffer,
1344 				 * as hs_makenode() may try to acquire
1345 				 * hs_hashlock, which may not be required
1346 				 * while a page is locked.
1347 				 */
1348 				fbrelse(fbp, S_READ);
1349 				did_fbrelse = 1;
1350 				*vpp = hs_makenode(&hd, lbn, *offset,
1351 				    dvp->v_vfsp);
1352 				if (*vpp == NULL) {
1353 					*error = ENFILE;
1354 					RESTORE_NM(rrip_tmp_name, nm);
1355 					PD_return(FOUND_ENTRY)
1356 				}
1357 
1358 				dhp->hs_offset = *offset;
1359 				RESTORE_NM(rrip_tmp_name, nm);
1360 				PD_return(FOUND_ENTRY)
1361 			} else if (parsedir_res != EAGAIN) {
1362 				/* improper dir entry */
1363 				*error = parsedir_res;
1364 				RESTORE_NM(rrip_tmp_name, nm);
1365 				PD_return(FOUND_ENTRY)
1366 			}
1367 		} else if (strict_iso9660_ordering && !is_rrip &&
1368 			!HSFS_HAVE_LOWER_CASE(fsp) && res < 0) {
1369 			/* name < dir entry */
1370 			RESTORE_NM(rrip_tmp_name, nm);
1371 			PD_return(WENT_PAST)
1372 		}
1373 		/*
1374 		 * name > dir entry,
1375 		 * look at next one.
1376 		 */
1377 skip_rec:
1378 		*offset += hdlen;
1379 		RESTORE_NM(rrip_tmp_name, nm);
1380 	}
1381 	PD_return(HIT_END)
1382 
1383 do_ret:
1384 	if (rrip_name_str)
1385 		kmem_free(rrip_name_str, rrip_name_size);
1386 	if (rrip_tmp_name)
1387 		kmem_free(rrip_tmp_name, rrip_name_size);
1388 	if (!did_fbrelse)
1389 		fbrelse(fbp, S_READ);
1390 	return (err);
1391 #undef PD_return
1392 }
1393 
1394 
1395 /*
1396  * Compare the names, returning < 0 if a < b,
1397  * 0 if a == b, and > 0 if a > b.
1398  */
1399 static int
1400 nmcmp(char *a, char *b, int len, int is_rrip)
1401 {
1402 	while (len--) {
1403 		if (*a == *b) {
1404 			b++; a++;
1405 		} else {
1406 			/* if file version, stop */
1407 			if (! is_rrip && ((*a == ';') && (*b == '\0')))
1408 				return (0);
1409 			return ((uchar_t)*b - (uchar_t)*a);
1410 		}
1411 	}
1412 	return (0);
1413 }
1414 
1415 /*
1416  * Strip trailing nulls or spaces from the name;
1417  * return adjusted length.  If we find such junk,
1418  * log a non-conformant disk message.
1419  */
1420 static int
1421 strip_trailing(struct hsfs *fsp, char *nm, int len)
1422 {
1423 	char *c;
1424 	int trailing_junk = 0;
1425 
1426 	for (c = nm + len - 1; c > nm; c--) {
1427 		if (*c == ' ' || *c == '\0')
1428 			trailing_junk = 1;
1429 		else
1430 			break;
1431 	}
1432 
1433 	if (trailing_junk)
1434 		hs_log_bogus_disk_warning(fsp, HSFS_ERR_TRAILING_JUNK, 0);
1435 
1436 	return ((int)(c - nm + 1));
1437 }
1438