]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/tarfs/tarfs_vnops.c
sys: Remove $FreeBSD$: one-line .c pattern
[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_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);