Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/tarfs/tarfs_vfsops.c
106180 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2013 Juniper Networks, Inc.
5
* Copyright (c) 2022-2024 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/buf.h>
34
#include <sys/conf.h>
35
#include <sys/fcntl.h>
36
#include <sys/libkern.h>
37
#include <sys/limits.h>
38
#include <sys/lock.h>
39
#include <sys/malloc.h>
40
#include <sys/mount.h>
41
#include <sys/mutex.h>
42
#include <sys/namei.h>
43
#include <sys/priv.h>
44
#include <sys/proc.h>
45
#include <sys/queue.h>
46
#include <sys/sbuf.h>
47
#include <sys/stat.h>
48
#include <sys/uio.h>
49
#include <sys/vnode.h>
50
51
#include <vm/vm_param.h>
52
53
#include <geom/geom.h>
54
#include <geom/geom_vfs.h>
55
56
#include <fs/tarfs/tarfs.h>
57
#include <fs/tarfs/tarfs_dbg.h>
58
59
CTASSERT(ZERO_REGION_SIZE >= TARFS_BLOCKSIZE);
60
61
struct ustar_header {
62
char name[100]; /* File name */
63
char mode[8]; /* Mode flags */
64
char uid[8]; /* User id */
65
char gid[8]; /* Group id */
66
char size[12]; /* Size */
67
char mtime[12]; /* Modified time */
68
char checksum[8]; /* Checksum */
69
char typeflag[1]; /* Type */
70
char linkname[100]; /* "old format" stops here */
71
char magic[6]; /* POSIX UStar "ustar\0" indicator */
72
char version[2]; /* POSIX UStar version "00" */
73
char uname[32]; /* User name */
74
char gname[32]; /* Group name */
75
char major[8]; /* Device major number */
76
char minor[8]; /* Device minor number */
77
char prefix[155]; /* Path prefix */
78
char _pad[12];
79
};
80
81
CTASSERT(sizeof(struct ustar_header) == TARFS_BLOCKSIZE);
82
83
#define TAR_EOF ((size_t)-1)
84
85
#define TAR_TYPE_FILE '0'
86
#define TAR_TYPE_HARDLINK '1'
87
#define TAR_TYPE_SYMLINK '2'
88
#define TAR_TYPE_CHAR '3'
89
#define TAR_TYPE_BLOCK '4'
90
#define TAR_TYPE_DIRECTORY '5'
91
#define TAR_TYPE_FIFO '6'
92
#define TAR_TYPE_CONTIG '7'
93
#define TAR_TYPE_GLOBAL_EXTHDR 'g'
94
#define TAR_TYPE_EXTHDR 'x'
95
#define TAR_TYPE_GNU_SPARSE 'S'
96
97
#define USTAR_MAGIC (uint8_t []){ 'u', 's', 't', 'a', 'r', 0 }
98
#define USTAR_VERSION (uint8_t []){ '0', '0' }
99
#define GNUTAR_MAGIC (uint8_t []){ 'u', 's', 't', 'a', 'r', ' ' }
100
#define GNUTAR_VERSION (uint8_t []){ ' ', '\x0' }
101
102
#define DEFDIRMODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
103
104
MALLOC_DEFINE(M_TARFSMNT, "tarfs mount", "tarfs mount structures");
105
MALLOC_DEFINE(M_TARFSNODE, "tarfs node", "tarfs node structures");
106
107
static vfs_mount_t tarfs_mount;
108
static vfs_unmount_t tarfs_unmount;
109
static vfs_root_t tarfs_root;
110
static vfs_statfs_t tarfs_statfs;
111
static vfs_fhtovp_t tarfs_fhtovp;
112
113
static const char *tarfs_opts[] = {
114
"as", "from", "gid", "mode", "uid", "verify",
115
NULL
116
};
117
118
/*
119
* Reads a len-width signed octal number from strp. Returns 0 on success
120
* and non-zero on error.
121
*/
122
static int
123
tarfs_str2octal(const char *strp, size_t len, int64_t *num)
124
{
125
int64_t val;
126
size_t idx;
127
int sign;
128
129
idx = 0;
130
if (strp[idx] == '-') {
131
sign = -1;
132
idx++;
133
} else {
134
sign = 1;
135
}
136
137
val = 0;
138
for (; idx < len && strp[idx] != '\0' && strp[idx] != ' '; idx++) {
139
if (strp[idx] < '0' || strp[idx] > '7')
140
return (EINVAL);
141
val <<= 3;
142
val += strp[idx] - '0';
143
if (val > INT64_MAX / 8)
144
return (ERANGE);
145
}
146
147
*num = val * sign;
148
return (0);
149
}
150
151
/*
152
* Reads a len-byte extended numeric value from strp. The first byte has
153
* bit 7 set to indicate the format; the remaining 7 bits + the (len - 1)
154
* bytes that follow form a big-endian signed two's complement binary
155
* number. Returns 0 on success and non-zero on error;
156
*/
157
static int
158
tarfs_str2base256(const char *strp, size_t len, int64_t *num)
159
{
160
int64_t val;
161
size_t idx;
162
163
KASSERT(strp[0] & 0x80, ("not an extended numeric value"));
164
165
/* Sign-extend the first byte */
166
if ((strp[0] & 0x40) != 0)
167
val = (int64_t)-1;
168
else
169
val = 0;
170
val <<= 6;
171
val |= (strp[0] & 0x3f);
172
173
/* Read subsequent bytes */
174
for (idx = 1; idx < len; idx++) {
175
val <<= 8;
176
val |= (0xff & (int64_t)strp[idx]);
177
if (val > INT64_MAX / 256 || val < INT64_MIN / 256)
178
return (ERANGE);
179
}
180
181
*num = val;
182
return (0);
183
}
184
185
/*
186
* Read a len-byte numeric field from strp. If bit 7 of the first byte it
187
* set, assume an extended numeric value (signed two's complement);
188
* otherwise, assume a signed octal value.
189
*/
190
static int
191
tarfs_str2int64(const char *strp, size_t len, int64_t *num)
192
{
193
if (len < 1)
194
return (EINVAL);
195
if ((strp[0] & 0x80) != 0)
196
return (tarfs_str2base256(strp, len, num));
197
return (tarfs_str2octal(strp, len, num));
198
}
199
200
/*
201
* Verifies the checksum of a header. Returns true if the checksum is
202
* valid, false otherwise.
203
*/
204
static boolean_t
205
tarfs_checksum(struct ustar_header *hdrp)
206
{
207
const unsigned char *ptr;
208
int64_t checksum, hdrsum;
209
210
if (tarfs_str2int64(hdrp->checksum, sizeof(hdrp->checksum), &hdrsum) != 0) {
211
TARFS_DPF(CHECKSUM, "%s: invalid header checksum \"%.*s\"\n",
212
__func__, (int)sizeof(hdrp->checksum), hdrp->checksum);
213
return (false);
214
}
215
TARFS_DPF(CHECKSUM, "%s: header checksum \"%.*s\" = %#lo\n", __func__,
216
(int)sizeof(hdrp->checksum), hdrp->checksum, hdrsum);
217
218
checksum = 0;
219
for (ptr = (const unsigned char *)hdrp;
220
ptr < (const unsigned char *)hdrp->checksum; ptr++)
221
checksum += *ptr;
222
for (;
223
ptr < (const unsigned char *)hdrp->typeflag; ptr++)
224
checksum += 0x20;
225
for (;
226
ptr < (const unsigned char *)(hdrp + 1); ptr++)
227
checksum += *ptr;
228
TARFS_DPF(CHECKSUM, "%s: calc unsigned checksum %#lo\n", __func__,
229
checksum);
230
if (hdrsum == checksum)
231
return (true);
232
233
/*
234
* Repeat test with signed bytes, some older formats use a broken
235
* form of the calculation
236
*/
237
checksum = 0;
238
for (ptr = (const unsigned char *)hdrp;
239
ptr < (const unsigned char *)&hdrp->checksum; ptr++)
240
checksum += *((const signed char *)ptr);
241
for (;
242
ptr < (const unsigned char *)&hdrp->typeflag; ptr++)
243
checksum += 0x20;
244
for (;
245
ptr < (const unsigned char *)(hdrp + 1); ptr++)
246
checksum += *((const signed char *)ptr);
247
TARFS_DPF(CHECKSUM, "%s: calc signed checksum %#lo\n", __func__,
248
checksum);
249
if (hdrsum == checksum)
250
return (true);
251
252
return (false);
253
}
254
255
256
/*
257
* Looks up a path in the tarfs node tree.
258
*
259
* - If the path exists, stores a pointer to the corresponding tarfs_node
260
* in retnode and a pointer to its parent in retparent.
261
*
262
* - If the path does not exist, but create_dirs is true, creates ancestor
263
* directories and returns NULL in retnode and the parent in retparent.
264
*
265
* - If the path does not exist and create_dirs is false, stops at the
266
* first missing path name component.
267
*
268
* - In all cases, on return, endp and sepp point to the beginning and
269
* end, respectively, of the last-processed path name component.
270
*
271
* - Returns 0 if the node was found, ENOENT if it was not, and some other
272
* positive errno value on failure.
273
*/
274
static int
275
tarfs_lookup_path(struct tarfs_mount *tmp, char *name, size_t namelen,
276
char **endp, char **sepp, struct tarfs_node **retparent,
277
struct tarfs_node **retnode, boolean_t create_dirs)
278
{
279
struct componentname cn = { };
280
struct tarfs_node *parent, *tnp;
281
char *sep;
282
size_t len;
283
int error;
284
boolean_t do_lookup;
285
286
MPASS(name != NULL && namelen != 0);
287
288
do_lookup = true;
289
error = 0;
290
parent = tnp = tmp->root;
291
if (tnp == NULL)
292
panic("%s: root node not yet created", __func__);
293
294
TARFS_DPF(LOOKUP, "%s: full path: %.*s\n", __func__,
295
(int)namelen, name);
296
297
sep = NULL;
298
for (;;) {
299
/* skip leading slash(es) */
300
while (name[0] == '/' && namelen > 0)
301
name++, namelen--;
302
303
/* did we reach the end? */
304
if (namelen == 0 || name[0] == '\0') {
305
name = do_lookup ? NULL : cn.cn_nameptr;
306
namelen = do_lookup ? 0 : cn.cn_namelen;
307
break;
308
}
309
310
/* we're not at the end, so we must be in a directory */
311
if (tnp != NULL && tnp->type != VDIR) {
312
TARFS_DPF(LOOKUP, "%s: %.*s is not a directory\n", __func__,
313
(int)tnp->namelen, tnp->name);
314
error = ENOTDIR;
315
break;
316
}
317
318
/* locate the next separator */
319
for (sep = name, len = 0;
320
*sep != '\0' && *sep != '/' && len < namelen;
321
sep++, len++)
322
/* nothing */ ;
323
324
/* check for . and .. */
325
if (name[0] == '.' && len == 1) {
326
name += len;
327
namelen -= len;
328
continue;
329
}
330
if (name[0] == '.' && name[1] == '.' && len == 2) {
331
if (tnp == tmp->root) {
332
error = EINVAL;
333
break;
334
}
335
tnp = parent;
336
parent = tnp->parent;
337
cn.cn_nameptr = tnp->name;
338
cn.cn_namelen = tnp->namelen;
339
do_lookup = true;
340
TARFS_DPF(LOOKUP, "%s: back to %.*s/\n", __func__,
341
(int)tnp->namelen, tnp->name);
342
name += len;
343
namelen -= len;
344
continue;
345
}
346
347
/* create parent if necessary */
348
if (!do_lookup) {
349
TARFS_DPF(ALLOC, "%s: creating %.*s\n", __func__,
350
(int)cn.cn_namelen, cn.cn_nameptr);
351
error = tarfs_alloc_node(tmp, cn.cn_nameptr,
352
cn.cn_namelen, VDIR, -1, 0, tmp->mtime, 0, 0,
353
DEFDIRMODE, 0, NULL, NODEV, parent, &tnp);
354
if (error != 0)
355
break;
356
}
357
358
parent = tnp;
359
tnp = NULL;
360
cn.cn_nameptr = name;
361
cn.cn_namelen = len;
362
TARFS_DPF(LOOKUP, "%s: looking up %.*s in %.*s/\n", __func__,
363
(int)cn.cn_namelen, cn.cn_nameptr,
364
(int)parent->namelen, parent->name);
365
if (do_lookup) {
366
tnp = tarfs_lookup_node(parent, NULL, &cn);
367
if (tnp == NULL) {
368
do_lookup = false;
369
if (!create_dirs) {
370
error = ENOENT;
371
break;
372
}
373
}
374
}
375
name += cn.cn_namelen;
376
namelen -= cn.cn_namelen;
377
}
378
379
TARFS_DPF(LOOKUP, "%s: parent %p node %p\n", __func__, parent, tnp);
380
381
if (retparent)
382
*retparent = parent;
383
if (retnode)
384
*retnode = tnp;
385
if (endp) {
386
if (namelen > 0)
387
*endp = name;
388
else
389
*endp = NULL;
390
}
391
if (sepp)
392
*sepp = sep;
393
return (error);
394
}
395
396
/*
397
* Frees a tarfs_mount structure and everything it references.
398
*/
399
static void
400
tarfs_free_mount(struct tarfs_mount *tmp)
401
{
402
struct mount *mp;
403
struct tarfs_node *tnp, *tnp_next;
404
405
MPASS(tmp != NULL);
406
407
TARFS_DPF(ALLOC, "%s: Freeing mount structure %p\n", __func__, tmp);
408
409
TARFS_DPF(ALLOC, "%s: freeing tarfs_node structures\n", __func__);
410
TAILQ_FOREACH_SAFE(tnp, &tmp->allnodes, entries, tnp_next) {
411
tarfs_free_node(tnp);
412
}
413
414
(void)tarfs_io_fini(tmp);
415
416
TARFS_DPF(ALLOC, "%s: deleting unr header\n", __func__);
417
delete_unrhdr(tmp->ino_unr);
418
mp = tmp->vfs;
419
mp->mnt_data = NULL;
420
421
TARFS_DPF(ALLOC, "%s: freeing structure\n", __func__);
422
free(tmp, M_TARFSMNT);
423
}
424
425
/*
426
* Processes the tar file header at block offset blknump and allocates and
427
* populates a tarfs_node structure for the file it describes. Updated
428
* blknump to point to the next unread tar file block, or TAR_EOF if EOF
429
* is reached. Returns 0 on success or EOF and a positive errno value on
430
* failure.
431
*/
432
static int
433
tarfs_alloc_one(struct tarfs_mount *tmp, size_t *blknump)
434
{
435
char block[TARFS_BLOCKSIZE];
436
struct ustar_header *hdrp = (struct ustar_header *)block;
437
struct sbuf *namebuf = NULL;
438
char *exthdr = NULL, *name = NULL, *link = NULL;
439
size_t blknum = *blknump;
440
int64_t num;
441
int endmarker = 0;
442
char *namep, *sep;
443
struct tarfs_node *parent, *tnp, *other;
444
size_t namelen = 0, linklen = 0, realsize = 0, extsize = 0, sz;
445
ssize_t res;
446
dev_t rdev;
447
gid_t gid;
448
mode_t mode;
449
time_t mtime;
450
uid_t uid;
451
long major = -1, minor = -1;
452
unsigned int flags = 0;
453
int error;
454
boolean_t sparse = false;
455
456
again:
457
/* read next header */
458
res = tarfs_io_read_buf(tmp, false, block,
459
TARFS_BLOCKSIZE * blknum, TARFS_BLOCKSIZE);
460
if (res < 0) {
461
error = -res;
462
goto bad;
463
} else if (res < TARFS_BLOCKSIZE) {
464
goto eof;
465
}
466
blknum++;
467
468
/* check for end marker */
469
if (memcmp(block, zero_region, TARFS_BLOCKSIZE) == 0) {
470
if (endmarker++) {
471
if (exthdr != NULL) {
472
TARFS_DPF(IO, "%s: orphaned extended header at %zu\n",
473
__func__, TARFS_BLOCKSIZE * (blknum - 1));
474
free(exthdr, M_TEMP);
475
}
476
TARFS_DPF(IO, "%s: end of archive at %zu\n", __func__,
477
TARFS_BLOCKSIZE * blknum);
478
tmp->nblocks = blknum;
479
*blknump = TAR_EOF;
480
return (0);
481
}
482
goto again;
483
}
484
485
/* verify magic */
486
if (memcmp(hdrp->magic, USTAR_MAGIC, sizeof(USTAR_MAGIC)) == 0 &&
487
memcmp(hdrp->version, USTAR_VERSION, sizeof(USTAR_VERSION)) == 0) {
488
/* POSIX */
489
} else if (memcmp(hdrp->magic, GNUTAR_MAGIC, sizeof(GNUTAR_MAGIC)) == 0 &&
490
memcmp(hdrp->magic, GNUTAR_MAGIC, sizeof(GNUTAR_MAGIC)) == 0) {
491
TARFS_DPF(ALLOC, "%s: GNU tar format at %zu\n", __func__,
492
TARFS_BLOCKSIZE * (blknum - 1));
493
error = EFTYPE;
494
goto bad;
495
} else {
496
TARFS_DPF(ALLOC, "%s: unsupported TAR format at %zu\n",
497
__func__, TARFS_BLOCKSIZE * (blknum - 1));
498
error = EINVAL;
499
goto bad;
500
}
501
502
/* verify checksum */
503
if (!tarfs_checksum(hdrp)) {
504
TARFS_DPF(ALLOC, "%s: header checksum failed at %zu\n",
505
__func__, TARFS_BLOCKSIZE * (blknum - 1));
506
error = EINVAL;
507
goto bad;
508
}
509
510
/* get standard attributes */
511
if (tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode), &num) != 0 ||
512
num < 0 || num > (S_IFMT|ALLPERMS)) {
513
TARFS_DPF(ALLOC, "%s: invalid file mode at %zu\n",
514
__func__, TARFS_BLOCKSIZE * (blknum - 1));
515
mode = S_IRUSR;
516
} else {
517
mode = num & ALLPERMS;
518
}
519
if (tarfs_str2int64(hdrp->uid, sizeof(hdrp->uid), &num) != 0 ||
520
num < 0 || num > UID_MAX) {
521
TARFS_DPF(ALLOC, "%s: invalid UID at %zu\n",
522
__func__, TARFS_BLOCKSIZE * (blknum - 1));
523
uid = tmp->root->uid;
524
mode &= ~S_ISUID;
525
} else {
526
uid = num;
527
}
528
if (tarfs_str2int64(hdrp->gid, sizeof(hdrp->gid), &num) != 0 ||
529
num < 0 || num > GID_MAX) {
530
TARFS_DPF(ALLOC, "%s: invalid GID at %zu\n",
531
__func__, TARFS_BLOCKSIZE * (blknum - 1));
532
gid = tmp->root->gid;
533
mode &= ~S_ISGID;
534
} else {
535
gid = num;
536
}
537
if (tarfs_str2int64(hdrp->size, sizeof(hdrp->size), &num) != 0 ||
538
num < 0) {
539
TARFS_DPF(ALLOC, "%s: invalid size at %zu\n",
540
__func__, TARFS_BLOCKSIZE * (blknum - 1));
541
error = EINVAL;
542
goto bad;
543
}
544
sz = num;
545
if (tarfs_str2int64(hdrp->mtime, sizeof(hdrp->mtime), &num) != 0) {
546
TARFS_DPF(ALLOC, "%s: invalid modification time at %zu\n",
547
__func__, TARFS_BLOCKSIZE * (blknum - 1));
548
error = EINVAL;
549
goto bad;
550
}
551
mtime = num;
552
rdev = NODEV;
553
TARFS_DPF(ALLOC, "%s: [%c] %zu @%jd %o %d:%d\n", __func__,
554
hdrp->typeflag[0], sz, (intmax_t)mtime, mode, uid, gid);
555
556
/* global extended header? */
557
if (hdrp->typeflag[0] == TAR_TYPE_GLOBAL_EXTHDR) {
558
TARFS_DPF(ALLOC, "%s: %zu-byte global extended header at %zu\n",
559
__func__, sz, TARFS_BLOCKSIZE * (blknum - 1));
560
goto skip;
561
}
562
563
/* extended header? */
564
if (hdrp->typeflag[0] == TAR_TYPE_EXTHDR) {
565
if (exthdr != NULL) {
566
TARFS_DPF(IO, "%s: multiple extended headers at %zu\n",
567
__func__, TARFS_BLOCKSIZE * (blknum - 1));
568
error = EFTYPE;
569
goto bad;
570
}
571
/* read the contents of the exthdr */
572
TARFS_DPF(ALLOC, "%s: %zu-byte extended header at %zu\n",
573
__func__, sz, TARFS_BLOCKSIZE * (blknum - 1));
574
exthdr = malloc(sz, M_TEMP, M_WAITOK);
575
res = tarfs_io_read_buf(tmp, false, exthdr,
576
TARFS_BLOCKSIZE * blknum, sz);
577
if (res < 0) {
578
error = -res;
579
goto bad;
580
}
581
if (res < sz) {
582
goto eof;
583
}
584
blknum += TARFS_SZ2BLKS(res);
585
/* XXX TODO: refactor this parser */
586
char *line = exthdr;
587
while (line < exthdr + sz) {
588
char *eol, *key, *value, *sep;
589
size_t len = strtoul(line, &sep, 10);
590
if (len == 0 || sep == line || *sep != ' ') {
591
goto syntax;
592
}
593
if ((uintptr_t)line + len < (uintptr_t)line ||
594
line + len > exthdr + sz) {
595
TARFS_DPF(ALLOC, "%s: exthdr overflow\n",
596
__func__);
597
error = EINVAL;
598
goto bad;
599
}
600
eol = line + len - 1;
601
*eol = '\0';
602
line += len;
603
key = sep + 1;
604
sep = strchr(key, '=');
605
if (sep == NULL) {
606
goto syntax;
607
}
608
*sep = '\0';
609
value = sep + 1;
610
TARFS_DPF(ALLOC, "%s: exthdr %s=%s\n", __func__,
611
key, value);
612
if (strcmp(key, "size") == 0) {
613
extsize = strtol(value, &sep, 10);
614
if (sep != eol) {
615
goto syntax;
616
}
617
} else if (strcmp(key, "path") == 0) {
618
name = value;
619
namelen = eol - value;
620
} else if (strcmp(key, "linkpath") == 0) {
621
link = value;
622
linklen = eol - value;
623
} else if (strcmp(key, "GNU.sparse.major") == 0) {
624
sparse = true;
625
major = strtol(value, &sep, 10);
626
if (sep != eol) {
627
goto syntax;
628
}
629
} else if (strcmp(key, "GNU.sparse.minor") == 0) {
630
sparse = true;
631
minor = strtol(value, &sep, 10);
632
if (sep != eol) {
633
goto syntax;
634
}
635
} else if (strcmp(key, "GNU.sparse.name") == 0) {
636
sparse = true;
637
name = value;
638
namelen = eol - value;
639
if (namelen == 0) {
640
goto syntax;
641
}
642
} else if (strcmp(key, "GNU.sparse.realsize") == 0) {
643
sparse = true;
644
realsize = strtoul(value, &sep, 10);
645
if (sep != eol) {
646
goto syntax;
647
}
648
} else if (strcmp(key, "SCHILY.fflags") == 0) {
649
flags |= tarfs_strtofflags(value, &sep);
650
if (sep != eol) {
651
goto syntax;
652
}
653
}
654
}
655
goto again;
656
}
657
658
/* do we have a size from an exthdr? */
659
if (extsize > 0) {
660
sz = extsize;
661
}
662
663
/* sparse file consistency checks */
664
if (sparse) {
665
TARFS_DPF(ALLOC, "%s: %s: sparse %ld.%ld (%zu bytes)\n", __func__,
666
name, major, minor, realsize);
667
if (major != 1 || minor != 0 || name == NULL || realsize == 0 ||
668
hdrp->typeflag[0] != TAR_TYPE_FILE) {
669
TARFS_DPF(ALLOC, "%s: invalid sparse format\n", __func__);
670
error = EINVAL;
671
goto bad;
672
}
673
}
674
675
/* file name */
676
if (name == NULL) {
677
if (hdrp->prefix[0] != '\0') {
678
namebuf = sbuf_new_auto();
679
sbuf_printf(namebuf, "%.*s/%.*s",
680
(int)sizeof(hdrp->prefix), hdrp->prefix,
681
(int)sizeof(hdrp->name), hdrp->name);
682
sbuf_finish(namebuf);
683
name = sbuf_data(namebuf);
684
namelen = sbuf_len(namebuf);
685
} else {
686
name = hdrp->name;
687
namelen = strnlen(hdrp->name, sizeof(hdrp->name));
688
}
689
}
690
691
error = tarfs_lookup_path(tmp, name, namelen, &namep,
692
&sep, &parent, &tnp, true);
693
if (error != 0) {
694
TARFS_DPF(ALLOC, "%s: failed to look up %.*s\n", __func__,
695
(int)namelen, name);
696
error = EINVAL;
697
goto bad;
698
}
699
if (tnp != NULL) {
700
if (hdrp->typeflag[0] == TAR_TYPE_DIRECTORY) {
701
/* XXX set attributes? */
702
goto skip;
703
}
704
TARFS_DPF(ALLOC, "%s: duplicate file %.*s\n", __func__,
705
(int)namelen, name);
706
error = EINVAL;
707
goto bad;
708
}
709
switch (hdrp->typeflag[0]) {
710
case TAR_TYPE_DIRECTORY:
711
error = tarfs_alloc_node(tmp, namep, sep - namep, VDIR,
712
0, 0, mtime, uid, gid, mode, flags, NULL, 0,
713
parent, &tnp);
714
break;
715
case TAR_TYPE_FILE:
716
error = tarfs_alloc_node(tmp, namep, sep - namep, VREG,
717
blknum * TARFS_BLOCKSIZE, sz, mtime, uid, gid, mode,
718
flags, NULL, 0, parent, &tnp);
719
if (error == 0 && sparse) {
720
error = tarfs_load_blockmap(tnp, realsize);
721
}
722
break;
723
case TAR_TYPE_HARDLINK:
724
if (link == NULL) {
725
link = hdrp->linkname;
726
linklen = strnlen(link, sizeof(hdrp->linkname));
727
}
728
if (linklen == 0) {
729
TARFS_DPF(ALLOC, "%s: %.*s: link without target\n",
730
__func__, (int)namelen, name);
731
error = EINVAL;
732
goto bad;
733
}
734
error = tarfs_lookup_path(tmp, link, linklen, NULL,
735
NULL, NULL, &other, false);
736
if (error != 0 || other == NULL ||
737
other->type != VREG || other->other != NULL) {
738
TARFS_DPF(ALLOC, "%s: %.*s: invalid link to %.*s\n",
739
__func__, (int)namelen, name, (int)linklen, link);
740
error = EINVAL;
741
goto bad;
742
}
743
error = tarfs_alloc_node(tmp, namep, sep - namep, VREG,
744
0, 0, 0, 0, 0, 0, 0, NULL, 0, parent, &tnp);
745
if (error == 0) {
746
tnp->other = other;
747
tnp->other->nlink++;
748
}
749
break;
750
case TAR_TYPE_SYMLINK:
751
if (link == NULL) {
752
link = hdrp->linkname;
753
linklen = strnlen(link, sizeof(hdrp->linkname));
754
}
755
if (linklen == 0) {
756
TARFS_DPF(ALLOC, "%s: %.*s: link without target\n",
757
__func__, (int)namelen, name);
758
error = EINVAL;
759
goto bad;
760
}
761
error = tarfs_alloc_node(tmp, namep, sep - namep, VLNK,
762
0, linklen, mtime, uid, gid, mode, flags, link, 0,
763
parent, &tnp);
764
break;
765
case TAR_TYPE_BLOCK:
766
if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 ||
767
num < 0 || num > INT_MAX) {
768
TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n",
769
__func__, (int)namelen, name);
770
error = EINVAL;
771
goto bad;
772
}
773
major = num;
774
if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 ||
775
num < 0 || num > INT_MAX) {
776
TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n",
777
__func__, (int)namelen, name);
778
error = EINVAL;
779
goto bad;
780
}
781
minor = num;
782
rdev = makedev(major, minor);
783
error = tarfs_alloc_node(tmp, namep, sep - namep, VBLK,
784
0, 0, mtime, uid, gid, mode, flags, NULL, rdev,
785
parent, &tnp);
786
break;
787
case TAR_TYPE_CHAR:
788
if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 ||
789
num < 0 || num > INT_MAX) {
790
TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n",
791
__func__, (int)namelen, name);
792
error = EINVAL;
793
goto bad;
794
}
795
major = num;
796
if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 ||
797
num < 0 || num > INT_MAX) {
798
TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n",
799
__func__, (int)namelen, name);
800
error = EINVAL;
801
goto bad;
802
}
803
minor = num;
804
rdev = makedev(major, minor);
805
error = tarfs_alloc_node(tmp, namep, sep - namep, VCHR,
806
0, 0, mtime, uid, gid, mode, flags, NULL, rdev,
807
parent, &tnp);
808
break;
809
default:
810
TARFS_DPF(ALLOC, "%s: unsupported type %c for %.*s\n",
811
__func__, hdrp->typeflag[0], (int)namelen, name);
812
error = EINVAL;
813
break;
814
}
815
if (error != 0)
816
goto bad;
817
818
skip:
819
blknum += TARFS_SZ2BLKS(sz);
820
tmp->nblocks = blknum;
821
*blknump = blknum;
822
if (exthdr != NULL) {
823
free(exthdr, M_TEMP);
824
}
825
if (namebuf != NULL) {
826
sbuf_delete(namebuf);
827
}
828
return (0);
829
syntax:
830
TARFS_DPF(ALLOC, "%s: exthdr syntax error\n", __func__);
831
error = EINVAL;
832
goto bad;
833
eof:
834
TARFS_DPF(IO, "%s: premature end of file\n", __func__);
835
error = EIO;
836
goto bad;
837
bad:
838
if (exthdr != NULL) {
839
free(exthdr, M_TEMP);
840
}
841
if (namebuf != NULL) {
842
sbuf_delete(namebuf);
843
}
844
return (error);
845
}
846
847
/*
848
* Allocates and populates the metadata structures for the tar file
849
* referenced by vp. On success, a pointer to the tarfs_mount structure
850
* is stored in tmpp. Returns 0 on success or a positive errno value on
851
* failure.
852
*/
853
static int
854
tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
855
uid_t root_uid, gid_t root_gid, mode_t root_mode,
856
struct tarfs_mount **tmpp)
857
{
858
struct vattr va;
859
struct thread *td = curthread;
860
struct tarfs_mount *tmp;
861
struct tarfs_node *root;
862
size_t blknum;
863
time_t mtime;
864
int error;
865
866
KASSERT(tmpp != NULL, ("tarfs mount return is NULL"));
867
ASSERT_VOP_LOCKED(vp, __func__);
868
869
tmp = NULL;
870
871
TARFS_DPF(ALLOC, "%s: Allocating tarfs mount structure for vp %p\n",
872
__func__, vp);
873
874
/* Get source metadata */
875
error = VOP_GETATTR(vp, &va, td->td_ucred);
876
if (error != 0) {
877
return (error);
878
}
879
VOP_UNLOCK(vp);
880
mtime = va.va_mtime.tv_sec;
881
882
mp->mnt_iosize_max = vp->v_mount->mnt_iosize_max;
883
884
/* Allocate and initialize tarfs mount structure */
885
tmp = malloc(sizeof(*tmp), M_TARFSMNT, M_WAITOK | M_ZERO);
886
TARFS_DPF(ALLOC, "%s: Allocated mount structure\n", __func__);
887
mp->mnt_data = tmp;
888
889
mtx_init(&tmp->allnode_lock, "tarfs allnode lock", NULL,
890
MTX_DEF);
891
TAILQ_INIT(&tmp->allnodes);
892
tmp->ino_unr = new_unrhdr(TARFS_MININO, INT_MAX, &tmp->allnode_lock);
893
tmp->vp = vp;
894
tmp->vfs = mp;
895
tmp->mtime = mtime;
896
897
/* Initialize I/O layer */
898
tmp->iosize = 1U << tarfs_ioshift;
899
error = tarfs_io_init(tmp);
900
if (error != 0)
901
goto bad;
902
903
error = tarfs_alloc_node(tmp, NULL, 0, VDIR, 0, 0, mtime, root_uid,
904
root_gid, root_mode & ALLPERMS, 0, NULL, NODEV, NULL, &root);
905
if (error != 0 || root == NULL)
906
goto bad;
907
tmp->root = root;
908
909
blknum = 0;
910
do {
911
if ((error = tarfs_alloc_one(tmp, &blknum)) != 0) {
912
printf("unsupported or corrupt tar file at %zu\n",
913
TARFS_BLOCKSIZE * blknum);
914
goto bad;
915
}
916
} while (blknum != TAR_EOF);
917
918
*tmpp = tmp;
919
920
TARFS_DPF(ALLOC, "%s: pfsmnt_root %p\n", __func__, tmp->root);
921
return (0);
922
923
bad:
924
tarfs_free_mount(tmp);
925
return (error);
926
}
927
928
/*
929
* VFS Operations.
930
*/
931
932
static int
933
tarfs_mount(struct mount *mp)
934
{
935
struct nameidata nd;
936
struct vattr va;
937
struct tarfs_mount *tmp = NULL;
938
struct thread *td = curthread;
939
struct vnode *vp;
940
char *as, *from;
941
uid_t root_uid;
942
gid_t root_gid;
943
mode_t root_mode;
944
int error, flags, aslen, len;
945
946
if (mp->mnt_flag & MNT_UPDATE)
947
return (EOPNOTSUPP);
948
949
if (vfs_filteropt(mp->mnt_optnew, tarfs_opts))
950
return (EINVAL);
951
952
vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY);
953
error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred);
954
VOP_UNLOCK(mp->mnt_vnodecovered);
955
if (error)
956
return (error);
957
958
if (mp->mnt_cred->cr_ruid != 0 ||
959
vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1)
960
root_gid = va.va_gid;
961
if (mp->mnt_cred->cr_ruid != 0 ||
962
vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1)
963
root_uid = va.va_uid;
964
if (mp->mnt_cred->cr_ruid != 0 ||
965
vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
966
root_mode = va.va_mode;
967
968
error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len);
969
if (error != 0 || from[len - 1] != '\0')
970
return (EINVAL);
971
error = vfs_getopt(mp->mnt_optnew, "as", (void **)&as, &aslen);
972
if (error != 0 || as[aslen - 1] != '\0')
973
as = from;
974
975
/* Find the source tarball */
976
TARFS_DPF(FS, "%s(%s%s%s, uid=%u, gid=%u, mode=%o)\n", __func__,
977
from, (as != from) ? " as " : "", (as != from) ? as : "",
978
root_uid, root_gid, root_mode);
979
flags = FREAD;
980
if (vfs_flagopt(mp->mnt_optnew, "verify", NULL, 0)) {
981
flags |= O_VERIFY;
982
}
983
NDINIT(&nd, LOOKUP, ISOPEN | FOLLOW | LOCKLEAF, UIO_SYSSPACE, from);
984
error = namei(&nd);
985
if (error != 0)
986
return (error);
987
NDFREE_PNBUF(&nd);
988
vp = nd.ni_vp;
989
TARFS_DPF(FS, "%s: N: hold %u use %u lock 0x%x\n", __func__,
990
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
991
/* vp is now held and locked */
992
993
/* Open the source tarball */
994
error = vn_open_vnode(vp, flags, td->td_ucred, td, NULL);
995
if (error != 0) {
996
TARFS_DPF(FS, "%s: failed to open %s: %d\n", __func__,
997
from, error);
998
vput(vp);
999
goto bad;
1000
}
1001
TARFS_DPF(FS, "%s: O: hold %u use %u lock 0x%x\n", __func__,
1002
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1003
if (vp->v_type != VREG) {
1004
TARFS_DPF(FS, "%s: not a regular file\n", __func__);
1005
error = EOPNOTSUPP;
1006
goto bad_open_locked;
1007
}
1008
error = priv_check(td, PRIV_VFS_MOUNT_PERM);
1009
if (error != 0) {
1010
TARFS_DPF(FS, "%s: not permitted to mount\n", __func__);
1011
goto bad_open_locked;
1012
}
1013
if (flags & O_VERIFY) {
1014
mp->mnt_flag |= MNT_VERIFIED;
1015
}
1016
1017
/* Allocate the tarfs mount */
1018
error = tarfs_alloc_mount(mp, vp, root_uid, root_gid, root_mode, &tmp);
1019
/* vp is now held but unlocked */
1020
if (error != 0) {
1021
TARFS_DPF(FS, "%s: failed to mount %s: %d\n", __func__,
1022
from, error);
1023
goto bad_open_unlocked;
1024
}
1025
TARFS_DPF(FS, "%s: M: hold %u use %u lock 0x%x\n", __func__,
1026
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1027
1028
/* Unconditionally mount as read-only */
1029
MNT_ILOCK(mp);
1030
mp->mnt_flag |= (MNT_LOCAL | MNT_RDONLY);
1031
MNT_IUNLOCK(mp);
1032
1033
vfs_getnewfsid(mp);
1034
vfs_mountedfrom(mp, as);
1035
TARFS_DPF(FS, "%s: success\n", __func__);
1036
1037
return (0);
1038
1039
bad_open_locked:
1040
/* vp must be held and locked */
1041
TARFS_DPF(FS, "%s: L: hold %u use %u lock 0x%x\n", __func__,
1042
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1043
VOP_UNLOCK(vp);
1044
bad_open_unlocked:
1045
/* vp must be held and unlocked */
1046
TARFS_DPF(FS, "%s: E: hold %u use %u lock 0x%x\n", __func__,
1047
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1048
(void)vn_close(vp, flags, td->td_ucred, td);
1049
bad:
1050
/* vp must be released and unlocked */
1051
TARFS_DPF(FS, "%s: X: hold %u use %u lock 0x%x\n", __func__,
1052
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1053
return (error);
1054
}
1055
1056
/*
1057
* Unmounts a tarfs filesystem.
1058
*/
1059
static int
1060
tarfs_unmount(struct mount *mp, int mntflags)
1061
{
1062
struct thread *td = curthread;
1063
struct tarfs_mount *tmp;
1064
struct vnode *vp;
1065
int error;
1066
int flags = 0;
1067
1068
TARFS_DPF(FS, "%s: Unmounting %p\n", __func__, mp);
1069
1070
/* Handle forced unmounts */
1071
if (mntflags & MNT_FORCE)
1072
flags |= FORCECLOSE;
1073
1074
/* Finalize all pending I/O */
1075
error = vflush(mp, 0, flags, curthread);
1076
if (error != 0)
1077
return (error);
1078
tmp = MP_TO_TARFS_MOUNT(mp);
1079
vp = tmp->vp;
1080
1081
MPASS(vp != NULL);
1082
TARFS_DPF(FS, "%s: U: hold %u use %u lock 0x%x\n", __func__,
1083
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1084
vn_close(vp, FREAD, td->td_ucred, td);
1085
TARFS_DPF(FS, "%s: C: hold %u use %u lock 0x%x\n", __func__,
1086
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1087
tarfs_free_mount(tmp);
1088
1089
return (0);
1090
}
1091
1092
/*
1093
* Gets the root of a tarfs filesystem. Returns 0 on success or a
1094
* positive errno value on failure.
1095
*/
1096
static int
1097
tarfs_root(struct mount *mp, int flags, struct vnode **vpp)
1098
{
1099
struct vnode *nvp;
1100
int error;
1101
1102
TARFS_DPF(FS, "%s: Getting root vnode\n", __func__);
1103
1104
error = VFS_VGET(mp, TARFS_ROOTINO, LK_EXCLUSIVE, &nvp);
1105
if (error != 0)
1106
return (error);
1107
1108
nvp->v_vflag |= VV_ROOT;
1109
*vpp = nvp;
1110
return (0);
1111
}
1112
1113
/*
1114
* Gets statistics for a tarfs filesystem. Returns 0.
1115
*/
1116
static int
1117
tarfs_statfs(struct mount *mp, struct statfs *sbp)
1118
{
1119
struct tarfs_mount *tmp;
1120
1121
tmp = MP_TO_TARFS_MOUNT(mp);
1122
1123
sbp->f_bsize = TARFS_BLOCKSIZE;
1124
sbp->f_iosize = tmp->iosize;
1125
sbp->f_blocks = tmp->nblocks;
1126
sbp->f_bfree = 0;
1127
sbp->f_bavail = 0;
1128
sbp->f_files = tmp->nfiles;
1129
sbp->f_ffree = 0;
1130
1131
return (0);
1132
}
1133
1134
/*
1135
* Gets a vnode for the given inode. On success, a pointer to the vnode
1136
* is stored in vpp. Returns 0 on success or a positive errno value on
1137
* failure.
1138
*/
1139
static int
1140
tarfs_vget(struct mount *mp, ino_t ino, int lkflags, struct vnode **vpp)
1141
{
1142
struct tarfs_mount *tmp;
1143
struct tarfs_node *tnp;
1144
struct thread *td;
1145
struct vnode *vp;
1146
int error;
1147
1148
TARFS_DPF(FS, "%s: mp %p, ino %lu, lkflags %d\n", __func__, mp, ino,
1149
lkflags);
1150
1151
td = curthread;
1152
error = vfs_hash_get(mp, ino, lkflags, td, vpp, NULL, NULL);
1153
if (error != 0)
1154
return (error);
1155
1156
if (*vpp != NULL) {
1157
TARFS_DPF(FS, "%s: found hashed vnode %p\n", __func__, *vpp);
1158
return (error);
1159
}
1160
1161
TARFS_DPF(FS, "%s: no hashed vnode for inode %lu\n", __func__, ino);
1162
1163
tmp = MP_TO_TARFS_MOUNT(mp);
1164
1165
if (ino == TARFS_ZIOINO) {
1166
error = vget(tmp->znode, lkflags);
1167
if (error != 0)
1168
return (error);
1169
*vpp = tmp->znode;
1170
return (0);
1171
}
1172
1173
/* XXX Should use hash instead? */
1174
TAILQ_FOREACH(tnp, &tmp->allnodes, entries) {
1175
if (tnp->ino == ino)
1176
break;
1177
}
1178
TARFS_DPF(FS, "%s: search of all nodes found %p\n", __func__, tnp);
1179
if (tnp == NULL)
1180
return (ENOENT);
1181
1182
(void)getnewvnode("tarfs", mp, &tarfs_vnodeops, &vp);
1183
TARFS_DPF(FS, "%s: allocated vnode\n", __func__);
1184
vp->v_data = tnp;
1185
vp->v_type = tnp->type;
1186
tnp->vnode = vp;
1187
1188
lockmgr(vp->v_vnlock, lkflags, NULL);
1189
error = insmntque(vp, mp);
1190
if (error != 0)
1191
goto bad;
1192
TARFS_DPF(FS, "%s: inserting entry into VFS hash\n", __func__);
1193
error = vfs_hash_insert(vp, ino, lkflags, td, vpp, NULL, NULL);
1194
if (error != 0 || *vpp != NULL)
1195
return (error);
1196
1197
vn_set_state(vp, VSTATE_CONSTRUCTED);
1198
*vpp = vp;
1199
return (0);
1200
1201
bad:
1202
*vpp = NULL;
1203
return (error);
1204
}
1205
1206
static int
1207
tarfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
1208
{
1209
struct tarfs_node *tnp;
1210
struct tarfs_fid *tfp;
1211
struct vnode *nvp;
1212
int error;
1213
1214
tfp = (struct tarfs_fid *)fhp;
1215
MP_TO_TARFS_MOUNT(mp);
1216
if (tfp->ino < TARFS_ROOTINO || tfp->ino > INT_MAX)
1217
return (ESTALE);
1218
1219
error = VFS_VGET(mp, tfp->ino, LK_EXCLUSIVE, &nvp);
1220
if (error != 0) {
1221
*vpp = NULL;
1222
return (error);
1223
}
1224
tnp = VP_TO_TARFS_NODE(nvp);
1225
if (tnp->mode == 0 ||
1226
tnp->gen != tfp->gen ||
1227
tnp->nlink <= 0) {
1228
vput(nvp);
1229
*vpp = NULL;
1230
return (ESTALE);
1231
}
1232
*vpp = nvp;
1233
return (0);
1234
}
1235
1236
static struct vfsops tarfs_vfsops = {
1237
.vfs_fhtovp = tarfs_fhtovp,
1238
.vfs_mount = tarfs_mount,
1239
.vfs_root = tarfs_root,
1240
.vfs_statfs = tarfs_statfs,
1241
.vfs_unmount = tarfs_unmount,
1242
.vfs_vget = tarfs_vget,
1243
};
1244
VFS_SET(tarfs_vfsops, tarfs, VFCF_READONLY);
1245
MODULE_VERSION(tarfs, 1);
1246
MODULE_DEPEND(tarfs, xz, 1, 1, 1);
1247
1248