From 3deba4d2cdebf4bc049b74e06393e1149d6d5433 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Sun, 7 Sep 2008 17:25:57 -0400 Subject: [PATCH] IFC SVN-Revision: 197 --- libarchive/archive_write_disk.c | 20 ++++- libarchive/test/Makefile | 4 +- .../test/test_read_format_gtar_sparse.c | 9 ++- libarchive/test/test_write_disk_secure.c | 76 ++++++++++++++++++- 4 files changed, 98 insertions(+), 11 deletions(-) diff --git a/libarchive/archive_write_disk.c b/libarchive/archive_write_disk.c index 40b401443..b385978f1 100644 --- a/libarchive/archive_write_disk.c +++ b/libarchive/archive_write_disk.c @@ -25,7 +25,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.35 2008/09/05 06:13:11 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.36 2008/09/07 05:22:33 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -907,14 +907,26 @@ restore_entry(struct archive_write_disk *a) * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ - if (lstat(a->name, &a->st) != 0) { + int r = 0; + /* + * The SECURE_SYMLINK logic has already removed a + * symlink to a dir if the client wants that. So + * follow the symlink if we're creating a dir. + */ + if (S_ISDIR(a->mode)) + r = stat(a->name, &a->st); + /* + * If it's not a dir (or it's a broken symlink), + * then don't follow it. + */ + if (r != 0 || !S_ISDIR(a->mode)) + r = lstat(a->name, &a->st); + if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_WARN); } - /* TODO: if it's a symlink... */ - /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ diff --git a/libarchive/test/Makefile b/libarchive/test/Makefile index e36001656..0bd338b0a 100644 --- a/libarchive/test/Makefile +++ b/libarchive/test/Makefile @@ -1,4 +1,4 @@ -# $FreeBSD: src/lib/libarchive/test/Makefile,v 1.24 2008/08/25 06:08:22 kientzle Exp $ +# $FreeBSD: src/lib/libarchive/test/Makefile,v 1.25 2008/09/08 00:58:12 kientzle Exp $ # Where to find the libarchive sources LA_SRCDIR=${.CURDIR}/.. @@ -89,7 +89,7 @@ CFLAGS+= -I${LA_SRCDIR} -I. # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC -#WARNS=6 +WARNS=6 # Build libarchive_test and run it. check test: libarchive_test diff --git a/libarchive/test/test_read_format_gtar_sparse.c b/libarchive/test/test_read_format_gtar_sparse.c index a8257f892..32aecf9e7 100644 --- a/libarchive/test/test_read_format_gtar_sparse.c +++ b/libarchive/test/test_read_format_gtar_sparse.c @@ -23,7 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" -__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse.c,v 1.9 2008/09/01 05:38:33 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse.c,v 1.10 2008/09/08 00:58:12 kientzle Exp $"); struct contents { @@ -187,6 +187,7 @@ verify_archive_file(const char *name, struct archive_contents *ac) struct contents expect; /* data, size, offset of block read from archive. */ struct contents actual; + const void *p; struct archive *a; extract_reference_file(name); @@ -206,10 +207,10 @@ verify_archive_file(const char *name, struct archive_contents *ac) expect = *cts++; while (0 == (err = archive_read_data_block(a, - (const void **)&actual.d, - &actual.s, &actual.o))) { + &p, &actual.s, &actual.o))) { + actual.d = p; while (actual.s > 0) { - char c = *(const char *)actual.d; + char c = *actual.d; if(actual.o < expect.o) { /* * Any byte before the expected diff --git a/libarchive/test/test_write_disk_secure.c b/libarchive/test/test_write_disk_secure.c index 67c6bc08e..be7353053 100644 --- a/libarchive/test/test_write_disk_secure.c +++ b/libarchive/test/test_write_disk_secure.c @@ -23,7 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" -__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_secure.c,v 1.5 2008/09/01 05:38:33 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_secure.c,v 1.8 2008/09/07 23:59:27 kientzle Exp $"); #define UMASK 022 @@ -105,6 +105,80 @@ DEFINE_TEST(test_write_disk_secure) archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); + /* + * Without security checks, extracting a dir over a link to a + * dir should follow the link. + */ + /* Create a symlink to a dir. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "link_to_dir3"); + archive_entry_set_mode(ae, S_IFLNK | 0777); + archive_entry_set_symlink(ae, "dir"); + archive_write_disk_set_options(a, 0); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Extract a dir whose name matches the symlink. */ + assert(archive_entry_clear(ae) != NULL); + archive_entry_copy_pathname(ae, "link_to_dir3"); + archive_entry_set_mode(ae, S_IFDIR | 0777); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Verify link was followed. */ + assertEqualInt(0, lstat("link_to_dir3", &st)); + assert(S_ISLNK(st.st_mode)); + archive_entry_free(ae); + + /* + * As above, but a broken link, so the link should get replaced. + */ + /* Create a symlink to a dir. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "link_to_dir4"); + archive_entry_set_mode(ae, S_IFLNK | 0777); + archive_entry_set_symlink(ae, "nonexistent_dir"); + archive_write_disk_set_options(a, 0); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Extract a dir whose name matches the symlink. */ + assert(archive_entry_clear(ae) != NULL); + archive_entry_copy_pathname(ae, "link_to_dir4"); + archive_entry_set_mode(ae, S_IFDIR | 0777); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Verify link was replaced. */ + assertEqualInt(0, lstat("link_to_dir4", &st)); + assert(S_ISDIR(st.st_mode)); + archive_entry_free(ae); + + /* + * As above, but a link to a non-dir, so the link should get replaced. + */ + /* Create a regular file and a symlink to it */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "non_dir"); + archive_entry_set_mode(ae, S_IFREG | 0777); + archive_write_disk_set_options(a, 0); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Create symlink to the file. */ + archive_entry_copy_pathname(ae, "link_to_dir5"); + archive_entry_set_mode(ae, S_IFLNK | 0777); + archive_entry_set_symlink(ae, "non_dir"); + archive_write_disk_set_options(a, 0); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Extract a dir whose name matches the symlink. */ + assert(archive_entry_clear(ae) != NULL); + archive_entry_copy_pathname(ae, "link_to_dir5"); + archive_entry_set_mode(ae, S_IFDIR | 0777); + assert(0 == archive_write_header(a, ae)); + assert(0 == archive_write_finish_entry(a)); + /* Verify link was replaced. */ + assertEqualInt(0, lstat("link_to_dir5", &st)); + assert(S_ISDIR(st.st_mode)); + archive_entry_free(ae); + + #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(a); #else -- 2.47.3