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/libarchive/archive_write_disk.c | |
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/libarchive/archive_write_disk.c')
-rw-r--r-- | lib/libarchive/archive_write_disk.c | 99 |
1 files changed, 93 insertions, 6 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 |