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.
This commit is contained in:
Tim Kientzle 2009-03-06 04:55:51 +00:00
parent 17fcda1a0b
commit ce077a6fd8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=189431
4 changed files with 249 additions and 8 deletions

View file

@ -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

View file

@ -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

View file

@ -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 \

View file

@ -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
}