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
tarfs_open(struct vop_open_args * ap)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
tarfs_close(struct vop_close_args * ap)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
tarfs_access(struct vop_access_args * ap)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
tarfs_bmap(struct vop_bmap_args * ap)129 tarfs_bmap(struct vop_bmap_args *ap)
130 {
131 struct tarfs_node *tnp;
132 struct vnode *vp;
133 off_t off;
134 uint64_t iosize;
135 int ra, rb, rmax;
136
137 vp = ap->a_vp;
138 iosize = vp->v_mount->mnt_stat.f_iosize;
139
140 if (ap->a_bop != NULL)
141 *ap->a_bop = &vp->v_bufobj;
142 if (ap->a_bnp != NULL)
143 *ap->a_bnp = ap->a_bn * btodb(iosize);
144 if (ap->a_runp == NULL)
145 return (0);
146
147 tnp = VP_TO_TARFS_NODE(vp);
148 off = ap->a_bn * iosize;
149
150 ra = rb = 0;
151 for (u_int i = 0; i < tnp->nblk; i++) {
152 off_t bs, be;
153
154 bs = tnp->blk[i].o;
155 be = tnp->blk[i].o + tnp->blk[i].l;
156 if (off > be)
157 continue;
158 else if (off < bs) {
159 /* We're in a hole. */
160 ra = bs - off < iosize ?
161 0 : howmany(bs - (off + iosize), iosize);
162 rb = howmany(off - (i == 0 ?
163 0 : tnp->blk[i - 1].o + tnp->blk[i - 1].l),
164 iosize);
165 break;
166 } else {
167 /* We'll be reading from the backing file. */
168 ra = be - off < iosize ?
169 0 : howmany(be - (off + iosize), iosize);
170 rb = howmany(off - bs, iosize);
171 break;
172 }
173 }
174
175 rmax = vp->v_mount->mnt_iosize_max / iosize - 1;
176 *ap->a_runp = imin(ra, rmax);
177 if (ap->a_runb != NULL)
178 *ap->a_runb = imin(rb, rmax);
179 return (0);
180 }
181
182 static int
tarfs_getattr(struct vop_getattr_args * ap)183 tarfs_getattr(struct vop_getattr_args *ap)
184 {
185 struct tarfs_node *tnp;
186 struct vnode *vp;
187 struct vattr *vap;
188
189 vp = ap->a_vp;
190 vap = ap->a_vap;
191 tnp = VP_TO_TARFS_NODE(vp);
192
193 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__,
194 tnp, tnp->name);
195
196 vap->va_type = vp->v_type;
197 vap->va_mode = tnp->mode;
198 vap->va_nlink = tnp->nlink;
199 vap->va_gid = tnp->gid;
200 vap->va_uid = tnp->uid;
201 vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
202 vap->va_fileid = tnp->ino;
203 vap->va_size = tnp->size;
204 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
205 vap->va_atime = tnp->atime;
206 vap->va_ctime = tnp->ctime;
207 vap->va_mtime = tnp->mtime;
208 vap->va_birthtime = tnp->birthtime;
209 vap->va_gen = tnp->gen;
210 vap->va_flags = tnp->flags;
211 vap->va_rdev = VN_ISDEV(vp) ? tnp->rdev : NODEV;
212 vap->va_bytes = round_page(tnp->physize);
213 vap->va_filerev = 0;
214
215 return (0);
216 }
217
218 static int
tarfs_lookup(struct vop_cachedlookup_args * ap)219 tarfs_lookup(struct vop_cachedlookup_args *ap)
220 {
221 struct tarfs_mount *tmp;
222 struct tarfs_node *dirnode, *parent, *tnp;
223 struct componentname *cnp;
224 struct vnode *dvp, **vpp;
225 #ifdef TARFS_DEBUG
226 struct vnode *vp;
227 #endif
228 int error;
229
230 dvp = ap->a_dvp;
231 vpp = ap->a_vpp;
232 cnp = ap->a_cnp;
233
234 *vpp = NULLVP;
235 dirnode = VP_TO_TARFS_NODE(dvp);
236 parent = dirnode->parent;
237 tmp = dirnode->tmp;
238 tnp = NULL;
239
240 TARFS_DPF(LOOKUP, "%s(%p=%s, %.*s)\n", __func__,
241 dirnode, dirnode->name,
242 (int)cnp->cn_namelen, cnp->cn_nameptr);
243
244 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, curthread);
245 if (error != 0)
246 return (error);
247
248 if (cnp->cn_flags & ISDOTDOT) {
249 /* Do not allow .. on the root node */
250 if (parent == NULL || parent == dirnode)
251 return (ENOENT);
252
253 /* Allocate a new vnode on the matching entry */
254 error = vn_vget_ino(dvp, parent->ino, cnp->cn_lkflags,
255 vpp);
256 if (error != 0)
257 return (error);
258 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
259 VREF(dvp);
260 *vpp = dvp;
261 #ifdef TARFS_DEBUG
262 } else if (dirnode == dirnode->tmp->root &&
263 (vp = dirnode->tmp->znode) != NULL &&
264 cnp->cn_namelen == TARFS_ZIO_NAMELEN &&
265 memcmp(cnp->cn_nameptr, TARFS_ZIO_NAME, TARFS_ZIO_NAMELEN) == 0) {
266 error = vn_lock(vp, cnp->cn_lkflags);
267 if (error != 0)
268 return (error);
269 vref(vp);
270 *vpp = vp;
271 return (0);
272 #endif
273 } else {
274 tnp = tarfs_lookup_node(dirnode, NULL, cnp);
275 if (tnp == NULL) {
276 TARFS_DPF(LOOKUP, "%s(%p=%s, %.*s): file not found\n", __func__,
277 dirnode, dirnode->name,
278 (int)cnp->cn_namelen, cnp->cn_nameptr);
279 return (ENOENT);
280 }
281
282 if ((cnp->cn_flags & ISLASTCN) == 0 &&
283 (tnp->type != VDIR && tnp->type != VLNK))
284 return (ENOTDIR);
285
286 error = VFS_VGET(tmp->vfs, tnp->ino, cnp->cn_lkflags, vpp);
287 if (error != 0)
288 return (error);
289 }
290
291 #ifdef TARFS_DEBUG
292 if (tnp == NULL)
293 tnp = VP_TO_TARFS_NODE(*vpp);
294 TARFS_DPF(LOOKUP, "%s: found vnode %p, tarfs_node %p\n", __func__,
295 *vpp, tnp);
296 #endif /* TARFS_DEBUG */
297
298 /* Store the result of the cache if MAKEENTRY is specified in flags */
299 if ((cnp->cn_flags & MAKEENTRY) != 0 && cnp->cn_nameiop != CREATE)
300 cache_enter(dvp, *vpp, cnp);
301
302 return (error);
303 }
304
305 static int
tarfs_readdir(struct vop_readdir_args * ap)306 tarfs_readdir(struct vop_readdir_args *ap)
307 {
308 struct dirent cde = { };
309 struct tarfs_node *current, *tnp;
310 struct vnode *vp;
311 struct uio *uio;
312 int *eofflag;
313 uint64_t **cookies;
314 int *ncookies;
315 off_t off;
316 u_int idx, ndirents;
317 int error;
318
319 vp = ap->a_vp;
320 uio = ap->a_uio;
321 eofflag = ap->a_eofflag;
322 cookies = ap->a_cookies;
323 ncookies = ap->a_ncookies;
324
325 if (vp->v_type != VDIR)
326 return (ENOTDIR);
327
328 tnp = VP_TO_TARFS_NODE(vp);
329 off = uio->uio_offset;
330 current = NULL;
331 ndirents = 0;
332
333 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__,
334 tnp, tnp->name, uio->uio_offset, uio->uio_resid);
335
336 if (uio->uio_offset == TARFS_COOKIE_EOF) {
337 if (eofflag != NULL) {
338 TARFS_DPF(VNODE, "%s: Setting EOF flag\n", __func__);
339 *eofflag = 1;
340 }
341 TARFS_DPF(VNODE, "%s: EOF\n", __func__);
342 return (0);
343 }
344
345 if (uio->uio_offset == TARFS_COOKIE_DOT) {
346 TARFS_DPF(VNODE, "%s: Generating . entry\n", __func__);
347 /* fake . entry */
348 cde.d_fileno = tnp->ino;
349 cde.d_type = DT_DIR;
350 cde.d_namlen = 1;
351 cde.d_name[0] = '.';
352 cde.d_name[1] = '\0';
353 cde.d_reclen = GENERIC_DIRSIZ(&cde);
354 if (cde.d_reclen > uio->uio_resid)
355 goto full;
356 dirent_terminate(&cde);
357 error = uiomove(&cde, cde.d_reclen, uio);
358 if (error)
359 return (error);
360 /* next is .. */
361 uio->uio_offset = TARFS_COOKIE_DOTDOT;
362 ndirents++;
363 }
364
365 if (uio->uio_offset == TARFS_COOKIE_DOTDOT) {
366 TARFS_DPF(VNODE, "%s: Generating .. entry\n", __func__);
367 /* fake .. entry */
368 MPASS(tnp->parent != NULL);
369 TARFS_NODE_LOCK(tnp->parent);
370 cde.d_fileno = tnp->parent->ino;
371 TARFS_NODE_UNLOCK(tnp->parent);
372 cde.d_type = DT_DIR;
373 cde.d_namlen = 2;
374 cde.d_name[0] = '.';
375 cde.d_name[1] = '.';
376 cde.d_name[2] = '\0';
377 cde.d_reclen = GENERIC_DIRSIZ(&cde);
378 if (cde.d_reclen > uio->uio_resid)
379 goto full;
380 dirent_terminate(&cde);
381 error = uiomove(&cde, cde.d_reclen, uio);
382 if (error)
383 return (error);
384 /* next is first child */
385 current = TAILQ_FIRST(&tnp->dir.dirhead);
386 if (current == NULL)
387 goto done;
388 uio->uio_offset = current->ino;
389 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n",
390 __func__, ndirents, current, current->name);
391 ndirents++;
392 }
393
394 /* resuming previous call */
395 if (current == NULL) {
396 current = tarfs_lookup_dir(tnp, uio->uio_offset);
397 if (current == NULL) {
398 error = EINVAL;
399 goto done;
400 }
401 uio->uio_offset = current->ino;
402 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n",
403 __func__, ndirents, current, current->name);
404 }
405
406 for (;;) {
407 cde.d_fileno = current->ino;
408 switch (current->type) {
409 case VBLK:
410 cde.d_type = DT_BLK;
411 break;
412 case VCHR:
413 cde.d_type = DT_CHR;
414 break;
415 case VDIR:
416 cde.d_type = DT_DIR;
417 break;
418 case VFIFO:
419 cde.d_type = DT_FIFO;
420 break;
421 case VLNK:
422 cde.d_type = DT_LNK;
423 break;
424 case VREG:
425 cde.d_type = DT_REG;
426 break;
427 default:
428 panic("%s: tarfs_node %p, type %d\n", __func__,
429 current, current->type);
430 }
431 cde.d_namlen = current->namelen;
432 MPASS(tnp->namelen < sizeof(cde.d_name));
433 (void)memcpy(cde.d_name, current->name, current->namelen);
434 cde.d_name[current->namelen] = '\0';
435 cde.d_reclen = GENERIC_DIRSIZ(&cde);
436 if (cde.d_reclen > uio->uio_resid)
437 goto full;
438 dirent_terminate(&cde);
439 error = uiomove(&cde, cde.d_reclen, uio);
440 if (error != 0)
441 goto done;
442 ndirents++;
443 /* next sibling */
444 current = TAILQ_NEXT(current, dirents);
445 if (current == NULL)
446 goto done;
447 uio->uio_offset = current->ino;
448 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n",
449 __func__, ndirents, current, current->name);
450 }
451 full:
452 if (cde.d_reclen > uio->uio_resid) {
453 TARFS_DPF(VNODE, "%s: out of space, returning\n",
454 __func__);
455 error = (ndirents == 0) ? EINVAL : 0;
456 }
457 done:
458 TARFS_DPF(VNODE, "%s: %u entries written\n", __func__, ndirents);
459 TARFS_DPF(VNODE, "%s: saving cache information\n", __func__);
460 if (current == NULL) {
461 uio->uio_offset = TARFS_COOKIE_EOF;
462 tnp->dir.lastcookie = 0;
463 tnp->dir.lastnode = NULL;
464 } else {
465 tnp->dir.lastcookie = current->ino;
466 tnp->dir.lastnode = current;
467 }
468
469 if (eofflag != NULL) {
470 TARFS_DPF(VNODE, "%s: Setting EOF flag\n", __func__);
471 *eofflag = (error == 0 && current == NULL);
472 }
473
474 /* Update for NFS */
475 if (error == 0 && cookies != NULL && ncookies != NULL) {
476 TARFS_DPF(VNODE, "%s: Updating NFS cookies\n", __func__);
477 current = NULL;
478 *cookies = malloc(ndirents * sizeof(off_t), M_TEMP, M_WAITOK);
479 *ncookies = ndirents;
480 for (idx = 0; idx < ndirents; idx++) {
481 if (off == TARFS_COOKIE_DOT)
482 off = TARFS_COOKIE_DOTDOT;
483 else {
484 if (off == TARFS_COOKIE_DOTDOT) {
485 current = TAILQ_FIRST(&tnp->dir.dirhead);
486 } else if (current != NULL) {
487 current = TAILQ_NEXT(current, dirents);
488 } else {
489 current = tarfs_lookup_dir(tnp, off);
490 current = TAILQ_NEXT(current, dirents);
491 }
492 if (current == NULL)
493 off = TARFS_COOKIE_EOF;
494 else
495 off = current->ino;
496 }
497
498 TARFS_DPF(VNODE, "%s: [%u] offset %zu\n", __func__,
499 idx, off);
500 (*cookies)[idx] = off;
501 }
502 MPASS(uio->uio_offset == off);
503 }
504
505 return (error);
506 }
507
508 static int
tarfs_read(struct vop_read_args * ap)509 tarfs_read(struct vop_read_args *ap)
510 {
511 struct tarfs_node *tnp;
512 struct uio *uiop;
513 struct vnode *vp;
514 size_t len;
515 off_t resid;
516 int error;
517
518 uiop = ap->a_uio;
519 vp = ap->a_vp;
520
521 if (VN_ISDEV(vp))
522 return (EOPNOTSUPP);
523
524 if (vp->v_type != VREG)
525 return (EISDIR);
526
527 if (uiop->uio_offset < 0)
528 return (EINVAL);
529
530 tnp = VP_TO_TARFS_NODE(vp);
531 error = 0;
532
533 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__,
534 tnp, tnp->name, uiop->uio_offset, uiop->uio_resid);
535
536 while ((resid = uiop->uio_resid) > 0) {
537 if (tnp->size <= uiop->uio_offset)
538 break;
539 len = MIN(tnp->size - uiop->uio_offset, resid);
540 if (len == 0)
541 break;
542
543 error = tarfs_read_file(tnp, len, uiop);
544 if (error != 0 || resid == uiop->uio_resid)
545 break;
546 }
547
548 return (error);
549 }
550
551 static int
tarfs_readlink(struct vop_readlink_args * ap)552 tarfs_readlink(struct vop_readlink_args *ap)
553 {
554 struct tarfs_node *tnp;
555 struct uio *uiop;
556 struct vnode *vp;
557 int error;
558
559 uiop = ap->a_uio;
560 vp = ap->a_vp;
561
562 MPASS(uiop->uio_offset == 0);
563 MPASS(vp->v_type == VLNK);
564
565 tnp = VP_TO_TARFS_NODE(vp);
566
567 TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__,
568 tnp, tnp->name);
569
570 error = uiomove(tnp->link.name,
571 MIN(tnp->size, uiop->uio_resid), uiop);
572
573 return (error);
574 }
575
576 static int
tarfs_reclaim(struct vop_reclaim_args * ap)577 tarfs_reclaim(struct vop_reclaim_args *ap)
578 {
579 struct tarfs_node *tnp;
580 struct vnode *vp;
581
582 vp = ap->a_vp;
583 tnp = VP_TO_TARFS_NODE(vp);
584
585 vfs_hash_remove(vp);
586
587 TARFS_NODE_LOCK(tnp);
588 tnp->vnode = NULLVP;
589 vp->v_data = NULL;
590 TARFS_NODE_UNLOCK(tnp);
591
592 return (0);
593 }
594
595 static int
tarfs_print(struct vop_print_args * ap)596 tarfs_print(struct vop_print_args *ap)
597 {
598 struct tarfs_node *tnp;
599 struct vnode *vp;
600
601 vp = ap->a_vp;
602 tnp = VP_TO_TARFS_NODE(vp);
603
604 printf("tag tarfs, tarfs_node %p, links %lu\n",
605 tnp, (unsigned long)tnp->nlink);
606 printf("\tmode 0%o, owner %d, group %d, size %zd\n",
607 tnp->mode, tnp->uid, tnp->gid,
608 tnp->size);
609
610 if (vp->v_type == VFIFO)
611 fifo_printinfo(vp);
612
613 printf("\n");
614
615 return (0);
616 }
617
618 static int
tarfs_strategy(struct vop_strategy_args * ap)619 tarfs_strategy(struct vop_strategy_args *ap)
620 {
621 struct uio auio;
622 struct iovec iov;
623 struct tarfs_node *tnp;
624 struct buf *bp;
625 off_t off;
626 size_t len;
627 int error;
628
629 tnp = VP_TO_TARFS_NODE(ap->a_vp);
630 bp = ap->a_bp;
631 MPASS(bp->b_iocmd == BIO_READ);
632 MPASS(bp->b_iooffset >= 0);
633 MPASS(bp->b_bcount > 0);
634 MPASS(bp->b_bufsize >= bp->b_bcount);
635 TARFS_DPF(VNODE, "%s(%p=%s, %zu, %ld/%ld)\n", __func__, tnp,
636 tnp->name, (size_t)bp->b_iooffset, bp->b_bcount, bp->b_bufsize);
637 iov.iov_base = bp->b_data;
638 iov.iov_len = bp->b_bcount;
639 off = bp->b_iooffset;
640 len = bp->b_bcount;
641 bp->b_resid = len;
642 if (off > tnp->size) {
643 /* XXX read beyond EOF - figure out correct handling */
644 error = EIO;
645 goto out;
646 }
647 if (off + len > tnp->size) {
648 /* clip to file length */
649 len = tnp->size - off;
650 }
651 auio.uio_iov = &iov;
652 auio.uio_iovcnt = 1;
653 auio.uio_offset = off;
654 auio.uio_resid = len;
655 auio.uio_segflg = UIO_SYSSPACE;
656 auio.uio_rw = UIO_READ;
657 auio.uio_td = curthread;
658 error = tarfs_read_file(tnp, len, &auio);
659 bp->b_resid -= len - auio.uio_resid;
660 out:
661 if (error != 0) {
662 bp->b_ioflags |= BIO_ERROR;
663 bp->b_error = error;
664 }
665 bp->b_flags |= B_DONE;
666 return (0);
667 }
668
669 static int
tarfs_vptofh(struct vop_vptofh_args * ap)670 tarfs_vptofh(struct vop_vptofh_args *ap)
671 {
672 struct tarfs_fid *tfp;
673 struct tarfs_node *tnp;
674 _Static_assert(sizeof(struct tarfs_fid) <= sizeof(struct fid),
675 "struct tarfs_fid cannot be larger than struct fid");
676
677 tfp = (struct tarfs_fid *)ap->a_fhp;
678 tnp = VP_TO_TARFS_NODE(ap->a_vp);
679
680 tfp->len = sizeof(struct tarfs_fid);
681 tfp->ino = tnp->ino;
682 tfp->gen = tnp->gen;
683
684 return (0);
685 }
686
687 struct vop_vector tarfs_vnodeops = {
688 .vop_default = &default_vnodeops,
689
690 .vop_access = tarfs_access,
691 .vop_bmap = tarfs_bmap,
692 .vop_cachedlookup = tarfs_lookup,
693 .vop_close = tarfs_close,
694 .vop_getattr = tarfs_getattr,
695 .vop_lookup = vfs_cache_lookup,
696 .vop_open = tarfs_open,
697 .vop_print = tarfs_print,
698 .vop_read = tarfs_read,
699 .vop_readdir = tarfs_readdir,
700 .vop_readlink = tarfs_readlink,
701 .vop_reclaim = tarfs_reclaim,
702 .vop_strategy = tarfs_strategy,
703 .vop_vptofh = tarfs_vptofh,
704 };
705 VFS_VOP_VECTOR_REGISTER(tarfs_vnodeops);
706