xref: /freebsd/sys/fs/tarfs/tarfs_vnops.c (revision 2a58b312)
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