1 /*- 2 * Copyright (c) 1982, 1986, 1989, 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * @(#)cd9660_node.c 8.2 (Berkeley) 1/23/94 39 * $Id: cd9660_node.c,v 1.13 1996/06/12 03:36:23 davidg Exp $ 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/mount.h> 45 #include <sys/proc.h> 46 #include <sys/file.h> 47 #include <sys/buf.h> 48 #include <sys/vnode.h> 49 #include <sys/kernel.h> 50 #include <sys/malloc.h> 51 #include <sys/stat.h> 52 53 #include <isofs/cd9660/iso.h> 54 #include <isofs/cd9660/cd9660_node.h> 55 #include <isofs/cd9660/iso_rrip.h> 56 57 #define INOHSZ 512 58 #if ((INOHSZ&(INOHSZ-1)) == 0) 59 #define INOHASH(dev,ino) (((dev)+((ino)>>12))&(INOHSZ-1)) 60 #else 61 #define INOHASH(dev,ino) (((unsigned)((dev)+((ino)>>12)))%INOHSZ) 62 #endif 63 64 union iso_ihead { 65 union iso_ihead *ih_head[2]; 66 struct iso_node *ih_chain[2]; 67 } iso_ihead[INOHSZ]; 68 69 #ifdef ISODEVMAP 70 #define DNOHSZ 64 71 #if ((DNOHSZ&(DNOHSZ-1)) == 0) 72 #define DNOHASH(dev,ino) (((dev)+((ino)>>12))&(DNOHSZ-1)) 73 #else 74 #define DNOHASH(dev,ino) (((unsigned)((dev)+((ino)>>12)))%DNOHSZ) 75 #endif 76 77 union iso_dhead { 78 union iso_dhead *dh_head[2]; 79 struct iso_dnode *dh_chain[2]; 80 } iso_dhead[DNOHSZ]; 81 #endif 82 83 static unsigned cd9660_chars2ui __P((unsigned char *begin, int len)); 84 85 /* 86 * Initialize hash links for inodes and dnodes. 87 */ 88 int 89 cd9660_init() 90 { 91 register int i; 92 register union iso_ihead *ih = iso_ihead; 93 #ifdef ISODEVMAP 94 register union iso_dhead *dh = iso_dhead; 95 #endif 96 97 for (i = INOHSZ; --i >= 0; ih++) { 98 ih->ih_head[0] = ih; 99 ih->ih_head[1] = ih; 100 } 101 #ifdef ISODEVMAP 102 for (i = DNOHSZ; --i >= 0; dh++) { 103 dh->dh_head[0] = dh; 104 dh->dh_head[1] = dh; 105 } 106 #endif 107 return (0); 108 } 109 110 #ifdef ISODEVMAP 111 /* 112 * Enter a new node into the device hash list 113 */ 114 struct iso_dnode * 115 iso_dmap(dev,ino,create) 116 dev_t dev; 117 ino_t ino; 118 int create; 119 { 120 struct iso_dnode *dp; 121 union iso_dhead *dh; 122 123 dh = &iso_dhead[DNOHASH(dev, ino)]; 124 for (dp = dh->dh_chain[0]; 125 dp != (struct iso_dnode *)dh; 126 dp = dp->d_forw) 127 if (ino == dp->i_number && dev == dp->i_dev) 128 return dp; 129 130 if (!create) 131 return (struct iso_dnode *)0; 132 133 MALLOC(dp,struct iso_dnode *,sizeof(struct iso_dnode),M_CACHE,M_WAITOK); 134 dp->i_dev = dev; 135 dp->i_number = ino; 136 insque(dp,dh); 137 138 return dp; 139 } 140 141 void 142 iso_dunmap(dev) 143 dev_t dev; 144 { 145 struct iso_dnode *dp, *dq; 146 union iso_dhead *dh; 147 148 for (dh = iso_dhead; dh < iso_dhead + DNOHSZ; dh++) { 149 for (dp = dh->dh_chain[0]; 150 dp != (struct iso_dnode *)dh; 151 dp = dq) { 152 dq = dp->d_forw; 153 if (dev == dp->i_dev) { 154 remque(dp); 155 FREE(dp,M_CACHE); 156 } 157 } 158 } 159 } 160 #endif 161 162 /* 163 * Look up a ISOFS dinode number to find its incore vnode. 164 * If it is not in core, read it in from the specified device. 165 * If it is in core, wait for the lock bit to clear, then 166 * return the inode locked. Detection and handling of mount 167 * points must be done by the calling routine. 168 */ 169 int 170 iso_iget(xp, ino, relocated, ipp, isodir) 171 struct iso_node *xp; 172 ino_t ino; 173 int relocated; 174 struct iso_node **ipp; 175 struct iso_directory_record *isodir; 176 { 177 dev_t dev = xp->i_dev; 178 struct mount *mntp = ITOV(xp)->v_mount; 179 register struct iso_node *ip, *iq; 180 register struct vnode *vp; 181 #ifdef ISODEVMAP 182 register struct iso_dnode *dp; 183 #endif 184 struct vnode *nvp; 185 struct buf *bp = NULL, *bp2 = NULL; 186 union iso_ihead *ih; 187 int error, result; 188 struct iso_mnt *imp; 189 190 ih = &iso_ihead[INOHASH(dev, ino)]; 191 loop: 192 for (ip = ih->ih_chain[0]; 193 ip != (struct iso_node *)ih; 194 ip = ip->i_forw) { 195 if (ino != ip->i_number || dev != ip->i_dev) 196 continue; 197 if ((ip->i_flag&ILOCKED) != 0) { 198 ip->i_flag |= IWANT; 199 (void) tsleep((caddr_t)ip, PINOD, "isoigt", 0); 200 goto loop; 201 } 202 if (vget(ITOV(ip), 1)) 203 goto loop; 204 *ipp = ip; 205 return 0; 206 } 207 208 /* 209 * Do the MALLOC before the getnewvnode since doing so afterward 210 * might cause a bogus v_data pointer to get dereferenced 211 * elsewhere if MALLOC should block. 212 */ 213 MALLOC(ip, struct iso_node *, sizeof(struct iso_node), M_ISOFSNODE, M_WAITOK); 214 215 /* 216 * Allocate a new vnode/iso_node. 217 */ 218 if ((error = getnewvnode(VT_ISOFS, mntp, cd9660_vnodeop_p, &nvp))) { 219 *ipp = NULL; 220 FREE(ip, M_ISOFSNODE); 221 return error; 222 } 223 bzero((caddr_t)ip, sizeof(struct iso_node)); 224 nvp->v_data = ip; 225 ip->i_vnode = nvp; 226 ip->i_flag = 0; 227 ip->i_devvp = 0; 228 ip->i_diroff = 0; 229 ip->i_lockf = 0; 230 231 /* 232 * Put it onto its hash chain and lock it so that other requests for 233 * this inode will block if they arrive while we are sleeping waiting 234 * for old data structures to be purged or for the contents of the 235 * disk portion of this inode to be read. 236 */ 237 ip->i_dev = dev; 238 ip->i_number = ino; 239 insque(ip, ih); 240 ISO_ILOCK(ip); 241 242 imp = VFSTOISOFS (mntp); 243 ip->i_mnt = imp; 244 ip->i_devvp = imp->im_devvp; 245 VREF(ip->i_devvp); 246 247 if (relocated) { 248 /* 249 * On relocated directories we must 250 * read the `.' entry out of a dir. 251 */ 252 ip->iso_start = ino >> imp->im_bshift; 253 if ((error = iso_blkatoff(ip,0,&bp))) { 254 vrele(ip->i_devvp); 255 remque(ip); 256 ip->i_forw = ip; 257 ip->i_back = ip; 258 iso_iput(ip); 259 *ipp = 0; 260 return error; 261 } 262 isodir = (struct iso_directory_record *)bp->b_un.b_addr; 263 } 264 265 ip->iso_extent = isonum_733(isodir->extent); 266 ip->i_size = isonum_733(isodir->size); 267 ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent; 268 269 vp = ITOV(ip); 270 271 /* 272 * Setup time stamp, attribute 273 */ 274 vp->v_type = VNON; 275 switch (imp->iso_ftype) { 276 default: /* ISO_FTYPE_9660 || ISO_FTYPE_HIGH_SIERRA */ 277 if ((imp->im_flags&ISOFSMNT_EXTATT) 278 && isonum_711(isodir->ext_attr_length)) 279 iso_blkatoff(ip,-isonum_711(isodir->ext_attr_length), 280 &bp2); 281 cd9660_defattr(isodir,ip,bp2,imp->iso_ftype ); 282 cd9660_deftstamp(isodir,ip,bp2,imp->iso_ftype ); 283 break; 284 case ISO_FTYPE_RRIP: 285 result = cd9660_rrip_analyze(isodir,ip,imp); 286 break; 287 } 288 if (bp2) 289 brelse(bp2); 290 if (bp) 291 brelse(bp); 292 293 /* 294 * Initialize the associated vnode 295 */ 296 vp->v_type = IFTOVT(ip->inode.iso_mode); 297 298 if ( vp->v_type == VFIFO ) { 299 vp->v_op = cd9660_fifoop_p; 300 } else if ( vp->v_type == VCHR || vp->v_type == VBLK ) { 301 /* 302 * if device, look at device number table for translation 303 */ 304 #ifdef ISODEVMAP 305 if (dp = iso_dmap(dev,ino,0)) 306 ip->inode.iso_rdev = dp->d_dev; 307 #endif 308 vp->v_op = cd9660_specop_p; 309 if ((nvp = checkalias(vp, ip->inode.iso_rdev, mntp))) { 310 /* 311 * Reinitialize aliased inode. 312 */ 313 vp = nvp; 314 iq = VTOI(vp); 315 iq->i_vnode = vp; 316 iq->i_flag = 0; 317 ISO_ILOCK(iq); 318 iq->i_dev = dev; 319 iq->i_number = ino; 320 iq->i_mnt = ip->i_mnt; 321 bcopy(&ip->iso_extent,&iq->iso_extent, 322 (char *)(ip + 1) - (char *)&ip->iso_extent); 323 insque(iq, ih); 324 /* 325 * Discard unneeded vnode 326 * (This introduces the need of INACTIVE modification) 327 */ 328 ip->inode.iso_mode = 0; 329 iso_iput(ip); 330 ip = iq; 331 } 332 } 333 334 if (ip->iso_extent == imp->root_extent) 335 vp->v_flag |= VROOT; 336 337 *ipp = ip; 338 return 0; 339 } 340 341 /* 342 * Unlock and decrement the reference count of an inode structure. 343 */ 344 int 345 iso_iput(ip) 346 register struct iso_node *ip; 347 { 348 349 if ((ip->i_flag & ILOCKED) == 0) 350 panic("iso_iput"); 351 ISO_IUNLOCK(ip); 352 vrele(ITOV(ip)); 353 return (0); 354 } 355 356 /* 357 * Last reference to an inode, write the inode out and if necessary, 358 * truncate and deallocate the file. 359 */ 360 int 361 cd9660_inactive(ap) 362 struct vop_inactive_args /* { 363 struct vnode *a_vp; 364 } */ *ap; 365 { 366 struct vnode *vp = ap->a_vp; 367 register struct iso_node *ip = VTOI(vp); 368 int error = 0; 369 370 if (prtactive && vp->v_usecount != 0) 371 vprint("cd9660_inactive: pushing active", vp); 372 373 ip->i_flag = 0; 374 /* 375 * If we are done with the inode, reclaim it 376 * so that it can be reused immediately. 377 */ 378 if (vp->v_usecount == 0 && ip->inode.iso_mode == 0) 379 vgone(vp); 380 return error; 381 } 382 383 /* 384 * Reclaim an inode so that it can be used for other purposes. 385 */ 386 int 387 cd9660_reclaim(ap) 388 struct vop_reclaim_args /* { 389 struct vnode *a_vp; 390 } */ *ap; 391 { 392 register struct vnode *vp = ap->a_vp; 393 register struct iso_node *ip = VTOI(vp); 394 395 if (prtactive && vp->v_usecount != 0) 396 vprint("cd9660_reclaim: pushing active", vp); 397 /* 398 * Remove the inode from its hash chain. 399 */ 400 remque(ip); 401 ip->i_forw = ip; 402 ip->i_back = ip; 403 /* 404 * Purge old data structures associated with the inode. 405 */ 406 cache_purge(vp); 407 if (ip->i_devvp) { 408 vrele(ip->i_devvp); 409 ip->i_devvp = 0; 410 } 411 FREE(vp->v_data, M_ISOFSNODE); 412 vp->v_data = NULL; 413 return 0; 414 } 415 416 /* 417 * Lock an inode. If its already locked, set the WANT bit and sleep. 418 */ 419 int 420 iso_ilock(ip) 421 register struct iso_node *ip; 422 { 423 424 while (ip->i_flag & ILOCKED) { 425 ip->i_flag |= IWANT; 426 if (ip->i_spare0 == curproc->p_pid) 427 panic("locking against myself"); 428 ip->i_spare1 = curproc->p_pid; 429 (void) tsleep((caddr_t)ip, PINOD, "isoilk", 0); 430 } 431 ip->i_spare1 = 0; 432 ip->i_spare0 = curproc->p_pid; 433 ip->i_flag |= ILOCKED; 434 return (0); 435 } 436 437 /* 438 * Unlock an inode. If WANT bit is on, wakeup. 439 */ 440 int 441 iso_iunlock(ip) 442 register struct iso_node *ip; 443 { 444 445 if ((ip->i_flag & ILOCKED) == 0) 446 vprint("iso_iunlock: unlocked inode", ITOV(ip)); 447 ip->i_spare0 = 0; 448 ip->i_flag &= ~ILOCKED; 449 if (ip->i_flag&IWANT) { 450 ip->i_flag &= ~IWANT; 451 wakeup((caddr_t)ip); 452 } 453 return (0); 454 } 455 456 /* 457 * File attributes 458 */ 459 void 460 cd9660_defattr(isodir,inop,bp,ftype) 461 struct iso_directory_record *isodir; 462 struct iso_node *inop; 463 struct buf *bp; 464 enum ISO_FTYPE ftype; 465 { 466 struct buf *bp2 = NULL; 467 struct iso_mnt *imp; 468 struct iso_extended_attributes *ap = NULL; 469 int off; 470 471 /* high sierra does not have timezone data, flag is one byte ahead */ 472 if (isonum_711(ftype == ISO_FTYPE_HIGH_SIERRA? 473 &isodir->date[6]: isodir->flags)&2) { 474 inop->inode.iso_mode = S_IFDIR; 475 /* 476 * If we return 2, fts() will assume there are no subdirectories 477 * (just links for the path and .), so instead we return 1. 478 */ 479 inop->inode.iso_links = 1; 480 } else { 481 inop->inode.iso_mode = S_IFREG; 482 inop->inode.iso_links = 1; 483 } 484 if (!bp 485 && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT) 486 && (off = isonum_711(isodir->ext_attr_length))) { 487 iso_blkatoff(inop,-off * imp->logical_block_size,&bp2); 488 bp = bp2; 489 } 490 if (bp) { 491 ap = (struct iso_extended_attributes *)bp->b_un.b_addr; 492 493 if (isonum_711(ap->version) == 1) { 494 if (!(ap->perm[0]&0x40)) 495 inop->inode.iso_mode |= VEXEC >> 6; 496 if (!(ap->perm[0]&0x10)) 497 inop->inode.iso_mode |= VREAD >> 6; 498 if (!(ap->perm[0]&4)) 499 inop->inode.iso_mode |= VEXEC >> 3; 500 if (!(ap->perm[0]&1)) 501 inop->inode.iso_mode |= VREAD >> 3; 502 if (!(ap->perm[1]&0x40)) 503 inop->inode.iso_mode |= VEXEC; 504 if (!(ap->perm[1]&0x10)) 505 inop->inode.iso_mode |= VREAD; 506 inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */ 507 inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */ 508 } else 509 ap = NULL; 510 } 511 if (!ap) { 512 inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6; 513 inop->inode.iso_uid = (uid_t)0; 514 inop->inode.iso_gid = (gid_t)0; 515 } 516 if (bp2) 517 brelse(bp2); 518 } 519 520 /* 521 * Time stamps 522 */ 523 void 524 cd9660_deftstamp(isodir,inop,bp,ftype) 525 struct iso_directory_record *isodir; 526 struct iso_node *inop; 527 struct buf *bp; 528 enum ISO_FTYPE ftype; 529 { 530 struct buf *bp2 = NULL; 531 struct iso_mnt *imp; 532 struct iso_extended_attributes *ap = NULL; 533 int off; 534 535 if (!bp 536 && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT) 537 && (off = isonum_711(isodir->ext_attr_length))) { 538 iso_blkatoff(inop,-off * imp->logical_block_size,&bp2); 539 bp = bp2; 540 } 541 if (bp) { 542 ap = (struct iso_extended_attributes *)bp->b_un.b_addr; 543 544 if (ftype != ISO_FTYPE_HIGH_SIERRA 545 && isonum_711(ap->version) == 1) { 546 if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime)) 547 cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime); 548 if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime)) 549 inop->inode.iso_ctime = inop->inode.iso_atime; 550 if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime)) 551 inop->inode.iso_mtime = inop->inode.iso_ctime; 552 } else 553 ap = NULL; 554 } 555 if (!ap) { 556 cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime,ftype); 557 inop->inode.iso_atime = inop->inode.iso_ctime; 558 inop->inode.iso_mtime = inop->inode.iso_ctime; 559 } 560 if (bp2) 561 brelse(bp2); 562 } 563 564 int 565 cd9660_tstamp_conv7(pi,pu,ftype) 566 char *pi; 567 struct timespec *pu; 568 enum ISO_FTYPE ftype; 569 { 570 int crtime, days; 571 int y, m, d, hour, minute, second, tz; 572 573 y = pi[0] + 1900; 574 m = pi[1]; 575 d = pi[2]; 576 hour = pi[3]; 577 minute = pi[4]; 578 second = pi[5]; 579 if(ftype != ISO_FTYPE_HIGH_SIERRA) 580 tz = pi[6]; 581 else 582 /* original high sierra misses timezone data */ 583 tz = 0; 584 585 if (y < 1970) { 586 pu->tv_sec = 0; 587 pu->tv_nsec = 0; 588 return 0; 589 } else { 590 #ifdef ORIGINAL 591 /* computes day number relative to Sept. 19th,1989 */ 592 /* don't even *THINK* about changing formula. It works! */ 593 days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100; 594 #else 595 /* 596 * Changed :-) to make it relative to Jan. 1st, 1970 597 * and to disambiguate negative division 598 */ 599 days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239; 600 #endif 601 crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; 602 603 /* timezone offset is unreliable on some disks */ 604 if (-48 <= tz && tz <= 52) 605 crtime -= tz * 15 * 60; 606 } 607 pu->tv_sec = crtime; 608 pu->tv_nsec = 0; 609 return 1; 610 } 611 612 static unsigned 613 cd9660_chars2ui(begin,len) 614 unsigned char *begin; 615 int len; 616 { 617 unsigned rc; 618 619 for (rc = 0; --len >= 0;) { 620 rc *= 10; 621 rc += *begin++ - '0'; 622 } 623 return rc; 624 } 625 626 int 627 cd9660_tstamp_conv17(pi,pu) 628 unsigned char *pi; 629 struct timespec *pu; 630 { 631 unsigned char buf[7]; 632 633 /* year:"0001"-"9999" -> -1900 */ 634 buf[0] = cd9660_chars2ui(pi,4) - 1900; 635 636 /* month: " 1"-"12" -> 1 - 12 */ 637 buf[1] = cd9660_chars2ui(pi + 4,2); 638 639 /* day: " 1"-"31" -> 1 - 31 */ 640 buf[2] = cd9660_chars2ui(pi + 6,2); 641 642 /* hour: " 0"-"23" -> 0 - 23 */ 643 buf[3] = cd9660_chars2ui(pi + 8,2); 644 645 /* minute:" 0"-"59" -> 0 - 59 */ 646 buf[4] = cd9660_chars2ui(pi + 10,2); 647 648 /* second:" 0"-"59" -> 0 - 59 */ 649 buf[5] = cd9660_chars2ui(pi + 12,2); 650 651 /* difference of GMT */ 652 buf[6] = pi[16]; 653 654 return cd9660_tstamp_conv7(buf, pu, ISO_FTYPE_DEFAULT); 655 } 656 657 void 658 isodirino(inump,isodir,imp) 659 ino_t *inump; 660 struct iso_directory_record *isodir; 661 struct iso_mnt *imp; 662 { 663 *inump = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length)) 664 * imp->logical_block_size; 665 } 666