1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 Juniper Networks, Inc. 5 * Copyright (c) 2022-2023 Klara, Inc. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_tarfs.h" 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bio.h> 34 #include <sys/buf.h> 35 #include <sys/dirent.h> 36 #include <sys/fcntl.h> 37 #include <sys/limits.h> 38 #include <sys/mount.h> 39 #include <sys/namei.h> 40 #include <sys/proc.h> 41 #include <sys/vnode.h> 42 43 #include <fs/tarfs/tarfs.h> 44 #include <fs/tarfs/tarfs_dbg.h> 45 46 static int 47 tarfs_open(struct vop_open_args *ap) 48 { 49 struct tarfs_node *tnp; 50 struct vnode *vp; 51 52 vp = ap->a_vp; 53 MPASS(VOP_ISLOCKED(vp)); 54 tnp = VP_TO_TARFS_NODE(vp); 55 56 TARFS_DPF(VNODE, "%s(%p=%s, %o)\n", __func__, 57 tnp, tnp->name, ap->a_mode); 58 59 if (vp->v_type != VREG && vp->v_type != VDIR) 60 return (EOPNOTSUPP); 61 62 vnode_create_vobject(vp, tnp->size, ap->a_td); 63 return (0); 64 } 65 66 static int 67 tarfs_close(struct vop_close_args *ap) 68 { 69 #ifdef TARFS_DEBUG 70 struct tarfs_node *tnp; 71 struct vnode *vp; 72 73 vp = ap->a_vp; 74 75 MPASS(VOP_ISLOCKED(vp)); 76 tnp = VP_TO_TARFS_NODE(vp); 77 78 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__, 79 tnp, tnp->name); 80 #else 81 (void)ap; 82 #endif 83 return (0); 84 } 85 86 static int 87 tarfs_access(struct vop_access_args *ap) 88 { 89 struct tarfs_node *tnp; 90 struct vnode *vp; 91 accmode_t accmode; 92 struct ucred *cred; 93 int error; 94 95 vp = ap->a_vp; 96 accmode = ap->a_accmode; 97 cred = ap->a_cred; 98 99 MPASS(VOP_ISLOCKED(vp)); 100 tnp = VP_TO_TARFS_NODE(vp); 101 102 TARFS_DPF(VNODE, "%s(%p=%s, %o)\n", __func__, 103 tnp, tnp->name, accmode); 104 105 switch (vp->v_type) { 106 case VDIR: 107 case VLNK: 108 case VREG: 109 if ((accmode & VWRITE) != 0) 110 return (EROFS); 111 break; 112 case VBLK: 113 case VCHR: 114 case VFIFO: 115 break; 116 default: 117 return (EINVAL); 118 } 119 120 if ((accmode & VWRITE) != 0) 121 return (EPERM); 122 123 error = vaccess(vp->v_type, tnp->mode, tnp->uid, 124 tnp->gid, accmode, cred); 125 return (error); 126 } 127 128 static int 129 tarfs_getattr(struct vop_getattr_args *ap) 130 { 131 struct tarfs_node *tnp; 132 struct vnode *vp; 133 struct vattr *vap; 134 135 vp = ap->a_vp; 136 vap = ap->a_vap; 137 tnp = VP_TO_TARFS_NODE(vp); 138 139 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__, 140 tnp, tnp->name); 141 142 vap->va_type = vp->v_type; 143 vap->va_mode = tnp->mode; 144 vap->va_nlink = tnp->nlink; 145 vap->va_gid = tnp->gid; 146 vap->va_uid = tnp->uid; 147 vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 148 vap->va_fileid = tnp->ino; 149 vap->va_size = tnp->size; 150 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; 151 vap->va_atime = tnp->atime; 152 vap->va_ctime = tnp->ctime; 153 vap->va_mtime = tnp->mtime; 154 vap->va_birthtime = tnp->birthtime; 155 vap->va_gen = tnp->gen; 156 vap->va_flags = tnp->flags; 157 vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ? 158 tnp->rdev : NODEV; 159 vap->va_bytes = round_page(tnp->physize); 160 vap->va_filerev = 0; 161 162 return (0); 163 } 164 165 static int 166 tarfs_lookup(struct vop_cachedlookup_args *ap) 167 { 168 struct tarfs_mount *tmp; 169 struct tarfs_node *dirnode, *parent, *tnp; 170 struct componentname *cnp; 171 struct vnode *dvp, **vpp; 172 #ifdef TARFS_DEBUG 173 struct vnode *vp; 174 #endif 175 int error; 176 177 dvp = ap->a_dvp; 178 vpp = ap->a_vpp; 179 cnp = ap->a_cnp; 180 181 *vpp = NULLVP; 182 dirnode = VP_TO_TARFS_NODE(dvp); 183 parent = dirnode->parent; 184 tmp = dirnode->tmp; 185 tnp = NULL; 186 187 TARFS_DPF(LOOKUP, "%s(%p=%s, %.*s)\n", __func__, 188 dirnode, dirnode->name, 189 (int)cnp->cn_namelen, cnp->cn_nameptr); 190 191 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, curthread); 192 if (error != 0) 193 return (error); 194 195 if (cnp->cn_flags & ISDOTDOT) { 196 /* Do not allow .. on the root node */ 197 if (parent == NULL || parent == dirnode) 198 return (ENOENT); 199 200 /* Allocate a new vnode on the matching entry */ 201 error = vn_vget_ino(dvp, parent->ino, cnp->cn_lkflags, 202 vpp); 203 if (error != 0) 204 return (error); 205 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 206 VREF(dvp); 207 *vpp = dvp; 208 #ifdef TARFS_DEBUG 209 } else if (dirnode == dirnode->tmp->root && 210 (vp = dirnode->tmp->znode) != NULL && 211 cnp->cn_namelen == TARFS_ZIO_NAMELEN && 212 memcmp(cnp->cn_nameptr, TARFS_ZIO_NAME, TARFS_ZIO_NAMELEN) == 0) { 213 error = vn_lock(vp, cnp->cn_lkflags); 214 if (error != 0) 215 return (error); 216 vref(vp); 217 *vpp = vp; 218 return (0); 219 #endif 220 } else { 221 tnp = tarfs_lookup_node(dirnode, NULL, cnp); 222 if (tnp == NULL) { 223 TARFS_DPF(LOOKUP, "%s(%p=%s, %.*s): file not found\n", __func__, 224 dirnode, dirnode->name, 225 (int)cnp->cn_namelen, cnp->cn_nameptr); 226 return (ENOENT); 227 } 228 229 if ((cnp->cn_flags & ISLASTCN) == 0 && 230 (tnp->type != VDIR && tnp->type != VLNK)) 231 return (ENOTDIR); 232 233 error = VFS_VGET(tmp->vfs, tnp->ino, cnp->cn_lkflags, vpp); 234 if (error != 0) 235 return (error); 236 } 237 238 #ifdef TARFS_DEBUG 239 if (tnp == NULL) 240 tnp = VP_TO_TARFS_NODE(*vpp); 241 TARFS_DPF(LOOKUP, "%s: found vnode %p, tarfs_node %p\n", __func__, 242 *vpp, tnp); 243 #endif /* TARFS_DEBUG */ 244 245 /* Store the result the the cache if MAKEENTRY is specified in flags */ 246 if ((cnp->cn_flags & MAKEENTRY) != 0 && cnp->cn_nameiop != CREATE) 247 cache_enter(dvp, *vpp, cnp); 248 249 return (error); 250 } 251 252 static int 253 tarfs_readdir(struct vop_readdir_args *ap) 254 { 255 struct dirent cde = { }; 256 struct tarfs_node *current, *tnp; 257 struct vnode *vp; 258 struct uio *uio; 259 int *eofflag; 260 uint64_t **cookies; 261 int *ncookies; 262 off_t off; 263 u_int idx, ndirents; 264 int error; 265 266 vp = ap->a_vp; 267 uio = ap->a_uio; 268 eofflag = ap->a_eofflag; 269 cookies = ap->a_cookies; 270 ncookies = ap->a_ncookies; 271 272 if (vp->v_type != VDIR) 273 return (ENOTDIR); 274 275 tnp = VP_TO_TARFS_NODE(vp); 276 off = uio->uio_offset; 277 current = NULL; 278 ndirents = 0; 279 280 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__, 281 tnp, tnp->name, uio->uio_offset, uio->uio_resid); 282 283 if (uio->uio_offset == TARFS_COOKIE_EOF) { 284 TARFS_DPF(VNODE, "%s: EOF\n", __func__); 285 return (0); 286 } 287 288 if (uio->uio_offset == TARFS_COOKIE_DOT) { 289 TARFS_DPF(VNODE, "%s: Generating . entry\n", __func__); 290 /* fake . entry */ 291 cde.d_fileno = tnp->ino; 292 cde.d_type = DT_DIR; 293 cde.d_namlen = 1; 294 cde.d_name[0] = '.'; 295 cde.d_name[1] = '\0'; 296 cde.d_reclen = GENERIC_DIRSIZ(&cde); 297 if (cde.d_reclen > uio->uio_resid) 298 goto full; 299 dirent_terminate(&cde); 300 error = uiomove(&cde, cde.d_reclen, uio); 301 if (error) 302 return (error); 303 /* next is .. */ 304 uio->uio_offset = TARFS_COOKIE_DOTDOT; 305 ndirents++; 306 } 307 308 if (uio->uio_offset == TARFS_COOKIE_DOTDOT) { 309 TARFS_DPF(VNODE, "%s: Generating .. entry\n", __func__); 310 /* fake .. entry */ 311 MPASS(tnp->parent != NULL); 312 TARFS_NODE_LOCK(tnp->parent); 313 cde.d_fileno = tnp->parent->ino; 314 TARFS_NODE_UNLOCK(tnp->parent); 315 cde.d_type = DT_DIR; 316 cde.d_namlen = 2; 317 cde.d_name[0] = '.'; 318 cde.d_name[1] = '.'; 319 cde.d_name[2] = '\0'; 320 cde.d_reclen = GENERIC_DIRSIZ(&cde); 321 if (cde.d_reclen > uio->uio_resid) 322 goto full; 323 dirent_terminate(&cde); 324 error = uiomove(&cde, cde.d_reclen, uio); 325 if (error) 326 return (error); 327 /* next is first child */ 328 current = TAILQ_FIRST(&tnp->dir.dirhead); 329 if (current == NULL) 330 goto done; 331 uio->uio_offset = current->ino; 332 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n", 333 __func__, ndirents, current, current->name); 334 ndirents++; 335 } 336 337 /* resuming previous call */ 338 if (current == NULL) { 339 current = tarfs_lookup_dir(tnp, uio->uio_offset); 340 if (current == NULL) { 341 error = EINVAL; 342 goto done; 343 } 344 uio->uio_offset = current->ino; 345 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n", 346 __func__, ndirents, current, current->name); 347 } 348 349 for (;;) { 350 cde.d_fileno = current->ino; 351 switch (current->type) { 352 case VBLK: 353 cde.d_type = DT_BLK; 354 break; 355 case VCHR: 356 cde.d_type = DT_CHR; 357 break; 358 case VDIR: 359 cde.d_type = DT_DIR; 360 break; 361 case VFIFO: 362 cde.d_type = DT_FIFO; 363 break; 364 case VLNK: 365 cde.d_type = DT_LNK; 366 break; 367 case VREG: 368 cde.d_type = DT_REG; 369 break; 370 default: 371 panic("%s: tarfs_node %p, type %d\n", __func__, 372 current, current->type); 373 } 374 cde.d_namlen = current->namelen; 375 MPASS(tnp->namelen < sizeof(cde.d_name)); 376 (void)memcpy(cde.d_name, current->name, current->namelen); 377 cde.d_name[current->namelen] = '\0'; 378 cde.d_reclen = GENERIC_DIRSIZ(&cde); 379 if (cde.d_reclen > uio->uio_resid) 380 goto full; 381 dirent_terminate(&cde); 382 error = uiomove(&cde, cde.d_reclen, uio); 383 if (error != 0) 384 goto done; 385 ndirents++; 386 /* next sibling */ 387 current = TAILQ_NEXT(current, dirents); 388 if (current == NULL) 389 goto done; 390 uio->uio_offset = current->ino; 391 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n", 392 __func__, ndirents, current, current->name); 393 } 394 full: 395 if (cde.d_reclen > uio->uio_resid) { 396 TARFS_DPF(VNODE, "%s: out of space, returning\n", 397 __func__); 398 error = (ndirents == 0) ? EINVAL : 0; 399 } 400 done: 401 TARFS_DPF(VNODE, "%s: %u entries written\n", __func__, ndirents); 402 TARFS_DPF(VNODE, "%s: saving cache information\n", __func__); 403 if (current == NULL) { 404 uio->uio_offset = TARFS_COOKIE_EOF; 405 tnp->dir.lastcookie = 0; 406 tnp->dir.lastnode = NULL; 407 } else { 408 tnp->dir.lastcookie = current->ino; 409 tnp->dir.lastnode = current; 410 } 411 412 if (eofflag != NULL) { 413 TARFS_DPF(VNODE, "%s: Setting EOF flag\n", __func__); 414 *eofflag = (error == 0 && current == NULL); 415 } 416 417 /* Update for NFS */ 418 if (error == 0 && cookies != NULL && ncookies != NULL) { 419 TARFS_DPF(VNODE, "%s: Updating NFS cookies\n", __func__); 420 current = NULL; 421 *cookies = malloc(ndirents * sizeof(off_t), M_TEMP, M_WAITOK); 422 *ncookies = ndirents; 423 for (idx = 0; idx < ndirents; idx++) { 424 if (off == TARFS_COOKIE_DOT) 425 off = TARFS_COOKIE_DOTDOT; 426 else { 427 if (off == TARFS_COOKIE_DOTDOT) { 428 current = TAILQ_FIRST(&tnp->dir.dirhead); 429 } else if (current != NULL) { 430 current = TAILQ_NEXT(current, dirents); 431 } else { 432 current = tarfs_lookup_dir(tnp, off); 433 current = TAILQ_NEXT(current, dirents); 434 } 435 if (current == NULL) 436 off = TARFS_COOKIE_EOF; 437 else 438 off = current->ino; 439 } 440 441 TARFS_DPF(VNODE, "%s: [%u] offset %zu\n", __func__, 442 idx, off); 443 (*cookies)[idx] = off; 444 } 445 MPASS(uio->uio_offset == off); 446 } 447 448 return (error); 449 } 450 451 static int 452 tarfs_read(struct vop_read_args *ap) 453 { 454 struct tarfs_node *tnp; 455 struct uio *uiop; 456 struct vnode *vp; 457 size_t len; 458 off_t resid; 459 int error; 460 461 uiop = ap->a_uio; 462 vp = ap->a_vp; 463 464 if (vp->v_type == VCHR || vp->v_type == VBLK) 465 return (EOPNOTSUPP); 466 467 if (vp->v_type != VREG) 468 return (EISDIR); 469 470 if (uiop->uio_offset < 0) 471 return (EINVAL); 472 473 tnp = VP_TO_TARFS_NODE(vp); 474 error = 0; 475 476 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__, 477 tnp, tnp->name, uiop->uio_offset, uiop->uio_resid); 478 479 while ((resid = uiop->uio_resid) > 0) { 480 if (tnp->size <= uiop->uio_offset) 481 break; 482 len = MIN(tnp->size - uiop->uio_offset, resid); 483 if (len == 0) 484 break; 485 486 error = tarfs_read_file(tnp, len, uiop); 487 if (error != 0 || resid == uiop->uio_resid) 488 break; 489 } 490 491 return (error); 492 } 493 494 static int 495 tarfs_readlink(struct vop_readlink_args *ap) 496 { 497 struct tarfs_node *tnp; 498 struct uio *uiop; 499 struct vnode *vp; 500 int error; 501 502 uiop = ap->a_uio; 503 vp = ap->a_vp; 504 505 MPASS(uiop->uio_offset == 0); 506 MPASS(vp->v_type == VLNK); 507 508 tnp = VP_TO_TARFS_NODE(vp); 509 510 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__, 511 tnp, tnp->name); 512 513 error = uiomove(tnp->link.name, 514 MIN(tnp->size, uiop->uio_resid), uiop); 515 516 return (error); 517 } 518 519 static int 520 tarfs_reclaim(struct vop_reclaim_args *ap) 521 { 522 struct tarfs_node *tnp; 523 struct vnode *vp; 524 525 vp = ap->a_vp; 526 tnp = VP_TO_TARFS_NODE(vp); 527 528 vfs_hash_remove(vp); 529 530 TARFS_NODE_LOCK(tnp); 531 tnp->vnode = NULLVP; 532 vp->v_data = NULL; 533 TARFS_NODE_UNLOCK(tnp); 534 535 return (0); 536 } 537 538 static int 539 tarfs_print(struct vop_print_args *ap) 540 { 541 struct tarfs_node *tnp; 542 struct vnode *vp; 543 544 vp = ap->a_vp; 545 tnp = VP_TO_TARFS_NODE(vp); 546 547 printf("tag tarfs, tarfs_node %p, links %lu\n", 548 tnp, (unsigned long)tnp->nlink); 549 printf("\tmode 0%o, owner %d, group %d, size %zd\n", 550 tnp->mode, tnp->uid, tnp->gid, 551 tnp->size); 552 553 if (vp->v_type == VFIFO) 554 fifo_printinfo(vp); 555 556 printf("\n"); 557 558 return (0); 559 } 560 561 static int 562 tarfs_strategy(struct vop_strategy_args *ap) 563 { 564 struct uio auio; 565 struct iovec iov; 566 struct tarfs_node *tnp; 567 struct buf *bp; 568 off_t off; 569 size_t len; 570 int error; 571 572 tnp = VP_TO_TARFS_NODE(ap->a_vp); 573 bp = ap->a_bp; 574 MPASS(bp->b_iocmd == BIO_READ); 575 MPASS(bp->b_iooffset >= 0); 576 MPASS(bp->b_bcount > 0); 577 MPASS(bp->b_bufsize >= bp->b_bcount); 578 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %ld/%ld)\n", __func__, tnp, 579 tnp->name, (size_t)bp->b_iooffset, bp->b_bcount, bp->b_bufsize); 580 iov.iov_base = bp->b_data; 581 iov.iov_len = bp->b_bcount; 582 off = bp->b_iooffset; 583 len = bp->b_bcount; 584 bp->b_resid = len; 585 if (off > tnp->size) { 586 /* XXX read beyond EOF - figure out correct handling */ 587 error = EIO; 588 goto out; 589 } 590 if (off + len > tnp->size) { 591 /* clip to file length */ 592 len = tnp->size - off; 593 } 594 auio.uio_iov = &iov; 595 auio.uio_iovcnt = 1; 596 auio.uio_offset = off; 597 auio.uio_resid = len; 598 auio.uio_segflg = UIO_SYSSPACE; 599 auio.uio_rw = UIO_READ; 600 auio.uio_td = curthread; 601 error = tarfs_read_file(tnp, len, &auio); 602 bp->b_resid -= len - auio.uio_resid; 603 out: 604 if (error != 0) { 605 bp->b_ioflags |= BIO_ERROR; 606 bp->b_error = error; 607 } 608 bp->b_flags |= B_DONE; 609 return (0); 610 } 611 612 static int 613 tarfs_vptofh(struct vop_vptofh_args *ap) 614 { 615 struct tarfs_fid *tfp; 616 struct tarfs_node *tnp; 617 618 tfp = (struct tarfs_fid *)ap->a_fhp; 619 tnp = VP_TO_TARFS_NODE(ap->a_vp); 620 621 tfp->len = sizeof(struct tarfs_fid); 622 tfp->ino = tnp->ino; 623 tfp->gen = tnp->gen; 624 625 return (0); 626 } 627 628 struct vop_vector tarfs_vnodeops = { 629 .vop_default = &default_vnodeops, 630 631 .vop_access = tarfs_access, 632 .vop_cachedlookup = tarfs_lookup, 633 .vop_close = tarfs_close, 634 .vop_getattr = tarfs_getattr, 635 .vop_lookup = vfs_cache_lookup, 636 .vop_open = tarfs_open, 637 .vop_print = tarfs_print, 638 .vop_read = tarfs_read, 639 .vop_readdir = tarfs_readdir, 640 .vop_readlink = tarfs_readlink, 641 .vop_reclaim = tarfs_reclaim, 642 .vop_strategy = tarfs_strategy, 643 .vop_vptofh = tarfs_vptofh, 644 }; 645 VFS_VOP_VECTOR_REGISTER(tarfs_vnodeops); 646