1 /*- 2 * Copyright (c) 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_vnops.c 8.19 (Berkeley) 5/27/95 39 * $FreeBSD$ 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/namei.h> 45 #include <sys/resourcevar.h> 46 #include <sys/kernel.h> 47 #include <sys/file.h> 48 #include <sys/stat.h> 49 #include <sys/buf.h> 50 #include <sys/proc.h> 51 #include <sys/conf.h> 52 #include <sys/mount.h> 53 #include <sys/vnode.h> 54 #include <miscfs/specfs/specdev.h> 55 #include <miscfs/fifofs/fifo.h> 56 #include <sys/malloc.h> 57 #include <sys/dir.h> 58 #include <sys/unistd.h> 59 60 #include <isofs/cd9660/iso.h> 61 #include <isofs/cd9660/cd9660_node.h> 62 #include <isofs/cd9660/iso_rrip.h> 63 64 static int cd9660_setattr __P((struct vop_setattr_args *)); 65 static int cd9660_open __P((struct vop_open_args *)); 66 static int cd9660_close __P((struct vop_close_args *)); 67 static int cd9660_access __P((struct vop_access_args *)); 68 static int cd9660_getattr __P((struct vop_getattr_args *)); 69 static int cd9660_read __P((struct vop_read_args *)); 70 static int cd9660_ioctl __P((struct vop_ioctl_args *)); 71 static int cd9660_select __P((struct vop_select_args *)); 72 static int cd9660_mmap __P((struct vop_mmap_args *)); 73 static int cd9660_seek __P((struct vop_seek_args *)); 74 struct isoreaddir; 75 static int iso_uiodir __P((struct isoreaddir *idp, struct dirent *dp, 76 off_t off)); 77 static int iso_shipdir __P((struct isoreaddir *idp)); 78 static int cd9660_readdir __P((struct vop_readdir_args *)); 79 static int cd9660_readlink __P((struct vop_readlink_args *ap)); 80 static int cd9660_abortop __P((struct vop_abortop_args *)); 81 static int cd9660_lock __P((struct vop_lock_args *)); 82 static int cd9660_unlock __P((struct vop_unlock_args *)); 83 static int cd9660_strategy __P((struct vop_strategy_args *)); 84 static int cd9660_print __P((struct vop_print_args *)); 85 static int cd9660_islocked __P((struct vop_islocked_args *)); 86 87 #if 0 88 /* 89 * Mknod vnode call 90 * Actually remap the device number 91 */ 92 static int 93 cd9660_mknod(ndp, vap, cred, p) 94 struct nameidata *ndp; 95 struct ucred *cred; 96 struct vattr *vap; 97 struct proc *p; 98 { 99 #ifndef ISODEVMAP 100 free(ndp->ni_pnbuf, M_NAMEI); 101 vput(ndp->ni_dvp); 102 vput(ndp->ni_vp); 103 return (EINVAL); 104 #else 105 register struct vnode *vp; 106 struct iso_node *ip; 107 struct iso_dnode *dp; 108 int error; 109 110 vp = ndp->ni_vp; 111 ip = VTOI(vp); 112 113 if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP 114 || vap->va_type != vp->v_type 115 || (vap->va_type != VCHR && vap->va_type != VBLK)) { 116 free(ndp->ni_pnbuf, M_NAMEI); 117 vput(ndp->ni_dvp); 118 vput(ndp->ni_vp); 119 return (EINVAL); 120 } 121 122 dp = iso_dmap(ip->i_dev,ip->i_number,1); 123 if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) { 124 /* same as the unmapped one, delete the mapping */ 125 remque(dp); 126 FREE(dp,M_CACHE); 127 } else 128 /* enter new mapping */ 129 dp->d_dev = vap->va_rdev; 130 131 /* 132 * Remove inode so that it will be reloaded by iget and 133 * checked to see if it is an alias of an existing entry 134 * in the inode cache. 135 */ 136 vput(vp); 137 vp->v_type = VNON; 138 vgone(vp); 139 return (0); 140 #endif 141 } 142 #endif 143 144 /* 145 * Setattr call. Only allowed for block and character special devices. 146 */ 147 int 148 cd9660_setattr(ap) 149 struct vop_setattr_args /* { 150 struct vnodeop_desc *a_desc; 151 struct vnode *a_vp; 152 struct vattr *a_vap; 153 struct ucred *a_cred; 154 struct proc *a_p; 155 } */ *ap; 156 { 157 struct vnode *vp = ap->a_vp; 158 struct vattr *vap = ap->a_vap; 159 160 if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL || 161 vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || 162 vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) 163 return (EROFS); 164 if (vap->va_size != (u_quad_t)VNOVAL) { 165 switch (vp->v_type) { 166 case VDIR: 167 return (EISDIR); 168 case VLNK: 169 case VREG: 170 return (EROFS); 171 case VCHR: 172 case VBLK: 173 case VSOCK: 174 case VFIFO: 175 return (0); 176 } 177 } 178 return (0); 179 } 180 181 /* 182 * Open called. 183 * 184 * Nothing to do. 185 */ 186 /* ARGSUSED */ 187 static int 188 cd9660_open(ap) 189 struct vop_open_args /* { 190 struct vnode *a_vp; 191 int a_mode; 192 struct ucred *a_cred; 193 struct proc *a_p; 194 } */ *ap; 195 { 196 return (0); 197 } 198 199 /* 200 * Close called 201 * 202 * Update the times on the inode on writeable file systems. 203 */ 204 /* ARGSUSED */ 205 static int 206 cd9660_close(ap) 207 struct vop_close_args /* { 208 struct vnode *a_vp; 209 int a_fflag; 210 struct ucred *a_cred; 211 struct proc *a_p; 212 } */ *ap; 213 { 214 return (0); 215 } 216 217 /* 218 * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC. 219 * The mode is shifted to select the owner/group/other fields. The 220 * super user is granted all permissions. 221 */ 222 /* ARGSUSED */ 223 static int 224 cd9660_access(ap) 225 struct vop_access_args /* { 226 struct vnode *a_vp; 227 int a_mode; 228 struct ucred *a_cred; 229 struct proc *a_p; 230 } */ *ap; 231 { 232 struct vnode *vp = ap->a_vp; 233 struct iso_node *ip = VTOI(vp); 234 struct ucred *cred = ap->a_cred; 235 mode_t mask, mode = ap->a_mode; 236 gid_t *gp; 237 int i; 238 239 /* 240 * Disallow write attempts unless the file is a socket, 241 * fifo, or a block or character device resident on the 242 * file system. 243 */ 244 if (mode & VWRITE) { 245 switch (vp->v_type) { 246 case VDIR: 247 case VLNK: 248 case VREG: 249 return (EROFS); 250 } 251 } 252 253 /* User id 0 always gets access. */ 254 if (cred->cr_uid == 0) 255 return (0); 256 257 mask = 0; 258 259 /* Otherwise, check the owner. */ 260 if (cred->cr_uid == ip->inode.iso_uid) { 261 if (mode & VEXEC) 262 mask |= S_IXUSR; 263 if (mode & VREAD) 264 mask |= S_IRUSR; 265 if (mode & VWRITE) 266 mask |= S_IWUSR; 267 return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); 268 } 269 270 /* Otherwise, check the groups. */ 271 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) 272 if (ip->inode.iso_gid == *gp) { 273 if (mode & VEXEC) 274 mask |= S_IXGRP; 275 if (mode & VREAD) 276 mask |= S_IRGRP; 277 if (mode & VWRITE) 278 mask |= S_IWGRP; 279 return ((ip->inode.iso_mode & mask) == mask ? 280 0 : EACCES); 281 } 282 283 /* Otherwise, check everyone else. */ 284 if (mode & VEXEC) 285 mask |= S_IXOTH; 286 if (mode & VREAD) 287 mask |= S_IROTH; 288 if (mode & VWRITE) 289 mask |= S_IWOTH; 290 return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); 291 } 292 293 static int 294 cd9660_getattr(ap) 295 struct vop_getattr_args /* { 296 struct vnode *a_vp; 297 struct vattr *a_vap; 298 struct ucred *a_cred; 299 struct proc *a_p; 300 } */ *ap; 301 302 { 303 struct vnode *vp = ap->a_vp; 304 register struct vattr *vap = ap->a_vap; 305 register struct iso_node *ip = VTOI(vp); 306 307 vap->va_fsid = ip->i_dev; 308 vap->va_fileid = ip->i_number; 309 310 vap->va_mode = ip->inode.iso_mode; 311 vap->va_nlink = ip->inode.iso_links; 312 vap->va_uid = ip->inode.iso_uid; 313 vap->va_gid = ip->inode.iso_gid; 314 vap->va_atime = ip->inode.iso_atime; 315 vap->va_mtime = ip->inode.iso_mtime; 316 vap->va_ctime = ip->inode.iso_ctime; 317 vap->va_rdev = ip->inode.iso_rdev; 318 319 vap->va_size = (u_quad_t) ip->i_size; 320 if (ip->i_size == 0 && (vap->va_mode & S_IFMT) == S_IFLNK) { 321 struct vop_readlink_args rdlnk; 322 struct iovec aiov; 323 struct uio auio; 324 char *cp; 325 326 MALLOC(cp, char *, MAXPATHLEN, M_TEMP, M_WAITOK); 327 aiov.iov_base = cp; 328 aiov.iov_len = MAXPATHLEN; 329 auio.uio_iov = &aiov; 330 auio.uio_iovcnt = 1; 331 auio.uio_offset = 0; 332 auio.uio_rw = UIO_READ; 333 auio.uio_segflg = UIO_SYSSPACE; 334 auio.uio_procp = ap->a_p; 335 auio.uio_resid = MAXPATHLEN; 336 rdlnk.a_uio = &auio; 337 rdlnk.a_vp = ap->a_vp; 338 rdlnk.a_cred = ap->a_cred; 339 if (cd9660_readlink(&rdlnk) == 0) 340 vap->va_size = MAXPATHLEN - auio.uio_resid; 341 FREE(cp, M_TEMP); 342 } 343 vap->va_flags = 0; 344 vap->va_gen = 1; 345 vap->va_blocksize = ip->i_mnt->logical_block_size; 346 vap->va_bytes = (u_quad_t) ip->i_size; 347 vap->va_type = vp->v_type; 348 vap->va_filerev = 0; 349 return (0); 350 } 351 352 #if ISO_DEFAULT_BLOCK_SIZE >= PAGE_SIZE 353 #ifdef DEBUG 354 extern int doclusterread; 355 #else 356 #define doclusterread 1 357 #endif 358 #else 359 /* XXX until cluster routines can handle block sizes less than one page */ 360 #if defined(__FreeBSD__) 361 #define doclusterread 1 362 #else 363 #define doclusterread 0 364 #endif 365 #endif 366 367 /* 368 * Vnode op for reading. 369 */ 370 static int 371 cd9660_read(ap) 372 struct vop_read_args /* { 373 struct vnode *a_vp; 374 struct uio *a_uio; 375 int a_ioflag; 376 struct ucred *a_cred; 377 } */ *ap; 378 { 379 struct vnode *vp = ap->a_vp; 380 register struct uio *uio = ap->a_uio; 381 register struct iso_node *ip = VTOI(vp); 382 register struct iso_mnt *imp; 383 struct buf *bp; 384 daddr_t lbn, rablock; 385 off_t diff; 386 int rasize, error = 0; 387 long size, n, on; 388 389 if (uio->uio_resid == 0) 390 return (0); 391 if (uio->uio_offset < 0) 392 return (EINVAL); 393 ip->i_flag |= IN_ACCESS; 394 imp = ip->i_mnt; 395 do { 396 lbn = lblkno(imp, uio->uio_offset); 397 on = blkoff(imp, uio->uio_offset); 398 n = min((u_int)(imp->logical_block_size - on), 399 uio->uio_resid); 400 diff = (off_t)ip->i_size - uio->uio_offset; 401 if (diff <= 0) 402 return (0); 403 if (diff < n) 404 n = diff; 405 size = blksize(imp, ip, lbn); 406 rablock = lbn + 1; 407 if (doclusterread) { 408 if (lblktosize(imp, rablock) <= ip->i_size) 409 error = cluster_read(vp, (off_t)ip->i_size, 410 lbn, size, NOCRED, uio->uio_resid, 411 (ap->a_ioflag >> 16), &bp); 412 else 413 error = bread(vp, lbn, size, NOCRED, &bp); 414 } else { 415 if (vp->v_lastr + 1 == lbn && 416 lblktosize(imp, rablock) < ip->i_size) { 417 rasize = blksize(imp, ip, rablock); 418 error = breadn(vp, lbn, size, &rablock, 419 &rasize, 1, NOCRED, &bp); 420 } else 421 error = bread(vp, lbn, size, NOCRED, &bp); 422 } 423 vp->v_lastr = lbn; 424 n = min(n, size - bp->b_resid); 425 if (error) { 426 brelse(bp); 427 return (error); 428 } 429 430 error = uiomove(bp->b_data + on, (int)n, uio); 431 brelse(bp); 432 } while (error == 0 && uio->uio_resid > 0 && n != 0); 433 return (error); 434 } 435 436 /* ARGSUSED */ 437 static int 438 cd9660_ioctl(ap) 439 struct vop_ioctl_args /* { 440 struct vnode *a_vp; 441 u_long a_command; 442 caddr_t a_data; 443 int a_fflag; 444 struct ucred *a_cred; 445 struct proc *a_p; 446 } */ *ap; 447 { 448 printf("You did ioctl for isofs !!\n"); 449 return (ENOTTY); 450 } 451 452 /* ARGSUSED */ 453 static int 454 cd9660_select(ap) 455 struct vop_select_args /* { 456 struct vnode *a_vp; 457 int a_which; 458 int a_fflags; 459 struct ucred *a_cred; 460 struct proc *a_p; 461 } */ *ap; 462 { 463 464 /* 465 * We should really check to see if I/O is possible. 466 */ 467 return (1); 468 } 469 470 /* 471 * Mmap a file 472 * 473 * NB Currently unsupported. 474 */ 475 /* ARGSUSED */ 476 static int 477 cd9660_mmap(ap) 478 struct vop_mmap_args /* { 479 struct vnode *a_vp; 480 int a_fflags; 481 struct ucred *a_cred; 482 struct proc *a_p; 483 } */ *ap; 484 { 485 486 return (EINVAL); 487 } 488 489 /* 490 * Seek on a file 491 * 492 * Nothing to do, so just return. 493 */ 494 /* ARGSUSED */ 495 static int 496 cd9660_seek(ap) 497 struct vop_seek_args /* { 498 struct vnode *a_vp; 499 off_t a_oldoff; 500 off_t a_newoff; 501 struct ucred *a_cred; 502 } */ *ap; 503 { 504 505 return (0); 506 } 507 508 /* 509 * Structure for reading directories 510 */ 511 struct isoreaddir { 512 struct dirent saveent; 513 struct dirent assocent; 514 struct dirent current; 515 off_t saveoff; 516 off_t assocoff; 517 off_t curroff; 518 struct uio *uio; 519 off_t uio_off; 520 int eofflag; 521 u_long *cookies; 522 int ncookies; 523 }; 524 525 int 526 iso_uiodir(idp,dp,off) 527 struct isoreaddir *idp; 528 struct dirent *dp; 529 off_t off; 530 { 531 int error; 532 533 dp->d_name[dp->d_namlen] = 0; 534 dp->d_reclen = DIRSIZ(dp); 535 536 if (idp->uio->uio_resid < dp->d_reclen) { 537 idp->eofflag = 0; 538 return (-1); 539 } 540 541 if (idp->cookies) { 542 if (idp->ncookies <= 0) { 543 idp->eofflag = 0; 544 return (-1); 545 } 546 547 *idp->cookies++ = off; 548 --idp->ncookies; 549 } 550 551 if (error = uiomove((caddr_t) dp,dp->d_reclen,idp->uio)) 552 return (error); 553 idp->uio_off = off; 554 return (0); 555 } 556 557 int 558 iso_shipdir(idp) 559 struct isoreaddir *idp; 560 { 561 struct dirent *dp; 562 int cl, sl, assoc; 563 int error; 564 char *cname, *sname; 565 566 cl = idp->current.d_namlen; 567 cname = idp->current.d_name; 568 assoc = (cl > 1) && (*cname == ASSOCCHAR); 569 if (assoc) { 570 cl--; 571 cname++; 572 } 573 574 dp = &idp->saveent; 575 sname = dp->d_name; 576 if (!(sl = dp->d_namlen)) { 577 dp = &idp->assocent; 578 sname = dp->d_name + 1; 579 sl = dp->d_namlen - 1; 580 } 581 if (sl > 0) { 582 if (sl != cl 583 || bcmp(sname,cname,sl)) { 584 if (idp->assocent.d_namlen) { 585 if (error = iso_uiodir(idp,&idp->assocent,idp->assocoff)) 586 return (error); 587 idp->assocent.d_namlen = 0; 588 } 589 if (idp->saveent.d_namlen) { 590 if (error = iso_uiodir(idp,&idp->saveent,idp->saveoff)) 591 return (error); 592 idp->saveent.d_namlen = 0; 593 } 594 } 595 } 596 idp->current.d_reclen = DIRSIZ(&idp->current); 597 if (assoc) { 598 idp->assocoff = idp->curroff; 599 bcopy(&idp->current,&idp->assocent,idp->current.d_reclen); 600 } else { 601 idp->saveoff = idp->curroff; 602 bcopy(&idp->current,&idp->saveent,idp->current.d_reclen); 603 } 604 return (0); 605 } 606 607 /* 608 * Vnode op for readdir 609 */ 610 static int 611 cd9660_readdir(ap) 612 struct vop_readdir_args /* { 613 struct vnode *a_vp; 614 struct uio *a_uio; 615 struct ucred *a_cred; 616 int *a_eofflag; 617 int *a_ncookies; 618 u_long *a_cookies; 619 } */ *ap; 620 { 621 register struct uio *uio = ap->a_uio; 622 struct isoreaddir *idp; 623 struct vnode *vdp = ap->a_vp; 624 struct iso_node *dp; 625 struct iso_mnt *imp; 626 struct buf *bp = NULL; 627 struct iso_directory_record *ep; 628 int entryoffsetinblock; 629 doff_t endsearch; 630 u_long bmask; 631 int error = 0; 632 int reclen; 633 u_short namelen; 634 int ncookies = 0; 635 u_long *cookies = NULL; 636 637 dp = VTOI(vdp); 638 imp = dp->i_mnt; 639 bmask = imp->im_bmask; 640 641 MALLOC(idp, struct isoreaddir *, sizeof(*idp), M_TEMP, M_WAITOK); 642 idp->saveent.d_namlen = idp->assocent.d_namlen = 0; 643 /* 644 * XXX 645 * Is it worth trying to figure out the type? 646 */ 647 idp->saveent.d_type = idp->assocent.d_type = idp->current.d_type = 648 DT_UNKNOWN; 649 idp->uio = uio; 650 if (ap->a_ncookies == NULL) { 651 idp->cookies = NULL; 652 } else { 653 /* 654 * Guess the number of cookies needed. 655 */ 656 ncookies = uio->uio_resid / 16; 657 MALLOC(cookies, u_long *, ncookies * sizeof(u_int), M_TEMP, 658 M_WAITOK); 659 idp->cookies = cookies; 660 idp->ncookies = ncookies; 661 } 662 idp->eofflag = 1; 663 idp->curroff = uio->uio_offset; 664 665 if ((entryoffsetinblock = idp->curroff & bmask) && 666 (error = VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp))) { 667 FREE(idp, M_TEMP); 668 return (error); 669 } 670 endsearch = dp->i_size; 671 672 while (idp->curroff < endsearch) { 673 /* 674 * If offset is on a block boundary, 675 * read the next directory block. 676 * Release previous if it exists. 677 */ 678 if ((idp->curroff & bmask) == 0) { 679 if (bp != NULL) 680 brelse(bp); 681 if (error = 682 VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp)) 683 break; 684 entryoffsetinblock = 0; 685 } 686 /* 687 * Get pointer to next entry. 688 */ 689 ep = (struct iso_directory_record *) 690 ((char *)bp->b_data + entryoffsetinblock); 691 692 reclen = isonum_711(ep->length); 693 if (reclen == 0) { 694 /* skip to next block, if any */ 695 idp->curroff = 696 (idp->curroff & ~bmask) + imp->logical_block_size; 697 continue; 698 } 699 700 if (reclen < ISO_DIRECTORY_RECORD_SIZE) { 701 error = EINVAL; 702 /* illegal entry, stop */ 703 break; 704 } 705 706 if (entryoffsetinblock + reclen > imp->logical_block_size) { 707 error = EINVAL; 708 /* illegal directory, so stop looking */ 709 break; 710 } 711 712 idp->current.d_namlen = isonum_711(ep->name_len); 713 714 if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { 715 error = EINVAL; 716 /* illegal entry, stop */ 717 break; 718 } 719 720 if (isonum_711(ep->flags)&2) 721 idp->current.d_fileno = isodirino(ep, imp); 722 else 723 idp->current.d_fileno = dbtob(bp->b_blkno) + 724 entryoffsetinblock; 725 726 idp->curroff += reclen; 727 728 switch (imp->iso_ftype) { 729 case ISO_FTYPE_RRIP: 730 cd9660_rrip_getname(ep,idp->current.d_name, &namelen, 731 &idp->current.d_fileno,imp); 732 idp->current.d_namlen = (u_char)namelen; 733 if (idp->current.d_namlen) 734 error = iso_uiodir(idp,&idp->current,idp->curroff); 735 break; 736 default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 || ISO_FTYPE_HIGH_SIERRA*/ 737 strcpy(idp->current.d_name,".."); 738 switch (ep->name[0]) { 739 case 0: 740 idp->current.d_namlen = 1; 741 error = iso_uiodir(idp,&idp->current,idp->curroff); 742 break; 743 case 1: 744 idp->current.d_namlen = 2; 745 error = iso_uiodir(idp,&idp->current,idp->curroff); 746 break; 747 default: 748 isofntrans(ep->name,idp->current.d_namlen, 749 idp->current.d_name, &namelen, 750 imp->iso_ftype == ISO_FTYPE_9660, 751 isonum_711(ep->flags)&4); 752 idp->current.d_namlen = (u_char)namelen; 753 if (imp->iso_ftype == ISO_FTYPE_DEFAULT) 754 error = iso_shipdir(idp); 755 else 756 error = iso_uiodir(idp,&idp->current,idp->curroff); 757 break; 758 } 759 } 760 if (error) 761 break; 762 763 entryoffsetinblock += reclen; 764 } 765 766 if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) { 767 idp->current.d_namlen = 0; 768 error = iso_shipdir(idp); 769 } 770 if (error < 0) 771 error = 0; 772 773 if (ap->a_ncookies != NULL) { 774 if (error) 775 free(cookies, M_TEMP); 776 else { 777 /* 778 * Work out the number of cookies actually used. 779 */ 780 *ap->a_ncookies = ncookies - idp->ncookies; 781 *ap->a_cookies = cookies; 782 } 783 } 784 785 if (bp) 786 brelse (bp); 787 788 uio->uio_offset = idp->uio_off; 789 *ap->a_eofflag = idp->eofflag; 790 791 FREE(idp, M_TEMP); 792 793 return (error); 794 } 795 796 /* 797 * Return target name of a symbolic link 798 * Shouldn't we get the parent vnode and read the data from there? 799 * This could eventually result in deadlocks in cd9660_lookup. 800 * But otherwise the block read here is in the block buffer two times. 801 */ 802 typedef struct iso_directory_record ISODIR; 803 typedef struct iso_node ISONODE; 804 typedef struct iso_mnt ISOMNT; 805 static int 806 cd9660_readlink(ap) 807 struct vop_readlink_args /* { 808 struct vnode *a_vp; 809 struct uio *a_uio; 810 struct ucred *a_cred; 811 } */ *ap; 812 { 813 ISONODE *ip; 814 ISODIR *dirp; 815 ISOMNT *imp; 816 struct buf *bp; 817 struct uio *uio; 818 u_short symlen; 819 int error; 820 char *symname; 821 822 ip = VTOI(ap->a_vp); 823 imp = ip->i_mnt; 824 uio = ap->a_uio; 825 826 if (imp->iso_ftype != ISO_FTYPE_RRIP) 827 return (EINVAL); 828 829 /* 830 * Get parents directory record block that this inode included. 831 */ 832 error = bread(imp->im_devvp, 833 (ip->i_number >> imp->im_bshift) << 834 (imp->im_bshift - DEV_BSHIFT), 835 imp->logical_block_size, NOCRED, &bp); 836 if (error) { 837 brelse(bp); 838 return (EINVAL); 839 } 840 841 /* 842 * Setup the directory pointer for this inode 843 */ 844 dirp = (ISODIR *)(bp->b_data + (ip->i_number & imp->im_bmask)); 845 846 /* 847 * Just make sure, we have a right one.... 848 * 1: Check not cross boundary on block 849 */ 850 if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length) 851 > (unsigned)imp->logical_block_size) { 852 brelse(bp); 853 return (EINVAL); 854 } 855 856 /* 857 * Now get a buffer 858 * Abuse a namei buffer for now. 859 */ 860 if (uio->uio_segflg == UIO_SYSSPACE) 861 symname = uio->uio_iov->iov_base; 862 else 863 MALLOC(symname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); 864 865 /* 866 * Ok, we just gathering a symbolic name in SL record. 867 */ 868 if (cd9660_rrip_getsymname(dirp, symname, &symlen, imp) == 0) { 869 if (uio->uio_segflg != UIO_SYSSPACE) 870 FREE(symname, M_NAMEI); 871 brelse(bp); 872 return (EINVAL); 873 } 874 /* 875 * Don't forget before you leave from home ;-) 876 */ 877 brelse(bp); 878 879 /* 880 * return with the symbolic name to caller's. 881 */ 882 if (uio->uio_segflg != UIO_SYSSPACE) { 883 error = uiomove(symname, symlen, uio); 884 FREE(symname, M_NAMEI); 885 return (error); 886 } 887 uio->uio_resid -= symlen; 888 uio->uio_iov->iov_base += symlen; 889 uio->uio_iov->iov_len -= symlen; 890 return (0); 891 } 892 893 /* 894 * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually 895 * done. If a buffer has been saved in anticipation of a CREATE, delete it. 896 */ 897 static int 898 cd9660_abortop(ap) 899 struct vop_abortop_args /* { 900 struct vnode *a_dvp; 901 struct componentname *a_cnp; 902 } */ *ap; 903 { 904 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) 905 FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); 906 return (0); 907 } 908 909 /* 910 * Lock an inode. 911 */ 912 static int 913 cd9660_lock(ap) 914 struct vop_lock_args /* { 915 struct vnode *a_vp; 916 int a_flags; 917 struct proc *a_p; 918 } */ *ap; 919 { 920 struct vnode *vp = ap->a_vp; 921 922 return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock, 923 ap->a_p)); 924 } 925 926 /* 927 * Unlock an inode. 928 */ 929 static int 930 cd9660_unlock(ap) 931 struct vop_unlock_args /* { 932 struct vnode *a_vp; 933 int a_flags; 934 struct proc *a_p; 935 } */ *ap; 936 { 937 struct vnode *vp = ap->a_vp; 938 939 return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE, 940 &vp->v_interlock, ap->a_p)); 941 } 942 943 /* 944 * Calculate the logical to physical mapping if not done already, 945 * then call the device strategy routine. 946 */ 947 static int 948 cd9660_strategy(ap) 949 struct vop_strategy_args /* { 950 struct buf *a_bp; 951 } */ *ap; 952 { 953 register struct buf *bp = ap->a_bp; 954 register struct vnode *vp = bp->b_vp; 955 register struct iso_node *ip; 956 int error; 957 958 ip = VTOI(vp); 959 if (vp->v_type == VBLK || vp->v_type == VCHR) 960 panic("cd9660_strategy: spec"); 961 if (bp->b_blkno == bp->b_lblkno) { 962 if ((error = 963 VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL))) { 964 bp->b_error = error; 965 bp->b_flags |= B_ERROR; 966 biodone(bp); 967 return (error); 968 } 969 if ((long)bp->b_blkno == -1) 970 clrbuf(bp); 971 } 972 if ((long)bp->b_blkno == -1) { 973 biodone(bp); 974 return (0); 975 } 976 vp = ip->i_devvp; 977 bp->b_dev = vp->v_rdev; 978 VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); 979 return (0); 980 } 981 982 /* 983 * Print out the contents of an inode. 984 */ 985 static int 986 cd9660_print(ap) 987 struct vop_print_args /* { 988 struct vnode *a_vp; 989 } */ *ap; 990 { 991 992 printf("tag VT_ISOFS, isofs vnode\n"); 993 return (0); 994 } 995 996 /* 997 * Check for a locked inode. 998 */ 999 int 1000 cd9660_islocked(ap) 1001 struct vop_islocked_args /* { 1002 struct vnode *a_vp; 1003 } */ *ap; 1004 { 1005 1006 return (lockstatus(&VTOI(ap->a_vp)->i_lock)); 1007 } 1008 1009 /* 1010 * Return POSIX pathconf information applicable to cd9660 filesystems. 1011 */ 1012 int 1013 cd9660_pathconf(ap) 1014 struct vop_pathconf_args /* { 1015 struct vnode *a_vp; 1016 int a_name; 1017 register_t *a_retval; 1018 } */ *ap; 1019 { 1020 1021 switch (ap->a_name) { 1022 case _PC_LINK_MAX: 1023 *ap->a_retval = 1; 1024 return (0); 1025 case _PC_NAME_MAX: 1026 if (VTOI(ap->a_vp)->i_mnt->iso_ftype == ISO_FTYPE_RRIP) 1027 *ap->a_retval = NAME_MAX; 1028 else 1029 *ap->a_retval = 37; 1030 return (0); 1031 case _PC_PATH_MAX: 1032 *ap->a_retval = PATH_MAX; 1033 return (0); 1034 case _PC_PIPE_BUF: 1035 *ap->a_retval = PIPE_BUF; 1036 return (0); 1037 case _PC_CHOWN_RESTRICTED: 1038 *ap->a_retval = 1; 1039 return (0); 1040 case _PC_NO_TRUNC: 1041 *ap->a_retval = 1; 1042 return (0); 1043 default: 1044 return (EINVAL); 1045 } 1046 /* NOTREACHED */ 1047 } 1048 1049 /* 1050 * Global vfs data structures for isofs 1051 */ 1052 #define cd9660_create \ 1053 ((int (*) __P((struct vop_create_args *)))eopnotsupp) 1054 #define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp) 1055 #define cd9660_write ((int (*) __P((struct vop_write_args *)))eopnotsupp) 1056 #ifdef NFS 1057 #define cd9660_lease_check lease_check 1058 #else 1059 #define cd9660_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) 1060 #endif 1061 #define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) 1062 #define cd9660_remove \ 1063 ((int (*) __P((struct vop_remove_args *)))eopnotsupp) 1064 #define cd9660_link ((int (*) __P((struct vop_link_args *)))eopnotsupp) 1065 #define cd9660_rename \ 1066 ((int (*) __P((struct vop_rename_args *)))eopnotsupp) 1067 #define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp) 1068 #define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp) 1069 #define cd9660_symlink \ 1070 ((int (*) __P((struct vop_symlink_args *)))eopnotsupp) 1071 #define cd9660_advlock \ 1072 ((int (*) __P((struct vop_advlock_args *)))eopnotsupp) 1073 #define cd9660_valloc ((int(*) __P(( \ 1074 struct vnode *pvp, \ 1075 int mode, \ 1076 struct ucred *cred, \ 1077 struct vnode **vpp))) eopnotsupp) 1078 #define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))eopnotsupp) 1079 #define cd9660_truncate \ 1080 ((int (*) __P((struct vop_truncate_args *)))eopnotsupp) 1081 #define cd9660_update \ 1082 ((int (*) __P((struct vop_update_args *)))eopnotsupp) 1083 #define cd9660_bwrite \ 1084 ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp) 1085 1086 /* 1087 * Global vfs data structures for cd9660 1088 */ 1089 vop_t **cd9660_vnodeop_p; 1090 struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = { 1091 1092 { &vop_default_desc, (vop_t *)vn_default_error }, 1093 { &vop_lookup_desc, (vop_t *)cd9660_lookup }, /* lookup */ 1094 { &vop_create_desc, (vop_t *)cd9660_create }, /* create */ 1095 { &vop_mknod_desc, (vop_t *)cd9660_mknod }, /* mknod */ 1096 { &vop_open_desc, (vop_t *)cd9660_open }, /* open */ 1097 { &vop_close_desc, (vop_t *)cd9660_close }, /* close */ 1098 { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ 1099 { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ 1100 { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ 1101 { &vop_read_desc, (vop_t *)cd9660_read }, /* read */ 1102 { &vop_write_desc, (vop_t *)cd9660_write }, /* write */ 1103 { &vop_lease_desc, (vop_t *)cd9660_lease_check },/* lease */ 1104 { &vop_ioctl_desc, (vop_t *)cd9660_ioctl }, /* ioctl */ 1105 { &vop_select_desc, (vop_t *)cd9660_select }, /* select */ 1106 { &vop_revoke_desc, (vop_t *)cd9660_revoke }, /* revoke */ 1107 { &vop_mmap_desc, (vop_t *)cd9660_mmap }, /* mmap */ 1108 { &vop_fsync_desc, (vop_t *)cd9660_fsync }, /* fsync */ 1109 { &vop_seek_desc, (vop_t *)cd9660_seek }, /* seek */ 1110 { &vop_remove_desc, (vop_t *)cd9660_remove }, /* remove */ 1111 { &vop_link_desc, (vop_t *)cd9660_link }, /* link */ 1112 { &vop_rename_desc, (vop_t *)cd9660_rename }, /* rename */ 1113 { &vop_mkdir_desc, (vop_t *)cd9660_mkdir }, /* mkdir */ 1114 { &vop_rmdir_desc, (vop_t *)cd9660_rmdir }, /* rmdir */ 1115 { &vop_symlink_desc, (vop_t *)cd9660_symlink }, /* symlink */ 1116 { &vop_readdir_desc, (vop_t *)cd9660_readdir }, /* readdir */ 1117 { &vop_readlink_desc, (vop_t *)cd9660_readlink },/* readlink */ 1118 { &vop_abortop_desc, (vop_t *)cd9660_abortop }, /* abortop */ 1119 { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ 1120 { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ 1121 { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ 1122 { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ 1123 { &vop_bmap_desc, (vop_t *)cd9660_bmap }, /* bmap */ 1124 { &vop_strategy_desc, (vop_t *)cd9660_strategy },/* strategy */ 1125 { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ 1126 { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ 1127 { &vop_pathconf_desc, (vop_t *)cd9660_pathconf },/* pathconf */ 1128 { &vop_advlock_desc, (vop_t *)cd9660_advlock }, /* advlock */ 1129 { &vop_blkatoff_desc, (vop_t *)cd9660_blkatoff },/* blkatoff */ 1130 { &vop_valloc_desc, (vop_t *)cd9660_valloc }, /* valloc */ 1131 { &vop_vfree_desc, (vop_t *)cd9660_vfree }, /* vfree */ 1132 { &vop_truncate_desc, (vop_t *)cd9660_truncate },/* truncate */ 1133 { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ 1134 { &vop_bwrite_desc, (vop_t *)vn_bwrite }, 1135 { NULL, NULL } 1136 }; 1137 static struct vnodeopv_desc cd9660_vnodeop_opv_desc = 1138 { &cd9660_vnodeop_p, cd9660_vnodeop_entries }; 1139 VNODEOP_SET(cd9660_vnodeop_opv_desc); 1140 1141 /* 1142 * Special device vnode ops 1143 */ 1144 vop_t **cd9660_specop_p; 1145 struct vnodeopv_entry_desc cd9660_specop_entries[] = { 1146 { &vop_default_desc, (vop_t *)vn_default_error }, 1147 { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ 1148 { &vop_create_desc, (vop_t *)spec_create }, /* create */ 1149 { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ 1150 { &vop_open_desc, (vop_t *)spec_open }, /* open */ 1151 { &vop_close_desc, (vop_t *)spec_close }, /* close */ 1152 { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ 1153 { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ 1154 { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ 1155 { &vop_read_desc, (vop_t *)spec_read }, /* read */ 1156 { &vop_write_desc, (vop_t *)spec_write }, /* write */ 1157 { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ 1158 { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ 1159 { &vop_select_desc, (vop_t *)spec_select }, /* select */ 1160 { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ 1161 { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ 1162 { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ 1163 { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ 1164 { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ 1165 { &vop_link_desc, (vop_t *)spec_link }, /* link */ 1166 { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ 1167 { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ 1168 { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ 1169 { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ 1170 { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ 1171 { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ 1172 { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ 1173 { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ 1174 { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ 1175 { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ 1176 { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ 1177 { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ 1178 { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ 1179 { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ 1180 { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ 1181 { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ 1182 { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ 1183 { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ 1184 { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ 1185 { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ 1186 { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ 1187 { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ 1188 { &vop_bwrite_desc, (vop_t *)vn_bwrite }, 1189 { NULL, NULL } 1190 }; 1191 static struct vnodeopv_desc cd9660_specop_opv_desc = 1192 { &cd9660_specop_p, cd9660_specop_entries }; 1193 VNODEOP_SET(cd9660_specop_opv_desc); 1194 1195 vop_t **cd9660_fifoop_p; 1196 struct vnodeopv_entry_desc cd9660_fifoop_entries[] = { 1197 { &vop_default_desc, (vop_t *)vn_default_error }, 1198 { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ 1199 { &vop_create_desc, (vop_t *)fifo_create }, /* create */ 1200 { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ 1201 { &vop_open_desc, (vop_t *)fifo_open }, /* open */ 1202 { &vop_close_desc, (vop_t *)fifo_close }, /* close */ 1203 { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ 1204 { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ 1205 { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ 1206 { &vop_read_desc, (vop_t *)fifo_read }, /* read */ 1207 { &vop_write_desc, (vop_t *)fifo_write }, /* write */ 1208 { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ 1209 { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ 1210 { &vop_select_desc, (vop_t *)fifo_select }, /* select */ 1211 { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ 1212 { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ 1213 { &vop_fsync_desc, (vop_t *)fifo_fsync }, /* fsync */ 1214 { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ 1215 { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ 1216 { &vop_link_desc, (vop_t *)fifo_link } , /* link */ 1217 { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ 1218 { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ 1219 { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ 1220 { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ 1221 { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ 1222 { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ 1223 { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ 1224 { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ 1225 { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ 1226 { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ 1227 { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ 1228 { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ 1229 { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ 1230 { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ 1231 { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ 1232 { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ 1233 { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ 1234 { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ 1235 { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ 1236 { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ 1237 { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ 1238 { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ 1239 { &vop_bwrite_desc, (vop_t *)vn_bwrite }, 1240 { NULL, NULL } 1241 }; 1242 static struct vnodeopv_desc cd9660_fifoop_opv_desc = 1243 { &cd9660_fifoop_p, cd9660_fifoop_entries }; 1244 1245 VNODEOP_SET(cd9660_fifoop_opv_desc); 1246