aboutsummaryrefslogtreecommitdiff
path: root/lib/libarchive
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@FreeBSD.org>2008-09-12 04:34:56 +0000
committerTim Kientzle <kientzle@FreeBSD.org>2008-09-12 04:34:56 +0000
commit46a0d1255ba186e960f119a7e63ba2ddf7df5aa6 (patch)
treea90a8dbac11f19c8e2f7759f8500f67c2f68b6f7 /lib/libarchive
parent66aed32b3929337af9fc22a44b3c0b2c1331428b (diff)
Notes
Diffstat (limited to 'lib/libarchive')
-rw-r--r--lib/libarchive/archive_write_disk.c18
-rw-r--r--lib/libarchive/test/test_write_disk_secure.c78
2 files changed, 90 insertions, 6 deletions
diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c
index 521c16263aff..4b921e5f8c11 100644
--- a/lib/libarchive/archive_write_disk.c
+++ b/lib/libarchive/archive_write_disk.c
@@ -859,14 +859,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/lib/libarchive/test/test_write_disk_secure.c b/lib/libarchive/test/test_write_disk_secure.c
index 9f9b5da6af91..08900d9950de 100644
--- a/lib/libarchive/test/test_write_disk_secure.c
+++ b/lib/libarchive/test/test_write_disk_secure.c
@@ -105,11 +105,83 @@ 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);
-#if ARCHIVE_API_VERSION > 1
- assert(0 == archive_write_finish(a));
-#else
+ /*
+ * 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
+ assert(0 == archive_write_finish(a));
#endif
/* Test the entries on disk. */