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, 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 TARFS_DPF(ALLOC, "%s: exthdr syntax error\n",
592 __func__);
593 error = EINVAL;
594 goto bad;
595 }
596 if ((uintptr_t)line + len < (uintptr_t)line ||
597 line + len > exthdr + sz) {
598 TARFS_DPF(ALLOC, "%s: exthdr overflow\n",
599 __func__);
600 error = EINVAL;
601 goto bad;
602 }
603 eol = line + len - 1;
604 *eol = '\0';
605 line += len;
606 key = sep + 1;
607 sep = strchr(key, '=');
608 if (sep == NULL) {
609 TARFS_DPF(ALLOC, "%s: exthdr syntax error\n",
610 __func__);
611 error = EINVAL;
612 goto bad;
613 }
614 *sep = '\0';
615 value = sep + 1;
616 TARFS_DPF(ALLOC, "%s: exthdr %s=%s\n", __func__,
617 key, value);
618 if (strcmp(key, "path") == 0) {
619 name = value;
620 namelen = eol - value;
621 } else if (strcmp(key, "linkpath") == 0) {
622 link = value;
623 linklen = eol - value;
624 } else if (strcmp(key, "GNU.sparse.major") == 0) {
625 sparse = true;
626 major = strtol(value, &sep, 10);
627 if (sep != eol) {
628 printf("exthdr syntax error\n");
629 error = EINVAL;
630 goto bad;
631 }
632 } else if (strcmp(key, "GNU.sparse.minor") == 0) {
633 sparse = true;
634 minor = strtol(value, &sep, 10);
635 if (sep != eol) {
636 printf("exthdr syntax error\n");
637 error = EINVAL;
638 goto bad;
639 }
640 } else if (strcmp(key, "GNU.sparse.name") == 0) {
641 sparse = true;
642 name = value;
643 namelen = eol - value;
644 if (namelen == 0) {
645 printf("exthdr syntax error\n");
646 error = EINVAL;
647 goto bad;
648 }
649 } else if (strcmp(key, "GNU.sparse.realsize") == 0) {
650 sparse = true;
651 realsize = strtoul(value, &sep, 10);
652 if (sep != eol) {
653 printf("exthdr syntax error\n");
654 error = EINVAL;
655 goto bad;
656 }
657 } else if (strcmp(key, "SCHILY.fflags") == 0) {
658 flags |= tarfs_strtofflags(value, &sep);
659 if (sep != eol) {
660 printf("exthdr syntax error\n");
661 error = EINVAL;
662 goto bad;
663 }
664 }
665 }
666 goto again;
667 }
668
669 /* sparse file consistency checks */
670 if (sparse) {
671 TARFS_DPF(ALLOC, "%s: %s: sparse %ld.%ld (%zu bytes)\n", __func__,
672 name, major, minor, realsize);
673 if (major != 1 || minor != 0 || name == NULL || realsize == 0 ||
674 hdrp->typeflag[0] != TAR_TYPE_FILE) {
675 TARFS_DPF(ALLOC, "%s: invalid sparse format\n", __func__);
676 error = EINVAL;
677 goto bad;
678 }
679 }
680
681 /* file name */
682 if (name == NULL) {
683 if (hdrp->prefix[0] != '\0') {
684 namebuf = sbuf_new_auto();
685 sbuf_printf(namebuf, "%.*s/%.*s",
686 (int)sizeof(hdrp->prefix), hdrp->prefix,
687 (int)sizeof(hdrp->name), hdrp->name);
688 sbuf_finish(namebuf);
689 name = sbuf_data(namebuf);
690 namelen = sbuf_len(namebuf);
691 } else {
692 name = hdrp->name;
693 namelen = strnlen(hdrp->name, sizeof(hdrp->name));
694 }
695 }
696
697 error = tarfs_lookup_path(tmp, name, namelen, &namep,
698 &sep, &parent, &tnp, true);
699 if (error != 0) {
700 TARFS_DPF(ALLOC, "%s: failed to look up %.*s\n", __func__,
701 (int)namelen, name);
702 error = EINVAL;
703 goto bad;
704 }
705 if (tnp != NULL) {
706 if (hdrp->typeflag[0] == TAR_TYPE_DIRECTORY) {
707 /* XXX set attributes? */
708 goto skip;
709 }
710 TARFS_DPF(ALLOC, "%s: duplicate file %.*s\n", __func__,
711 (int)namelen, name);
712 error = EINVAL;
713 goto bad;
714 }
715 switch (hdrp->typeflag[0]) {
716 case TAR_TYPE_DIRECTORY:
717 error = tarfs_alloc_node(tmp, namep, sep - namep, VDIR,
718 0, 0, mtime, uid, gid, mode, flags, NULL, 0,
719 parent, &tnp);
720 break;
721 case TAR_TYPE_FILE:
722 error = tarfs_alloc_node(tmp, namep, sep - namep, VREG,
723 blknum * TARFS_BLOCKSIZE, sz, mtime, uid, gid, mode,
724 flags, NULL, 0, parent, &tnp);
725 if (error == 0 && sparse) {
726 error = tarfs_load_blockmap(tnp, realsize);
727 }
728 break;
729 case TAR_TYPE_HARDLINK:
730 if (link == NULL) {
731 link = hdrp->linkname;
732 linklen = strnlen(link, sizeof(hdrp->linkname));
733 }
734 if (linklen == 0) {
735 TARFS_DPF(ALLOC, "%s: %.*s: link without target\n",
736 __func__, (int)namelen, name);
737 error = EINVAL;
738 goto bad;
739 }
740 error = tarfs_lookup_path(tmp, link, linklen, NULL,
741 NULL, NULL, &other, false);
742 if (error != 0 || other == NULL ||
743 other->type != VREG || other->other != NULL) {
744 TARFS_DPF(ALLOC, "%s: %.*s: invalid link to %.*s\n",
745 __func__, (int)namelen, name, (int)linklen, link);
746 error = EINVAL;
747 goto bad;
748 }
749 error = tarfs_alloc_node(tmp, namep, sep - namep, VREG,
750 0, 0, 0, 0, 0, 0, 0, NULL, 0, parent, &tnp);
751 if (error == 0) {
752 tnp->other = other;
753 tnp->other->nlink++;
754 }
755 break;
756 case TAR_TYPE_SYMLINK:
757 if (link == NULL) {
758 link = hdrp->linkname;
759 linklen = strnlen(link, sizeof(hdrp->linkname));
760 }
761 if (linklen == 0) {
762 TARFS_DPF(ALLOC, "%s: %.*s: link without target\n",
763 __func__, (int)namelen, name);
764 error = EINVAL;
765 goto bad;
766 }
767 error = tarfs_alloc_node(tmp, namep, sep - namep, VLNK,
768 0, linklen, mtime, uid, gid, mode, flags, link, 0,
769 parent, &tnp);
770 break;
771 case TAR_TYPE_BLOCK:
772 if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 ||
773 num < 0 || num > INT_MAX) {
774 TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n",
775 __func__, (int)namelen, name);
776 error = EINVAL;
777 goto bad;
778 }
779 major = num;
780 if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 ||
781 num < 0 || num > INT_MAX) {
782 TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n",
783 __func__, (int)namelen, name);
784 error = EINVAL;
785 goto bad;
786 }
787 minor = num;
788 rdev = makedev(major, minor);
789 error = tarfs_alloc_node(tmp, namep, sep - namep, VBLK,
790 0, 0, mtime, uid, gid, mode, flags, NULL, rdev,
791 parent, &tnp);
792 break;
793 case TAR_TYPE_CHAR:
794 if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 ||
795 num < 0 || num > INT_MAX) {
796 TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n",
797 __func__, (int)namelen, name);
798 error = EINVAL;
799 goto bad;
800 }
801 major = num;
802 if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 ||
803 num < 0 || num > INT_MAX) {
804 TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n",
805 __func__, (int)namelen, name);
806 error = EINVAL;
807 goto bad;
808 }
809 minor = num;
810 rdev = makedev(major, minor);
811 error = tarfs_alloc_node(tmp, namep, sep - namep, VCHR,
812 0, 0, mtime, uid, gid, mode, flags, NULL, rdev,
813 parent, &tnp);
814 break;
815 default:
816 TARFS_DPF(ALLOC, "%s: unsupported type %c for %.*s\n",
817 __func__, hdrp->typeflag[0], (int)namelen, name);
818 error = EINVAL;
819 break;
820 }
821 if (error != 0)
822 goto bad;
823
824 skip:
825 blknum += TARFS_SZ2BLKS(sz);
826 tmp->nblocks = blknum;
827 *blknump = blknum;
828 if (exthdr != NULL) {
829 free(exthdr, M_TEMP);
830 }
831 if (namebuf != NULL) {
832 sbuf_delete(namebuf);
833 }
834 return (0);
835 eof:
836 TARFS_DPF(IO, "%s: premature end of file\n", __func__);
837 error = EIO;
838 goto bad;
839 bad:
840 if (exthdr != NULL) {
841 free(exthdr, M_TEMP);
842 }
843 if (namebuf != NULL) {
844 sbuf_delete(namebuf);
845 }
846 return (error);
847 }
848
849 /*
850 * Allocates and populates the metadata structures for the tar file
851 * referenced by vp. On success, a pointer to the tarfs_mount structure
852 * is stored in tmpp. Returns 0 on success or a positive errno value on
853 * failure.
854 */
855 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)856 tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
857 uid_t root_uid, gid_t root_gid, mode_t root_mode,
858 struct tarfs_mount **tmpp)
859 {
860 struct vattr va;
861 struct thread *td = curthread;
862 struct tarfs_mount *tmp;
863 struct tarfs_node *root;
864 size_t blknum;
865 time_t mtime;
866 int error;
867
868 KASSERT(tmpp != NULL, ("tarfs mount return is NULL"));
869 ASSERT_VOP_LOCKED(vp, __func__);
870
871 tmp = NULL;
872
873 TARFS_DPF(ALLOC, "%s: Allocating tarfs mount structure for vp %p\n",
874 __func__, vp);
875
876 /* Get source metadata */
877 error = VOP_GETATTR(vp, &va, td->td_ucred);
878 if (error != 0) {
879 return (error);
880 }
881 VOP_UNLOCK(vp);
882 mtime = va.va_mtime.tv_sec;
883
884 mp->mnt_iosize_max = vp->v_mount->mnt_iosize_max;
885
886 /* Allocate and initialize tarfs mount structure */
887 tmp = malloc(sizeof(*tmp), M_TARFSMNT, M_WAITOK | M_ZERO);
888 TARFS_DPF(ALLOC, "%s: Allocated mount structure\n", __func__);
889 mp->mnt_data = tmp;
890
891 mtx_init(&tmp->allnode_lock, "tarfs allnode lock", NULL,
892 MTX_DEF);
893 TAILQ_INIT(&tmp->allnodes);
894 tmp->ino_unr = new_unrhdr(TARFS_MININO, INT_MAX, &tmp->allnode_lock);
895 tmp->vp = vp;
896 tmp->vfs = mp;
897 tmp->mtime = mtime;
898
899 /* Initialize I/O layer */
900 tmp->iosize = 1U << tarfs_ioshift;
901 error = tarfs_io_init(tmp);
902 if (error != 0)
903 goto bad;
904
905 error = tarfs_alloc_node(tmp, NULL, 0, VDIR, 0, 0, mtime, root_uid,
906 root_gid, root_mode & ALLPERMS, 0, NULL, NODEV, NULL, &root);
907 if (error != 0 || root == NULL)
908 goto bad;
909 tmp->root = root;
910
911 blknum = 0;
912 do {
913 if ((error = tarfs_alloc_one(tmp, &blknum)) != 0) {
914 printf("unsupported or corrupt tar file at %zu\n",
915 TARFS_BLOCKSIZE * blknum);
916 goto bad;
917 }
918 } while (blknum != TAR_EOF);
919
920 *tmpp = tmp;
921
922 TARFS_DPF(ALLOC, "%s: pfsmnt_root %p\n", __func__, tmp->root);
923 return (0);
924
925 bad:
926 tarfs_free_mount(tmp);
927 return (error);
928 }
929
930 /*
931 * VFS Operations.
932 */
933
934 static int
tarfs_mount(struct mount * mp)935 tarfs_mount(struct mount *mp)
936 {
937 struct nameidata nd;
938 struct vattr va;
939 struct tarfs_mount *tmp = NULL;
940 struct thread *td = curthread;
941 struct vnode *vp;
942 char *as, *from;
943 uid_t root_uid;
944 gid_t root_gid;
945 mode_t root_mode;
946 int error, flags, aslen, len;
947
948 if (mp->mnt_flag & MNT_UPDATE)
949 return (EOPNOTSUPP);
950
951 if (vfs_filteropt(mp->mnt_optnew, tarfs_opts))
952 return (EINVAL);
953
954 vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY);
955 error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred);
956 VOP_UNLOCK(mp->mnt_vnodecovered);
957 if (error)
958 return (error);
959
960 if (mp->mnt_cred->cr_ruid != 0 ||
961 vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1)
962 root_gid = va.va_gid;
963 if (mp->mnt_cred->cr_ruid != 0 ||
964 vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1)
965 root_uid = va.va_uid;
966 if (mp->mnt_cred->cr_ruid != 0 ||
967 vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
968 root_mode = va.va_mode;
969
970 error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len);
971 if (error != 0 || from[len - 1] != '\0')
972 return (EINVAL);
973 error = vfs_getopt(mp->mnt_optnew, "as", (void **)&as, &aslen);
974 if (error != 0 || as[aslen - 1] != '\0')
975 as = from;
976
977 /* Find the source tarball */
978 TARFS_DPF(FS, "%s(%s%s%s, uid=%u, gid=%u, mode=%o)\n", __func__,
979 from, (as != from) ? " as " : "", (as != from) ? as : "",
980 root_uid, root_gid, root_mode);
981 flags = FREAD;
982 if (vfs_flagopt(mp->mnt_optnew, "verify", NULL, 0)) {
983 flags |= O_VERIFY;
984 }
985 NDINIT(&nd, LOOKUP, ISOPEN | FOLLOW | LOCKLEAF, UIO_SYSSPACE, from);
986 error = namei(&nd);
987 if (error != 0)
988 return (error);
989 NDFREE_PNBUF(&nd);
990 vp = nd.ni_vp;
991 TARFS_DPF(FS, "%s: N: hold %u use %u lock 0x%x\n", __func__,
992 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
993 /* vp is now held and locked */
994
995 /* Open the source tarball */
996 error = vn_open_vnode(vp, flags, td->td_ucred, td, NULL);
997 if (error != 0) {
998 TARFS_DPF(FS, "%s: failed to open %s: %d\n", __func__,
999 from, error);
1000 vput(vp);
1001 goto bad;
1002 }
1003 TARFS_DPF(FS, "%s: O: hold %u use %u lock 0x%x\n", __func__,
1004 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1005 if (vp->v_type != VREG) {
1006 TARFS_DPF(FS, "%s: not a regular file\n", __func__);
1007 error = EOPNOTSUPP;
1008 goto bad_open_locked;
1009 }
1010 error = priv_check(td, PRIV_VFS_MOUNT_PERM);
1011 if (error != 0) {
1012 TARFS_DPF(FS, "%s: not permitted to mount\n", __func__);
1013 goto bad_open_locked;
1014 }
1015 if (flags & O_VERIFY) {
1016 mp->mnt_flag |= MNT_VERIFIED;
1017 }
1018
1019 /* Allocate the tarfs mount */
1020 error = tarfs_alloc_mount(mp, vp, root_uid, root_gid, root_mode, &tmp);
1021 /* vp is now held but unlocked */
1022 if (error != 0) {
1023 TARFS_DPF(FS, "%s: failed to mount %s: %d\n", __func__,
1024 from, error);
1025 goto bad_open_unlocked;
1026 }
1027 TARFS_DPF(FS, "%s: M: hold %u use %u lock 0x%x\n", __func__,
1028 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1029
1030 /* Unconditionally mount as read-only */
1031 MNT_ILOCK(mp);
1032 mp->mnt_flag |= (MNT_LOCAL | MNT_RDONLY);
1033 MNT_IUNLOCK(mp);
1034
1035 vfs_getnewfsid(mp);
1036 vfs_mountedfrom(mp, as);
1037 TARFS_DPF(FS, "%s: success\n", __func__);
1038
1039 return (0);
1040
1041 bad_open_locked:
1042 /* vp must be held and locked */
1043 TARFS_DPF(FS, "%s: L: hold %u use %u lock 0x%x\n", __func__,
1044 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1045 VOP_UNLOCK(vp);
1046 bad_open_unlocked:
1047 /* vp must be held and unlocked */
1048 TARFS_DPF(FS, "%s: E: hold %u use %u lock 0x%x\n", __func__,
1049 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1050 (void)vn_close(vp, flags, td->td_ucred, td);
1051 bad:
1052 /* vp must be released and unlocked */
1053 TARFS_DPF(FS, "%s: X: hold %u use %u lock 0x%x\n", __func__,
1054 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1055 return (error);
1056 }
1057
1058 /*
1059 * Unmounts a tarfs filesystem.
1060 */
1061 static int
tarfs_unmount(struct mount * mp,int mntflags)1062 tarfs_unmount(struct mount *mp, int mntflags)
1063 {
1064 struct thread *td = curthread;
1065 struct tarfs_mount *tmp;
1066 struct vnode *vp;
1067 int error;
1068 int flags = 0;
1069
1070 TARFS_DPF(FS, "%s: Unmounting %p\n", __func__, mp);
1071
1072 /* Handle forced unmounts */
1073 if (mntflags & MNT_FORCE)
1074 flags |= FORCECLOSE;
1075
1076 /* Finalize all pending I/O */
1077 error = vflush(mp, 0, flags, curthread);
1078 if (error != 0)
1079 return (error);
1080 tmp = MP_TO_TARFS_MOUNT(mp);
1081 vp = tmp->vp;
1082
1083 MPASS(vp != NULL);
1084 TARFS_DPF(FS, "%s: U: hold %u use %u lock 0x%x\n", __func__,
1085 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1086 vn_close(vp, FREAD, td->td_ucred, td);
1087 TARFS_DPF(FS, "%s: C: hold %u use %u lock 0x%x\n", __func__,
1088 vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
1089 tarfs_free_mount(tmp);
1090
1091 return (0);
1092 }
1093
1094 /*
1095 * Gets the root of a tarfs filesystem. Returns 0 on success or a
1096 * positive errno value on failure.
1097 */
1098 static int
tarfs_root(struct mount * mp,int flags,struct vnode ** vpp)1099 tarfs_root(struct mount *mp, int flags, struct vnode **vpp)
1100 {
1101 struct vnode *nvp;
1102 int error;
1103
1104 TARFS_DPF(FS, "%s: Getting root vnode\n", __func__);
1105
1106 error = VFS_VGET(mp, TARFS_ROOTINO, LK_EXCLUSIVE, &nvp);
1107 if (error != 0)
1108 return (error);
1109
1110 nvp->v_vflag |= VV_ROOT;
1111 *vpp = nvp;
1112 return (0);
1113 }
1114
1115 /*
1116 * Gets statistics for a tarfs filesystem. Returns 0.
1117 */
1118 static int
tarfs_statfs(struct mount * mp,struct statfs * sbp)1119 tarfs_statfs(struct mount *mp, struct statfs *sbp)
1120 {
1121 struct tarfs_mount *tmp;
1122
1123 tmp = MP_TO_TARFS_MOUNT(mp);
1124
1125 sbp->f_bsize = TARFS_BLOCKSIZE;
1126 sbp->f_iosize = tmp->iosize;
1127 sbp->f_blocks = tmp->nblocks;
1128 sbp->f_bfree = 0;
1129 sbp->f_bavail = 0;
1130 sbp->f_files = tmp->nfiles;
1131 sbp->f_ffree = 0;
1132
1133 return (0);
1134 }
1135
1136 /*
1137 * Gets a vnode for the given inode. On success, a pointer to the vnode
1138 * is stored in vpp. Returns 0 on success or a positive errno value on
1139 * failure.
1140 */
1141 static int
tarfs_vget(struct mount * mp,ino_t ino,int lkflags,struct vnode ** vpp)1142 tarfs_vget(struct mount *mp, ino_t ino, int lkflags, struct vnode **vpp)
1143 {
1144 struct tarfs_mount *tmp;
1145 struct tarfs_node *tnp;
1146 struct thread *td;
1147 struct vnode *vp;
1148 int error;
1149
1150 TARFS_DPF(FS, "%s: mp %p, ino %lu, lkflags %d\n", __func__, mp, ino,
1151 lkflags);
1152
1153 td = curthread;
1154 error = vfs_hash_get(mp, ino, lkflags, td, vpp, NULL, NULL);
1155 if (error != 0)
1156 return (error);
1157
1158 if (*vpp != NULL) {
1159 TARFS_DPF(FS, "%s: found hashed vnode %p\n", __func__, *vpp);
1160 return (error);
1161 }
1162
1163 TARFS_DPF(FS, "%s: no hashed vnode for inode %lu\n", __func__, ino);
1164
1165 tmp = MP_TO_TARFS_MOUNT(mp);
1166
1167 if (ino == TARFS_ZIOINO) {
1168 error = vget(tmp->znode, lkflags);
1169 if (error != 0)
1170 return (error);
1171 *vpp = tmp->znode;
1172 return (0);
1173 }
1174
1175 /* XXX Should use hash instead? */
1176 TAILQ_FOREACH(tnp, &tmp->allnodes, entries) {
1177 if (tnp->ino == ino)
1178 break;
1179 }
1180 TARFS_DPF(FS, "%s: search of all nodes found %p\n", __func__, tnp);
1181 if (tnp == NULL)
1182 return (ENOENT);
1183
1184 (void)getnewvnode("tarfs", mp, &tarfs_vnodeops, &vp);
1185 TARFS_DPF(FS, "%s: allocated vnode\n", __func__);
1186 vp->v_data = tnp;
1187 vp->v_type = tnp->type;
1188 tnp->vnode = vp;
1189
1190 lockmgr(vp->v_vnlock, lkflags, NULL);
1191 error = insmntque(vp, mp);
1192 if (error != 0)
1193 goto bad;
1194 TARFS_DPF(FS, "%s: inserting entry into VFS hash\n", __func__);
1195 error = vfs_hash_insert(vp, ino, lkflags, td, vpp, NULL, NULL);
1196 if (error != 0 || *vpp != NULL)
1197 return (error);
1198
1199 vn_set_state(vp, VSTATE_CONSTRUCTED);
1200 *vpp = vp;
1201 return (0);
1202
1203 bad:
1204 *vpp = NULLVP;
1205 return (error);
1206 }
1207
1208 static int
tarfs_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)1209 tarfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
1210 {
1211 struct tarfs_node *tnp;
1212 struct tarfs_fid *tfp;
1213 struct vnode *nvp;
1214 int error;
1215
1216 tfp = (struct tarfs_fid *)fhp;
1217 MP_TO_TARFS_MOUNT(mp);
1218 if (tfp->ino < TARFS_ROOTINO || tfp->ino > INT_MAX)
1219 return (ESTALE);
1220
1221 error = VFS_VGET(mp, tfp->ino, LK_EXCLUSIVE, &nvp);
1222 if (error != 0) {
1223 *vpp = NULLVP;
1224 return (error);
1225 }
1226 tnp = VP_TO_TARFS_NODE(nvp);
1227 if (tnp->mode == 0 ||
1228 tnp->gen != tfp->gen ||
1229 tnp->nlink <= 0) {
1230 vput(nvp);
1231 *vpp = NULLVP;
1232 return (ESTALE);
1233 }
1234 *vpp = nvp;
1235 return (0);
1236 }
1237
1238 static struct vfsops tarfs_vfsops = {
1239 .vfs_fhtovp = tarfs_fhtovp,
1240 .vfs_mount = tarfs_mount,
1241 .vfs_root = tarfs_root,
1242 .vfs_statfs = tarfs_statfs,
1243 .vfs_unmount = tarfs_unmount,
1244 .vfs_vget = tarfs_vget,
1245 };
1246 VFS_SET(tarfs_vfsops, tarfs, VFCF_READONLY);
1247 MODULE_VERSION(tarfs, 1);
1248 MODULE_DEPEND(tarfs, xz, 1, 1, 1);
1249