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