summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2009-03-06 04:55:51 +0000
committerkientzle <kientzle@FreeBSD.org>2009-03-06 04:55:51 +0000
commit0e3959e0048adeb8162dda94e7d22719dc53d19d (patch)
treee7f6ccf2c08974aeaad7391510b0ad147506d8f5 /lib
parent409cc977f6064716e39b1fec8562cb8aaab07e48 (diff)
downloadFreeBSD-src-0e3959e0048adeb8162dda94e7d22719dc53d19d.zip
FreeBSD-src-0e3959e0048adeb8162dda94e7d22719dc53d19d.tar.gz
Merge r416 from libarchive.googlecode.com:
Restoring POSIX.1e Extended Attributes on FreeBSD, part 1 This implements the basic ability to restore extended attributes on FreeBSD, including a test suite.
Diffstat (limited to 'lib')
-rw-r--r--lib/libarchive/archive_write_disk.c99
-rw-r--r--lib/libarchive/config_freebsd.h3
-rw-r--r--lib/libarchive/test/Makefile1
-rw-r--r--lib/libarchive/test/test_extattr_freebsd.c154
4 files changed, 249 insertions, 8 deletions
diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c
index a9417f4..b684150 100644
--- a/lib/libarchive/archive_write_disk.c
+++ b/lib/libarchive/archive_write_disk.c
@@ -33,6 +33,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_ACL_H
#include <sys/acl.h>
#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
#ifdef HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#endif
@@ -411,6 +414,8 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
a->todo |= TODO_TIMES;
if (a->flags & ARCHIVE_EXTRACT_ACL)
a->todo |= TODO_ACLS;
+ if (a->flags & ARCHIVE_EXTRACT_XATTR)
+ a->todo |= TODO_XATTR;
if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
a->todo |= TODO_FFLAGS;
if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
@@ -425,6 +430,17 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
ret = restore_entry(a);
+ /*
+ * On the GNU tar mailing list, some people working with new
+ * Linux filesystems observed that system xattrs used as
+ * layout hints need to be restored before the file contents
+ * are written, so this can't be done at file close.
+ */
+ if (a->todo & TODO_XATTR) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
#ifdef HAVE_FCHDIR
/* If we changed directory above, restore it here. */
if (a->restore_pwd >= 0) {
@@ -720,14 +736,18 @@ _archive_write_finish_entry(struct archive *_a)
int r2 = set_acls(a);
if (r2 < ret) ret = r2;
}
- if (a->todo & TODO_XATTR) {
- int r2 = set_xattrs(a);
- if (r2 < ret) ret = r2;
- }
+ /*
+ * Some flags prevent file modification; they must be restored after
+ * file contents are written.
+ */
if (a->todo & TODO_FFLAGS) {
int r2 = set_fflags(a);
if (r2 < ret) ret = r2;
}
+ /*
+ * Time has to be restored after all other metadata;
+ * otherwise atime will get changed.
+ */
if (a->todo & TODO_TIMES) {
int r2 = set_times(a);
if (r2 < ret) ret = r2;
@@ -1012,7 +1032,8 @@ restore_entry(struct archive_write_disk *a)
if (en) {
/* Everything failed; give up here. */
- archive_set_error(&a->archive, en, "Can't create '%s'", a->name);
+ archive_set_error(&a->archive, en, "Can't create '%s'",
+ a->name);
return (ARCHIVE_FAILED);
}
@@ -1657,7 +1678,8 @@ create_dir(struct archive_write_disk *a, char *path)
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
return (ARCHIVE_OK);
- archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path);
+ archive_set_error(&a->archive, errno, "Failed to create dir '%s'",
+ path);
return (ARCHIVE_FAILED);
}
@@ -2326,6 +2348,71 @@ set_xattrs(struct archive_write_disk *a)
}
return (ret);
}
+#elif HAVE_EXTATTR_SET_FILE
+/*
+ * Restore extended attributes - FreeBSD implementation
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ struct archive_entry *entry = a->entry;
+ static int warning_done = 0;
+ int ret = ARCHIVE_OK;
+ int i = archive_entry_xattr_reset(entry);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ size_t size;
+ archive_entry_xattr_next(entry, &name, &value, &size);
+ if (name != NULL) {
+ int e;
+ int namespace;
+
+ if (strncmp(name, "user.", 5) == 0) {
+ /* "user." attributes go to user namespace */
+ name += 5;
+ namespace = EXTATTR_NAMESPACE_USER;
+ } else {
+ /* Warn about other extended attributes. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't restore extended attribute ``%s''",
+ name);
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ errno = 0;
+#if HAVE_EXTATTR_SET_FD
+ if (a->fd >= 0)
+ e = extattr_set_fd(a->fd, namespace, name, value, size);
+ else
+#endif
+ /* TODO: should we use extattr_set_link() instead? */
+ {
+ e = extattr_set_file(archive_entry_pathname(entry),
+ namespace, name, value, size);
+ }
+ if (e != (int)size) {
+ if (errno == ENOTSUP) {
+ if (!warning_done) {
+ warning_done = 1;
+ archive_set_error(&a->archive, errno,
+ "Cannot restore extended "
+ "attributes on this file "
+ "system");
+ }
+ } else {
+ archive_set_error(&a->archive, errno,
+ "Failed to set extended attribute");
+ }
+
+ ret = ARCHIVE_WARN;
+ }
+ }
+ }
+ return (ret);
+}
#else
/*
* Restore extended attributes - stub implementation for unsupported systems
diff --git a/lib/libarchive/config_freebsd.h b/lib/libarchive/config_freebsd.h
index 54e26c8..a4cd329 100644
--- a/lib/libarchive/config_freebsd.h
+++ b/lib/libarchive/config_freebsd.h
@@ -25,7 +25,7 @@
* $FreeBSD$
*/
-/* FreeBSD 5.0 and later have ACL support. */
+/* FreeBSD 5.0 and later have ACL and extattr support. */
#if __FreeBSD__ > 4
#define HAVE_ACL_CREATE_ENTRY 1
#define HAVE_ACL_GET_PERM_NP 1
@@ -101,7 +101,6 @@
#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
#define HAVE_STRUCT_STAT_ST_FLAGS 1
#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
-#define HAVE_SYS_ACL_H 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_SELECT_H 1
#define HAVE_SYS_STAT_H 1
diff --git a/lib/libarchive/test/Makefile b/lib/libarchive/test/Makefile
index 634f510..cc43abd 100644
--- a/lib/libarchive/test/Makefile
+++ b/lib/libarchive/test/Makefile
@@ -21,6 +21,7 @@ TESTS= \
test_empty_write.c \
test_entry.c \
test_entry_strmode.c \
+ test_extattr_freebsd.c \
test_fuzz.c \
test_link_resolver.c \
test_pax_filename_encoding.c \
diff --git a/lib/libarchive/test/test_extattr_freebsd.c b/lib/libarchive/test/test_extattr_freebsd.c
new file mode 100644
index 0000000..72575dd
--- /dev/null
+++ b/lib/libarchive/test/test_extattr_freebsd.c
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 2003-2009 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$");
+
+#if defined(__FreeBSD__) && __FreeBSD__ > 4
+#include <sys/extattr.h>
+#endif
+
+/*
+ * Verify extended attribute restore-to-disk. This test is FreeBSD-specific.
+ */
+
+DEFINE_TEST(test_extattr_freebsd)
+{
+#if !defined(__FreeBSD__)
+ skipping("FreeBSD-specific extattr restore test");
+#elif __FreeBSD__ < 5
+ skipping("extattr restore supported only on FreeBSD 5.0 and later");
+#else
+ char buff[64];
+ struct stat st;
+ struct archive *a;
+ struct archive_entry *ae;
+ int n, fd;
+ int extattr_privilege_bug = 0;
+
+ /*
+ * First, do a quick manual set/read of an extended attribute
+ * to verify that the local filesystem does support it. If it
+ * doesn't, we'll simply skip the remaining tests.
+ */
+ /* Create a test file and try to set an ACL on it. */
+ fd = open("pretest", O_RDWR | O_CREAT, 0777);
+ failure("Could not create test file?!");
+ if (!assert(fd >= 0))
+ return;
+
+ errno = 0;
+ n = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, "testattr", "1234", 4);
+ if (n != 4 && errno == EOPNOTSUPP) {
+ close(fd);
+ skipping("extattr tests require that extattr support be enabled on the filesystem");
+ return;
+ }
+ failure("extattr_set_fd(): errno=%d (%s)", errno, strerror(errno));
+ assertEqualInt(4, n);
+ close(fd);
+
+ /*
+ * Repeat the above, but with file permissions set to 0000.
+ * This should work (extattr_set_fd() should follow fd
+ * permissions, not file permissions), but is known broken on
+ * some versions of FreeBSD.
+ */
+ fd = open("pretest2", O_RDWR | O_CREAT, 00000);
+ failure("Could not create test file?!");
+ if (!assert(fd >= 0))
+ return;
+
+ n = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, "testattr", "1234", 4);
+ if (n != 4) {
+ skipping("Restoring xattr to an unwritable file (broken in some versions of FreeBSD");
+ extattr_privilege_bug = 1;
+ }
+ close(fd);
+
+ /* Create a write-to-disk object. */
+ assert(NULL != (a = archive_write_disk_new()));
+ archive_write_disk_set_options(a,
+ ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_XATTR);
+
+ /* Populate an archive entry with an extended attribute. */
+ ae = archive_entry_new();
+ assert(ae != NULL);
+ archive_entry_set_pathname(ae, "test0");
+ archive_entry_set_mtime(ae, 123456, 7890);
+ archive_entry_set_size(ae, 0);
+ archive_entry_set_mode(ae, 0755);
+ archive_entry_xattr_add_entry(ae, "user.foo", "12345", 5);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /* Another entry; similar but with mode = 0. */
+ ae = archive_entry_new();
+ assert(ae != NULL);
+ archive_entry_set_pathname(ae, "test1");
+ archive_entry_set_mtime(ae, 12345678, 7890);
+ archive_entry_set_size(ae, 0);
+ archive_entry_set_mode(ae, 0);
+ archive_entry_xattr_add_entry(ae, "user.bar", "123456", 6);
+ if (extattr_privilege_bug)
+ /* If the bug is here, write_header will return warning. */
+ assertEqualIntA(a, ARCHIVE_WARN,
+ archive_write_header(a, ae));
+ else
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /* Close the archive. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_finish(a));
+
+ /* Verify the data on disk. */
+ assertEqualInt(0, stat("test0", &st));
+ assertEqualInt(st.st_mtime, 123456);
+ /* Verify extattr */
+ n = extattr_get_file("test0", EXTATTR_NAMESPACE_USER,
+ "foo", buff, sizeof(buff));
+ if (assertEqualInt(n, 5)) {
+ buff[n] = '\0';
+ assertEqualString(buff, "12345");
+ }
+
+ /* Verify the data on disk. */
+ assertEqualInt(0, stat("test1", &st));
+ assertEqualInt(st.st_mtime, 12345678);
+ /* Verify extattr */
+ n = extattr_get_file("test1", EXTATTR_NAMESPACE_USER,
+ "bar", buff, sizeof(buff));
+ if (extattr_privilege_bug) {
+ /* If we have the bug, the extattr won't have been written. */
+ assertEqualInt(n, -1);
+ } else {
+ if (assertEqualInt(n, 6)) {
+ buff[n] = '\0';
+ assertEqualString(buff, "123456");
+ }
+ }
+#endif
+}
OpenPOWER on IntegriCloud