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