Skip to content

Commit

Permalink
tarfs: Support paths that spill into exthdrs.
Browse files Browse the repository at this point in the history
MFC after:	3 days
Sponsored by:	Juniper Networks, Inc.
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D44599

(cherry picked from commit b1fd95c)

tarfs: Ignore global extended headers.

Previously, we would error out if we encountered a global extended
header, because we don't know what it means.  This doesn't really
matter though, and traditionally, tar implementations have either
ignored them or treated them as plain files, so just ignore them.
This allows tarfs to mount tar files created by `git archive`.

MFC after:	3 days
Sponsored by:	Juniper Networks, Inc.
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D44600

(cherry picked from commit 584e1c3)

tarfs: Fix 32-bit build.

MFC after:	3 days
Sponsored by:	Juniper Networks, Inc.
Sponsored by:	Klara, Inc.
Reviewed by:	bapt
Differential Revision:	https://reviews.freebsd.org/D44613

(cherry picked from commit 0238d37)
  • Loading branch information
dag-erling committed Apr 8, 2024
1 parent 2ad8333 commit 59c3e7a
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 11 deletions.
28 changes: 17 additions & 11 deletions sys/fs/tarfs/tarfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ struct ustar_header {

CTASSERT(sizeof(struct ustar_header) == TARFS_BLOCKSIZE);

#define TAR_EOF ((off_t)-1)
#define TAR_EOF ((size_t)-1)

#define TAR_TYPE_FILE '0'
#define TAR_TYPE_HARDLINK '1'
Expand Down Expand Up @@ -430,13 +430,13 @@ tarfs_free_mount(struct tarfs_mount *tmp)
* failure.
*/
static int
tarfs_alloc_one(struct tarfs_mount *tmp, off_t *blknump)
tarfs_alloc_one(struct tarfs_mount *tmp, size_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;
size_t blknum = *blknump;
int64_t num;
int endmarker = 0;
char *namep, *sep;
Expand Down Expand Up @@ -553,13 +553,14 @@ tarfs_alloc_one(struct tarfs_mount *tmp, off_t *blknump)
TARFS_DPF(ALLOC, "%s: [%c] %zu @%jd %o %d:%d\n", __func__,
hdrp->typeflag[0], sz, (intmax_t)mtime, mode, uid, gid);

/* extended header? */
/* global extended header? */
if (hdrp->typeflag[0] == TAR_TYPE_GLOBAL_EXTHDR) {
printf("%s: unsupported global extended header at %zu\n",
__func__, (size_t)(TARFS_BLOCKSIZE * (blknum - 1)));
error = EFTYPE;
goto bad;
TARFS_DPF(ALLOC, "%s: %zu-byte global extended header at %zu\n",
__func__, sz, TARFS_BLOCKSIZE * (blknum - 1));
goto skip;
}

/* extended header? */
if (hdrp->typeflag[0] == TAR_TYPE_EXTHDR) {
if (exthdr != NULL) {
TARFS_DPF(IO, "%s: multiple extended headers at %zu\n",
Expand All @@ -568,7 +569,7 @@ tarfs_alloc_one(struct tarfs_mount *tmp, off_t *blknump)
goto bad;
}
/* read the contents of the exthdr */
TARFS_DPF(ALLOC, "%s: %zu-byte extended header at %zd\n",
TARFS_DPF(ALLOC, "%s: %zu-byte extended header at %zu\n",
__func__, sz, TARFS_BLOCKSIZE * (blknum - 1));
exthdr = malloc(sz, M_TEMP, M_WAITOK);
res = tarfs_io_read_buf(tmp, false, exthdr,
Expand Down Expand Up @@ -614,7 +615,10 @@ tarfs_alloc_one(struct tarfs_mount *tmp, off_t *blknump)
value = sep + 1;
TARFS_DPF(ALLOC, "%s: exthdr %s=%s\n", __func__,
key, value);
if (strcmp(key, "linkpath") == 0) {
if (strcmp(key, "path") == 0) {
name = value;
namelen = eol - value;
} else if (strcmp(key, "linkpath") == 0) {
link = value;
linklen = eol - value;
} else if (strcmp(key, "GNU.sparse.major") == 0) {
Expand Down Expand Up @@ -857,7 +861,7 @@ tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
struct thread *td = curthread;
struct tarfs_mount *tmp;
struct tarfs_node *root;
off_t blknum;
size_t blknum;
time_t mtime;
int error;

Expand Down Expand Up @@ -905,6 +909,8 @@ tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
blknum = 0;
do {
if ((error = tarfs_alloc_one(tmp, &blknum)) != 0) {
printf("unsupported or corrupt tar file at %zu\n",
TARFS_BLOCKSIZE * blknum);
goto bad;
}
} while (blknum != TAR_EOF);
Expand Down
74 changes: 74 additions & 0 deletions tests/sys/fs/tarfs/tarfs_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,77 @@ tarfs_checksum_cleanup() {
tarfs_cleanup
}

atf_test_case tarfs_long_names cleanup
tarfs_long_names_head() {
atf_set "descr" "Verify that tarfs supports long file names"
atf_set "require.user" "root"
}
tarfs_long_names_body() {
tarfs_setup
local a b c d e
a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
c="cccccccccccccccccccccccccccccccccccccccc"
d="dddddddddddddddddddddddddddddddddddddddd"
e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
mkdir -p "${a}"
touch "${a}/${b}_${c}_${d}_${e}_foo"
ln "${a}/${b}_${c}_${d}_${e}_foo" "${a}/${b}_${c}_${d}_${e}_bar"
ln -s "${b}_${c}_${d}_${e}_bar" "${a}/${b}_${c}_${d}_${e}_baz"
tar -cf tarfs_long_names.tar "${a}"
atf_check mount -rt tarfs tarfs_long_names.tar "${mnt}"
}
tarfs_long_names_cleanup() {
tarfs_cleanup
}

atf_test_case tarfs_long_paths cleanup
tarfs_long_paths_head() {
atf_set "descr" "Verify that tarfs supports long paths"
atf_set "require.user" "root"
}
tarfs_long_paths_body() {
tarfs_setup
local a b c d e
a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
c="cccccccccccccccccccccccccccccccccccccccc"
d="dddddddddddddddddddddddddddddddddddddddd"
e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
mkdir -p "${a}/${b}/${c}/${d}/${e}"
touch "${a}/${b}/${c}/${d}/${e}/foo"
ln "${a}/${b}/${c}/${d}/${e}/foo" "${a}/${b}/${c}/${d}/${e}/bar"
ln -s "${b}/${c}/${d}/${e}/bar" "${a}/baz"
tar -cf tarfs_long_paths.tar "${a}"
atf_check mount -rt tarfs tarfs_long_paths.tar "${mnt}"
}
tarfs_long_paths_cleanup() {
tarfs_cleanup
}

atf_test_case tarfs_git_archive cleanup
tarfs_git_archive_head() {
atf_set "descr" "Verify that tarfs supports archives created by git"
atf_set "require.user" "root"
atf_set "require.progs" "git"
}
tarfs_git_archive_body() {
tarfs_setup
mkdir foo
echo "Hello, world!" >foo/bar
git -C foo init --initial-branch=tarfs
git -C foo config user.name "File System"
git -C foo config user.email [email protected]
git -C foo add bar
git -C foo commit -m bar
git -C foo archive --output=../tarfs_git_archive.tar HEAD
atf_check mount -rt tarfs tarfs_git_archive.tar "${mnt}"
atf_check -o file:foo/bar cat "${mnt}"/bar
}
tarfs_git_archive_cleanup() {
tarfs_cleanup
}

atf_init_test_cases() {
atf_add_test_case tarfs_basic
atf_add_test_case tarfs_basic_gnu
Expand All @@ -324,4 +395,7 @@ atf_init_test_cases() {
atf_add_test_case tarfs_linktodir
atf_add_test_case tarfs_linktononexistent
atf_add_test_case tarfs_checksum
atf_add_test_case tarfs_long_names
atf_add_test_case tarfs_long_paths
atf_add_test_case tarfs_git_archive
}

0 comments on commit 59c3e7a

Please sign in to comment.