svn commit: r189431 - in head/lib/libarchive: . test

Tim Kientzle kientzle at FreeBSD.org
Thu Mar 5 20:55:52 PST 2009


Author: kientzle
Date: Fri Mar  6 04:55:51 2009
New Revision: 189431
URL: http://svn.freebsd.org/changeset/base/189431

Log:
  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.

Added:
  head/lib/libarchive/test/test_extattr_freebsd.c   (contents, props changed)
Modified:
  head/lib/libarchive/archive_write_disk.c
  head/lib/libarchive/config_freebsd.h
  head/lib/libarchive/test/Makefile

Modified: head/lib/libarchive/archive_write_disk.c
==============================================================================
--- head/lib/libarchive/archive_write_disk.c	Fri Mar  6 04:50:39 2009	(r189430)
+++ head/lib/libarchive/archive_write_disk.c	Fri Mar  6 04:55:51 2009	(r189431)
@@ -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
 		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
 
 	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 archi
 		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 
 
 	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,
 	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

Modified: head/lib/libarchive/config_freebsd.h
==============================================================================
--- head/lib/libarchive/config_freebsd.h	Fri Mar  6 04:50:39 2009	(r189430)
+++ head/lib/libarchive/config_freebsd.h	Fri Mar  6 04:55:51 2009	(r189431)
@@ -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

Modified: head/lib/libarchive/test/Makefile
==============================================================================
--- head/lib/libarchive/test/Makefile	Fri Mar  6 04:50:39 2009	(r189430)
+++ head/lib/libarchive/test/Makefile	Fri Mar  6 04:55:51 2009	(r189431)
@@ -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		\

Added: head/lib/libarchive/test/test_extattr_freebsd.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libarchive/test/test_extattr_freebsd.c	Fri Mar  6 04:55:51 2009	(r189431)
@@ -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
+}


More information about the svn-src-head mailing list