]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/tarfs/tarfs_vnops.c
Merge llvm-project main llvmorg-15-init-17826-g1f8ae9d7e7e4
[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                 dirent_terminate(&cde);
298                 error = uiomove(&cde, cde.d_reclen, uio);
299                 if (error)
300                         return (error);
301                 /* next is .. */
302                 uio->uio_offset = TARFS_COOKIE_DOTDOT;
303                 ndirents++;
304         }
305
306         if (uio->uio_offset == TARFS_COOKIE_DOTDOT) {
307                 TARFS_DPF(VNODE, "%s: Generating .. entry\n", __func__);
308                 /* fake .. entry */
309                 MPASS(tnp->parent != NULL);
310                 TARFS_NODE_LOCK(tnp->parent);
311                 cde.d_fileno = tnp->parent->ino;
312                 TARFS_NODE_UNLOCK(tnp->parent);
313                 cde.d_type = DT_DIR;
314                 cde.d_namlen = 2;
315                 cde.d_name[0] = '.';
316                 cde.d_name[1] = '.';
317                 cde.d_name[2] = '\0';
318                 cde.d_reclen = GENERIC_DIRSIZ(&cde);
319                 if (cde.d_reclen > uio->uio_resid)
320                         goto full;
321                 dirent_terminate(&cde);
322                 error = uiomove(&cde, cde.d_reclen, uio);
323                 if (error)
324                         return (error);
325                 /* next is first child */
326                 current = TAILQ_FIRST(&tnp->dir.dirhead);
327                 if (current == NULL)
328                         goto done;
329                 uio->uio_offset = current->ino;
330                 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n",
331                     __func__, ndirents, current, current->name);
332                 ndirents++;
333         }
334
335         /* resuming previous call */
336         if (current == NULL) {
337                 current = tarfs_lookup_dir(tnp, uio->uio_offset);
338                 if (current == NULL) {
339                         error = EINVAL;
340                         goto done;
341                 }
342                 uio->uio_offset = current->ino;
343                 TARFS_DPF(VNODE, "%s: [%u] setting current node to %p=%s\n",
344                     __func__, ndirents, current, current->name);
345         }
346
347         for (;;) {
348                 cde.d_fileno = current->ino;
349                 switch (current->type) {
350                 case VBLK:
351                         cde.d_type = DT_BLK;
352                         break;
353                 case VCHR:
354                         cde.d_type = DT_CHR;
355                         break;
356                 case VDIR:
357                         cde.d_type = DT_DIR;
358                         break;
359                 case VFIFO:
360                         cde.d_type = DT_FIFO;
361                         break;
362                 case VLNK:
363                         cde.d_type = DT_LNK;
364                         break;
365                 case VREG:
366                         cde.d_type = DT_REG;
367                         break;
368                 default:
369                         panic("%s: tarfs_node %p, type %d\n", __func__,
370                             current, current->type);
371                 }
372                 cde.d_namlen = current->namelen;
373                 MPASS(tnp->namelen < sizeof(cde.d_name));
374                 (void)memcpy(cde.d_name, current->name, current->namelen);
375                 cde.d_name[current->namelen] = '\0';
376                 cde.d_reclen = GENERIC_DIRSIZ(&cde);
377                 if (cde.d_reclen > uio->uio_resid)
378                         goto full;
379                 dirent_terminate(&cde);
380                 error = uiomove(&cde, cde.d_reclen, uio);
381                 if (error != 0)
382                         goto done;
383                 ndirents++;
384                 /* next sibling */
385                 current = TAILQ_NEXT(current, dirents);
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         }
392 full:
393         if (cde.d_reclen > uio->uio_resid) {
394                 TARFS_DPF(VNODE, "%s: out of space, returning\n",
395                     __func__);
396                 error = (ndirents == 0) ? EINVAL : 0;
397         }
398 done:
399         TARFS_DPF(VNODE, "%s: %u entries written\n", __func__, ndirents);
400         TARFS_DPF(VNODE, "%s: saving cache information\n", __func__);
401         if (current == NULL) {
402                 uio->uio_offset = TARFS_COOKIE_EOF;
403                 tnp->dir.lastcookie = 0;
404                 tnp->dir.lastnode = NULL;
405         } else {
406                 tnp->dir.lastcookie = current->ino;
407                 tnp->dir.lastnode = current;
408         }
409
410         if (eofflag != NULL) {
411                 TARFS_DPF(VNODE, "%s: Setting EOF flag\n", __func__);
412                 *eofflag = (error == 0 && current == NULL);
413         }
414
415         /* Update for NFS */
416         if (error == 0 && cookies != NULL && ncookies != NULL) {
417                 TARFS_DPF(VNODE, "%s: Updating NFS cookies\n", __func__);
418                 current = NULL;
419                 *cookies = malloc(ndirents * sizeof(off_t), M_TEMP, M_WAITOK);
420                 *ncookies = ndirents;
421                 for (idx = 0; idx < ndirents; idx++) {
422                         if (off == TARFS_COOKIE_DOT)
423                                 off = TARFS_COOKIE_DOTDOT;
424                         else {
425                                 if (off == TARFS_COOKIE_DOTDOT) {
426                                         current = TAILQ_FIRST(&tnp->dir.dirhead);
427                                 } else if (current != NULL) {
428                                         current = TAILQ_NEXT(current, dirents);
429                                 } else {
430                                         current = tarfs_lookup_dir(tnp, off);
431                                         current = TAILQ_NEXT(current, dirents);
432                                 }
433                                 if (current == NULL)
434                                         off = TARFS_COOKIE_EOF;
435                                 else
436                                         off = current->ino;
437                         }
438
439                         TARFS_DPF(VNODE, "%s: [%u] offset %zu\n", __func__,
440                             idx, off);
441                         (*cookies)[idx] = off;
442                 }
443                 MPASS(uio->uio_offset == off);
444         }
445
446         return (error);
447 }
448
449 static int
450 tarfs_read(struct vop_read_args *ap)
451 {
452         struct tarfs_node *tnp;
453         struct uio *uiop;
454         struct vnode *vp;
455         size_t len;
456         off_t resid;
457         int error;
458
459         uiop = ap->a_uio;
460         vp = ap->a_vp;
461
462         if (vp->v_type == VCHR || vp->v_type == VBLK)
463                 return (EOPNOTSUPP);
464
465         if (vp->v_type != VREG)
466                 return (EISDIR);
467
468         if (uiop->uio_offset < 0)
469                 return (EINVAL);
470
471         tnp = VP_TO_TARFS_NODE(vp);
472         error = 0;
473
474         TARFS_DPF(VNODE, "%s(%p=%s, %zu, %zd)\n", __func__,
475             tnp, tnp->name, uiop->uio_offset, uiop->uio_resid);
476
477         while ((resid = uiop->uio_resid) > 0) {
478                 if (tnp->size <= uiop->uio_offset)
479                         break;
480                 len = MIN(tnp->size - uiop->uio_offset, resid);
481                 if (len == 0)
482                         break;
483
484                 error = tarfs_read_file(tnp, len, uiop);
485                 if (error != 0 || resid == uiop->uio_resid)
486                         break;
487         }
488
489         return (error);
490 }
491
492 static int
493 tarfs_readlink(struct vop_readlink_args *ap)
494 {
495         struct tarfs_node *tnp;
496         struct uio *uiop;
497         struct vnode *vp;
498         int error;
499
500         uiop = ap->a_uio;
501         vp = ap->a_vp;
502
503         MPASS(uiop->uio_offset == 0);
504         MPASS(vp->v_type == VLNK);
505
506         tnp = VP_TO_TARFS_NODE(vp);
507
508         TARFS_DPF(VNODE, "%s(%p=%s)\n", __func__,
509             tnp, tnp->name);
510
511         error = uiomove(tnp->link.name,
512             MIN(tnp->size, uiop->uio_resid), uiop);
513
514         return (error);
515 }
516
517 static int
518 tarfs_reclaim(struct vop_reclaim_args *ap)
519 {
520         struct tarfs_node *tnp;
521         struct vnode *vp;
522
523         vp = ap->a_vp;
524         tnp = VP_TO_TARFS_NODE(vp);
525
526         vfs_hash_remove(vp);
527
528         TARFS_NODE_LOCK(tnp);
529         tnp->vnode = NULLVP;
530         vp->v_data = NULL;
531         TARFS_NODE_UNLOCK(tnp);
532
533         return (0);
534 }
535
536 static int
537 tarfs_print(struct vop_print_args *ap)
538 {
539         struct tarfs_node *tnp;
540         struct vnode *vp;
541
542         vp = ap->a_vp;
543         tnp = VP_TO_TARFS_NODE(vp);
544
545         printf("tag tarfs, tarfs_node %p, links %lu\n",
546             tnp, (unsigned long)tnp->nlink);
547         printf("\tmode 0%o, owner %d, group %d, size %zd\n",
548             tnp->mode, tnp->uid, tnp->gid,
549             tnp->size);
550
551         if (vp->v_type == VFIFO)
552                 fifo_printinfo(vp);
553
554         printf("\n");
555
556         return (0);
557 }
558
559 static int
560 tarfs_strategy(struct vop_strategy_args *ap)
561 {
562         struct uio auio;
563         struct iovec iov;
564         struct tarfs_node *tnp;
565         struct buf *bp;
566         off_t off;
567         size_t len;
568         int error;
569
570         tnp = VP_TO_TARFS_NODE(ap->a_vp);
571         bp = ap->a_bp;
572         MPASS(bp->b_iocmd == BIO_READ);
573         MPASS(bp->b_iooffset >= 0);
574         MPASS(bp->b_bcount > 0);
575         MPASS(bp->b_bufsize >= bp->b_bcount);
576         TARFS_DPF(VNODE, "%s(%p=%s, %zu, %ld/%ld)\n", __func__, tnp,
577             tnp->name, (size_t)bp->b_iooffset, bp->b_bcount, bp->b_bufsize);
578         iov.iov_base = bp->b_data;
579         iov.iov_len = bp->b_bcount;
580         off = bp->b_iooffset;
581         len = bp->b_bcount;
582         bp->b_resid = len;
583         if (off > tnp->size) {
584                 /* XXX read beyond EOF - figure out correct handling */
585                 error = EIO;
586                 goto out;
587         }
588         if (off + len > tnp->size) {
589                 /* clip to file length */
590                 len = tnp->size - off;
591         }
592         auio.uio_iov = &iov;
593         auio.uio_iovcnt = 1;
594         auio.uio_offset = off;
595         auio.uio_resid = len;
596         auio.uio_segflg = UIO_SYSSPACE;
597         auio.uio_rw = UIO_READ;
598         auio.uio_td = curthread;
599         error = tarfs_read_file(tnp, len, &auio);
600         bp->b_resid -= len - auio.uio_resid;
601 out:
602         if (error != 0) {
603                 bp->b_ioflags |= BIO_ERROR;
604                 bp->b_error = error;
605         }
606         bp->b_flags |= B_DONE;
607         return (0);
608 }
609
610 static int
611 tarfs_vptofh(struct vop_vptofh_args *ap)
612 {
613         struct tarfs_fid *tfp;
614         struct tarfs_node *tnp;
615
616         tfp = (struct tarfs_fid *)ap->a_fhp;
617         tnp = VP_TO_TARFS_NODE(ap->a_vp);
618
619         tfp->len = sizeof(struct tarfs_fid);
620         tfp->ino = tnp->ino;
621         tfp->gen = tnp->gen;
622
623         return (0);
624 }
625
626 struct vop_vector tarfs_vnodeops = {
627         .vop_default =          &default_vnodeops,
628
629         .vop_access =           tarfs_access,
630         .vop_cachedlookup =     tarfs_lookup,
631         .vop_close =            tarfs_close,
632         .vop_getattr =          tarfs_getattr,
633         .vop_lookup =           vfs_cache_lookup,
634         .vop_open =             tarfs_open,
635         .vop_print =            tarfs_print,
636         .vop_read =             tarfs_read,
637         .vop_readdir =          tarfs_readdir,
638         .vop_readlink =         tarfs_readlink,
639         .vop_reclaim =          tarfs_reclaim,
640         .vop_strategy =         tarfs_strategy,
641         .vop_vptofh =           tarfs_vptofh,
642 };
643 VFS_VOP_VECTOR_REGISTER(tarfs_vnodeops);