From: Michihiro NAKAJIMA Date: Sat, 31 Dec 2011 19:29:09 +0000 (-0500) Subject: Introduce archive_read_disk_honor_nodump() to eliminate the platform X-Git-Tag: v3.0.4~2^2~235 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e6673020211c99cd7095887e8ee00f59694ffb5;p=thirdparty%2Flibarchive.git Introduce archive_read_disk_honor_nodump() to eliminate the platform dependent code testing nodump flag from bsdtar and bsdpax. SVN-Revision: 4052 --- diff --git a/libarchive/archive.h b/libarchive/archive.h index 3d853fe22..d5141a1b7 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -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 *, diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 8ce88b380..8a83dfe22 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -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 diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index 241276cc5..2987063d6 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -52,6 +52,19 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_LINUX_MAGIC_H #include #endif +#ifdef HAVE_LINUX_FS_H +#include +#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 /* for Linux file flags */ +#endif +#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) +#include /* Linux file flags, broken on Cygwin */ +#endif #ifdef HAVE_DIRECT_H #include #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)); diff --git a/libarchive/archive_read_disk_private.h b/libarchive/archive_read_disk_private.h index 14038d88a..0637e861d 100644 --- a/libarchive/archive_read_disk_private.h +++ b/libarchive/archive_read_disk_private.h @@ -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; }; diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index 302f3c7f7..738be9fb3 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -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 diff --git a/libarchive/test/test_read_disk_directory_traversals.c b/libarchive/test/test_read_disk_directory_traversals.c index 44ab3197d..32fc2470d 100644 --- a/libarchive/test/test_read_disk_directory_traversals.c +++ b/libarchive/test/test_read_disk_directory_traversals.c @@ -25,7 +25,13 @@ #include "test.h" __FBSDID("$FreeBSD$"); +#ifdef HAVE_SYS_STAT_H +#include +#endif #include +#ifdef HAVE_UNISTD_H +#include +#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(); }