Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/tarfs/tarfs_vnops.c
39586 views
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_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
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
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 = NULL;
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
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
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
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
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 = NULL;
589
vp->v_data = NULL;
590
TARFS_NODE_UNLOCK(tnp);
591
592
return (0);
593
}
594
595
static int
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
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
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
707