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.6 1994/09/26 00:32:56 gpalmer 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 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 #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 * 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 extern int (**cd9660_fifoop_p)(); 293 vp->v_op = cd9660_fifoop_p; 294 } else if ( vp->v_type == VCHR || vp->v_type == VBLK ) { 295 extern int (**cd9660_specop_p)(); 296 297 /* 298 * if device, look at device number table for translation 299 */ 300 #ifdef ISODEVMAP 301 if (dp = iso_dmap(dev,ino,0)) 302 ip->inode.iso_rdev = dp->d_dev; 303 #endif 304 vp->v_op = cd9660_specop_p; 305 if ((nvp = checkalias(vp, ip->inode.iso_rdev, mntp))) { 306 /* 307 * Reinitialize aliased inode. 308 */ 309 vp = nvp; 310 iq = VTOI(vp); 311 iq->i_vnode = vp; 312 iq->i_flag = 0; 313 ISO_ILOCK(iq); 314 iq->i_dev = dev; 315 iq->i_number = ino; 316 iq->i_mnt = ip->i_mnt; 317 bcopy(&ip->iso_extent,&iq->iso_extent, 318 (char *)(ip + 1) - (char *)&ip->iso_extent); 319 insque(iq, ih); 320 /* 321 * Discard unneeded vnode 322 * (This introduces the need of INACTIVE modification) 323 */ 324 ip->inode.iso_mode = 0; 325 iso_iput(ip); 326 ip = iq; 327 } 328 } 329 330 if (ip->iso_extent == imp->root_extent) 331 vp->v_flag |= VROOT; 332 333 *ipp = ip; 334 return 0; 335 } 336 337 /* 338 * Unlock and decrement the reference count of an inode structure. 339 */ 340 int 341 iso_iput(ip) 342 register struct iso_node *ip; 343 { 344 345 if ((ip->i_flag & ILOCKED) == 0) 346 panic("iso_iput"); 347 ISO_IUNLOCK(ip); 348 vrele(ITOV(ip)); 349 return (0); 350 } 351 352 /* 353 * Last reference to an inode, write the inode out and if necessary, 354 * truncate and deallocate the file. 355 */ 356 int 357 cd9660_inactive(ap) 358 struct vop_inactive_args /* { 359 struct vnode *a_vp; 360 } */ *ap; 361 { 362 struct vnode *vp = ap->a_vp; 363 register struct iso_node *ip = VTOI(vp); 364 int error = 0; 365 366 if (prtactive && vp->v_usecount != 0) 367 vprint("cd9660_inactive: pushing active", vp); 368 369 ip->i_flag = 0; 370 /* 371 * If we are done with the inode, reclaim it 372 * so that it can be reused immediately. 373 */ 374 if (vp->v_usecount == 0 && ip->inode.iso_mode == 0) 375 vgone(vp); 376 return error; 377 } 378 379 /* 380 * Reclaim an inode so that it can be used for other purposes. 381 */ 382 int 383 cd9660_reclaim(ap) 384 struct vop_reclaim_args /* { 385 struct vnode *a_vp; 386 } */ *ap; 387 { 388 register struct vnode *vp = ap->a_vp; 389 register struct iso_node *ip = VTOI(vp); 390 391 if (prtactive && vp->v_usecount != 0) 392 vprint("cd9660_reclaim: pushing active", vp); 393 /* 394 * Remove the inode from its hash chain. 395 */ 396 remque(ip); 397 ip->i_forw = ip; 398 ip->i_back = ip; 399 /* 400 * Purge old data structures associated with the inode. 401 */ 402 cache_purge(vp); 403 if (ip->i_devvp) { 404 vrele(ip->i_devvp); 405 ip->i_devvp = 0; 406 } 407 FREE(vp->v_data, M_ISOFSNODE); 408 vp->v_data = NULL; 409 return 0; 410 } 411 412 /* 413 * Lock an inode. If its already locked, set the WANT bit and sleep. 414 */ 415 int 416 iso_ilock(ip) 417 register struct iso_node *ip; 418 { 419 420 while (ip->i_flag & ILOCKED) { 421 ip->i_flag |= IWANT; 422 if (ip->i_spare0 == curproc->p_pid) 423 panic("locking against myself"); 424 ip->i_spare1 = curproc->p_pid; 425 (void) tsleep((caddr_t)ip, PINOD, "isoilk", 0); 426 } 427 ip->i_spare1 = 0; 428 ip->i_spare0 = curproc->p_pid; 429 ip->i_flag |= ILOCKED; 430 return (0); 431 } 432 433 /* 434 * Unlock an inode. If WANT bit is on, wakeup. 435 */ 436 int 437 iso_iunlock(ip) 438 register struct iso_node *ip; 439 { 440 441 if ((ip->i_flag & ILOCKED) == 0) 442 vprint("iso_iunlock: unlocked inode", ITOV(ip)); 443 ip->i_spare0 = 0; 444 ip->i_flag &= ~ILOCKED; 445 if (ip->i_flag&IWANT) { 446 ip->i_flag &= ~IWANT; 447 wakeup((caddr_t)ip); 448 } 449 return (0); 450 } 451 452 /* 453 * File attributes 454 */ 455 void 456 cd9660_defattr(isodir,inop,bp) 457 struct iso_directory_record *isodir; 458 struct iso_node *inop; 459 struct buf *bp; 460 { 461 struct buf *bp2 = NULL; 462 struct iso_mnt *imp; 463 struct iso_extended_attributes *ap = NULL; 464 int off; 465 466 if (isonum_711(isodir->flags)&2) { 467 inop->inode.iso_mode = S_IFDIR; 468 /* 469 * If we return 2, fts() will assume there are no subdirectories 470 * (just links for the path and .), so instead we return 1. 471 */ 472 inop->inode.iso_links = 1; 473 } else { 474 inop->inode.iso_mode = S_IFREG; 475 inop->inode.iso_links = 1; 476 } 477 if (!bp 478 && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT) 479 && (off = isonum_711(isodir->ext_attr_length))) { 480 iso_blkatoff(inop,-off * imp->logical_block_size,&bp2); 481 bp = bp2; 482 } 483 if (bp) { 484 ap = (struct iso_extended_attributes *)bp->b_un.b_addr; 485 486 if (isonum_711(ap->version) == 1) { 487 if (!(ap->perm[0]&0x40)) 488 inop->inode.iso_mode |= VEXEC >> 6; 489 if (!(ap->perm[0]&0x10)) 490 inop->inode.iso_mode |= VREAD >> 6; 491 if (!(ap->perm[0]&4)) 492 inop->inode.iso_mode |= VEXEC >> 3; 493 if (!(ap->perm[0]&1)) 494 inop->inode.iso_mode |= VREAD >> 3; 495 if (!(ap->perm[1]&0x40)) 496 inop->inode.iso_mode |= VEXEC; 497 if (!(ap->perm[1]&0x10)) 498 inop->inode.iso_mode |= VREAD; 499 inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */ 500 inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */ 501 } else 502 ap = NULL; 503 } 504 if (!ap) { 505 inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6; 506 inop->inode.iso_uid = (uid_t)0; 507 inop->inode.iso_gid = (gid_t)0; 508 } 509 if (bp2) 510 brelse(bp2); 511 } 512 513 /* 514 * Time stamps 515 */ 516 void 517 cd9660_deftstamp(isodir,inop,bp) 518 struct iso_directory_record *isodir; 519 struct iso_node *inop; 520 struct buf *bp; 521 { 522 struct buf *bp2 = NULL; 523 struct iso_mnt *imp; 524 struct iso_extended_attributes *ap = NULL; 525 int off; 526 527 if (!bp 528 && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT) 529 && (off = isonum_711(isodir->ext_attr_length))) { 530 iso_blkatoff(inop,-off * imp->logical_block_size,&bp2); 531 bp = bp2; 532 } 533 if (bp) { 534 ap = (struct iso_extended_attributes *)bp->b_un.b_addr; 535 536 if (isonum_711(ap->version) == 1) { 537 if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime)) 538 cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime); 539 if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime)) 540 inop->inode.iso_ctime = inop->inode.iso_atime; 541 if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime)) 542 inop->inode.iso_mtime = inop->inode.iso_ctime; 543 } else 544 ap = NULL; 545 } 546 if (!ap) { 547 cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime); 548 inop->inode.iso_atime = inop->inode.iso_ctime; 549 inop->inode.iso_mtime = inop->inode.iso_ctime; 550 } 551 if (bp2) 552 brelse(bp2); 553 } 554 555 int 556 cd9660_tstamp_conv7(pi,pu) 557 char *pi; 558 struct timespec *pu; 559 { 560 int crtime, days; 561 int y, m, d, hour, minute, second, tz; 562 563 y = pi[0] + 1900; 564 m = pi[1]; 565 d = pi[2]; 566 hour = pi[3]; 567 minute = pi[4]; 568 second = pi[5]; 569 tz = pi[6]; 570 571 if (y < 1970) { 572 pu->ts_sec = 0; 573 pu->ts_nsec = 0; 574 return 0; 575 } else { 576 #ifdef ORIGINAL 577 /* computes day number relative to Sept. 19th,1989 */ 578 /* don't even *THINK* about changing formula. It works! */ 579 days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100; 580 #else 581 /* 582 * Changed :-) to make it relative to Jan. 1st, 1970 583 * and to disambiguate negative division 584 */ 585 days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239; 586 #endif 587 crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; 588 589 /* timezone offset is unreliable on some disks */ 590 if (-48 <= tz && tz <= 52) 591 crtime += tz * 15 * 60; 592 } 593 pu->ts_sec = crtime; 594 pu->ts_nsec = 0; 595 return 1; 596 } 597 598 static unsigned 599 cd9660_chars2ui(begin,len) 600 unsigned char *begin; 601 int len; 602 { 603 unsigned rc; 604 605 for (rc = 0; --len >= 0;) { 606 rc *= 10; 607 rc += *begin++ - '0'; 608 } 609 return rc; 610 } 611 612 int 613 cd9660_tstamp_conv17(pi,pu) 614 unsigned char *pi; 615 struct timespec *pu; 616 { 617 unsigned char buf[7]; 618 619 /* year:"0001"-"9999" -> -1900 */ 620 buf[0] = cd9660_chars2ui(pi,4) - 1900; 621 622 /* month: " 1"-"12" -> 1 - 12 */ 623 buf[1] = cd9660_chars2ui(pi + 4,2); 624 625 /* day: " 1"-"31" -> 1 - 31 */ 626 buf[2] = cd9660_chars2ui(pi + 6,2); 627 628 /* hour: " 0"-"23" -> 0 - 23 */ 629 buf[3] = cd9660_chars2ui(pi + 8,2); 630 631 /* minute:" 0"-"59" -> 0 - 59 */ 632 buf[4] = cd9660_chars2ui(pi + 10,2); 633 634 /* second:" 0"-"59" -> 0 - 59 */ 635 buf[5] = cd9660_chars2ui(pi + 12,2); 636 637 /* difference of GMT */ 638 buf[6] = pi[16]; 639 640 return cd9660_tstamp_conv7(buf,pu); 641 } 642 643 void 644 isodirino(inump,isodir,imp) 645 ino_t *inump; 646 struct iso_directory_record *isodir; 647 struct iso_mnt *imp; 648 { 649 *inump = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length)) 650 * imp->logical_block_size; 651 } 652