]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/tarfs/tarfs_subr.c
Merge llvm-project release/15.x llvmorg-15.0.0-rc2-40-gfbd2950d8d0d
[FreeBSD/FreeBSD.git] / sys / fs / tarfs / tarfs_subr.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/stat.h>
33 #include <sys/systm.h>
34 #include <sys/buf.h>
35 #include <sys/fcntl.h>
36 #include <sys/libkern.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mount.h>
40 #include <sys/namei.h>
41 #include <sys/proc.h>
42 #include <sys/queue.h>
43 #include <sys/sysctl.h>
44 #include <sys/vnode.h>
45
46 #include <vm/vm_param.h>
47
48 #include <fs/tarfs/tarfs.h>
49 #include <fs/tarfs/tarfs_dbg.h>
50
51 MALLOC_DEFINE(M_TARFSNAME, "tarfs name", "tarfs file names");
52 MALLOC_DEFINE(M_TARFSBLK, "tarfs blk", "tarfs block maps");
53
54 SYSCTL_NODE(_vfs, OID_AUTO, tarfs, CTLFLAG_RW, 0, "Tar filesystem");
55
56 unsigned int tarfs_ioshift = TARFS_IOSHIFT_DEFAULT;
57
58 static int
59 tarfs_sysctl_handle_ioshift(SYSCTL_HANDLER_ARGS)
60 {
61         unsigned int tmp;
62         int error;
63
64         tmp = *(unsigned int *)arg1;
65         if ((error = SYSCTL_OUT(req, &tmp, sizeof(tmp))) != 0)
66                 return (error);
67         if (req->newptr != NULL) {
68                 if ((error = SYSCTL_IN(req, &tmp, sizeof(tmp))) != 0)
69                         return (error);
70                 if (tmp == 0)
71                         tmp = TARFS_IOSHIFT_DEFAULT;
72                 if (tmp < TARFS_IOSHIFT_MIN)
73                         tmp = TARFS_IOSHIFT_MIN;
74                 if (tmp > TARFS_IOSHIFT_MAX)
75                         tmp = TARFS_IOSHIFT_MAX;
76                 *(unsigned int *)arg1 = tmp;
77         }
78         return (0);
79 }
80
81 SYSCTL_PROC(_vfs_tarfs, OID_AUTO, ioshift,
82     CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_TUN,
83     &tarfs_ioshift, 0, tarfs_sysctl_handle_ioshift, "IU",
84     "Tar filesystem preferred I/O size (log 2)");
85
86 #ifdef TARFS_DEBUG
87 int tarfs_debug;
88 SYSCTL_INT(_vfs_tarfs, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
89     &tarfs_debug, 0, "Tar filesystem debug mask");
90 #endif  /* TARFS_DEBUG */
91
92 struct tarfs_node *
93 tarfs_lookup_node(struct tarfs_node *tnp, struct tarfs_node *f,
94     struct componentname *cnp)
95 {
96         boolean_t found;
97         struct tarfs_node *entry;
98
99         TARFS_DPF(LOOKUP, "%s: name: %.*s\n", __func__, (int)cnp->cn_namelen,
100             cnp->cn_nameptr);
101
102         found = false;
103         TAILQ_FOREACH(entry, &tnp->dir.dirhead, dirents) {
104                 if (f != NULL && entry != f)
105                         continue;
106
107                 if (entry->namelen == cnp->cn_namelen &&
108                     bcmp(entry->name, cnp->cn_nameptr,
109                     entry->namelen) == 0) {
110                         found = 1;
111                         break;
112                 }
113         }
114
115         if (found) {
116                 if (entry->type == VREG && entry->other != NULL) {
117                         TARFS_DPF_IFF(LOOKUP, "%s: following hard link %p\n",
118                             __func__, entry);
119                         entry = entry->other;
120                 }
121                 TARFS_DPF(LOOKUP, "%s: found tarfs_node %p\n", __func__,
122                     entry);
123                 return (entry);
124         }
125
126         TARFS_DPF(LOOKUP, "%s: no match found\n", __func__);
127         return (NULL);
128 }
129
130 struct tarfs_node *
131 tarfs_lookup_dir(struct tarfs_node *tnp, off_t cookie)
132 {
133         struct tarfs_node *current;
134
135         TARFS_DPF(LOOKUP, "%s: tarfs_node %p, cookie %jd\n", __func__, tnp,
136             cookie);
137         TARFS_DPF(LOOKUP, "%s: name: %s\n", __func__,
138             (tnp->name == NULL) ? "<<root>>" : tnp->name);
139
140         if (cookie == tnp->dir.lastcookie &&
141             tnp->dir.lastnode != NULL) {
142                 TARFS_DPF(LOOKUP, "%s: Using cached entry: tarfs_node %p, "
143                     "cookie %jd\n", __func__, tnp->dir.lastnode,
144                     tnp->dir.lastcookie);
145                 return (tnp->dir.lastnode);
146         }
147
148         TAILQ_FOREACH(current, &tnp->dir.dirhead, dirents) {
149                 TARFS_DPF(LOOKUP, "%s: tarfs_node %p, current %p, ino %lu\n",
150                     __func__, tnp, current, current->ino);
151                 TARFS_DPF_IFF(LOOKUP, current->name != NULL,
152                     "%s: name: %s\n", __func__, current->name);
153                 if (current->ino == cookie) {
154                         TARFS_DPF(LOOKUP, "%s: Found entry: tarfs_node %p, "
155                             "cookie %lu\n", __func__, current,
156                             current->ino);
157                         break;
158                 }
159         }
160
161         return (current);
162 }
163
164 int
165 tarfs_alloc_node(struct tarfs_mount *tmp, const char *name, size_t namelen,
166     enum vtype type, off_t off, size_t sz, time_t mtime, uid_t uid, gid_t gid,
167     mode_t mode, unsigned int flags, const char *linkname, dev_t rdev,
168     struct tarfs_node *parent, struct tarfs_node **retnode)
169 {
170         struct tarfs_node *tnp;
171
172         TARFS_DPF(ALLOC, "%s(%.*s)\n", __func__, (int)namelen, name);
173
174         tnp = malloc(sizeof(struct tarfs_node), M_TARFSNODE, M_WAITOK | M_ZERO);
175         mtx_init(&tnp->lock, "tarfs node lock", NULL, MTX_DEF);
176         tnp->gen = arc4random();
177         tnp->tmp = tmp;
178         if (namelen > 0) {
179                 tnp->name = malloc(namelen + 1, M_TARFSNAME, M_WAITOK);
180                 tnp->namelen = namelen;
181                 memcpy(tnp->name, name, namelen);
182                 tnp->name[namelen] = '\0';
183         }
184         tnp->type = type;
185         tnp->uid = uid;
186         tnp->gid = gid;
187         tnp->mode = mode;
188         tnp->nlink = 1;
189         vfs_timestamp(&tnp->atime);
190         tnp->mtime.tv_sec = mtime;
191         tnp->birthtime = tnp->atime;
192         tnp->ctime = tnp->mtime;
193         if (parent != NULL) {
194                 tnp->ino = alloc_unr(tmp->ino_unr);
195         }
196         tnp->offset = off;
197         tnp->size = tnp->physize = sz;
198         switch (type) {
199         case VDIR:
200                 MPASS(parent != tnp);
201                 MPASS(parent != NULL || tmp->root == NULL);
202                 TAILQ_INIT(&tnp->dir.dirhead);
203                 tnp->nlink++;
204                 if (parent == NULL) {
205                         tnp->ino = TARFS_ROOTINO;
206                 }
207                 tnp->physize = 0;
208                 break;
209         case VLNK:
210                 tnp->link.name = malloc(sz + 1, M_TARFSNAME,
211                     M_WAITOK);
212                 tnp->link.namelen = sz;
213                 memcpy(tnp->link.name, linkname, sz);
214                 tnp->link.name[sz] = '\0';
215                 break;
216         case VREG:
217                 /* create dummy block map */
218                 tnp->nblk = 1;
219                 tnp->blk = malloc(sizeof(*tnp->blk), M_TARFSBLK, M_WAITOK);
220                 tnp->blk[0].i = 0;
221                 tnp->blk[0].o = 0;
222                 tnp->blk[0].l = tnp->physize;
223                 break;
224         case VFIFO:
225                 /* Nothing extra to do */
226                 break;
227         case VBLK:
228         case VCHR:
229                 tnp->rdev = rdev;
230                 tnp->physize = 0;
231                 break;
232         default:
233                 panic("%s: type %d not allowed", __func__, type);
234         }
235         if (parent != NULL) {
236                 MPASS(parent->type == VDIR);
237                 TARFS_NODE_LOCK(parent);
238                 TAILQ_INSERT_TAIL(&parent->dir.dirhead, tnp, dirents);
239                 parent->size += sizeof(struct tarfs_node);
240                 tnp->parent = parent;
241                 if (type == VDIR) {
242                         parent->nlink++;
243                 }
244                 TARFS_NODE_UNLOCK(parent);
245         } else {
246                 tnp->parent = tnp;
247         }
248         MPASS(tnp->ino != 0);
249
250         TARFS_ALLNODES_LOCK(tmp);
251         TAILQ_INSERT_TAIL(&tmp->allnodes, tnp, entries);
252         TARFS_ALLNODES_UNLOCK(tmp);
253
254         *retnode = tnp;
255         tmp->nfiles++;
256         return (0);
257 }
258
259 #define is09(ch) ((ch) >= '0' && (ch) <= '9')
260
261 int
262 tarfs_load_blockmap(struct tarfs_node *tnp, size_t realsize)
263 {
264         struct tarfs_blk *blk = NULL;
265         char *map = NULL;
266         size_t nmap = 0, nblk = 0;
267         char *p, *q;
268         ssize_t res;
269         unsigned int i;
270         long n;
271
272         /*
273          * Load the entire map into memory.  We don't know how big it is,
274          * but as soon as we start reading it we will know how many
275          * entries it contains, and then we can count newlines.
276          */
277         do {
278                 nmap++;
279                 if (tnp->size < nmap * TARFS_BLOCKSIZE) {
280                         TARFS_DPF(MAP, "%s: map too large\n", __func__);
281                         goto bad;
282                 }
283                 /* grow the map */
284                 map = realloc(map, nmap * TARFS_BLOCKSIZE + 1, M_TARFSBLK,
285                     M_ZERO | M_WAITOK);
286                 /* read an additional block */
287                 res = tarfs_io_read_buf(tnp->tmp, false,
288                     map + (nmap - 1) * TARFS_BLOCKSIZE,
289                     tnp->offset + (nmap - 1) * TARFS_BLOCKSIZE,
290                     TARFS_BLOCKSIZE);
291                 if (res < 0)
292                         return (-res);
293                 else if (res < TARFS_BLOCKSIZE)
294                         return (EIO);
295                 map[nmap * TARFS_BLOCKSIZE] = '\0'; /* sentinel */
296                 if (nblk == 0) {
297                         n = strtol(p = map, &q, 10);
298                         if (q == p || *q != '\n' || n < 1)
299                                 goto syntax;
300                         nblk = n;
301                 }
302                 for (n = 0, p = map; *p != '\0'; ++p) {
303                         if (*p == '\n') {
304                                 ++n;
305                         }
306                 }
307                 TARFS_DPF(MAP, "%s: %ld newlines in map\n", __func__, n);
308         } while (n < nblk * 2 + 1);
309         TARFS_DPF(MAP, "%s: block map length %zu\n", __func__, nblk);
310         blk = malloc(sizeof(*blk) * nblk, M_TARFSBLK, M_WAITOK | M_ZERO);
311         p = strchr(map, '\n') + 1;
312         for (i = 0; i < nblk; i++) {
313                 if (i == 0)
314                         blk[i].i = nmap * TARFS_BLOCKSIZE;
315                 else
316                         blk[i].i = blk[i - 1].i + blk[i - 1].l;
317                 n = strtol(p, &q, 10);
318                 if (q == p || *q != '\n' || n < 0)
319                         goto syntax;
320                 p = q + 1;
321                 blk[i].o = n;
322                 n = strtol(p, &q, 10);
323                 if (q == p || *q != '\n' || n < 0)
324                         goto syntax;
325                 p = q + 1;
326                 blk[i].l = n;
327                 TARFS_DPF(MAP, "%s: %3d %12zu %12zu %12zu\n", __func__,
328                     i, blk[i].i, blk[i].o, blk[i].l);
329                 /*
330                  * Check block alignment if the block is of non-zero
331                  * length (a zero-length block indicates the end of a
332                  * trailing hole).  Checking i indirectly checks the
333                  * previous block's l.  It's ok for the final block to
334                  * have an uneven length.
335                  */
336                 if (blk[i].l == 0) {
337                         TARFS_DPF(MAP, "%s: zero-length block\n", __func__);
338                 } else if (blk[i].i % TARFS_BLOCKSIZE != 0 ||
339                     blk[i].o % TARFS_BLOCKSIZE != 0) {
340                         TARFS_DPF(MAP, "%s: misaligned map entry\n", __func__);
341                         goto bad;
342                 }
343                 /*
344                  * Check that this block starts after the end of the
345                  * previous one.
346                  */
347                 if (i > 0 && blk[i].o < blk[i - 1].o + blk[i - 1].l) {
348                         TARFS_DPF(MAP, "%s: overlapping map entries\n", __func__);
349                         goto bad;
350                 }
351                 /*
352                  * Check that the block is within the file, both
353                  * physically and logically.
354                  */
355                 if (blk[i].i + blk[i].l > tnp->physize ||
356                     blk[i].o + blk[i].l > realsize) {
357                         TARFS_DPF(MAP, "%s: map overflow\n", __func__);
358                         goto bad;
359                 }
360         }
361         free(map, M_TARFSBLK);
362
363         /* store in node */
364         free(tnp->blk, M_TARFSBLK);
365         tnp->nblk = nblk;
366         tnp->blk = blk;
367         tnp->size = realsize;
368         return (0);
369 syntax:
370         TARFS_DPF(MAP, "%s: syntax error in block map\n", __func__);
371 bad:
372         free(map, M_TARFSBLK);
373         free(blk, M_TARFSBLK);
374         return (EINVAL);
375 }
376
377 void
378 tarfs_free_node(struct tarfs_node *tnp)
379 {
380         struct tarfs_mount *tmp;
381
382         MPASS(tnp != NULL);
383         tmp = tnp->tmp;
384
385         switch (tnp->type) {
386         case VLNK:
387                 if (tnp->link.name)
388                         free(tnp->link.name, M_TARFSNAME);
389                 break;
390         default:
391                 break;
392         }
393         if (tnp->name != NULL)
394                 free(tnp->name, M_TARFSNAME);
395         if (tnp->blk != NULL)
396                 free(tnp->blk, M_TARFSBLK);
397         if (tnp->ino >= TARFS_MININO)
398                 free_unr(tmp->ino_unr, tnp->ino);
399         free(tnp, M_TARFSNODE);
400         tmp->nfiles--;
401 }
402
403 int
404 tarfs_read_file(struct tarfs_node *tnp, size_t len, struct uio *uiop)
405 {
406         struct uio auio;
407         size_t resid = len;
408         size_t copylen;
409         unsigned int i;
410         int error;
411
412         TARFS_DPF(VNODE, "%s(%s, %zu, %zu)\n", __func__,
413             tnp->name, uiop->uio_offset, resid);
414         for (i = 0; i < tnp->nblk && resid > 0; ++i) {
415                 if (uiop->uio_offset > tnp->blk[i].o + tnp->blk[i].l) {
416                         /* skip this block */
417                         continue;
418                 }
419                 while (resid > 0 &&
420                     uiop->uio_offset < tnp->blk[i].o) {
421                         /* move out some zeroes... */
422                         copylen = tnp->blk[i].o - uiop->uio_offset;
423                         if (copylen > resid)
424                                 copylen = resid;
425                         if (copylen > ZERO_REGION_SIZE)
426                                 copylen = ZERO_REGION_SIZE;
427                         auio = *uiop;
428                         auio.uio_offset = 0;
429                         auio.uio_resid = copylen;
430                         error = uiomove(__DECONST(void *, zero_region),
431                             copylen, &auio);
432                         if (error != 0)
433                                 return (error);
434                         TARFS_DPF(MAP, "%s(%s) = zero %zu\n", __func__,
435                             tnp->name, copylen - auio.uio_resid);
436                         uiop->uio_offset += copylen - auio.uio_resid;
437                         uiop->uio_resid -= copylen - auio.uio_resid;
438                         resid -= copylen - auio.uio_resid;
439                 }
440                 while (resid > 0 &&
441                     uiop->uio_offset < tnp->blk[i].o + tnp->blk[i].l) {
442                         /* now actual data */
443                         copylen = tnp->blk[i].l;
444                         if (copylen > resid)
445                                 copylen = resid;
446                         auio = *uiop;
447                         auio.uio_offset = tnp->offset + tnp->blk[i].i +
448                             uiop->uio_offset - tnp->blk[i].o;
449                         auio.uio_resid = copylen;
450                         error = tarfs_io_read(tnp->tmp, false, &auio);
451                         if (error != 0)
452                                 return (error);
453                         TARFS_DPF(MAP, "%s(%s) = data %zu\n", __func__,
454                             tnp->name, copylen - auio.uio_resid);
455                         uiop->uio_offset += copylen - auio.uio_resid;
456                         uiop->uio_resid -= copylen - auio.uio_resid;
457                         resid -= copylen - auio.uio_resid;
458                 }
459         }
460         TARFS_DPF(VNODE, "%s(%s) = %zu\n", __func__,
461             tnp->name, len - resid);
462         return (0);
463 }
464
465 /*
466  * XXX ugly file flag parser which could easily be a finite state machine
467  * driven by a small precomputed table.
468  *
469  * Note that unlike strtofflags(3), we make no attempt to handle negated
470  * flags, since they shouldn't appear in tar files.
471  */
472 static const struct tarfs_flag {
473         const char *name;
474         unsigned int flag;
475 } tarfs_flags[] = {
476         { "nodump",     UF_NODUMP },
477         { "uchg",       UF_IMMUTABLE },
478         { "uappnd",     UF_APPEND },
479         { "opaque",     UF_OPAQUE },
480         { "uunlnk",     UF_NOUNLINK },
481         { "arch",       SF_ARCHIVED },
482         { "schg",       SF_IMMUTABLE },
483         { "sappnd",     SF_APPEND },
484         { "sunlnk",     SF_NOUNLINK },
485         { NULL, 0 },
486 };
487
488 unsigned int
489 tarfs_strtofflags(const char *str, char **end)
490 {
491         const struct tarfs_flag *tf;
492         const char *p, *q;
493         unsigned int ret;
494
495         ret = 0;
496         for (p = q = str; *q != '\0'; p = q + 1) {
497                 for (q = p; *q != '\0' && *q != ','; ++q) {
498                         if (*q < 'a' || *q > 'z') {
499                                 goto end;
500                         }
501                         /* nothing */
502                 }
503                 for (tf = tarfs_flags; tf->name != NULL; tf++) {
504                         if (strncmp(tf->name, p, q - p) == 0 &&
505                             tf->name[q - p] == '\0') {
506                                 TARFS_DPF(ALLOC, "%s: %.*s = 0x%06x\n", __func__,
507                                     (int)(q - p), p, tf->flag);
508                                 ret |= tf->flag;
509                                 break;
510                         }
511                 }
512                 if (tf->name == NULL) {
513                         TARFS_DPF(ALLOC, "%s: %.*s = 0x??????\n",
514                             __func__, (int)(q - p), p);
515                         goto end;
516                 }
517         }
518 end:
519         if (*end != NULL) {
520                 *end = __DECONST(char *, q);
521         }
522         return (ret);
523 }