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
tarfs_str2octal(const char * strp,size_t len,int64_t * num)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
tarfs_str2base256(const char * strp,size_t len,int64_t * num)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
tarfs_str2int64(const char * strp,size_t len,int64_t * num)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
tarfs_checksum(struct ustar_header * hdrp)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
tarfs_lookup_path(struct tarfs_mount * tmp,char * name,size_t namelen,char ** endp,char ** sepp,struct tarfs_node ** retparent,struct tarfs_node ** retnode,boolean_t create_dirs)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
tarfs_free_mount(struct tarfs_mount * tmp)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
tarfs_alloc_one(struct tarfs_mount * tmp,size_t * blknump)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
tarfs_alloc_mount(struct mount * mp,struct vnode * vp,uid_t root_uid,gid_t root_gid,mode_t root_mode,struct tarfs_mount ** tmpp)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
tarfs_mount(struct mount * mp)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
tarfs_unmount(struct mount * mp,int mntflags)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
tarfs_root(struct mount * mp,int flags,struct vnode ** vpp)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
tarfs_statfs(struct mount * mp,struct statfs * sbp)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
tarfs_vget(struct mount * mp,ino_t ino,int lkflags,struct vnode ** vpp)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
tarfs_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)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