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