]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
IFC
authorTim Kientzle <kientzle@gmail.com>
Sat, 13 Sep 2008 22:55:05 +0000 (18:55 -0400)
committerTim Kientzle <kientzle@gmail.com>
Sat, 13 Sep 2008 22:55:05 +0000 (18:55 -0400)
SVN-Revision: 203

configure.ac
libarchive/archive_write_disk.c
tar/config_freebsd.h
tar/test/Makefile
tar/test/test_symlink_dir.c [new file with mode: 0644]
tar/write.c

index 0fb7d37c67fcd6e30cdf958208ddb46e5efcc304..7cdba25f6f26e6f7a96e5a0545676cfd4c10f27c 100644 (file)
@@ -195,6 +195,8 @@ AC_CHECK_MEMBERS([struct stat.st_mtimespec.tv_nsec])
 AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec])
 # Check for block size support in struct stat
 AC_CHECK_MEMBERS([struct stat.st_blksize])
+# Check for st_flags in struct stat (BSD fflags)
+AC_CHECK_MEMBERS([struct stat.st_flags])
 
 # If you have uintmax_t, we assume printf supports %ju
 # If you have unsigned long long, we assume printf supports %llu
index 7a301ffe369cf04fb1d0f7b87e8159bf93e30577..11ef08aa30b8108c83104e9a1cec614006cbaac1 100644 (file)
@@ -1963,7 +1963,10 @@ set_fflags(struct archive_write_disk *a)
 }
 
 
-#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux)
+#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS)
+/*
+ * BSD reads flags using stat() and sets them with one of {f,l,}chflags()
+ */
 static int
 set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
     mode_t mode, unsigned long set, unsigned long clear)
@@ -2012,11 +2015,9 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
        return (ARCHIVE_WARN);
 }
 
-#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS)
-
+#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS)
 /*
- * Linux has flags too, but uses ioctl() to access them instead of
- * having a separate chflags() system call.
+ * Linux uses ioctl() to read and write file flags.
  */
 static int
 set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
@@ -2084,7 +2085,7 @@ cleanup:
        return (ret);
 }
 
-#else /* Not HAVE_CHFLAGS && Not __linux */
+#else
 
 /*
  * Of course, some systems have neither BSD chflags() nor Linux' flags
index 69790d8f5745f19adaa7d9598d652cba0fda50a0..25aceea717691bff2bc20df7f18ffc13a3655b51 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/tar/config_freebsd.h,v 1.5 2008/07/05 02:09:13 kientzle Exp $
+ * $FreeBSD: src/usr.bin/tar/config_freebsd.h,v 1.6 2008/09/14 03:49:00 kientzle Exp $
  */
 
 /* A default configuration for FreeBSD, used if there is no config.h. */
@@ -89,6 +89,7 @@
 #define        HAVE_STRINGS_H 1
 #define        HAVE_STRING_H 1
 #define        HAVE_STRRCHR 1
+#define        HAVE_STRUCT_STAT_ST_FLAGS 1
 #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
 #define        HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
 #define        HAVE_SYS_ACL_H 1
index 079210f2a153e17f8c348c2f64cdbfa942c83703..ac5c56f52f047d7c186aaafe299fbece709e3ef1 100644 (file)
@@ -1,4 +1,4 @@
-# $FreeBSD: src/usr.bin/tar/test/Makefile,v 1.3 2008/08/22 01:22:55 kientzle Exp $
+# $FreeBSD: src/usr.bin/tar/test/Makefile,v 1.4 2008/09/14 02:16:04 kientzle Exp $
 
 # Where to find the tar sources (for the internal unit tests)
 TAR_SRCDIR=${.CURDIR}/..
@@ -18,6 +18,7 @@ TESTS=        \
        test_option_q.c                         \
        test_patterns.c                         \
        test_stdio.c                            \
+       test_symlink_dir.c                      \
        test_version.c
 
 # Build the test program
diff --git a/tar/test/test_symlink_dir.c b/tar/test/test_symlink_dir.c
new file mode 100644 (file)
index 0000000..569da65
--- /dev/null
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_symlink_dir.c,v 1.1 2008/09/14 02:16:04 kientzle Exp $");
+
+/*
+ * tar -x -P should follow existing symlinks for dirs, but not other
+ * content.  Plain tar -x should remove symlinks when they're in the
+ * way of a dir extraction.
+ */
+
+static int
+mkfile(const char *name, int mode, const char *contents, ssize_t size)
+{
+       int fd = open(name, O_CREAT | O_WRONLY, mode);
+       if (fd < 0)
+               return (-1);
+       if (size != write(fd, contents, size)) {
+               close(fd);
+               return (-1);
+       }
+       close(fd);
+       return (0);
+}
+
+DEFINE_TEST(test_symlink_dir)
+{
+       struct stat st, st2;
+       int oldumask;
+
+       oldumask = umask(0);
+
+       assertEqualInt(0, mkdir("source", 0755));
+       assertEqualInt(0, mkfile("source/file", 0755, "a", 1));
+       assertEqualInt(0, mkfile("source/file2", 0755, "ab", 2));
+       assertEqualInt(0, mkdir("source/dir", 0755));
+       assertEqualInt(0, mkdir("source/dir/d", 0755));
+       assertEqualInt(0, mkfile("source/dir/f", 0755, "abc", 3));
+       assertEqualInt(0, mkdir("source/dir2", 0755));
+       assertEqualInt(0, mkdir("source/dir2/d2", 0755));
+       assertEqualInt(0, mkfile("source/dir2/f2", 0755, "abcd", 4));
+       assertEqualInt(0, mkdir("source/dir3", 0755));
+       assertEqualInt(0, mkdir("source/dir3/d3", 0755));
+       assertEqualInt(0, mkfile("source/dir3/f3", 0755, "abcde", 5));
+
+       assertEqualInt(0,
+           systemf("%s -cf test.tar -C source dir dir2 dir3 file file2",
+               testprog));
+
+       /*
+        * Extract with -x and without -P.
+        */
+       assertEqualInt(0, mkdir("dest1", 0755));
+       /* "dir" is a symlink to an existing "real_dir" */
+       assertEqualInt(0, mkdir("dest1/real_dir", 0755));
+       assertEqualInt(0, symlink("real_dir", "dest1/dir"));
+       /* "dir2" is a symlink to a non-existing "real_dir2" */
+       assertEqualInt(0, symlink("real_dir2", "dest1/dir2"));
+       /* "dir3" is a symlink to an existing "non_dir3" */
+       assertEqualInt(0, mkfile("dest1/non_dir3", 0755, "abcdef", 6));
+       assertEqualInt(0, symlink("non_dir3", "dest1/dir3"));
+       /* "file" is a symlink to existing "real_file" */
+       assertEqualInt(0, mkfile("dest1/real_file", 0755, "abcdefg", 7));
+       assertEqualInt(0, symlink("real_file", "dest1/file"));
+       /* "file2" is a symlink to non-existing "real_file2" */
+       assertEqualInt(0, symlink("real_file2", "dest1/file2"));
+
+       assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog));
+
+       /* dest1/dir symlink should be removed */
+       assertEqualInt(0, lstat("dest1/dir", &st));
+       failure("symlink to dir was followed when it shouldn't be");
+       assert(S_ISDIR(st.st_mode));
+       /* dest1/dir2 symlink should be removed */
+       assertEqualInt(0, lstat("dest1/dir2", &st));
+       failure("Broken symlink wasn't replaced with dir");
+       assert(S_ISDIR(st.st_mode));
+       /* dest1/dir3 symlink should be removed */
+       assertEqualInt(0, lstat("dest1/dir3", &st));
+       failure("Symlink to non-dir wasn't replaced with dir");
+       assert(S_ISDIR(st.st_mode));
+       /* dest1/file symlink should be removed */
+       assertEqualInt(0, lstat("dest1/file", &st));
+       failure("Symlink to existing file should be removed");
+       assert(S_ISREG(st.st_mode));
+       /* dest1/file2 symlink should be removed */
+       assertEqualInt(0, lstat("dest1/file2", &st));
+       failure("Symlink to non-existing file should be removed");
+       assert(S_ISREG(st.st_mode));
+
+       /*
+        * Extract with both -x and -P
+        */
+       assertEqualInt(0, mkdir("dest2", 0755));
+       /* "dir" is a symlink to existing "real_dir" */
+       assertEqualInt(0, mkdir("dest2/real_dir", 0755));
+       assertEqualInt(0, symlink("real_dir", "dest2/dir"));
+       /* "dir2" is a symlink to a non-existing "real_dir2" */
+       assertEqualInt(0, symlink("real_dir2", "dest2/dir2"));
+       /* "dir3" is a symlink to an existing "non_dir3" */
+       assertEqualInt(0, mkfile("dest2/non_dir3", 0755, "abcdefgh", 8));
+       assertEqualInt(0, symlink("non_dir3", "dest2/dir3"));
+       /* "file" is a symlink to existing "real_file" */
+       assertEqualInt(0, mkfile("dest2/real_file", 0755, "abcdefghi", 9));
+       assertEqualInt(0, symlink("real_file", "dest2/file"));
+       /* "file2" is a symlink to non-existing "real_file2" */
+       assertEqualInt(0, symlink("real_file2", "dest2/file2"));
+
+       assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog));
+
+       /* dest2/dir symlink should be followed */
+       assertEqualInt(0, lstat("dest2/dir", &st));
+       failure("tar -xP removed symlink instead of following it");
+       if (assert(S_ISLNK(st.st_mode))) {
+               /* Only verify what the symlink points to if it
+                * really is a symlink. */
+               failure("The symlink should point to a directory");
+               assertEqualInt(0, stat("dest2/dir", &st));
+               assert(S_ISDIR(st.st_mode));
+               failure("The pre-existing directory should still be there");
+               assertEqualInt(0, lstat("dest2/real_dir", &st2));
+               assert(S_ISDIR(st2.st_mode));
+               assertEqualInt(st.st_dev, st2.st_dev);
+               failure("symlink should still point to the existing directory");
+               assertEqualInt(st.st_ino, st2.st_ino);
+       }
+       /* Contents of 'dir' should be restored */
+       assertEqualInt(0, lstat("dest2/dir/d", &st));
+       assert(S_ISDIR(st.st_mode));
+       assertEqualInt(0, lstat("dest2/dir/f", &st));
+       assert(S_ISREG(st.st_mode));
+       assertEqualInt(3, st.st_size);
+       /* dest2/dir2 symlink should be removed */
+       assertEqualInt(0, lstat("dest2/dir2", &st));
+       failure("Broken symlink wasn't replaced with dir");
+       assert(S_ISDIR(st.st_mode));
+       /* dest2/dir3 symlink should be removed */
+       assertEqualInt(0, lstat("dest2/dir3", &st));
+       failure("Symlink to non-dir wasn't replaced with dir");
+       assert(S_ISDIR(st.st_mode));
+       /* dest2/file symlink should be removed;
+        * even -P shouldn't follow symlinks for files */
+       assertEqualInt(0, lstat("dest2/file", &st));
+       failure("Symlink to existing file should be removed");
+       assert(S_ISREG(st.st_mode));
+       /* dest2/file2 symlink should be removed */
+       assertEqualInt(0, lstat("dest2/file2", &st));
+       failure("Symlink to non-existing file should be removed");
+       assert(S_ISREG(st.st_mode));
+}
index d038fac9e105c756dc871322aff81d21c2d87519..fca62568887bf82b77088a87adba201379c53a40 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.76 2008/07/05 08:10:55 cperciva Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.77 2008/09/14 03:49:00 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -641,10 +641,6 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
        dev_t first_dev = 0;
        int dev_recorded = 0;
        int tree_ret;
-#ifdef __linux
-       int      fd, r;
-       unsigned long fflags;
-#endif
 
        tree = tree_open(path);
 
@@ -707,23 +703,25 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
                 * If this file/dir is flagged "nodump" and we're
                 * honoring such flags, skip this file/dir.
                 */
-#ifdef HAVE_CHFLAGS
+#ifdef HAVE_STRUCT_STAT_ST_FLAGS
+               /* BSD systems store flags in struct stat */
                if (bsdtar->option_honor_nodump &&
                    (lst->st_flags & UF_NODUMP))
                        continue;
 #endif
 
-#ifdef __linux
-               /*
-                * Linux has a nodump flag too but to read it
-                * we have to open() the file/dir and do an ioctl on it...
-                */
-               if (bsdtar->option_honor_nodump &&
-                   ((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0) &&
-                   ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)),
-                       close(fd), r) >= 0 &&
-                   (fflags & EXT2_NODUMP_FL))
-                       continue;
+#if defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL)
+               /* Linux uses ioctl to read flags. */
+               if (bsdtar->option_honor_nodump) {
+                       int fd = open(name, O_RDONLY | O_NONBLOCK);
+                       if (fd >= 0) {
+                               unsigned long fflags;
+                               int r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags);
+                               close(fd);
+                               if (r >= 0 && (fflags & EXT2_NODUMP_FL))
+                                       continue;
+                       }
+               }
 #endif
 
                /*
@@ -849,10 +847,6 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
     const char *pathname, const char *accpath)
 {
        struct archive_entry    *entry, *sparse_entry;
-#ifdef __linux
-       int                      r;
-       unsigned long            stflags;
-#endif
        static char              linkbuffer[PATH_MAX+1];
 
        entry = archive_entry_new();
@@ -902,18 +896,25 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
        archive_entry_set_uname(entry, lookup_uname(bsdtar, st->st_uid));
        archive_entry_set_gname(entry, lookup_gname(bsdtar, st->st_gid));
 
-#ifdef HAVE_CHFLAGS
+#ifdef HAVE_STRUCT_STAT_ST_FLAGS
+       /* XXX TODO: archive_entry_copy_stat() should copy st_flags
+        * on platforms that support it.  This should go away then. */
        if (st->st_flags != 0)
                archive_entry_set_fflags(entry, st->st_flags, 0);
 #endif
 
-#ifdef __linux
-       int fd;
-       if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) &&
-           ((fd = open(accpath, O_RDONLY|O_NONBLOCK)) >= 0) &&
-           ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), (fd = -1), r) >= 0 &&
-           stflags) {
-               archive_entry_set_fflags(entry, stflags, 0);
+#ifdef EXT2_IOC_GETFLAGS
+       /* XXX TODO: Find a way to merge this with the
+        * flags fetch for nodump support earlier. */
+       if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
+               int fd = open(accpath, O_RDONLY | O_NONBLOCK);
+               if (fd >= 0) {
+                       unsigned long stflags;
+                       int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
+                       close(fd);
+                       if (r == 0 && stflags != 0)
+                               archive_entry_set_fflags(entry, stflags, 0);
+               }
        }
 #endif