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