2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2013 Juniper Networks, Inc.
5 * Copyright (c) 2022-2023 Klara, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
29 #include "opt_tarfs.h"
31 #include <sys/param.h>
32 #include <sys/systm.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>
41 #include <sys/vnode.h>
43 #include <fs/tarfs/tarfs.h>
44 #include <fs/tarfs/tarfs_dbg.h>
47 tarfs_open(struct vop_open_args *ap)
49 struct tarfs_node *tnp;
53 MPASS(VOP_ISLOCKED(vp));
54 tnp = VP_TO_TARFS_NODE(vp);
56 TARFS_DPF(VNODE, "%s(%p=%s, %o)\n", __func__,
57 tnp, tnp->name, ap->a_mode);
59 if (vp->v_type != VREG && vp->v_type != VDIR)
62 vnode_create_vobject(vp, tnp->size, ap->a_td);
67 tarfs_close(struct vop_close_args *ap)
70 struct tarfs_node *tnp;
75 MPASS(VOP_ISLOCKED(vp));
76 tnp = VP_TO_TARFS_NODE(vp);
78 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__,
87 tarfs_access(struct vop_access_args *ap)
89 struct tarfs_node *tnp;
96 accmode = ap->a_accmode;
99 MPASS(VOP_ISLOCKED(vp));
100 tnp = VP_TO_TARFS_NODE(vp);
102 TARFS_DPF(VNODE, "%s(%p=%s, %o)\n", __func__,
103 tnp, tnp->name, accmode);
105 switch (vp->v_type) {
109 if ((accmode & VWRITE) != 0)
120 if ((accmode & VWRITE) != 0)
123 error = vaccess(vp->v_type, tnp->mode, tnp->uid,
124 tnp->gid, accmode, cred);
129 tarfs_getattr(struct vop_getattr_args *ap)
131 struct tarfs_node *tnp;
137 tnp = VP_TO_TARFS_NODE(vp);
139 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__,
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) ?
159 vap->va_bytes = round_page(tnp->physize);
166 tarfs_lookup(struct vop_cachedlookup_args *ap)
168 struct tarfs_mount *tmp;
169 struct tarfs_node *dirnode, *parent, *tnp;
170 struct componentname *cnp;
171 struct vnode *dvp, **vpp;
182 dirnode = VP_TO_TARFS_NODE(dvp);
183 parent = dirnode->parent;
187 TARFS_DPF(LOOKUP, "%s(%p=%s, %.*s)\n", __func__,
188 dirnode, dirnode->name,
189 (int)cnp->cn_namelen, cnp->cn_nameptr);
191 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, curthread);
195 if (cnp->cn_flags & ISDOTDOT) {
196 /* Do not allow .. on the root node */
197 if (parent == NULL || parent == dirnode)
200 /* Allocate a new vnode on the matching entry */
201 error = vn_vget_ino(dvp, parent->ino, cnp->cn_lkflags,
205 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
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);
221 tnp = tarfs_lookup_node(dirnode, NULL, cnp);
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);
229 if ((cnp->cn_flags & ISLASTCN) == 0 &&
230 (tnp->type != VDIR && tnp->type != VLNK))
233 error = VFS_VGET(tmp->vfs, tnp->ino, cnp->cn_lkflags, vpp);
240 tnp = VP_TO_TARFS_NODE(*vpp);
241 TARFS_DPF(LOOKUP, "%s: found vnode %p, tarfs_node %p\n", __func__,
243 #endif /* TARFS_DEBUG */
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);
253 tarfs_readdir(struct vop_readdir_args *ap)
255 struct dirent cde = { };
256 struct tarfs_node *current, *tnp;
268 eofflag = ap->a_eofflag;
269 cookies = ap->a_cookies;
270 ncookies = ap->a_ncookies;
272 if (vp->v_type != VDIR)
275 tnp = VP_TO_TARFS_NODE(vp);
276 off = uio->uio_offset;
280 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__,
281 tnp, tnp->name, uio->uio_offset, uio->uio_resid);
283 if (uio->uio_offset == TARFS_COOKIE_EOF) {
284 TARFS_DPF(VNODE, "%s: EOF\n", __func__);
288 if (uio->uio_offset == TARFS_COOKIE_DOT) {
289 TARFS_DPF(VNODE, "%s: Generating . entry\n", __func__);
291 cde.d_fileno = tnp->ino;
295 cde.d_name[1] = '\0';
296 cde.d_reclen = GENERIC_DIRSIZ(&cde);
297 if (cde.d_reclen > uio->uio_resid)
299 dirent_terminate(&cde);
300 error = uiomove(&cde, cde.d_reclen, uio);
304 uio->uio_offset = TARFS_COOKIE_DOTDOT;
308 if (uio->uio_offset == TARFS_COOKIE_DOTDOT) {
309 TARFS_DPF(VNODE, "%s: Generating .. entry\n", __func__);
311 MPASS(tnp->parent != NULL);
312 TARFS_NODE_LOCK(tnp->parent);
313 cde.d_fileno = tnp->parent->ino;
314 TARFS_NODE_UNLOCK(tnp->parent);
319 cde.d_name[2] = '\0';
320 cde.d_reclen = GENERIC_DIRSIZ(&cde);
321 if (cde.d_reclen > uio->uio_resid)
323 dirent_terminate(&cde);
324 error = uiomove(&cde, cde.d_reclen, uio);
327 /* next is first child */
328 current = TAILQ_FIRST(&tnp->dir.dirhead);
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);
337 /* resuming previous call */
338 if (current == NULL) {
339 current = tarfs_lookup_dir(tnp, uio->uio_offset);
340 if (current == NULL) {
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);
350 cde.d_fileno = current->ino;
351 switch (current->type) {
362 cde.d_type = DT_FIFO;
371 panic("%s: tarfs_node %p, type %d\n", __func__,
372 current, current->type);
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)
381 dirent_terminate(&cde);
382 error = uiomove(&cde, cde.d_reclen, uio);
387 current = TAILQ_NEXT(current, dirents);
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);
395 if (cde.d_reclen > uio->uio_resid) {
396 TARFS_DPF(VNODE, "%s: out of space, returning\n",
398 error = (ndirents == 0) ? EINVAL : 0;
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;
408 tnp->dir.lastcookie = current->ino;
409 tnp->dir.lastnode = current;
412 if (eofflag != NULL) {
413 TARFS_DPF(VNODE, "%s: Setting EOF flag\n", __func__);
414 *eofflag = (error == 0 && current == NULL);
418 if (error == 0 && cookies != NULL && ncookies != NULL) {
419 TARFS_DPF(VNODE, "%s: Updating NFS cookies\n", __func__);
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;
427 if (off == TARFS_COOKIE_DOTDOT) {
428 current = TAILQ_FIRST(&tnp->dir.dirhead);
429 } else if (current != NULL) {
430 current = TAILQ_NEXT(current, dirents);
432 current = tarfs_lookup_dir(tnp, off);
433 current = TAILQ_NEXT(current, dirents);
436 off = TARFS_COOKIE_EOF;
441 TARFS_DPF(VNODE, "%s: [%u] offset %zu\n", __func__,
443 (*cookies)[idx] = off;
445 MPASS(uio->uio_offset == off);
452 tarfs_read(struct vop_read_args *ap)
454 struct tarfs_node *tnp;
464 if (vp->v_type == VCHR || vp->v_type == VBLK)
467 if (vp->v_type != VREG)
470 if (uiop->uio_offset < 0)
473 tnp = VP_TO_TARFS_NODE(vp);
476 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__,
477 tnp, tnp->name, uiop->uio_offset, uiop->uio_resid);
479 while ((resid = uiop->uio_resid) > 0) {
480 if (tnp->size <= uiop->uio_offset)
482 len = MIN(tnp->size - uiop->uio_offset, resid);
486 error = tarfs_read_file(tnp, len, uiop);
487 if (error != 0 || resid == uiop->uio_resid)
495 tarfs_readlink(struct vop_readlink_args *ap)
497 struct tarfs_node *tnp;
505 MPASS(uiop->uio_offset == 0);
506 MPASS(vp->v_type == VLNK);
508 tnp = VP_TO_TARFS_NODE(vp);
510 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__,
513 error = uiomove(tnp->link.name,
514 MIN(tnp->size, uiop->uio_resid), uiop);
520 tarfs_reclaim(struct vop_reclaim_args *ap)
522 struct tarfs_node *tnp;
526 tnp = VP_TO_TARFS_NODE(vp);
530 TARFS_NODE_LOCK(tnp);
533 TARFS_NODE_UNLOCK(tnp);
539 tarfs_print(struct vop_print_args *ap)
541 struct tarfs_node *tnp;
545 tnp = VP_TO_TARFS_NODE(vp);
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,
553 if (vp->v_type == VFIFO)
562 tarfs_strategy(struct vop_strategy_args *ap)
566 struct tarfs_node *tnp;
572 tnp = VP_TO_TARFS_NODE(ap->a_vp);
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;
585 if (off > tnp->size) {
586 /* XXX read beyond EOF - figure out correct handling */
590 if (off + len > tnp->size) {
591 /* clip to file length */
592 len = tnp->size - off;
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;
605 bp->b_ioflags |= BIO_ERROR;
608 bp->b_flags |= B_DONE;
613 tarfs_vptofh(struct vop_vptofh_args *ap)
615 struct tarfs_fid *tfp;
616 struct tarfs_node *tnp;
618 tfp = (struct tarfs_fid *)ap->a_fhp;
619 tnp = VP_TO_TARFS_NODE(ap->a_vp);
621 tfp->len = sizeof(struct tarfs_fid);
628 struct vop_vector tarfs_vnodeops = {
629 .vop_default = &default_vnodeops,
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,
645 VFS_VOP_VECTOR_REGISTER(tarfs_vnodeops);