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