]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Introduce archive_read_disk_honor_nodump() to eliminate the platform
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sat, 31 Dec 2011 19:29:09 +0000 (14:29 -0500)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sat, 31 Dec 2011 19:29:09 +0000 (14:29 -0500)
dependent code testing nodump flag from bsdtar and bsdpax.

SVN-Revision: 4052

libarchive/archive.h
libarchive/archive_read_disk_entry_from_file.c
libarchive/archive_read_disk_posix.c
libarchive/archive_read_disk_private.h
libarchive/archive_read_disk_windows.c
libarchive/test/test_read_disk_directory_traversals.c

index 3d853fe226b1b72da38f0c0cb18db34452ad1372..d5141a1b7b387befd2e57d061fdd226faea624ad 100644 (file)
@@ -764,6 +764,7 @@ __LA_DECL int       archive_read_disk_current_filesystem_is_synthetic(struct archive *
 __LA_DECL int  archive_read_disk_current_filesystem_is_remote(struct archive *);
 /* Request that the access time of the entry visited by travesal be restored. */
 __LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);
+__LA_DECL int  archive_read_disk_honor_nodump(struct archive *);
 
 __LA_DECL int  archive_read_disk_set_name_filter_callback(struct archive *,
                    int (*_name_filter_func)(struct archive *, void *,
index 8ce88b380fca8994b9c734b81c468da08ac15312..8a83dfe22f6664c49923fa63460f8c91933f5d07 100644 (file)
@@ -192,8 +192,17 @@ archive_read_disk_entry_from_file(struct archive *_a,
                if (fd >= 0) {
                        unsigned long stflags;
                        int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
-                       if (r == 0 && stflags != 0)
+                       if (r == 0 && stflags != 0) {
                                archive_entry_set_fflags(entry, stflags, 0);
+#if defined(EXT2_NODUMP_FL)
+                               if (a->tree != NULL && a->honor_nodump &&
+                                   (stflags & EXT2_NODUMP_FL) != 0) {
+                                       if (initial_fd != fd)
+                                               close(fd);
+                                       return (ARCHIVE_OK);
+                               }
+#endif
+                       }
                }
        }
 #endif
index 241276cc53840bc2679f1289a09b20ddf91c2b87..2987063d64d18720039048a1b40fc6d5cea1db67 100644 (file)
@@ -52,6 +52,19 @@ __FBSDID("$FreeBSD$");
 #ifdef HAVE_LINUX_MAGIC_H
 #include <linux/magic.h>
 #endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>      /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
+#endif
 #ifdef HAVE_DIRECT_H
 #include <direct.h>
 #endif
@@ -555,6 +568,16 @@ archive_read_disk_set_atime_restored(struct archive *_a)
 #endif
 }
 
+int
+archive_read_disk_honor_nodump(struct archive *_a)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+       a->honor_nodump = 1;
+       return (ARCHIVE_OK);
+}
+
 /*
  * Trivial implementations of gname/uname lookup functions.
  * These are normally overridden by the client, but these stub
@@ -918,6 +941,19 @@ next_entry:
                        goto next_entry;
                }
        }
+
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+       if (a->honor_nodump) {
+               if (st->st_flags & UF_NODUMP) {
+                       archive_entry_unset_atime(entry);
+                       archive_entry_unset_birthtime(entry);
+                       archive_entry_unset_ctime(entry);
+                       archive_entry_unset_mtime(entry);
+                       goto next_entry;
+               }
+       }
+#endif
+
        archive_entry_copy_sourcepath(entry, tree_current_access_path(t));
 
        /* Save the times to be restored. */
@@ -958,6 +994,21 @@ next_entry:
        if (fd >= 0)
                close(fd);
 
+#if defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+       /* Linux uses ioctl to read flags. */
+       if (r == ARCHIVE_OK && a->honor_nodump) {
+               unsigned long flags, clear;
+               archive_entry_fflags(entry, &flags, &clear);
+               if (flags & EXT2_NODUMP_FL) {
+                       archive_entry_unset_atime(entry);
+                       archive_entry_unset_birthtime(entry);
+                       archive_entry_unset_ctime(entry);
+                       archive_entry_unset_mtime(entry);
+                       goto next_entry;
+               }
+       }
+#endif
+
        /* Return to the initial directory. */
        tree_enter_initial_dir(t);
        archive_entry_copy_sourcepath(entry, tree_current_path(t));
index 14038d88afdba6126538bad41c5ec2956911ab3e..0637e861d04e3fc568c724e30f398fb236703092 100644 (file)
@@ -59,6 +59,8 @@ struct archive_read_disk {
 
        /* Set 1 if users request to restore atime . */
        int              restore_time;
+       /* Set 1 if users request to honor nodump flag . */
+       int              honor_nodump;
        int              entry_wd_fd;
 
        const char * (*lookup_gname)(void *private, int64_t gid);
@@ -68,12 +70,12 @@ struct archive_read_disk {
        void    (*cleanup_uname)(void *private);
        void     *lookup_uname_data;
 
-       int                     (*name_filter_func)(struct archive *,
-                               void *, struct archive_entry *);
-       void                    *name_filter_data;
-       int                     (*time_filter_func)(struct archive *,
-                               void *, struct archive_entry *);
-       void                    *time_filter_data;
+       int     (*name_filter_func)(struct archive *, void *,
+                       struct archive_entry *);
+       void    *name_filter_data;
+       int     (*time_filter_func)(struct archive *, void *,
+                       struct archive_entry *);
+       void    *time_filter_data;
 
 };
 
index 302f3c7f7dff7a9ba96bfcc7db119f8718d40e9f..738be9fb308e6e8900c8e15c6e870778780c1c66 100644 (file)
@@ -536,6 +536,16 @@ archive_read_disk_set_atime_restored(struct archive *_a)
        return (ARCHIVE_OK);
 }
 
+int
+archive_read_disk_honor_nodump(struct archive *_a)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+       a->honor_nodump = 1;
+       return (ARCHIVE_OK);
+}
+
 /*
  * Trivial implementations of gname/uname lookup functions.
  * These are normally overridden by the client, but these stub
index 44ab3197d7bf2fb4cfe5d4c37eb710924161110a..32fc2470d2d2c5a5624d1f0eaa0591955712c948 100644 (file)
 #include "test.h"
 __FBSDID("$FreeBSD$");
 
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
 #include <limits.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 #if defined(_WIN32) && !defined(__CYGWIN__)
 # if !defined(__BORLANDC__)
 #  define getcwd _getcwd
@@ -1232,6 +1238,181 @@ test_callbacks(void)
        archive_entry_free(ae);
 }
 
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+static int
+canNodump()
+{
+       const char *path = "cannodumptest";
+       struct stat sb;
+
+       assertMakeFile(path, 0644, NULL);
+       if (chflags(path, UF_NODUMP) < 0)
+               return (0);
+       if (stat(path, &sb) < 0)
+               return (0);
+       if (sb.st_flags & UF_NODUMP)
+               return (1);
+       return (0);
+}
+
+static int
+assertNodump(const char *path)
+{
+       return (assertEqualInt(0, chflags(path, UF_NODUMP)));
+}
+
+#else
+
+static int
+canNodump()
+{
+       return (0);
+}
+
+static int
+assertNodump(const char *path)
+{
+       (void)path; /* UNUSED */
+       return (0);
+}
+
+#endif
+
+static void
+test_nodump(void)
+{
+       struct archive *a;
+       struct archive_entry *ae;
+       const void *p;
+       size_t size;
+       int64_t offset;
+       int file_count;
+
+       if (!canNodump()) {
+               skipping("Can't test nodump on this filesystem");
+               return;
+       }
+
+       assertMakeDir("nd", 0755);
+       assertMakeFile("nd/f1", 0644, "0123456789");
+       assertMakeFile("nd/f2", 0644, "hello world");
+       assertMakeFile("nd/fe", 0644, NULL);
+       assertNodump("nd/f2");
+       assertUtimes("nd/f1", 886600, 0, 886600, 0);
+       assertUtimes("nd/f2", 886611, 0, 886611, 0);
+       assertUtimes("nd/fe", 886611, 0, 886611, 0);
+       assertUtimes("nd", 886622, 0, 886622, 0);
+
+       assert((ae = archive_entry_new()) != NULL);
+       assert((a = archive_read_disk_new()) != NULL);
+
+       /*
+        * Test1: Traversals without archive_read_disk_honor_nodump().
+        */
+       failure("Directory traversals should work as well");
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd"));
+
+       file_count = 4;
+       while (file_count--) {
+               archive_entry_clear(ae);
+               assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
+               if (strcmp(archive_entry_pathname(ae), "nd") == 0) {
+                       assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
+               } else if (strcmp(archive_entry_pathname(ae), "nd/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);
+               } else if (strcmp(archive_entry_pathname(ae), "nd/f2") == 0) {
+                       assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+                       assertEqualInt(archive_entry_size(ae), 11);
+                       assertEqualIntA(a, ARCHIVE_OK,
+                           archive_read_data_block(a, &p, &size, &offset));
+                       assertEqualInt((int)size, 11);
+                       assertEqualInt((int)offset, 0);
+                       assertEqualMem(p, "hello world", 11);
+                       assertEqualInt(ARCHIVE_EOF,
+                           archive_read_data_block(a, &p, &size, &offset));
+                       assertEqualInt((int)size, 0);
+                       assertEqualInt((int)offset, 11);
+               } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) {
+                       assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+                       assertEqualInt(archive_entry_size(ae), 0);
+               }
+               if (archive_read_disk_can_descend(a)) {
+                       /* Descend into the current object */
+                       assertEqualIntA(a, ARCHIVE_OK,
+                           archive_read_disk_descend(a));
+               }
+       }
+       /* There is no entry. */
+       failure("There should be no entry");
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae));
+
+       /* Close the disk object. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+
+       /*
+        * Test2: Traversals with archive_read_disk_honor_nodump().
+        */
+       assertUtimes("nd/f1", 886600, 0, 886600, 0);
+       assertUtimes("nd/f2", 886611, 0, 886611, 0);
+       assertUtimes("nd/fe", 886611, 0, 886611, 0);
+       assertUtimes("nd", 886622, 0, 886622, 0);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_honor_nodump(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a));
+       failure("Directory traversals should work as well");
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd"));
+
+       file_count = 3;
+       while (file_count--) {
+               archive_entry_clear(ae);
+               assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
+               failure("File 'nd/f2' should be exclueded");
+               assert(strcmp(archive_entry_pathname(ae), "nd/f2") != 0);
+               if (strcmp(archive_entry_pathname(ae), "nd") == 0) {
+                       assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
+               } else if (strcmp(archive_entry_pathname(ae), "nd/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);
+               } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) {
+                       assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+                       assertEqualInt(archive_entry_size(ae), 0);
+               }
+               if (archive_read_disk_can_descend(a)) {
+                       /* Descend into the current object */
+                       assertEqualIntA(a, ARCHIVE_OK,
+                           archive_read_disk_descend(a));
+               }
+       }
+       /* There is no entry. */
+       failure("There should be no entry");
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae));
+
+       failure("Atime should be restored");
+       assertFileAtime("nd/f2", 886611, 0);
+
+       /* Destroy the disk object. */
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+       archive_entry_free(ae);
+}
 
 DEFINE_TEST(test_read_disk_directory_traversals)
 {
@@ -1247,4 +1428,6 @@ DEFINE_TEST(test_read_disk_directory_traversals)
        test_restore_atime();
        /* Test callbacks. */
        test_callbacks();
+       /* Test nodump. */
+       test_nodump();
 }