aboutsummaryrefslogtreecommitdiffstats
path: root/libarchive/test/test_read_disk_directory_traversals.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/test/test_read_disk_directory_traversals.c')
-rw-r--r--libarchive/test/test_read_disk_directory_traversals.c299
1 files changed, 286 insertions, 13 deletions
diff --git a/libarchive/test/test_read_disk_directory_traversals.c b/libarchive/test/test_read_disk_directory_traversals.c
index 705b3d989cd5..7dd19157d4d2 100644
--- a/libarchive/test/test_read_disk_directory_traversals.c
+++ b/libarchive/test/test_read_disk_directory_traversals.c
@@ -40,7 +40,30 @@ atimeIsUpdated(void)
{
const char *fn = "fs_noatime";
struct stat st;
-
+#if defined(_WIN32) && !defined(CYGWIN)
+ char *buff = NULL;
+ char *ptr;
+ int r;
+
+ r = systemf("fsutil behavior query disableLastAccess > query_atime");
+ if (r == 0) {
+ buff = slurpfile(NULL, "query_atime");
+ if (buff != NULL) {
+ ptr = buff;
+ while(*ptr != '\0' && !isdigit(*ptr)) {
+ ptr++;
+ }
+ if (*ptr == '0') {
+ free(buff);
+ return(1);
+ } else if (*ptr == '1' || *ptr == '2') {
+ free(buff);
+ return(0);
+ }
+ free(buff);
+ }
+ }
+#endif
if (!assertMakeFile(fn, 0666, "a"))
return (0);
if (!assertUtimes(fn, 1, 0, 1, 0))
@@ -570,13 +593,13 @@ test_symlink_hybrid(void)
assertMakeDir("h", 0755);
assertChdir("h");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1");
+ assertMakeSymlink("ld1", "d1", 1);
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
- assertMakeSymlink("d1/link1", "file1");
- assertMakeSymlink("d1/linkX", "fileX");
- assertMakeSymlink("link2", "d1/file2");
- assertMakeSymlink("linkY", "d1/fileY");
+ assertMakeSymlink("d1/link1", "file1", 0);
+ assertMakeSymlink("d1/linkX", "fileX", 0);
+ assertMakeSymlink("link2", "d1/file2", 0);
+ assertMakeSymlink("linkY", "d1/fileY", 0);
assertChdir("..");
assert((ae = archive_entry_new()) != NULL);
@@ -727,13 +750,13 @@ test_symlink_logical(void)
assertMakeDir("l", 0755);
assertChdir("l");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1");
+ assertMakeSymlink("ld1", "d1", 1);
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
- assertMakeSymlink("d1/link1", "file1");
- assertMakeSymlink("d1/linkX", "fileX");
- assertMakeSymlink("link2", "d1/file2");
- assertMakeSymlink("linkY", "d1/fileY");
+ assertMakeSymlink("d1/link1", "file1", 0);
+ assertMakeSymlink("d1/linkX", "fileX", 0);
+ assertMakeSymlink("link2", "d1/file2", 0);
+ assertMakeSymlink("linkY", "d1/fileY", 0);
assertChdir("..");
/* Note: this test uses archive_read_next_header()
@@ -961,8 +984,8 @@ test_symlink_logical_loop(void)
assertMakeDir("d1/d2/d3", 0755);
assertMakeDir("d2", 0755);
assertMakeFile("d2/file1", 0644, "d2/file1");
- assertMakeSymlink("d1/d2/ld1", "../../d1");
- assertMakeSymlink("d1/d2/ld2", "../../d2");
+ assertMakeSymlink("d1/d2/ld1", "../../d1", 1);
+ assertMakeSymlink("d1/d2/ld2", "../../d2", 1);
assertChdir("..");
assert((ae = archive_entry_new()) != NULL);
@@ -1567,6 +1590,254 @@ test_nodump(void)
archive_entry_free(ae);
}
+static void
+test_parent(void)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ const void *p;
+ size_t size;
+ int64_t offset;
+ int file_count;
+ int match_count;
+ int r;
+
+ assertMakeDir("lock", 0311);
+ assertMakeDir("lock/dir1", 0755);
+ assertMakeFile("lock/dir1/f1", 0644, "0123456789");
+ assertMakeDir("lock/lock2", 0311);
+ assertMakeDir("lock/lock2/dir1", 0755);
+ assertMakeFile("lock/lock2/dir1/f1", 0644, "0123456789");
+
+ assert((ae = archive_entry_new()) != NULL);
+ assert((a = archive_read_disk_new()) != NULL);
+
+ /*
+ * Test1: Traverse lock/dir1 as .
+ */
+ assertChdir("lock/dir1");
+
+ failure("Directory traversals should work as well");
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "."));
+
+ file_count = 2;
+ match_count = 0;
+ while (file_count--) {
+ archive_entry_clear(ae);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
+ if (strcmp(archive_entry_pathname(ae), ".") == 0) {
+ assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
+ ++match_count;
+ } else if (strcmp(archive_entry_pathname(ae), "./f1") == 0) {
+ assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+ assertEqualInt(archive_entry_size(ae), 10);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_data_block(a, &p, &size, &offset));
+ assertEqualInt((int)size, 10);
+ assertEqualInt((int)offset, 0);
+ assertEqualMem(p, "0123456789", 10);
+ assertEqualInt(ARCHIVE_EOF,
+ archive_read_data_block(a, &p, &size, &offset));
+ assertEqualInt((int)size, 0);
+ assertEqualInt((int)offset, 10);
+ ++match_count;
+ }
+ if (archive_read_disk_can_descend(a)) {
+ /* Descend into the current object */
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_descend(a));
+ }
+ }
+ failure("Did not match expected filenames");
+ assertEqualInt(match_count, 2);
+ /*
+ * There is no entry. This will however fail if the directory traverse
+ * tries to ascend past the initial directory, since it lacks permission
+ * to do so.
+ */
+ failure("There should be no entry and no error");
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae));
+
+ /* Close the disk object. */
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+
+ assertChdir("../..");
+
+ /*
+ * Test2: Traverse lock/dir1 directly
+ */
+ failure("Directory traversals should work as well");
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1"));
+
+ file_count = 2;
+ match_count = 0;
+ while (file_count--) {
+ archive_entry_clear(ae);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
+ if (strcmp(archive_entry_pathname(ae), "lock/dir1") == 0) {
+ assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
+ ++match_count;
+ } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/f1") == 0) {
+ assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+ assertEqualInt(archive_entry_size(ae), 10);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_data_block(a, &p, &size, &offset));
+ assertEqualInt((int)size, 10);
+ assertEqualInt((int)offset, 0);
+ assertEqualMem(p, "0123456789", 10);
+ assertEqualInt(ARCHIVE_EOF,
+ archive_read_data_block(a, &p, &size, &offset));
+ assertEqualInt((int)size, 0);
+ assertEqualInt((int)offset, 10);
+ ++match_count;
+ }
+ if (archive_read_disk_can_descend(a)) {
+ /* Descend into the current object */
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_descend(a));
+ }
+ }
+ failure("Did not match expected filenames");
+ assertEqualInt(match_count, 2);
+ /*
+ * There is no entry. This will however fail if the directory traverse
+ * tries to ascend past the initial directory, since it lacks permission
+ * to do so.
+ */
+ failure("There should be no entry and no error");
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae));
+
+ /* Close the disk object. */
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+
+ /*
+ * Test3: Traverse lock/dir1/.
+ */
+ failure("Directory traversals should work as well");
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1/."));
+
+ file_count = 2;
+ match_count = 0;
+ while (file_count--) {
+ archive_entry_clear(ae);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
+ if (strcmp(archive_entry_pathname(ae), "lock/dir1/.") == 0) {
+ assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
+ ++match_count;
+ } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/./f1") == 0) {
+ assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+ assertEqualInt(archive_entry_size(ae), 10);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_data_block(a, &p, &size, &offset));
+ assertEqualInt((int)size, 10);
+ assertEqualInt((int)offset, 0);
+ assertEqualMem(p, "0123456789", 10);
+ assertEqualInt(ARCHIVE_EOF,
+ archive_read_data_block(a, &p, &size, &offset));
+ assertEqualInt((int)size, 0);
+ assertEqualInt((int)offset, 10);
+ ++match_count;
+ }
+ if (archive_read_disk_can_descend(a)) {
+ /* Descend into the current object */
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_descend(a));
+ }
+ }
+ failure("Did not match expected filenames");
+ assertEqualInt(match_count, 2);
+ /*
+ * There is no entry. This will however fail if the directory traverse
+ * tries to ascend past the initial directory, since it lacks permission
+ * to do so.
+ */
+ failure("There should be no entry and no error");
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae));
+
+ /* Close the disk object. */
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+
+ /*
+ * Test4: Traverse lock/lock2/dir1 from inside lock.
+ *
+ * This test is expected to fail on platforms with no O_EXEC or
+ * equivalent (e.g. O_PATH on Linux or O_SEARCH on SunOS), because
+ * the current traversal code can't handle the case where it can't
+ * obtain an open fd for the initial current directory. We need to
+ * check that condition here, because if O_EXEC _does_ exist, we don't
+ * want to overlook any failure.
+ */
+ assertChdir("lock");
+
+ failure("Directory traversals should work as well");
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock2/dir1"));
+
+ archive_entry_clear(ae);
+ r = archive_read_next_header2(a, ae);
+ if (r == ARCHIVE_FAILED) {
+#if defined(O_PATH) || defined(O_SEARCH) || defined(O_EXEC)
+ assertEqualIntA(a, ARCHIVE_OK, r);
+#endif
+ /* Close the disk object. */
+ archive_read_close(a);
+ } else {
+ file_count = 2;
+ match_count = 0;
+ while (file_count--) {
+ if (file_count == 0)
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_next_header2(a, ae));
+ if (strcmp(archive_entry_pathname(ae),
+ "lock2/dir1") == 0) {
+ assertEqualInt(archive_entry_filetype(ae),
+ AE_IFDIR);
+ ++match_count;
+ } else if (strcmp(archive_entry_pathname(ae),
+ "lock2/dir1/f1") == 0) {
+ assertEqualInt(archive_entry_filetype(ae),
+ AE_IFREG);
+ assertEqualInt(archive_entry_size(ae), 10);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_data_block(a, &p, &size,
+ &offset));
+ assertEqualInt((int)size, 10);
+ assertEqualInt((int)offset, 0);
+ assertEqualMem(p, "0123456789", 10);
+ assertEqualInt(ARCHIVE_EOF,
+ archive_read_data_block(a, &p, &size,
+ &offset));
+ assertEqualInt((int)size, 0);
+ assertEqualInt((int)offset, 10);
+ ++match_count;
+ }
+ if (archive_read_disk_can_descend(a)) {
+ /* Descend into the current object */
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_descend(a));
+ }
+ }
+ failure("Did not match expected filenames");
+ assertEqualInt(match_count, 2);
+ /*
+ * There is no entry. This will however fail if the directory
+ * traverse tries to ascend past the initial directory, since
+ * it lacks permission to do so.
+ */
+ failure("There should be no entry and no error");
+ assertEqualIntA(a, ARCHIVE_EOF,
+ archive_read_next_header2(a, ae));
+
+ /* Close the disk object. */
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ }
+
+ assertChdir("..");
+
+ /* Destroy the disk object. */
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ archive_entry_free(ae);
+}
+
DEFINE_TEST(test_read_disk_directory_traversals)
{
/* Basic test. */
@@ -1583,4 +1854,6 @@ DEFINE_TEST(test_read_disk_directory_traversals)
test_callbacks();
/* Test nodump. */
test_nodump();
+ /* Test parent overshoot. */
+ test_parent();
}