Module Name:    src
Committed By:   christos
Date:           Sun Jan 12 16:08:31 UTC 2020

Modified Files:
        src/external/bsd/libarchive/dist/libarchive: archive.h
            archive_private.h archive_util.c archive_write_disk_posix.c
        src/external/bsd/libarchive/dist/tar: bsdtar.c

Log Message:
Introduce ARCHIVE_EXTRACT_ATOMIC and set it by default on bsdtar.

This flag changes the way that regular files are extracted:

Instead of removing existing files first and re-creating them in
order to replace their contents, a temporary file is created and
when writing to the temporary file is completed, the file is
rename(2)d to the final destination name.

This has the effect of presenting a consistent view of the file to
the system (either the file with the new contents or the file with
the old contents). Removing and overwriting the file has the
undesired side effect that the the system can either not see the
file at all (from the time it is being removed till the time it is
being re-created), or worse it can see partial file contents. This
is problematic when extracting system files (for example shared
libraries).

Perhaps there should be a flag to disable it, when for example it
is not desirable because of space constraints, but then again
one can specify to unlink the file before.

(this is pull request 1289)


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 \
    src/external/bsd/libarchive/dist/libarchive/archive.h
cvs rdiff -u -r1.1.1.3 -r1.2 \
    src/external/bsd/libarchive/dist/libarchive/archive_private.h
cvs rdiff -u -r1.1.1.4 -r1.2 \
    src/external/bsd/libarchive/dist/libarchive/archive_util.c
cvs rdiff -u -r1.1.1.2 -r1.2 \
    src/external/bsd/libarchive/dist/libarchive/archive_write_disk_posix.c
cvs rdiff -u -r1.1.1.4 -r1.2 src/external/bsd/libarchive/dist/tar/bsdtar.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/libarchive/dist/libarchive/archive.h
diff -u src/external/bsd/libarchive/dist/libarchive/archive.h:1.4 src/external/bsd/libarchive/dist/libarchive/archive.h:1.5
--- src/external/bsd/libarchive/dist/libarchive/archive.h:1.4	Wed Jul 24 10:03:57 2019
+++ src/external/bsd/libarchive/dist/libarchive/archive.h	Sun Jan 12 11:08:31 2020
@@ -693,6 +693,8 @@ __LA_DECL int archive_read_set_passphras
 #define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
 /* Default: Do not clear no-change flags when unlinking object */
 #define	ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS	(0x20000)
+/* Default: Do not extract atomically (using rename) */
+#define	ARCHIVE_EXTRACT_ATOMIC			(0x40000)
 
 __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
 		     int flags);

Index: src/external/bsd/libarchive/dist/libarchive/archive_private.h
diff -u src/external/bsd/libarchive/dist/libarchive/archive_private.h:1.1.1.3 src/external/bsd/libarchive/dist/libarchive/archive_private.h:1.2
--- src/external/bsd/libarchive/dist/libarchive/archive_private.h:1.1.1.3	Thu Apr 20 08:55:34 2017
+++ src/external/bsd/libarchive/dist/libarchive/archive_private.h	Sun Jan 12 11:08:31 2020
@@ -153,6 +153,7 @@ void	__archive_errx(int retvalue, const 
 
 void	__archive_ensure_cloexec_flag(int fd);
 int	__archive_mktemp(const char *tmpdir);
+int	__archive_mktempx(const char *tmpdir, struct archive_string *template);
 
 int	__archive_clean(struct archive *);
 

Index: src/external/bsd/libarchive/dist/libarchive/archive_util.c
diff -u src/external/bsd/libarchive/dist/libarchive/archive_util.c:1.1.1.4 src/external/bsd/libarchive/dist/libarchive/archive_util.c:1.2
--- src/external/bsd/libarchive/dist/libarchive/archive_util.c:1.1.1.4	Wed Jul 24 09:50:21 2019
+++ src/external/bsd/libarchive/dist/libarchive/archive_util.c	Sun Jan 12 11:08:31 2020
@@ -389,28 +389,33 @@ get_tempdir(struct archive_string *tempp
  */
 
 int
-__archive_mktemp(const char *tmpdir)
+__archive_mktempx(const char *tmpdir, struct archive_string *template)
 {
 	struct archive_string temp_name;
 	int fd = -1;
 
-	archive_string_init(&temp_name);
-	if (tmpdir == NULL) {
-		if (get_tempdir(&temp_name) != ARCHIVE_OK)
-			goto exit_tmpfile;
-	} else {
-		archive_strcpy(&temp_name, tmpdir);
-		if (temp_name.s[temp_name.length-1] != '/')
-			archive_strappend_char(&temp_name, '/');
+	if (template == NULL) {
+		archive_string_init(template = &temp_name);
+		if (tmpdir == NULL) {
+			if (get_tempdir(&temp_name) != ARCHIVE_OK)
+				goto exit_tmpfile;
+		} else {
+			archive_strcpy(&temp_name, tmpdir);
+			if (temp_name.s[temp_name.length-1] != '/')
+				archive_strappend_char(&temp_name, '/');
+		}
+		archive_strcat(&temp_name, "libarchive_XXXXXX");
 	}
-	archive_strcat(&temp_name, "libarchive_XXXXXX");
-	fd = mkstemp(temp_name.s);
+	fd = mkstemp(template->s);
 	if (fd < 0)
 		goto exit_tmpfile;
 	__archive_ensure_cloexec_flag(fd);
-	unlink(temp_name.s);
+
+	if (template == &temp_name)
+		unlink(temp_name.s);
 exit_tmpfile:
-	archive_string_free(&temp_name);
+	if (template == &temp_name)
+		archive_string_free(&temp_name);
 	return (fd);
 }
 
@@ -421,7 +426,7 @@ exit_tmpfile:
  */
 
 int
-__archive_mktemp(const char *tmpdir)
+__archive_mktempx(const char *tmpdir, struct archive_string *template)
 {
         static const char num[] = {
 		'0', '1', '2', '3', '4', '5', '6', '7',
@@ -439,26 +444,36 @@ __archive_mktemp(const char *tmpdir)
 	char *tp, *ep;
 
 	fd = -1;
-	archive_string_init(&temp_name);
-	if (tmpdir == NULL) {
-		if (get_tempdir(&temp_name) != ARCHIVE_OK)
+	if (template == NULL) {
+		archive_string_init(template = &temp_name);
+		if (tmpdir == NULL) {
+			if (get_tempdir(&temp_name) != ARCHIVE_OK)
+				goto exit_tmpfile;
+		} else
+			archive_strcpy(&temp_name, tmpdir);
+		if (temp_name.s[temp_name.length-1] == '/') {
+			temp_name.s[temp_name.length-1] = '\0';
+			temp_name.length --;
+		}
+		if (la_stat(temp_name.s, &st) < 0)
 			goto exit_tmpfile;
-	} else
-		archive_strcpy(&temp_name, tmpdir);
-	if (temp_name.s[temp_name.length-1] == '/') {
-		temp_name.s[temp_name.length-1] = '\0';
-		temp_name.length --;
-	}
-	if (la_stat(temp_name.s, &st) < 0)
-		goto exit_tmpfile;
-	if (!S_ISDIR(st.st_mode)) {
-		errno = ENOTDIR;
-		goto exit_tmpfile;
+		if (!S_ISDIR(st.st_mode)) {
+			errno = ENOTDIR;
+			goto exit_tmpfile;
+		}
+		archive_strcat(&temp_name, "/libarchive_");
+		tp = temp_name.s + archive_strlen(&temp_name);
+		archive_strcat(&temp_name, "XXXXXXXXXX");
+		ep = temp_name.s + archive_strlen(&temp_name);
+	} else {
+		tp = strchr(template->s, 'X');
+		if (tp == NULL)	/* No X, programming error */
+			abort();
+		for (ep = *tp; *ep == 'X'; ep++)
+			continue;
+		if (*ep)	/* X followed by non X, programming error */
+			abort();
 	}
-	archive_strcat(&temp_name, "/libarchive_");
-	tp = temp_name.s + archive_strlen(&temp_name);
-	archive_strcat(&temp_name, "XXXXXXXXXX");
-	ep = temp_name.s + archive_strlen(&temp_name);
 
 	do {
 		char *p;
@@ -469,19 +484,27 @@ __archive_mktemp(const char *tmpdir)
 			int d = *((unsigned char *)p) % sizeof(num);
 			*p++ = num[d];
 		}
-		fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
+		fd = open(template->s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
 			  0600);
 	} while (fd < 0 && errno == EEXIST);
 	if (fd < 0)
 		goto exit_tmpfile;
 	__archive_ensure_cloexec_flag(fd);
-	unlink(temp_name.s);
+	if (template == &temp_name)
+		unlink(temp_name.s);
 exit_tmpfile:
-	archive_string_free(&temp_name);
+	if (template == &temp_name)
+		archive_string_free(&temp_name);
 	return (fd);
 }
 
 #endif /* HAVE_MKSTEMP */
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+	return __archive_mktempx(tmpdir, NULL);
+}
 #endif /* !_WIN32 || __CYGWIN__ */
 
 /*

Index: src/external/bsd/libarchive/dist/libarchive/archive_write_disk_posix.c
diff -u src/external/bsd/libarchive/dist/libarchive/archive_write_disk_posix.c:1.1.1.2 src/external/bsd/libarchive/dist/libarchive/archive_write_disk_posix.c:1.2
--- src/external/bsd/libarchive/dist/libarchive/archive_write_disk_posix.c:1.1.1.2	Wed Jul 24 09:50:21 2019
+++ src/external/bsd/libarchive/dist/libarchive/archive_write_disk_posix.c	Sun Jan 12 11:08:31 2020
@@ -253,6 +253,8 @@ struct archive_write_disk {
 	struct archive_entry	*entry; /* Entry being extracted. */
 	char			*name; /* Name of entry, possibly edited. */
 	struct archive_string	 _name_data; /* backing store for 'name' */
+	char			*tmpname; /* Temporary name * */
+	struct archive_string	 _tmpname_data; /* backing store for 'tmpname' */
 	/* Tasks remaining for this object. */
 	int			 todo;
 	/* Tasks deferred until end-of-archive. */
@@ -407,6 +409,30 @@ static ssize_t	_archive_write_disk_data_
 		    size_t, int64_t);
 
 static int
+la_mktemp(struct archive_write_disk *a)
+{
+	int oerrno, fd;
+	mode_t mode;
+
+	archive_string_empty(&a->_tmpname_data);
+	archive_string_sprintf(&a->_tmpname_data, "%sXXXXXX", a->name);
+	a->tmpname = a->_tmpname_data.s;
+
+	fd = __archive_mktempx(NULL, &a->_tmpname_data);
+	if (fd == -1)
+		return -1;
+
+	mode = a->mode & 0777 & ~a->user_umask;
+	if (fchmod(fd, mode) == -1) {
+		oerrno = errno;
+		close(fd);
+		errno = oerrno;
+		return -1;
+	}
+	return fd;
+}
+
+static int
 la_opendirat(int fd, const char *path) {
 	const int flags = O_CLOEXEC
 #if defined(O_BINARY)
@@ -1826,6 +1852,14 @@ finish_metadata:
 	if (a->fd >= 0) {
 		close(a->fd);
 		a->fd = -1;
+		if (a->tmpname) {
+			if (rename(a->tmpname, a->name) == -1) {
+				archive_set_error(&a->archive, errno,
+				    "rename failed");
+				ret = ARCHIVE_FATAL;
+			}
+			a->tmpname = NULL;
+		}
 	}
 	/* If there's an entry, we can release it now. */
 	archive_entry_free(a->entry);
@@ -2103,17 +2137,28 @@ restore_entry(struct archive_write_disk 
 		}
 
 		if (!S_ISDIR(a->st.st_mode)) {
-			/* A non-dir is in the way, unlink it. */
 			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
 				(void)clear_nochange_fflags(a);
-			if (unlink(a->name) != 0) {
-				archive_set_error(&a->archive, errno,
-				    "Can't unlink already-existing object");
-				return (ARCHIVE_FAILED);
+
+			if ((a->flags & ARCHIVE_EXTRACT_ATOMIC) &&
+			    S_ISREG(a->st.st_mode)) {
+				/* Use a temporary file to extract */
+				if ((a->fd = la_mktemp(a)) == -1)
+					return ARCHIVE_FAILED;
+				a->pst = NULL;
+				en = 0;
+			} else {
+				/* A non-dir is in the way, unlink it. */
+				if (unlink(a->name) != 0) {
+					archive_set_error(&a->archive, errno,
+					    "Can't unlink already-existing "
+					    "object");
+					return (ARCHIVE_FAILED);
+				}
+				a->pst = NULL;
+				/* Try again. */
+				en = create_filesystem_object(a);
 			}
-			a->pst = NULL;
-			/* Try again. */
-			en = create_filesystem_object(a);
 		} else if (!S_ISDIR(a->mode)) {
 			/* A dir is in the way of a non-dir, rmdir it. */
 			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
@@ -2288,6 +2333,7 @@ create_filesystem_object(struct archive_
 		/* POSIX requires that we fall through here. */
 		/* FALLTHROUGH */
 	case AE_IFREG:
+		a->tmpname = NULL;
 		a->fd = open(a->name,
 		    O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);
 		__archive_ensure_cloexec_flag(a->fd);
@@ -2449,6 +2495,7 @@ _archive_write_disk_free(struct archive 
 	archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
 	archive_entry_free(a->entry);
 	archive_string_free(&a->_name_data);
+	archive_string_free(&a->_tmpname_data);
 	archive_string_free(&a->archive.error_string);
 	archive_string_free(&a->path_safe);
 	a->archive.magic = 0;

Index: src/external/bsd/libarchive/dist/tar/bsdtar.c
diff -u src/external/bsd/libarchive/dist/tar/bsdtar.c:1.1.1.4 src/external/bsd/libarchive/dist/tar/bsdtar.c:1.2
--- src/external/bsd/libarchive/dist/tar/bsdtar.c:1.1.1.4	Wed Jul 24 09:50:41 2019
+++ src/external/bsd/libarchive/dist/tar/bsdtar.c	Sun Jan 12 11:08:31 2020
@@ -231,6 +231,9 @@ main(int argc, char **argv)
 	/* Default: Perform basic security checks. */
 	bsdtar->extract_flags |= SECURITY;
 
+	/* Default: Extract atomically if possible */
+	bsdtar->extract_flags |= ARCHIVE_EXTRACT_ATOMIC;
+
 #ifndef _WIN32
 	/* On POSIX systems, assume --same-owner and -p when run by
 	 * the root user.  This doesn't make any sense on Windows. */

Reply via email to