Changeset View
Standalone View
sys/fs/tarfs/tarfs_vfsops.c
- This file was added.
/*- | |||||
* Copyright (c) 2013 Juniper Networks, Inc. | |||||
* Copyright (c) 2022 Klara, Inc. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
* IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
/* XXX GNU tar format is not supported by this driver */ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include "opt_tarfs.h" | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/buf.h> | |||||
#include <sys/conf.h> | |||||
#include <sys/fcntl.h> | |||||
#include <sys/libkern.h> | |||||
#include <sys/limits.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/mount.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/namei.h> | |||||
#include <sys/priv.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/queue.h> | |||||
#include <sys/sbuf.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/uio.h> | |||||
#include <sys/vnode.h> | |||||
#include <vm/vm_param.h> | |||||
#include <geom/geom.h> | |||||
#include <geom/geom_vfs.h> | |||||
#include <fs/tarfs/tarfs.h> | |||||
#include <fs/tarfs/tarfs_dbg.h> | |||||
CTASSERT(ZERO_REGION_SIZE > TARFS_BLOCKSIZE); | |||||
struct ustar_header { | |||||
char name[100]; /* File name */ | |||||
char mode[8]; /* Mode flags */ | |||||
char uid[8]; /* User id */ | |||||
char gid[8]; /* Group id */ | |||||
char size[12]; /* Size */ | |||||
char mtime[12]; /* Modified time */ | |||||
char checksum[8]; /* Checksum */ | |||||
char typeflag[1]; /* Type */ | |||||
char linkname[100]; /* "old format" stops here */ | |||||
char magic[6]; /* POSIX UStar "ustar\0" indicator */ | |||||
char version[2]; /* POSIX UStar version "00" */ | |||||
char uname[32]; /* User name */ | |||||
char gname[32]; /* Group name */ | |||||
char major[8]; /* Device major number */ | |||||
char minor[8]; /* Device minor number */ | |||||
char prefix[155]; /* Path prefix */ | |||||
}; | |||||
#define TAR_EOF ((off_t)-1) | |||||
#define TAR_TYPE_FILE '0' | |||||
#define TAR_TYPE_HARDLINK '1' | |||||
#define TAR_TYPE_SYMLINK '2' | |||||
#define TAR_TYPE_CHAR '3' | |||||
#define TAR_TYPE_BLOCK '4' | |||||
#define TAR_TYPE_DIRECTORY '5' | |||||
#define TAR_TYPE_FIFO '6' | |||||
#define TAR_TYPE_CONTIG '7' | |||||
#define TAR_TYPE_GLOBAL_EXTHDR 'g' | |||||
#define TAR_TYPE_EXTHDR 'x' | |||||
#define TAR_TYPE_GNU_SPARSE 'S' | |||||
#define USTAR_MAGIC (uint8_t []){ 'u', 's', 't', 'a', 'r', 0 } | |||||
#define USTAR_VERSION (uint8_t []){ '0', '0' } | |||||
#define GNUTAR_MAGIC (uint8_t []){ 'u', 's', 't', 'a', 'r', ' ' } | |||||
#define GNUTAR_VERSION (uint8_t []){ ' ', '\x0' } | |||||
#define DEFDIRMODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) | |||||
MALLOC_DEFINE(M_TARFSMNT, "tarfs mount", "tarfs mount structures"); | |||||
MALLOC_DEFINE(M_TARFSNODE, "tarfs node", "tarfs node structures"); | |||||
static vfs_mount_t tarfs_mount; | |||||
static vfs_unmount_t tarfs_unmount; | |||||
static vfs_root_t tarfs_root; | |||||
static vfs_statfs_t tarfs_statfs; | |||||
static vfs_fhtovp_t tarfs_fhtovp; | |||||
static const char *tarfs_opts[] = { | |||||
"from", "gid", "mode", "uid", "verify", | |||||
NULL | |||||
}; | |||||
/* | |||||
* Reads a len-width signed octal number from strp. Returns the value. | |||||
* XXX Does not report errors. | |||||
*/ | |||||
static int64_t | |||||
tarfs_str2octal(const char *strp, size_t len) | |||||
{ | |||||
int64_t val; | |||||
size_t idx; | |||||
int sign; | |||||
/* | |||||
* Skip leading spaces or tabs. | |||||
* XXX why? POSIX requires numeric fields to be 0-padded. | |||||
*/ | |||||
for (idx = 0; idx < len; idx++) | |||||
if (strp[idx] != ' ' && strp[idx] != '\t') | |||||
break; | |||||
if (idx == len) | |||||
return (0); | |||||
if (strp[idx] == '-') { | |||||
sign = -1; | |||||
idx++; | |||||
} else | |||||
sign = 1; | |||||
val = 0; | |||||
for (; idx < len; idx++) { | |||||
if (strp[idx] < '0' || strp[idx] > '7') | |||||
break; | |||||
val <<= 3; | |||||
val += (strp[idx] - '0'); | |||||
/* Truncate on overflow */ | |||||
if (val > INT64_MAX / 8) { | |||||
val = INT64_MAX; | |||||
break; | |||||
} | |||||
} | |||||
return (sign > 0) ? val : -val; | |||||
} | |||||
/* | |||||
* Reads a len-byte extended numeric value from strp. The first byte has | |||||
* bit 7 set to indicate the format; the remaining 7 bits + the (len - 1) | |||||
* bytes that follow form a big-endian signed two's complement binary | |||||
* number. Returns the value. XXX Does not report errors. | |||||
*/ | |||||
static int64_t | |||||
tarfs_str2base256(const char *strp, size_t len) | |||||
{ | |||||
int64_t val; | |||||
size_t idx; | |||||
KASSERT(strp[0] & 0x80, ("not an extended numeric value")); | |||||
/* Sign-extend the first byte */ | |||||
if ((strp[0] & 0x40) != 0) | |||||
val = (int64_t)-1; | |||||
else | |||||
val = 0; | |||||
val <<= 6; | |||||
val |= (strp[0] & 0x3f); | |||||
/* Read subsequent bytes */ | |||||
for (idx = 1; idx < len; idx++) { | |||||
val <<= 8; | |||||
val |= (0xff & (int64_t)strp[idx]); | |||||
/* Truncate on overflow and underflow */ | |||||
if (val > INT64_MAX / 256) { | |||||
val = INT64_MAX; | |||||
break; | |||||
} else if (val < INT64_MAX / 256) { | |||||
val = INT64_MIN; | |||||
break; | |||||
} | |||||
} | |||||
return (val); | |||||
} | |||||
/* | |||||
* Read a len-byte numeric field from strp. If bit 7 of the first byte it | |||||
* set, assume an extended numeric value (signed two's complement); | |||||
* otherwise, assume a signed octal value. | |||||
* | |||||
* XXX practically no error checking or handling | |||||
*/ | |||||
static int64_t | |||||
tarfs_str2int64(const char *strp, size_t len) | |||||
{ | |||||
if (len < 1) | |||||
return (0); | |||||
if ((strp[0] & 0x80) != 0) | |||||
return (tarfs_str2base256(strp, len)); | |||||
return (tarfs_str2octal(strp, len)); | |||||
} | |||||
/* | |||||
* Verifies the checksum of a header. Returns true if the checksum is | |||||
* valid, false otherwise. | |||||
*/ | |||||
static boolean_t | |||||
tarfs_checksum(struct ustar_header *hdrp) | |||||
{ | |||||
const unsigned char *ptr; | |||||
int64_t checksum, hdrsum; | |||||
size_t idx; | |||||
hdrsum = tarfs_str2int64(hdrp->checksum, sizeof(hdrp->checksum)); | |||||
TARFS_DPF(CHECKSUM, "%s: header checksum %lx\n", __func__, hdrsum); | |||||
checksum = 0; | |||||
for (ptr = (const unsigned char *)hdrp; | |||||
ptr < (const unsigned char *)hdrp->checksum; ptr++) | |||||
checksum += *ptr; | |||||
for (idx = 0; idx < sizeof(hdrp->checksum); idx++) | |||||
checksum += 0x20; | |||||
for (ptr = (const unsigned char *)hdrp->typeflag; | |||||
ptr < (const unsigned char *)(hdrp + 1); ptr++) | |||||
checksum += *ptr; | |||||
TARFS_DPF(CHECKSUM, "%s: calc unsigned checksum %lx\n", __func__, | |||||
checksum); | |||||
if (hdrsum == checksum) | |||||
return (true); | |||||
/* | |||||
* Repeat test with signed bytes, some older formats use a broken | |||||
* form of the calculation | |||||
*/ | |||||
checksum = 0; | |||||
for (ptr = (const unsigned char *)hdrp; | |||||
ptr < (const unsigned char *)&hdrp->checksum; ptr++) | |||||
checksum += *((const signed char *)ptr); | |||||
for (idx = 0; idx < sizeof(hdrp->checksum); idx++) | |||||
checksum += 0x20; | |||||
for (ptr = (const unsigned char *)&hdrp->typeflag; | |||||
ptr < (const unsigned char *)(hdrp + 1); ptr++) | |||||
checksum += *((const signed char *)ptr); | |||||
TARFS_DPF(CHECKSUM, "%s: calc signed checksum %lx\n", __func__, | |||||
checksum); | |||||
if (hdrsum == checksum) | |||||
return (true); | |||||
return (false); | |||||
} | |||||
/* | |||||
* Looks up a path in the tarfs node tree. | |||||
* | |||||
* - If the path exists, stores a pointer to the corresponding tarfs_node | |||||
* in retnode and a pointer to its parent in retparent. | |||||
* | |||||
* - If the path does not exist, but create_dirs is true, creates ancestor | |||||
* directories and returns NULL in retnode and the parent in retparent. | |||||
* | |||||
* - If the path does not exist and create_dirs is false, stops at the | |||||
* first missing path name component. | |||||
* | |||||
* - In all cases, on return, endp and sepp point to the beginning and | |||||
* end, respectively, of the last-processed path name component. | |||||
* | |||||
* - Returns 0 if the node was found, ENOENT if it was not, and some other | |||||
* positive errno value on failure. | |||||
*/ | |||||
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) | |||||
{ | |||||
struct componentname cn; | |||||
struct tarfs_node *parent, *tnp; | |||||
char *sep; | |||||
size_t len; | |||||
int error; | |||||
boolean_t do_lookup; | |||||
MPASS(name != NULL && namelen != 0); | |||||
do_lookup = true; | |||||
error = 0; | |||||
parent = tnp = tmp->root; | |||||
if (tnp == NULL) | |||||
panic("%s: root node not yet created", __func__); | |||||
bzero(&cn, sizeof(cn)); | |||||
TARFS_DPF(LOOKUP, "%s: Full path: %.*s\n", __func__, (int)namelen, | |||||
name); | |||||
sep = NULL; | |||||
for (;;) { | |||||
/* skip leading slash(es) */ | |||||
while (name[0] == '/' && namelen > 0) | |||||
name++, namelen--; | |||||
/* did we reach the end? */ | |||||
if (namelen == 0 || name[0] == '\0') { | |||||
name = do_lookup ? NULL : cn.cn_nameptr; | |||||
namelen = do_lookup ? 0 : cn.cn_namelen; | |||||
break; | |||||
} | |||||
/* locate the next separator */ | |||||
for (sep = name, len = 0; | |||||
*sep != '\0' && *sep != '/' && len < namelen; | |||||
sep++, len++) | |||||
/* nothing */ ; | |||||
/* check for . and .. */ | |||||
if (name[0] == '.' && len <= 2) { | |||||
if (len == 1) { | |||||
/* . */ | |||||
name += len; | |||||
namelen -= len; | |||||
continue; | |||||
} else if (name[1] == '.') { | |||||
/* .. */ | |||||
if (tnp == tmp->root) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
tnp = tnp->parent; | |||||
parent = tnp->parent; | |||||
name += len; | |||||
namelen -= len; | |||||
continue; | |||||
} | |||||
} | |||||
/* create parent if necessary */ | |||||
if (!do_lookup) { | |||||
TARFS_DPF(ALLOC, "%s: creating %.*s\n", __func__, | |||||
(int)cn.cn_namelen, cn.cn_nameptr); | |||||
error = tarfs_alloc_node(tmp, cn.cn_nameptr, | |||||
cn.cn_namelen, VDIR, -1, 0, tmp->mtime, 0, 0, | |||||
DEFDIRMODE, 0, NULL, NODEV, parent, &tnp); | |||||
if (error != 0) | |||||
break; | |||||
} | |||||
parent = tnp; | |||||
tnp = NULL; | |||||
cn.cn_nameptr = name; | |||||
cn.cn_namelen = len; | |||||
TARFS_DPF(LOOKUP, "%s: Search: %.*s\n", __func__, | |||||
(int)cn.cn_namelen, cn.cn_nameptr); | |||||
if (do_lookup) { | |||||
tnp = tarfs_lookup_node(parent, NULL, &cn); | |||||
if (tnp == NULL) { | |||||
do_lookup = false; | |||||
if (!create_dirs) | |||||
break; | |||||
} | |||||
} | |||||
name += cn.cn_namelen; | |||||
namelen -= cn.cn_namelen; | |||||
} | |||||
TARFS_DPF(LOOKUP, "%s: Parent %p, node %p\n", __func__, parent, tnp); | |||||
if (retparent) | |||||
*retparent = parent; | |||||
if (retnode) | |||||
*retnode = tnp; | |||||
if (endp) { | |||||
if (namelen > 0) | |||||
*endp = name; | |||||
else | |||||
*endp = NULL; | |||||
} | |||||
if (sepp) | |||||
*sepp = sep; | |||||
return (error); | |||||
} | |||||
/* | |||||
* Frees a tarfs_mount structure and everything it references. | |||||
*/ | |||||
static void | |||||
tarfs_free_mount(struct tarfs_mount *tmp) | |||||
{ | |||||
struct mount *mp; | |||||
struct tarfs_node *tnp; | |||||
MPASS(tmp != NULL); | |||||
TARFS_DPF(ALLOC, "%s: Freeing mount structure %p\n", __func__, tmp); | |||||
TARFS_DPF(ALLOC, "%s: freeing tarfs_node structures\n", __func__); | |||||
while (!TAILQ_EMPTY(&tmp->allnodes)) { | |||||
tnp = TAILQ_FIRST(&tmp->allnodes); | |||||
TAILQ_REMOVE(&tmp->allnodes, tnp, entries); | |||||
tarfs_free_node(tnp); | |||||
} | |||||
(void)tarfs_io_fini(tmp); | |||||
TARFS_DPF(ALLOC, "%s: deleting unr header\n", __func__); | |||||
delete_unrhdr(tmp->ino_unr); | |||||
mp = tmp->vfs; | |||||
mp->mnt_data = NULL; | |||||
TARFS_DPF(ALLOC, "%s: freeing structure\n", __func__); | |||||
free(tmp, M_TARFSMNT); | |||||
} | |||||
/* | |||||
* Processes the tar file header at block offset blknump and allocates and | |||||
* populates a tarfs_node structure for the file it describes. Updated | |||||
* blknump to point to the next unread tar file block, or TAR_EOF if EOF | |||||
* is reached. Returns 0 on success or EOF and a positive errno value on | |||||
* failure. | |||||
*/ | |||||
static int | |||||
tarfs_alloc_one(struct tarfs_mount *tmp, off_t *blknump) | |||||
{ | |||||
char block[TARFS_BLOCKSIZE]; | |||||
struct ustar_header *hdrp = (struct ustar_header *)block; | |||||
struct sbuf *namebuf = NULL; | |||||
char *exthdr = NULL, *name = NULL, *link = NULL; | |||||
off_t blknum = *blknump; | |||||
int endmarker = 0; | |||||
char *namep, *sep; | |||||
struct tarfs_node *parent, *tnp; | |||||
size_t namelen = 0, linklen = 0, realsize = 0, sz; | |||||
ssize_t res; | |||||
dev_t rdev; | |||||
gid_t gid; | |||||
mode_t mode; | |||||
time_t mtime; | |||||
uid_t uid; | |||||
long major = -1, minor = -1; | |||||
unsigned int flags = 0; | |||||
int error; | |||||
boolean_t sparse = false; | |||||
again: | |||||
/* read next header */ | |||||
res = tarfs_io_read_buf(tmp, false, block, | |||||
TARFS_BLOCKSIZE * blknum, TARFS_BLOCKSIZE); | |||||
if (res < 0) { | |||||
error = -res; | |||||
goto bad; | |||||
} else if (res < TARFS_BLOCKSIZE) { | |||||
goto eof; | |||||
} | |||||
blknum++; | |||||
/* check for end marker */ | |||||
if (memcmp(block, zero_region, TARFS_BLOCKSIZE) == 0) { | |||||
if (endmarker++) { | |||||
if (exthdr != NULL) { | |||||
TARFS_DPF(IO, "%s: orphaned extended header at %zu\n", | |||||
__func__, TARFS_BLOCKSIZE * (blknum - 1)); | |||||
free(exthdr, M_TEMP); | |||||
} | |||||
TARFS_DPF(IO, "%s: end of archive at %zu\n", __func__, | |||||
TARFS_BLOCKSIZE * blknum); | |||||
tmp->nblocks = blknum; | |||||
*blknump = TAR_EOF; | |||||
return (0); | |||||
} | |||||
goto again; | |||||
} | |||||
/* verify magic */ | |||||
if (memcmp(hdrp->magic, USTAR_MAGIC, sizeof(USTAR_MAGIC)) == 0 && | |||||
memcmp(hdrp->version, USTAR_VERSION, sizeof(USTAR_VERSION)) == 0) { | |||||
/* POSIX */ | |||||
} else if (memcmp(hdrp->magic, GNUTAR_MAGIC, sizeof(GNUTAR_MAGIC)) == 0 && | |||||
imp: Good, it filters out the odd-ball tar formats (mostly pre-posix)
Sadly, that also filters out… | |||||
memcmp(hdrp->magic, GNUTAR_MAGIC, sizeof(GNUTAR_MAGIC)) == 0) { | |||||
TARFS_DPF(ALLOC, "%s: GNU tar format at %zu\n", __func__, | |||||
TARFS_BLOCKSIZE * (blknum - 1)); | |||||
Done Inline ActionsI'd be tempted to toss an 'unsupported' in this message somewhere since we're returning an error... imp: I'd be tempted to toss an 'unsupported' in this message somewhere since we're returning an… | |||||
Done Inline ActionsI half hope to add support for it, though at present it's out of scope & out of budget. des: I half hope to add support for it, though at present it's out of scope & out of budget. | |||||
error = EFTYPE; | |||||
goto bad; | |||||
} else { | |||||
TARFS_DPF(ALLOC, "%s: unsupported TAR format at %zu\n", | |||||
__func__, TARFS_BLOCKSIZE * (blknum - 1)); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
/* verify checksum */ | |||||
if (!tarfs_checksum(hdrp)) { | |||||
TARFS_DPF(ALLOC, "%s: header checksum failed at %zu\n", | |||||
__func__, TARFS_BLOCKSIZE * (blknum - 1)); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
/* get standard attributes */ | |||||
mode = tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode)); | |||||
uid = tarfs_str2int64(hdrp->uid, sizeof(hdrp->uid)); | |||||
gid = tarfs_str2int64(hdrp->gid, sizeof(hdrp->gid)); | |||||
sz = tarfs_str2int64(hdrp->size, sizeof(hdrp->size)); | |||||
mtime = tarfs_str2int64(hdrp->mtime, sizeof(hdrp->mtime)); | |||||
rdev = NODEV; | |||||
TARFS_DPF(ALLOC, "%s: [%c] %zu @%jd %o %d:%d\n", __func__, | |||||
hdrp->typeflag[0], sz, (intmax_t)mtime, mode, uid, gid); | |||||
/* extended header? */ | |||||
if (hdrp->typeflag[0] == TAR_TYPE_GLOBAL_EXTHDR) { | |||||
printf("%s: unsupported global extended header at %zd\n", | |||||
__func__, TARFS_BLOCKSIZE * (blknum - 1)); | |||||
error = EFTYPE; | |||||
goto bad; | |||||
} | |||||
if (hdrp->typeflag[0] == TAR_TYPE_EXTHDR) { | |||||
if (exthdr != NULL) { | |||||
TARFS_DPF(IO, "%s: multiple extended headers at %zu\n", | |||||
__func__, TARFS_BLOCKSIZE * (blknum - 1)); | |||||
Done Inline ActionsI'd add unsupported in this message as well... or error or failure... imp: I'd add unsupported in this message as well... or error or failure... | |||||
error = EFTYPE; | |||||
goto bad; | |||||
} | |||||
/* read the contents of the exthdr */ | |||||
TARFS_DPF(ALLOC, "%s: %zu-byte extended header at %zd\n", | |||||
__func__, sz, TARFS_BLOCKSIZE * (blknum - 1)); | |||||
exthdr = malloc(sz, M_TEMP, M_WAITOK); | |||||
res = tarfs_io_read_buf(tmp, false, exthdr, | |||||
TARFS_BLOCKSIZE * blknum, sz); | |||||
if (res < 0) { | |||||
error = -res; | |||||
goto bad; | |||||
} | |||||
if (res < sz) { | |||||
goto eof; | |||||
} | |||||
blknum += TARFS_SZ2BLKS(res); | |||||
/* XXX TODO: refactor this parser */ | |||||
char *line = exthdr; | |||||
while (line < exthdr + sz) { | |||||
char *eol, *key, *value, *sep; | |||||
size_t len = strtoul(line, &sep, 10); | |||||
if (len == 0 || sep == line || *sep != ' ') { | |||||
TARFS_DPF(ALLOC, "%s: exthdr syntax error\n", | |||||
__func__); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
if (line + len > exthdr + sz) { | |||||
TARFS_DPF(ALLOC, "%s: exthdr overflow\n", | |||||
__func__); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
eol = line + len - 1; | |||||
*eol = '\0'; | |||||
line += len; | |||||
key = sep + 1; | |||||
sep = strchr(key, '='); | |||||
if (sep == NULL) { | |||||
TARFS_DPF(ALLOC, "%s: exthdr syntax error\n", | |||||
__func__); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
*sep = '\0'; | |||||
value = sep + 1; | |||||
TARFS_DPF(ALLOC, "%s: exthdr %s=%s\n", __func__, | |||||
key, value); | |||||
if (strcmp(key, "linkpath") == 0) { | |||||
link = value; | |||||
linklen = eol - value; | |||||
} else if (strcmp(key, "GNU.sparse.major") == 0) { | |||||
sparse = true; | |||||
major = strtol(value, &sep, 10); | |||||
if (sep != eol) { | |||||
printf("exthdr syntax error\n"); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
} else if (strcmp(key, "GNU.sparse.minor") == 0) { | |||||
sparse = true; | |||||
minor = strtol(value, &sep, 10); | |||||
if (sep != eol) { | |||||
printf("exthdr syntax error\n"); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
} else if (strcmp(key, "GNU.sparse.name") == 0) { | |||||
sparse = true; | |||||
name = value; | |||||
namelen = eol - value; | |||||
if (namelen == 0) { | |||||
printf("exthdr syntax error\n"); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
} else if (strcmp(key, "GNU.sparse.realsize") == 0) { | |||||
sparse = true; | |||||
realsize = strtoul(value, &sep, 10); | |||||
if (sep != eol) { | |||||
printf("exthdr syntax error\n"); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
} else if (strcmp(key, "SCHILY.fflags") == 0) { | |||||
flags |= tarfs_strtofflags(value, &sep); | |||||
if (sep != eol) { | |||||
printf("exthdr syntax error\n"); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
} | |||||
} | |||||
goto again; | |||||
} | |||||
/* sparse file consistency checks */ | |||||
if (sparse) { | |||||
TARFS_DPF(ALLOC, "%s: %s: sparse %ld.%ld (%zu bytes)\n", __func__, | |||||
name, major, minor, realsize); | |||||
if (major != 1 || minor != 0 || name == NULL || realsize == 0 || | |||||
hdrp->typeflag[0] != TAR_TYPE_FILE) { | |||||
TARFS_DPF(ALLOC, "%s: invalid sparse format\n", __func__); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
} | |||||
/* file name */ | |||||
if (name == NULL) { | |||||
if (hdrp->prefix[0] != '\0') { | |||||
namebuf = sbuf_new_auto(); | |||||
sbuf_printf(namebuf, "%.*s/%.*s", | |||||
(int)sizeof(hdrp->prefix), hdrp->prefix, | |||||
(int)sizeof(hdrp->name), hdrp->name); | |||||
sbuf_finish(namebuf); | |||||
name = sbuf_data(namebuf); | |||||
namelen = sbuf_len(namebuf); | |||||
} else { | |||||
name = hdrp->name; | |||||
namelen = strnlen(hdrp->name, sizeof(hdrp->name)); | |||||
} | |||||
} | |||||
error = tarfs_lookup_path(tmp, name, namelen, &namep, | |||||
&sep, &parent, &tnp, true); | |||||
if (error != 0) | |||||
goto bad; | |||||
if (tnp != NULL) { | |||||
if (hdrp->typeflag[0] == TAR_TYPE_DIRECTORY) { | |||||
/* XXX set attributes? */ | |||||
goto skip; | |||||
} | |||||
TARFS_DPF(ALLOC, "%s: duplicate file %.*s\n", __func__, | |||||
(int)namelen, name); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
switch (hdrp->typeflag[0]) { | |||||
case TAR_TYPE_DIRECTORY: | |||||
error = tarfs_alloc_node(tmp, namep, sep - namep, VDIR, | |||||
0, 0, mtime, uid, gid, mode, flags, NULL, 0, | |||||
parent, &tnp); | |||||
break; | |||||
case TAR_TYPE_FILE: | |||||
error = tarfs_alloc_node(tmp, namep, sep - namep, VREG, | |||||
blknum * TARFS_BLOCKSIZE, sz, mtime, uid, gid, mode, | |||||
flags, NULL, 0, parent, &tnp); | |||||
if (error == 0 && sparse) { | |||||
error = tarfs_load_blockmap(tnp, realsize); | |||||
} | |||||
break; | |||||
case TAR_TYPE_HARDLINK: | |||||
if (link == NULL) { | |||||
link = hdrp->linkname; | |||||
linklen = strnlen(link, sizeof(hdrp->linkname)); | |||||
} | |||||
error = tarfs_alloc_node(tmp, namep, sep - namep, VREG, | |||||
0, 0, 0, 0, 0, 0, 0, NULL, 0, parent, &tnp); | |||||
if (error != 0) { | |||||
goto bad; | |||||
} | |||||
error = tarfs_lookup_path(tmp, link, linklen, NULL, | |||||
NULL, NULL, &tnp->other, false); | |||||
if (tnp->other == NULL || | |||||
tnp->other->type != VREG || | |||||
tnp->other->other != NULL) { | |||||
TARFS_DPF(ALLOC, "%s: %.*s: dead hard link to %.*s\n", | |||||
__func__, (int)namelen, name, (int)linklen, link); | |||||
error = EINVAL; | |||||
goto bad; | |||||
} | |||||
break; | |||||
case TAR_TYPE_SYMLINK: | |||||
if (link == NULL) { | |||||
link = hdrp->linkname; | |||||
linklen = strnlen(link, sizeof(hdrp->linkname)); | |||||
} | |||||
error = tarfs_alloc_node(tmp, namep, sep - namep, VLNK, | |||||
0, linklen, mtime, uid, gid, mode, flags, link, 0, | |||||
parent, &tnp); | |||||
break; | |||||
case TAR_TYPE_BLOCK: | |||||
major = tarfs_str2int64(hdrp->major, sizeof(hdrp->major)); | |||||
Done Inline ActionsI'd be tempted to disallow block and character nodes entirely. devfs handles that sort of thing, no? And major numbers on FreeBSD are almost completely arbitrary... imp: I'd be tempted to disallow block and character nodes entirely. devfs handles that sort of thing… | |||||
minor = tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor)); | |||||
rdev = makedev(major, minor); | |||||
error = tarfs_alloc_node(tmp, namep, sep - namep, VBLK, | |||||
0, 0, mtime, uid, gid, mode, flags, NULL, rdev, | |||||
parent, &tnp); | |||||
break; | |||||
case TAR_TYPE_CHAR: | |||||
major = tarfs_str2int64(hdrp->major, sizeof(hdrp->major)); | |||||
minor = tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor)); | |||||
rdev = makedev(major, minor); | |||||
error = tarfs_alloc_node(tmp, namep, sep - namep, VCHR, | |||||
0, 0, mtime, uid, gid, mode, flags, NULL, rdev, | |||||
parent, &tnp); | |||||
break; | |||||
default: | |||||
TARFS_DPF(ALLOC, "%s: unsupported type %c for %.*s\n", | |||||
__func__, hdrp->typeflag[0], (int)namelen, name); | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
if (error != 0) | |||||
goto bad; | |||||
skip: | |||||
blknum += TARFS_SZ2BLKS(sz); | |||||
tmp->nblocks = blknum; | |||||
*blknump = blknum; | |||||
if (exthdr != NULL) { | |||||
free(exthdr, M_TEMP); | |||||
} | |||||
if (namebuf != NULL) { | |||||
sbuf_delete(namebuf); | |||||
} | |||||
return (0); | |||||
eof: | |||||
TARFS_DPF(IO, "%s: premature end of file\n", __func__); | |||||
error = EIO; | |||||
goto bad; | |||||
bad: | |||||
if (exthdr != NULL) { | |||||
free(exthdr, M_TEMP); | |||||
} | |||||
if (namebuf != NULL) { | |||||
sbuf_delete(namebuf); | |||||
} | |||||
return (error); | |||||
} | |||||
/* | |||||
* Allocates and populates the metadata structures for the tar file | |||||
* referenced by vp. On success, a pointer to the tarfs_mount structure | |||||
* is stored in tmpp. Returns 0 on success or a positive errno value on | |||||
* failure. | |||||
*/ | |||||
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) | |||||
{ | |||||
struct vattr va; | |||||
struct thread *td = curthread; | |||||
char *fullpath; | |||||
struct tarfs_mount *tmp; | |||||
struct tarfs_node *root; | |||||
struct g_consumer *cp; | |||||
struct cdev *dev; | |||||
off_t blknum; | |||||
time_t mtime; | |||||
int error; | |||||
KASSERT(tmpp != NULL, ("tarfs mount return is NULL")); | |||||
ASSERT_VOP_LOCKED(vp, __func__); | |||||
tmp = NULL; | |||||
dev = NULL; | |||||
cp = NULL; | |||||
fullpath = NULL; | |||||
TARFS_DPF(ALLOC, "%s: Allocating tarfs mount structure for vp %p\n", | |||||
__func__, vp); | |||||
/* Get source metadata */ | |||||
error = VOP_GETATTR(vp, &va, td->td_ucred); | |||||
if (error != 0) { | |||||
return (error); | |||||
} | |||||
VOP_UNLOCK(vp); | |||||
mtime = va.va_mtime.tv_sec; | |||||
/* Allocate and initialize tarfs mount structure */ | |||||
tmp = (struct tarfs_mount *)malloc(sizeof(struct tarfs_mount), | |||||
M_TARFSMNT, M_WAITOK | M_ZERO); | |||||
Done Inline ActionsUnnecessary cast. pstef: Unnecessary cast. | |||||
TARFS_DPF(ALLOC, "%s: Allocated mount structure\n", __func__); | |||||
mp->mnt_data = tmp; | |||||
mtx_init(&tmp->allnode_lock, "tarfs allnode lock", NULL, | |||||
MTX_DEF); | |||||
TAILQ_INIT(&tmp->allnodes); | |||||
tmp->ino_unr = new_unrhdr(TARFS_MININO, INT_MAX, &tmp->allnode_lock); | |||||
tmp->vp = vp; | |||||
tmp->vfs = mp; | |||||
tmp->cp = cp; | |||||
tmp->dev = dev; | |||||
tmp->mtime = mtime; | |||||
/* | |||||
* XXX The decompression layer passes everything through the | |||||
* buffer cache, and the buffer cache wants to know our blocksize, | |||||
* but mnt_stat normally isn't populated until after we return, so | |||||
* we have to cheat a bit. | |||||
*/ | |||||
tmp->iosize = 1U << tarfs_ioshift; | |||||
mp->mnt_stat.f_iosize = tmp->iosize; | |||||
/* Initialize decompression layer */ | |||||
error = tarfs_io_init(tmp); | |||||
if (error != 0) | |||||
goto bad; | |||||
error = tarfs_alloc_node(tmp, NULL, 0, VDIR, 0, 0, mtime, root_uid, | |||||
root_gid, root_mode & ALLPERMS, 0, NULL, NODEV, NULL, &root); | |||||
if (error != 0 || root == NULL) | |||||
goto bad; | |||||
tmp->root = root; | |||||
blknum = 0; | |||||
do { | |||||
if ((error = tarfs_alloc_one(tmp, &blknum)) != 0) { | |||||
goto bad; | |||||
} | |||||
} while (blknum != TAR_EOF); | |||||
*tmpp = tmp; | |||||
TARFS_DPF(ALLOC, "%s: pfsmnt_root %p\n", __func__, tmp->root); | |||||
Done Inline ActionsPlease consider how to improve the names of these. pstef: Please consider how to improve the names of these. | |||||
Done Inline Actionstmp = pointer to tarfs_mount tmpp = pointer to pointer to tarfs_mount the confusion with “temporary” is unfortunate but you get used to it. des: `tmp` = pointer to `tarfs_mount`
`tmpp` = pointer to pointer to `tarfs_mount`
the confusion… | |||||
return (0); | |||||
bad: | |||||
if (tmp != NULL) | |||||
tarfs_free_mount(tmp); | |||||
if (cp != NULL) { | |||||
g_topology_lock(); | |||||
g_vfs_close(cp); | |||||
g_topology_unlock(); | |||||
} | |||||
free(fullpath, M_TEMP); | |||||
return (error); | |||||
} | |||||
/* | |||||
* VFS Operations. | |||||
*/ | |||||
static int | |||||
tarfs_mount(struct mount *mp) | |||||
{ | |||||
struct nameidata nd; | |||||
struct vattr va; | |||||
struct tarfs_mount *tmp = NULL; | |||||
struct thread *td = curthread; | |||||
struct vnode *vp; | |||||
char *from; | |||||
uid_t root_uid; | |||||
gid_t root_gid; | |||||
mode_t root_mode; | |||||
int error, flags, len; | |||||
if (mp->mnt_flag & MNT_UPDATE) | |||||
return (EOPNOTSUPP); | |||||
if (vfs_filteropt(mp->mnt_optnew, tarfs_opts)) | |||||
return (EINVAL); | |||||
vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY); | |||||
error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred); | |||||
VOP_UNLOCK(mp->mnt_vnodecovered); | |||||
if (error) | |||||
return (error); | |||||
if (mp->mnt_cred->cr_ruid != 0 || | |||||
vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1) | |||||
root_gid = va.va_gid; | |||||
if (mp->mnt_cred->cr_ruid != 0 || | |||||
vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1) | |||||
root_uid = va.va_uid; | |||||
if (mp->mnt_cred->cr_ruid != 0 || | |||||
vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1) | |||||
root_mode = va.va_mode; | |||||
error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len); | |||||
if (error != 0 || from[len - 1] != '\0') | |||||
return (EINVAL); | |||||
/* Find the source tarball */ | |||||
TARFS_DPF(FS, "%s(%s, uid=%u, gid=%u, mode=%o)\n", __func__, | |||||
from, root_uid, root_gid, root_mode); | |||||
flags = FREAD; | |||||
if (vfs_flagopt(mp->mnt_optnew, "verify", NULL, 0)) { | |||||
flags |= O_VERIFY; | |||||
} | |||||
NDINIT(&nd, LOOKUP, ISOPEN | FOLLOW | LOCKLEAF, UIO_SYSSPACE, from); | |||||
error = namei(&nd); | |||||
if (error != 0) | |||||
return (error); | |||||
NDFREE_PNBUF(&nd); | |||||
vp = nd.ni_vp; | |||||
TARFS_DPF(FS, "%s: N: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
/* vp is now held and locked */ | |||||
/* Open the source tarball */ | |||||
error = vn_open_vnode(vp, flags, td->td_ucred, td, NULL); | |||||
if (error != 0) { | |||||
TARFS_DPF(FS, "%s: failed to open %s: %d\n", __func__, | |||||
from, error); | |||||
vput(vp); | |||||
goto bad; | |||||
} | |||||
TARFS_DPF(FS, "%s: O: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
if (vp->v_type != VREG) { | |||||
TARFS_DPF(FS, "%s: not a regular file\n", __func__); | |||||
error = EOPNOTSUPP; | |||||
goto bad_open_locked; | |||||
} | |||||
error = priv_check(td, PRIV_VFS_MOUNT_PERM); | |||||
if (error != 0) { | |||||
TARFS_DPF(FS, "%s: not permitted to mount\n", __func__); | |||||
goto bad_open_locked; | |||||
} | |||||
if (flags & O_VERIFY) { | |||||
mp->mnt_flag |= MNT_VERIFIED; | |||||
} | |||||
/* Allocate the tarfs mount */ | |||||
error = tarfs_alloc_mount(mp, vp, root_uid, root_gid, root_mode, &tmp); | |||||
/* vp is now held but unlocked */ | |||||
if (error != 0) { | |||||
TARFS_DPF(FS, "%s: failed to mount %s: %d\n", __func__, | |||||
from, error); | |||||
goto bad_open_unlocked; | |||||
} | |||||
TARFS_DPF(FS, "%s: M: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
/* Unconditionally mount as read-only */ | |||||
MNT_ILOCK(mp); | |||||
mp->mnt_flag |= (MNT_LOCAL | MNT_RDONLY); | |||||
MNT_IUNLOCK(mp); | |||||
vfs_getnewfsid(mp); | |||||
vfs_mountedfrom(mp, "tarfs"); | |||||
TARFS_DPF(FS, "%s: success\n", __func__); | |||||
return (0); | |||||
bad_open_locked: | |||||
/* vp must be held and locked */ | |||||
TARFS_DPF(FS, "%s: L: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
VOP_UNLOCK(vp); | |||||
bad_open_unlocked: | |||||
/* vp must be held and unlocked */ | |||||
TARFS_DPF(FS, "%s: E: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
(void)vn_close(vp, flags, td->td_ucred, td); | |||||
bad: | |||||
/* vp must be released and unlocked */ | |||||
TARFS_DPF(FS, "%s: X: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
return (error); | |||||
} | |||||
/* | |||||
* Unmounts a tarfs filesystem. | |||||
*/ | |||||
static int | |||||
tarfs_unmount(struct mount *mp, int mntflags) | |||||
{ | |||||
struct thread *td = curthread; | |||||
struct tarfs_mount *tmp; | |||||
struct vnode *vp; | |||||
int error; | |||||
int flags = 0; | |||||
TARFS_DPF(FS, "%s: Unmounting %p\n", __func__, mp); | |||||
/* Handle forced unmounts */ | |||||
if (mntflags & MNT_FORCE) | |||||
flags |= FORCECLOSE; | |||||
/* Finalize all pending I/O */ | |||||
error = vflush(mp, 0, flags, curthread); | |||||
if (error != 0) | |||||
return (error); | |||||
tmp = MP_TO_TARFS_MOUNT(mp); | |||||
vp = tmp->vp; | |||||
MPASS(vp != NULL); | |||||
TARFS_DPF(FS, "%s: U: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
vn_close(vp, FREAD, td->td_ucred, td); | |||||
TARFS_DPF(FS, "%s: C: hold %u use %u lock 0x%x\n", __func__, | |||||
vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp)); | |||||
tarfs_free_mount(tmp); | |||||
return (0); | |||||
} | |||||
/* | |||||
Done Inline ActionsWhat is the purpose of this cleanup? kib: What is the purpose of this cleanup? | |||||
Done Inline ActionsWhich part of the cleanup are you referring to? Everything done in the unmount or specifically clearing the MNT_LOCAL flag? stevek: Which part of the cleanup are you referring to? Everything done in the unmount or specifically… | |||||
Done Inline ActionsI mean clearing on MNT_LOCAL kib: I mean clearing on MNT_LOCAL | |||||
Done Inline ActionsIt's something that is done by most (all?) the file systems in the tree. See the various unmount functions in the sources under sys/fs/ I don't recall what the reasoning was for it. stevek: It's something that is done by most (all?) the file systems in the tree.
See the various… | |||||
Done Inline Actionskib: D37966 | |||||
* Gets the root of a tarfs filesystem. Returns 0 on success or a | |||||
* positive errno value on failure. | |||||
*/ | |||||
static int | |||||
tarfs_root(struct mount *mp, int flags, struct vnode **vpp) | |||||
{ | |||||
struct vnode *nvp; | |||||
int error; | |||||
TARFS_DPF(FS, "%s: Getting root vnode\n", __func__); | |||||
error = VFS_VGET(mp, TARFS_ROOTINO, LK_EXCLUSIVE, &nvp); | |||||
if (error != 0) | |||||
return (error); | |||||
nvp->v_vflag |= VV_ROOT; | |||||
*vpp = nvp; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Gets statistics for a tarfs filesystem. Returns 0. | |||||
*/ | |||||
static int | |||||
tarfs_statfs(struct mount *mp, struct statfs *sbp) | |||||
{ | |||||
struct tarfs_mount *tmp; | |||||
tmp = MP_TO_TARFS_MOUNT(mp); | |||||
sbp->f_bsize = TARFS_BLOCKSIZE; | |||||
sbp->f_iosize = tmp->iosize; | |||||
sbp->f_blocks = tmp->nblocks; | |||||
sbp->f_bfree = 0; | |||||
sbp->f_bavail = 0; | |||||
sbp->f_files = tmp->nfiles; | |||||
sbp->f_ffree = 0; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Gets a vnode for the given inode. On success, a pointer to the vnode | |||||
* is stored in vpp. Returns 0 on success or a positive errno value on | |||||
* failure. | |||||
*/ | |||||
static int | |||||
tarfs_vget(struct mount *mp, ino_t ino, int lkflags, struct vnode **vpp) | |||||
{ | |||||
struct tarfs_mount *tmp; | |||||
struct tarfs_node *tnp; | |||||
struct thread *td; | |||||
struct vnode *vp; | |||||
int error; | |||||
TARFS_DPF(FS, "%s: mp %p, ino %lu, lkflags %d\n", __func__, mp, ino, | |||||
lkflags); | |||||
td = curthread; | |||||
error = vfs_hash_get(mp, ino, lkflags, td, vpp, NULL, NULL); | |||||
if (error != 0) | |||||
return (error); | |||||
if (*vpp != NULL) { | |||||
TARFS_DPF(FS, "%s: found hashed vnode %p\n", __func__, *vpp); | |||||
return (error); | |||||
} | |||||
TARFS_DPF(FS, "%s: no hashed vnode for inode %lu\n", __func__, ino); | |||||
tmp = MP_TO_TARFS_MOUNT(mp); | |||||
if (ino == TARFS_ZIOINO) { | |||||
error = vn_lock(tmp->znode, lkflags); | |||||
if (error != 0) | |||||
return (error); | |||||
vref(tmp->znode); | |||||
*vpp = tmp->znode; | |||||
Done Inline Actionsvn_lock/vref is better written as vget() kib: vn_lock/vref is better written as vget() | |||||
return (0); | |||||
} | |||||
/* XXX Should use hash instead? */ | |||||
TAILQ_FOREACH(tnp, &tmp->allnodes, entries) { | |||||
if (tnp->ino == ino) | |||||
break; | |||||
} | |||||
TARFS_DPF(FS, "%s: search of all nodes found %p\n", __func__, tnp); | |||||
if (tnp == NULL) | |||||
return (ENOENT); | |||||
error = getnewvnode("tarfs", mp, &tarfs_vnodeops, &vp); | |||||
if (error != 0) | |||||
goto bad; | |||||
TARFS_DPF(FS, "%s: allocated vnode\n", __func__); | |||||
vp->v_data = tnp; | |||||
vp->v_type = tnp->type; | |||||
tnp->vnode = vp; | |||||
lockmgr(vp->v_vnlock, lkflags, NULL); | |||||
error = insmntque(vp, mp); | |||||
if (error != 0) | |||||
goto bad; | |||||
TARFS_DPF(FS, "%s: inserting entry into VFS hash\n", __func__); | |||||
error = vfs_hash_insert(vp, ino, lkflags, td, vpp, NULL, NULL); | |||||
if (error != 0 || *vpp != NULL) | |||||
return (error); | |||||
vn_set_state(vp, VSTATE_CONSTRUCTED); | |||||
*vpp = vp; | |||||
return (0); | |||||
bad: | |||||
*vpp = NULLVP; | |||||
return (error); | |||||
} | |||||
static int | |||||
tarfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) | |||||
{ | |||||
struct tarfs_node *tnp; | |||||
struct tarfs_fid *tfp; | |||||
struct vnode *nvp; | |||||
int error; | |||||
tfp = (struct tarfs_fid *)fhp; | |||||
MP_TO_TARFS_MOUNT(mp); | |||||
if (tfp->ino < TARFS_ROOTINO || tfp->ino > INT_MAX) | |||||
return (ESTALE); | |||||
error = VFS_VGET(mp, tfp->ino, LK_EXCLUSIVE, &nvp); | |||||
if (error != 0) { | |||||
*vpp = NULLVP; | |||||
return (error); | |||||
} | |||||
tnp = VP_TO_TARFS_NODE(nvp); | |||||
if (tnp->mode == 0 || | |||||
tnp->gen != tfp->gen || | |||||
tnp->nlink <= 0) { | |||||
vput(nvp); | |||||
*vpp = NULLVP; | |||||
return (ESTALE); | |||||
} | |||||
*vpp = nvp; | |||||
return (0); | |||||
} | |||||
static struct vfsops tarfs_vfsops = { | |||||
.vfs_fhtovp = tarfs_fhtovp, | |||||
Done Inline ActionsSince you seems to support mmaping files from tarfs (looking at the open implementation), there you should do vnode_create_vobject() as well. kib: Since you seems to support mmaping files from tarfs (looking at the open implementation), there… | |||||
Done Inline ActionsCan you elaborate? Is it not enough to call it in tarfs_open()? des: Can you elaborate? Is it not enough to call it in `tarfs_open()`? | |||||
.vfs_mount = tarfs_mount, | |||||
.vfs_root = tarfs_root, | |||||
.vfs_statfs = tarfs_statfs, | |||||
.vfs_unmount = tarfs_unmount, | |||||
.vfs_vget = tarfs_vget, | |||||
}; | |||||
VFS_SET(tarfs_vfsops, tarfs, VFCF_READONLY); | |||||
MODULE_VERSION(tarfs, 1); | |||||
MODULE_DEPEND(tarfs, xz, 1, 1, 1); |
Good, it filters out the odd-ball tar formats (mostly pre-posix)
Sadly, that also filters out cpio files, since Linux uses them for its initrd file... But asking for that to be added is well beyond the scope of work :).