git: 59c3e7a18c7c - stable/14 - tarfs: Support paths that spill into exthdrs.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Mon, 08 Apr 2024 10:11:04 UTC
The branch stable/14 has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=59c3e7a18c7c1795f90705a3d9d6d5d7480b7364

commit 59c3e7a18c7c1795f90705a3d9d6d5d7480b7364
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-04-03 09:55:01 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-04-08 10:10:41 +0000

    tarfs: Support paths that spill into exthdrs.
    
    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 b1fd95c9e24791d44593e611406b41e57826a5b8)
    
    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 584e1c355ae3c994331005b7196cc87a714e5317)
    
    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 0238d3711d9b888f678fce4274eccc9175168395)
---
 sys/fs/tarfs/tarfs_vfsops.c      | 28 +++++++++------
 tests/sys/fs/tarfs/tarfs_test.sh | 74 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 11 deletions(-)

diff --git a/sys/fs/tarfs/tarfs_vfsops.c b/sys/fs/tarfs/tarfs_vfsops.c
index d1af7070e706..365234113168 100644
--- a/sys/fs/tarfs/tarfs_vfsops.c
+++ b/sys/fs/tarfs/tarfs_vfsops.c
@@ -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'
@@ -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;
@@ -553,13 +553,14 @@ again:
 	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",
@@ -568,7 +569,7 @@ again:
 			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,
@@ -614,7 +615,10 @@ again:
 			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) {
@@ -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;
 
@@ -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);
diff --git a/tests/sys/fs/tarfs/tarfs_test.sh b/tests/sys/fs/tarfs/tarfs_test.sh
index 2a5dfc434201..f1322033fbad 100644
--- a/tests/sys/fs/tarfs/tarfs_test.sh
+++ b/tests/sys/fs/tarfs/tarfs_test.sh
@@ -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 fs@freebsd.org
+	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
@@ -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
 }