diff options
author | kientzle <kientzle@FreeBSD.org> | 2009-03-06 04:55:51 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2009-03-06 04:55:51 +0000 |
commit | 0e3959e0048adeb8162dda94e7d22719dc53d19d (patch) | |
tree | e7f6ccf2c08974aeaad7391510b0ad147506d8f5 /lib | |
parent | 409cc977f6064716e39b1fec8562cb8aaab07e48 (diff) | |
download | FreeBSD-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.c | 99 | ||||
-rw-r--r-- | lib/libarchive/config_freebsd.h | 3 | ||||
-rw-r--r-- | lib/libarchive/test/Makefile | 1 | ||||
-rw-r--r-- | lib/libarchive/test/test_extattr_freebsd.c | 154 |
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 +} |