From c3593848067cea3b41bc11eec15f391318675cb4 Mon Sep 17 00:00:00 2001
From: Tim Kientzle <kientzle@acm.org>
Date: Tue, 28 Oct 2025 17:13:18 -0700
Subject: [PATCH] Merge pull request #2753 from KlaraSystems/des/temp-files

Create temporary files in the target directory

(cherry picked from commit d2e861769c25470427656b36a14b535f17d47d03)

Upstream-Status: Backport [https://github.com/libarchive/libarchive/commit/c3593848067cea3b41bc11eec15f391318675cb4]
Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 .../archive_read_disk_entry_from_file.c       | 10 ++---
 libarchive/archive_string.c                   | 20 ++++++++++
 libarchive/archive_string.h                   |  4 ++
 libarchive/archive_write_disk_posix.c         | 20 ++++++----
 libarchive/test/test_archive_string.c         | 38 +++++++++++++++++++
 5 files changed, 79 insertions(+), 13 deletions(-)

diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c
index 42af4034..121af198 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -358,12 +358,10 @@ setup_mac_metadata(struct archive_read_disk *a,
 		return (ARCHIVE_OK);
 
 	archive_string_init(&tempfile);
-	if (__archive_get_tempdir(&tempfile) != ARCHIVE_OK) {
-		ret = ARCHIVE_WARN;
-		goto cleanup;
-	}
-	archive_strcat(&tempfile, "tar.md.XXXXXX");
-	tempfd = mkstemp(tempfile.s);
+	archive_strcpy(&tempfile, name);
+	archive_string_dirname(&tempfile);
+	archive_strcat(&tempfile, "/tar.XXXXXXXX");
+	tempfd = __archive_mkstemp(tempfile.s);
 	if (tempfd < 0) {
 		archive_set_error(&a->archive, errno,
 		    "Could not open extended attribute file");
diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c
index 3bb97833..740308b6 100644
--- a/libarchive/archive_string.c
+++ b/libarchive/archive_string.c
@@ -2039,6 +2039,26 @@ archive_strncat_l(struct archive_string *as, const void *_p, size_t n,
 	return (r);
 }
 
+struct archive_string *
+archive_string_dirname(struct archive_string *as)
+{
+	/* strip trailing separators */
+	while (as->length > 1 && as->s[as->length - 1] == '/')
+		as->length--;
+	/* strip final component */
+	while (as->length > 0 && as->s[as->length - 1] != '/')
+		as->length--;
+	/* empty path -> cwd */
+	if (as->length == 0)
+		return (archive_strcat(as, "."));
+	/* strip separator(s) */
+	while (as->length > 1 && as->s[as->length - 1] == '/')
+		as->length--;
+	/* terminate */
+	as->s[as->length] = '\0';
+	return (as);
+}
+
 #if HAVE_ICONV
 
 /*
diff --git a/libarchive/archive_string.h b/libarchive/archive_string.h
index e8987867..d5f5c03a 100644
--- a/libarchive/archive_string.h
+++ b/libarchive/archive_string.h
@@ -192,6 +192,10 @@ void	archive_string_vsprintf(struct archive_string *, const char *,
 void	archive_string_sprintf(struct archive_string *, const char *, ...)
 	    __LA_PRINTF(2, 3);
 
+/* Equivalent to dirname(3) */
+struct archive_string *
+archive_string_dirname(struct archive_string *);
+
 /* Translates from MBS to Unicode. */
 /* Returns non-zero if conversion failed in any way. */
 int archive_wstring_append_from_mbs(struct archive_wstring *dest,
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
index 6fcf3929..cd256203 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -412,12 +412,14 @@ static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,
 static int
 la_mktemp(struct archive_write_disk *a)
 {
+	struct archive_string *tmp = &a->_tmpname_data;
 	int oerrno, fd;
 	mode_t mode;
 
-	archive_string_empty(&a->_tmpname_data);
-	archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name);
-	a->tmpname = a->_tmpname_data.s;
+	archive_strcpy(tmp, a->name);
+	archive_string_dirname(tmp);
+	archive_strcat(tmp, "/tar.XXXXXXXX");
+	a->tmpname = tmp->s;
 
 	fd = __archive_mkstemp(a->tmpname);
 	if (fd == -1)
@@ -4283,8 +4285,10 @@ create_tempdatafork(struct archive_write_disk *a, const char *pathname)
 	int tmpfd;
 
 	archive_string_init(&tmpdatafork);
-	archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");
-	tmpfd = mkstemp(tmpdatafork.s);
+	archive_strcpy(&tmpdatafork, pathname);
+	archive_string_dirname(&tmpdatafork);
+	archive_strcat(&tmpdatafork, "/tar.XXXXXXXX");
+	tmpfd = __archive_mkstemp(tmpdatafork.s);
 	if (tmpfd < 0) {
 		archive_set_error(&a->archive, errno,
 		    "Failed to mkstemp");
@@ -4363,8 +4367,10 @@ set_mac_metadata(struct archive_write_disk *a, const char *pathname,
 	 * silly dance of writing the data to disk just so that
 	 * copyfile() can read it back in again. */
 	archive_string_init(&tmp);
-	archive_strcpy(&tmp, "tar.mmd.XXXXXX");
-	fd = mkstemp(tmp.s);
+	archive_strcpy(&tmp, pathname);
+	archive_string_dirname(&tmp);
+	archive_strcat(&tmp, "/tar.XXXXXXXX");
+	fd = __archive_mkstemp(tmp.s);
 
 	if (fd < 0) {
 		archive_set_error(&a->archive, errno,
diff --git a/libarchive/test/test_archive_string.c b/libarchive/test/test_archive_string.c
index 30f7a800..bf822c0d 100644
--- a/libarchive/test/test_archive_string.c
+++ b/libarchive/test/test_archive_string.c
@@ -353,6 +353,43 @@ test_archive_string_sprintf(void)
 	archive_string_free(&s);
 }
 
+static void
+test_archive_string_dirname(void)
+{
+	static struct pair { const char *str, *exp; } pairs[] = {
+		{ "",		"." },
+		{ "/",		"/" },
+		{ "//",		"/" },
+		{ "///",	"/" },
+		{ "./",		"." },
+		{ ".",		"." },
+		{ "..",		"." },
+		{ "foo",	"." },
+		{ "foo/",	"." },
+		{ "foo//",	"." },
+		{ "foo/bar",	"foo" },
+		{ "foo/bar/",	"foo" },
+		{ "foo/bar//",	"foo" },
+		{ "foo//bar",	"foo" },
+		{ "foo//bar/",	"foo" },
+		{ "foo//bar//",	"foo" },
+		{ "/foo",	"/" },
+		{ "//foo",	"/" },
+		{ "//foo/",	"/" },
+		{ "//foo//",	"/" },
+		{ 0 },
+	};
+	struct pair *pair;
+	struct archive_string s;
+
+	archive_string_init(&s);
+	for (pair = pairs; pair->str; pair++) {
+		archive_strcpy(&s, pair->str);
+		archive_string_dirname(&s);
+		assertEqualString(pair->exp, s.s);
+	}
+}
+
 DEFINE_TEST(test_archive_string)
 {
 	test_archive_string_ensure();
@@ -364,6 +401,7 @@ DEFINE_TEST(test_archive_string)
 	test_archive_string_concat();
 	test_archive_string_copy();
 	test_archive_string_sprintf();
+	test_archive_string_dirname();
 }
 
 static const char *strings[] =
