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