From 6674b4aa0f47ce6a98d24cfc949a2e4a3ffd254c Mon Sep 17 00:00:00 2001 From: Paul Spangler Date: Thu, 19 Oct 2017 14:33:12 -0500 Subject: [PATCH] archive_write_disk_{posix,windows}.c: Don't modify attributes for existing directories when ARCHIVE_EXTRACT_NO_OVERWRITE is set Enables unpacking multiple archives into a single directory whose permissions, owner, and other attributes are pre-configured or otherwise determined ahead of time by a single archive without the need to repeat the same attributes in every archive, such as in package installation scenarios. Signed-off-by: Paul Spangler --- libarchive/archive_write_disk_posix.c | 4 +++ libarchive/archive_write_disk_windows.c | 4 +++ libarchive/test/test_write_disk_perms.c | 43 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 61961278b..3fd5f5798 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -1981,6 +1981,10 @@ restore_entry(struct archive_write_disk *a) if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ + if (S_ISDIR(a->mode)) { + /* Don't overwrite any settings on existing directories. */ + a->todo = 0; + } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index 8fe99a07f..b09c2712f 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -1325,6 +1325,10 @@ restore_entry(struct archive_write_disk *a) if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ + if (S_ISDIR(a->mode)) { + /* Don't overwrite any settings on existing directories. */ + a->todo = 0; + } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } diff --git a/libarchive/test/test_write_disk_perms.c b/libarchive/test/test_write_disk_perms.c index 4b68e52b4..0872138c5 100644 --- a/libarchive/test/test_write_disk_perms.c +++ b/libarchive/test/test_write_disk_perms.c @@ -201,6 +201,49 @@ DEFINE_TEST(test_write_disk_perms) failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); assertEqualInt(st.st_mode & 0777, 0744); + /* For dir, the owner should get left when not overwritting. */ + assertMakeDir("dir_owner", 0744); + if (getuid() == 0) { + assertEqualInt(0, chown("dir_owner", getuid()+1, getgid())); + /* Check original owner. */ + assertEqualInt(0, stat("dir_owner", &st)); + failure("dir_owner: st.st_uid=%d", st.st_uid); + assertEqualInt(st.st_uid, getuid()+1); + /* Shouldn't edit the owner when no overwrite option is set. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dir_owner"); + archive_entry_set_mode(ae, S_IFDIR | 0744); + archive_entry_set_uid(ae, getuid()); + archive_write_disk_set_options(a, + ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_NO_OVERWRITE); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); + /* Make sure they're unchanged. */ + assertEqualInt(0, stat("dir_owner", &st)); + failure("dir_owner: st.st_uid=%d", st.st_uid); + assertEqualInt(st.st_uid, getuid()+1); + } else { + /* Check original owner. */ + assertEqualInt(0, stat("dir_owner", &st)); + failure("dir_owner: st.st_uid=%d", st.st_uid); + assertEqualInt(st.st_uid, getuid()); + /* Shouldn't fail to set owner when no overwrite option is set. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dir_owner"); + archive_entry_set_mode(ae, S_IFDIR | 0744); + archive_entry_set_uid(ae, getuid()+1); + archive_write_disk_set_options(a, + ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_NO_OVERWRITE); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); + /* Make sure they're unchanged. */ + assertEqualInt(0, stat("dir_owner", &st)); + failure("dir_owner: st.st_uid=%d", st.st_uid); + assertEqualInt(st.st_uid, getuid()); + } + /* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file_no_suid"); -- 2.47.2