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$ 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 int prtactive; /* 1 => print out reclaim of active vnodes */ 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 register struct iso_dnode *dp; 182 struct vnode *nvp; 183 struct buf *bp = NULL, *bp2 = NULL; 184 union iso_ihead *ih; 185 union iso_dhead *dh; 186 int i, error, result; 187 struct iso_mnt *imp; 188 ino_t defino; 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 sleep((caddr_t)ip, PINOD); 200 goto loop; 201 } 202 if (vget(ITOV(ip), 1)) 203 goto loop; 204 *ipp = ip; 205 return 0; 206 } 207 /* 208 * Allocate a new vnode/iso_node. 209 */ 210 if (error = getnewvnode(VT_ISOFS, mntp, cd9660_vnodeop_p, &nvp)) { 211 *ipp = 0; 212 return error; 213 } 214 MALLOC(ip, struct iso_node *, sizeof(struct iso_node), 215 M_ISOFSNODE, M_WAITOK); 216 bzero((caddr_t)ip, sizeof(struct iso_node)); 217 nvp->v_data = ip; 218 ip->i_vnode = nvp; 219 ip->i_flag = 0; 220 ip->i_devvp = 0; 221 ip->i_diroff = 0; 222 ip->i_lockf = 0; 223 224 /* 225 * Put it onto its hash chain and lock it so that other requests for 226 * this inode will block if they arrive while we are sleeping waiting 227 * for old data structures to be purged or for the contents of the 228 * disk portion of this inode to be read. 229 */ 230 ip->i_dev = dev; 231 ip->i_number = ino; 232 insque(ip, ih); 233 ISO_ILOCK(ip); 234 235 imp = VFSTOISOFS (mntp); 236 ip->i_mnt = imp; 237 ip->i_devvp = imp->im_devvp; 238 VREF(ip->i_devvp); 239 240 if (relocated) { 241 /* 242 * On relocated directories we must 243 * read the `.' entry out of a dir. 244 */ 245 ip->iso_start = ino >> imp->im_bshift; 246 if (error = iso_blkatoff(ip,0,&bp)) { 247 vrele(ip->i_devvp); 248 remque(ip); 249 ip->i_forw = ip; 250 ip->i_back = ip; 251 iso_iput(ip); 252 *ipp = 0; 253 return error; 254 } 255 isodir = (struct iso_directory_record *)bp->b_un.b_addr; 256 } 257 258 ip->iso_extent = isonum_733(isodir->extent); 259 ip->i_size = isonum_733(isodir->size); 260 ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent; 261 262 vp = ITOV(ip); 263 264 /* 265 * Setup time stamp, attribute 266 */ 267 vp->v_type = VNON; 268 switch (imp->iso_ftype) { 269 default: /* ISO_FTYPE_9660 */ 270 if ((imp->im_flags&ISOFSMNT_EXTATT) 271 && isonum_711(isodir->ext_attr_length)) 272 iso_blkatoff(ip,-isonum_711(isodir->ext_attr_length), 273 &bp2); 274 cd9660_defattr(isodir,ip,bp2 ); 275 cd9660_deftstamp(isodir,ip,bp2 ); 276 break; 277 case ISO_FTYPE_RRIP: 278 result = cd9660_rrip_analyze(isodir,ip,imp); 279 break; 280 } 281 if (bp2) 282 brelse(bp2); 283 if (bp) 284 brelse(bp); 285 286 /* 287 * Initialize the associated vnode 288 */ 289 vp->v_type = IFTOVT(ip->inode.iso_mode); 290 291 if ( vp->v_type == VFIFO ) { 292 #ifdef FIFO 293 extern int (**cd9660_fifoop_p)(); 294 vp->v_op = cd9660_fifoop_p; 295 #else 296 iso_iput(ip); 297 *ipp = 0; 298 return EOPNOTSUPP; 299 #endif /* FIFO */ 300 } else if ( vp->v_type == VCHR || vp->v_type == VBLK ) { 301 extern int (**cd9660_specop_p)(); 302 303 /* 304 * if device, look at device number table for translation 305 */ 306 #ifdef ISODEVMAP 307 if (dp = iso_dmap(dev,ino,0)) 308 ip->inode.iso_rdev = dp->d_dev; 309 #endif 310 vp->v_op = cd9660_specop_p; 311 if (nvp = checkalias(vp, ip->inode.iso_rdev, mntp)) { 312 /* 313 * Reinitialize aliased inode. 314 */ 315 vp = nvp; 316 iq = VTOI(vp); 317 iq->i_vnode = vp; 318 iq->i_flag = 0; 319 ISO_ILOCK(iq); 320 iq->i_dev = dev; 321 iq->i_number = ino; 322 iq->i_mnt = ip->i_mnt; 323 bcopy(&ip->iso_extent,&iq->iso_extent, 324 (char *)(ip + 1) - (char *)&ip->iso_extent); 325 insque(iq, ih); 326 /* 327 * Discard unneeded vnode 328 * (This introduces the need of INACTIVE modification) 329 */ 330 ip->inode.iso_mode = 0; 331 iso_iput(ip); 332 ip = iq; 333 } 334 } 335 336 if (ip->iso_extent == imp->root_extent) 337 vp->v_flag |= VROOT; 338 339 *ipp = ip; 340 return 0; 341 } 342 343 /* 344 * Unlock and decrement the reference count of an inode structure. 345 */ 346 int 347 iso_iput(ip) 348 register struct iso_node *ip; 349 { 350 351 if ((ip->i_flag & ILOCKED) == 0) 352 panic("iso_iput"); 353 ISO_IUNLOCK(ip); 354 vrele(ITOV(ip)); 355 return (0); 356 } 357 358 /* 359 * Last reference to an inode, write the inode out and if necessary, 360 * truncate and deallocate the file. 361 */ 362 int 363 cd9660_inactive(ap) 364 struct vop_inactive_args /* { 365 struct vnode *a_vp; 366 } */ *ap; 367 { 368 struct vnode *vp = ap->a_vp; 369 register struct iso_node *ip = VTOI(vp); 370 int mode, error = 0; 371 372 if (prtactive && vp->v_usecount != 0) 373 vprint("cd9660_inactive: pushing active", vp); 374 375 ip->i_flag = 0; 376 /* 377 * If we are done with the inode, reclaim it 378 * so that it can be reused immediately. 379 */ 380 if (vp->v_usecount == 0 && ip->inode.iso_mode == 0) 381 vgone(vp); 382 return error; 383 } 384 385 /* 386 * Reclaim an inode so that it can be used for other purposes. 387 */ 388 int 389 cd9660_reclaim(ap) 390 struct vop_reclaim_args /* { 391 struct vnode *a_vp; 392 } */ *ap; 393 { 394 register struct vnode *vp = ap->a_vp; 395 register struct iso_node *ip = VTOI(vp); 396 int i; 397 398 if (prtactive && vp->v_usecount != 0) 399 vprint("cd9660_reclaim: pushing active", vp); 400 /* 401 * Remove the inode from its hash chain. 402 */ 403 remque(ip); 404 ip->i_forw = ip; 405 ip->i_back = ip; 406 /* 407 * Purge old data structures associated with the inode. 408 */ 409 cache_purge(vp); 410 if (ip->i_devvp) { 411 vrele(ip->i_devvp); 412 ip->i_devvp = 0; 413 } 414 FREE(vp->v_data, M_ISOFSNODE); 415 vp->v_data = NULL; 416 return 0; 417 } 418 419 /* 420 * Lock an inode. If its already locked, set the WANT bit and sleep. 421 */ 422 int 423 iso_ilock(ip) 424 register struct iso_node *ip; 425 { 426 427 while (ip->i_flag & ILOCKED) { 428 ip->i_flag |= IWANT; 429 if (ip->i_spare0 == curproc->p_pid) 430 panic("locking against myself"); 431 ip->i_spare1 = curproc->p_pid; 432 (void) sleep((caddr_t)ip, PINOD); 433 } 434 ip->i_spare1 = 0; 435 ip->i_spare0 = curproc->p_pid; 436 ip->i_flag |= ILOCKED; 437 return (0); 438 } 439 440 /* 441 * Unlock an inode. If WANT bit is on, wakeup. 442 */ 443 int 444 iso_iunlock(ip) 445 register struct iso_node *ip; 446 { 447 448 if ((ip->i_flag & ILOCKED) == 0) 449 vprint("iso_iunlock: unlocked inode", ITOV(ip)); 450 ip->i_spare0 = 0; 451 ip->i_flag &= ~ILOCKED; 452 if (ip->i_flag&IWANT) { 453 ip->i_flag &= ~IWANT; 454 wakeup((caddr_t)ip); 455 } 456 return (0); 457 } 458 459 /* 460 * File attributes 461 */ 462 void 463 cd9660_defattr(isodir,inop,bp) 464 struct iso_directory_record *isodir; 465 struct iso_node *inop; 466 struct buf *bp; 467 { 468 struct buf *bp2 = NULL; 469 struct iso_mnt *imp; 470 struct iso_extended_attributes *ap = NULL; 471 int off; 472 473 if (isonum_711(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) 525 struct iso_directory_record *isodir; 526 struct iso_node *inop; 527 struct buf *bp; 528 { 529 struct buf *bp2 = NULL; 530 struct iso_mnt *imp; 531 struct iso_extended_attributes *ap = NULL; 532 int off; 533 534 if (!bp 535 && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT) 536 && (off = isonum_711(isodir->ext_attr_length))) { 537 iso_blkatoff(inop,-off * imp->logical_block_size,&bp2); 538 bp = bp2; 539 } 540 if (bp) { 541 ap = (struct iso_extended_attributes *)bp->b_un.b_addr; 542 543 if (isonum_711(ap->version) == 1) { 544 if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime)) 545 cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime); 546 if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime)) 547 inop->inode.iso_ctime = inop->inode.iso_atime; 548 if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime)) 549 inop->inode.iso_mtime = inop->inode.iso_ctime; 550 } else 551 ap = NULL; 552 } 553 if (!ap) { 554 cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime); 555 inop->inode.iso_atime = inop->inode.iso_ctime; 556 inop->inode.iso_mtime = inop->inode.iso_ctime; 557 } 558 if (bp2) 559 brelse(bp2); 560 } 561 562 int 563 cd9660_tstamp_conv7(pi,pu) 564 char *pi; 565 struct timeval *pu; 566 { 567 int i; 568 int crtime, days; 569 int y, m, d, hour, minute, second, tz; 570 571 y = pi[0] + 1900; 572 m = pi[1]; 573 d = pi[2]; 574 hour = pi[3]; 575 minute = pi[4]; 576 second = pi[5]; 577 tz = pi[6]; 578 579 if (y < 1970) { 580 pu->tv_sec = 0; 581 pu->tv_usec = 0; 582 return 0; 583 } else { 584 #ifdef ORIGINAL 585 /* computes day number relative to Sept. 19th,1989 */ 586 /* don't even *THINK* about changing formula. It works! */ 587 days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100; 588 #else 589 /* 590 * Changed :-) to make it relative to Jan. 1st, 1970 591 * and to disambiguate negative division 592 */ 593 days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239; 594 #endif 595 crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; 596 597 /* timezone offset is unreliable on some disks */ 598 if (-48 <= tz && tz <= 52) 599 crtime += tz * 15 * 60; 600 } 601 pu->tv_sec = crtime; 602 pu->tv_usec = 0; 603 return 1; 604 } 605 606 static unsigned 607 cd9660_chars2ui(begin,len) 608 unsigned char *begin; 609 int len; 610 { 611 unsigned rc; 612 613 for (rc = 0; --len >= 0;) { 614 rc *= 10; 615 rc += *begin++ - '0'; 616 } 617 return rc; 618 } 619 620 int 621 cd9660_tstamp_conv17(pi,pu) 622 unsigned char *pi; 623 struct timeval *pu; 624 { 625 unsigned char buf[7]; 626 627 /* year:"0001"-"9999" -> -1900 */ 628 buf[0] = cd9660_chars2ui(pi,4) - 1900; 629 630 /* month: " 1"-"12" -> 1 - 12 */ 631 buf[1] = cd9660_chars2ui(pi + 4,2); 632 633 /* day: " 1"-"31" -> 1 - 31 */ 634 buf[2] = cd9660_chars2ui(pi + 6,2); 635 636 /* hour: " 0"-"23" -> 0 - 23 */ 637 buf[3] = cd9660_chars2ui(pi + 8,2); 638 639 /* minute:" 0"-"59" -> 0 - 59 */ 640 buf[4] = cd9660_chars2ui(pi + 10,2); 641 642 /* second:" 0"-"59" -> 0 - 59 */ 643 buf[5] = cd9660_chars2ui(pi + 12,2); 644 645 /* difference of GMT */ 646 buf[6] = pi[16]; 647 648 return cd9660_tstamp_conv7(buf,pu); 649 } 650 651 void 652 isodirino(inump,isodir,imp) 653 ino_t *inump; 654 struct iso_directory_record *isodir; 655 struct iso_mnt *imp; 656 { 657 *inump = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length)) 658 * imp->logical_block_size; 659 } 660