summaryrefslogtreecommitdiffstats
path: root/lib/libarchive/archive_read_extract.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libarchive/archive_read_extract.c')
-rw-r--r--lib/libarchive/archive_read_extract.c754
1 files changed, 754 insertions, 0 deletions
diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c
new file mode 100644
index 0000000..328010b
--- /dev/null
+++ b/lib/libarchive/archive_read_extract.c
@@ -0,0 +1,754 @@
+/*-
+ * Copyright (c) 2003-2004 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
+ * in this position and unchanged.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+#include <sys/acl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tar.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+static void archive_extract_cleanup(struct archive *);
+static int archive_read_extract_block_device(struct archive *,
+ struct archive_entry *, int);
+static int archive_read_extract_char_device(struct archive *,
+ struct archive_entry *, int);
+static int archive_read_extract_device(struct archive *,
+ struct archive_entry *, int flags, mode_t mode);
+static int archive_read_extract_dir(struct archive *,
+ struct archive_entry *, int);
+static int archive_read_extract_dir_create(struct archive *,
+ const char *name, int mode, int flags);
+static int archive_read_extract_fifo(struct archive *,
+ struct archive_entry *, int);
+static int archive_read_extract_hard_link(struct archive *,
+ struct archive_entry *, int);
+static int archive_read_extract_regular(struct archive *,
+ struct archive_entry *, int);
+static int archive_read_extract_regular_open(struct archive *,
+ const char *name, int mode, int flags);
+static int archive_read_extract_symbolic_link(struct archive *,
+ struct archive_entry *, int);
+static int mkdirpath(struct archive *, const char *);
+static int mkdirpath_recursive(char *path);
+static int mksubdir(char *path);
+static int set_acls(struct archive *, struct archive_entry *);
+static int set_extended_perm(struct archive *, struct archive_entry *,
+ int flags);
+static int set_fflags(struct archive *, struct archive_entry *);
+static int set_ownership(struct archive *, struct archive_entry *, int);
+static int set_perm(struct archive *, struct archive_entry *, int mode, int flags);
+static int set_time(struct archive *, struct archive_entry *, int);
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks. Is there any way to validate hardlinks
+ * without keeping a complete list of filenames from the entire archive?? Ugh.
+ *
+ */
+int
+archive_read_extract(struct archive *a, struct archive_entry *entry, int flags)
+{
+ mode_t writable_mode;
+ struct archive_extract_dir_entry *le;
+ int ret;
+ int restore_pwd;
+
+ restore_pwd = -1;
+ if (S_ISDIR(archive_entry_stat(entry)->st_mode)) {
+ /*
+ * TODO: Does this really work under all conditions?
+ *
+ * E.g., root restores a dir owned by someone else?
+ */
+ writable_mode = archive_entry_stat(entry)->st_mode | 0700;
+
+ /*
+ * If this dir isn't writable, restore it with write
+ * permissions and add it to the fixup list for later
+ * handling.
+ */
+ if (archive_entry_stat(entry)->st_mode != writable_mode) {
+ le = malloc(sizeof(struct archive_extract_dir_entry));
+ le->next = a->archive_extract_dir_list;
+ a->archive_extract_dir_list = le;
+ le->mode = archive_entry_stat(entry)->st_mode;
+ le->name =
+ malloc(strlen(archive_entry_pathname(entry)) + 1);
+ strcpy(le->name, archive_entry_pathname(entry));
+ a->cleanup_archive_extract = archive_extract_cleanup;
+ /* Make sure I can write to this directory. */
+ archive_entry_set_mode(entry, writable_mode);
+ }
+ }
+
+ if (archive_entry_hardlink(entry) != NULL)
+ return (archive_read_extract_hard_link(a, entry, flags));
+
+ /*
+ * TODO: If pathname is longer than PATH_MAX, record starting
+ * directory and move to a suitable intermediate dir, which
+ * might require creating them!
+ */
+ if (strlen(archive_entry_pathname(entry)) > PATH_MAX) {
+ restore_pwd = open(".", O_RDONLY);
+ /* XXX chdir() to a suitable intermediate dir XXX */
+ /* XXX Update pathname in 'entry' XXX */
+ }
+
+ switch (archive_entry_stat(entry)->st_mode & S_IFMT) {
+ default:
+ /* Fall through, as required by POSIX. */
+ case S_IFREG:
+ ret = archive_read_extract_regular(a, entry, flags);
+ break;
+ case S_IFLNK: /* Symlink */
+ ret = archive_read_extract_symbolic_link(a, entry, flags);
+ break;
+ case S_IFCHR:
+ ret = archive_read_extract_char_device(a, entry, flags);
+ break;
+ case S_IFBLK:
+ ret = archive_read_extract_block_device(a, entry, flags);
+ break;
+ case S_IFDIR:
+ ret = archive_read_extract_dir(a, entry, flags);
+ break;
+ case S_IFIFO:
+ ret = archive_read_extract_fifo(a, entry, flags);
+ break;
+ }
+
+ /* If we changed directory above, restore it here. */
+ if (restore_pwd >= 0)
+ fchdir(restore_pwd);
+
+ return (ret);
+}
+
+/*
+ * Cleanup function for archive_extract. Free name/mode list and
+ * restore permissions.
+ *
+ * TODO: Restore times here as well.
+ *
+ * Registering this function (rather than calling it explicitly by
+ * name from archive_read_finish) reduces link pollution, since
+ * applications that don't use this API won't get this file linked in.
+ */
+static
+void archive_extract_cleanup(struct archive *a)
+{
+ struct archive_extract_dir_entry *lp;
+
+ /*
+ * TODO: Does dir list need to be sorted so permissions are restored
+ * depth-first?
+ */
+ while (a->archive_extract_dir_list) {
+ lp = a->archive_extract_dir_list->next;
+ chmod(a->archive_extract_dir_list->name,
+ a->archive_extract_dir_list->mode);
+ /*
+ * TODO: Consider using this hook to restore dir
+ * timestamps as well. However, dir timestamps don't
+ * really matter, and it would be a memory issue to
+ * record timestamps for every directory
+ * extracted... Ugh.
+ */
+ if (a->archive_extract_dir_list->name)
+ free(a->archive_extract_dir_list->name);
+ free(a->archive_extract_dir_list);
+ a->archive_extract_dir_list = lp;
+ }
+}
+
+static int
+archive_read_extract_regular(struct archive *a, struct archive_entry *entry,
+ int flags)
+{
+ int fd, r;
+ ssize_t s;
+
+ r = ARCHIVE_OK;
+ fd = archive_read_extract_regular_open(a,
+ archive_entry_pathname(entry), archive_entry_stat(entry)->st_mode,
+ flags);
+ if (fd < 0) {
+ archive_set_error(a, errno, "Can't open");
+ return (ARCHIVE_WARN);
+ }
+ s = archive_read_data_into_fd(a, fd);
+ if (s < archive_entry_size(entry)) {
+ /* Didn't read enough data? Complain but keep going. */
+ archive_set_error(a, EIO, "Archive data truncated");
+ r = ARCHIVE_WARN;
+ }
+ set_ownership(a, entry, flags);
+ set_time(a, entry, flags);
+ /* set_perm(a, entry, mode, flags); */ /* Handled implicitly by open.*/
+ set_extended_perm(a, entry, flags);
+ close(fd);
+ return (r);
+}
+
+/*
+ * Keep trying until we either open the file or run out of tricks.
+ *
+ * Note: the GNU tar 'unlink first' option seems redundant
+ * with this strategy, since we never actually write over an
+ * existing file. (If it already exists, we remove it.)
+ */
+static int
+archive_read_extract_regular_open(struct archive *a,
+ const char *name, int mode, int flags)
+{
+ int fd;
+
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (fd >= 0)
+ return (fd);
+
+ /* Try removing a pre-existing file. */
+ if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ unlink(name);
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (fd >= 0)
+ return (fd);
+ }
+
+ /* Might be a non-existent parent dir; try fixing that. */
+ mkdirpath(a, name);
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (fd >= 0)
+ return (fd);
+
+ return (-1);
+}
+
+static int
+archive_read_extract_dir(struct archive *a, struct archive_entry *entry,
+ int flags)
+{
+ int mode, ret, ret2;
+
+ mode = archive_entry_stat(entry)->st_mode;
+
+ if (archive_read_extract_dir_create(a, archive_entry_pathname(entry),
+ mode, flags)) {
+ /* Unable to create directory; just use the existing dir. */
+ return (ARCHIVE_WARN);
+ }
+
+ set_ownership(a, entry, flags);
+ /*
+ * There is no point in setting the time here.
+ *
+ * Note that future extracts into this directory will reset
+ * the times, so to get correct results, the client has to
+ * track timestamps for directories and update them at the end
+ * of the run anyway.
+ */
+ /* set_time(t, flags); */
+
+ /*
+ * This next line may appear redundant, but it's not. If the
+ * directory already exists, it won't get re-created by
+ * mkdir(), so we have to manually set permissions to get
+ * everything right.
+ */
+ ret = set_perm(a, entry, mode, flags);
+ ret2 = set_extended_perm(a, entry, flags);
+
+ /* XXXX TODO: Fix this to work the right way. XXXX */
+ if (ret == ARCHIVE_OK)
+ return (ret2);
+ else
+ return (ret);
+}
+
+/*
+ * Create the directory: try until something works or we run out of magic.
+ */
+static int
+archive_read_extract_dir_create(struct archive *a, const char *name, int mode,
+ int flags)
+{
+ /* Don't try to create '.' */
+ if (name[0] == '.' && name[1] == 0)
+ return (ARCHIVE_OK);
+ if (mkdir(name, mode) == 0)
+ return (ARCHIVE_OK);
+ if (errno == ENOENT) { /* Missing parent directory. */
+ mkdirpath(a, name);
+ if (mkdir(name, mode) == 0)
+ return (ARCHIVE_OK);
+ }
+
+ if (errno != EEXIST)
+ return (ARCHIVE_WARN);
+ if ((flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ archive_set_error(a, EEXIST, "Directory already exists");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Could be a file; try unlinking. */
+ if (unlink(name) == 0 &&
+ mkdir(name, mode) == 0)
+ return (ARCHIVE_OK);
+
+ /* Unlink failed. It's okay if it failed because it's already a dir. */
+ if (errno != EPERM) {
+ archive_set_error(a, errno, "Couldn't create dir");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Try removing the directory and recreating it from scratch. */
+ if (rmdir(name)) {
+ /* Failure to remove a non-empty directory is not a problem. */
+ if (errno == ENOTEMPTY)
+ return (ARCHIVE_OK);
+ /* Any other failure is a problem. */
+ archive_set_error(a, errno,
+ "Error attempting to remove existing directory");
+ return (ARCHIVE_WARN);
+ }
+
+ /* We successfully removed the directory; now recreate it. */
+ if (mkdir(name, mode) == 0)
+ return (ARCHIVE_OK);
+
+ archive_set_error(a, errno, "Failed to create dir");
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_extract_hard_link(struct archive *a, struct archive_entry *entry,
+ int flags)
+{
+ /* Just remove any pre-existing file with this name. */
+ if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+ unlink(archive_entry_pathname(entry));
+
+ if (link(archive_entry_hardlink(entry),
+ archive_entry_pathname(entry))) {
+ archive_set_error(a, errno, "Can't restore hardlink");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Set ownership, time, permission information. */
+ set_ownership(a, entry, flags);
+ set_time(a, entry, flags);
+ set_perm(a, entry, archive_entry_stat(entry)->st_mode, flags);
+ set_extended_perm(a, entry, flags);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_extract_symbolic_link(struct archive *a,
+ struct archive_entry *entry, int flags)
+{
+ /* Just remove any pre-existing file with this name. */
+ if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+ unlink(archive_entry_pathname(entry));
+
+ if (symlink(archive_entry_symlink(entry),
+ archive_entry_pathname(entry))) {
+ /* XXX Better error message here XXX */
+ archive_set_error(a, errno, "Can't restore symlink to '%s'",
+ archive_entry_symlink(entry));
+ return (ARCHIVE_WARN);
+ }
+
+ /* Set ownership, time, permission information. */
+ set_ownership(a, entry, flags);
+ set_time(a, entry, flags);
+ set_perm(a, entry, archive_entry_stat(entry)->st_mode, flags);
+ set_extended_perm(a, entry, flags);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_extract_device(struct archive *a, struct archive_entry *entry,
+ int flags, mode_t mode)
+{
+ int r;
+
+ /* Just remove any pre-existing file with this name. */
+ if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+ unlink(archive_entry_pathname(entry));
+
+ r = mknod(archive_entry_pathname(entry), mode,
+ archive_entry_stat(entry)->st_rdev);
+
+ /* Might be a non-existent parent dir; try fixing that. */
+ if (r != 0 && errno == ENOENT) {
+ mkdirpath(a, archive_entry_pathname(entry));
+ r = mknod(archive_entry_pathname(entry), mode,
+ archive_entry_stat(entry)->st_rdev);
+ }
+
+ if (r != 0) {
+ archive_set_error(a, errno, "Can't recreate device node");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Set ownership, time, permission information. */
+ set_ownership(a, entry, flags);
+ set_time(a, entry, flags);
+ set_perm(a, entry, archive_entry_stat(entry)->st_mode, flags);
+ set_extended_perm(a, entry, flags);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_extract_char_device(struct archive *a,
+ struct archive_entry *entry, int flags)
+{
+ mode_t mode;
+
+ mode = (archive_entry_stat(entry)->st_mode & ~S_IFMT) | S_IFCHR;
+ return (archive_read_extract_device(a, entry, flags, mode));
+}
+
+static int
+archive_read_extract_block_device(struct archive *a,
+ struct archive_entry *entry, int flags)
+{
+ mode_t mode;
+
+ mode = (archive_entry_stat(entry)->st_mode & ~S_IFMT) | S_IFBLK;
+ return (archive_read_extract_device(a, entry, flags, mode));
+}
+
+static int
+archive_read_extract_fifo(struct archive *a,
+ struct archive_entry *entry, int flags)
+{
+ int r;
+
+ /* Just remove any pre-existing file with this name. */
+ if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+ unlink(archive_entry_pathname(entry));
+
+ r = mkfifo(archive_entry_pathname(entry),
+ archive_entry_stat(entry)->st_mode);
+
+ /* Might be a non-existent parent dir; try fixing that. */
+ if (r != 0 && errno == ENOENT) {
+ mkdirpath(a, archive_entry_pathname(entry));
+ r = mkfifo(archive_entry_pathname(entry),
+ archive_entry_stat(entry)->st_mode);
+ }
+
+ if (r != 0) {
+ archive_set_error(a, errno, "Can't restore fifo");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Set ownership, time, permission information. */
+ set_ownership(a, entry, flags);
+ set_time(a, entry, flags);
+ /* Done by mkfifo. */
+ /* set_perm(a, entry, archive_entry_stat(entry)->st_mode, flags); */
+ set_extended_perm(a, entry, flags);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Returns 0 if it successfully created necessary directories.
+ * Otherwise, returns ARCHIVE_WARN.
+ */
+
+static int
+mkdirpath(struct archive *a, const char *path)
+{
+ char *p;
+
+ /* Copy path to mutable storage, then call mkdirpath_recursive. */
+ archive_strcpy(&(a->extract_mkdirpath), path);
+ /* Prune a trailing '/' character. */
+ p = a->extract_mkdirpath.s;
+ if (p[strlen(p)-1] == '/')
+ p[strlen(p)-1] = 0;
+ /* Recursively try to build the path. */
+ return (mkdirpath_recursive(p));
+}
+
+/*
+ * For efficiency, just try creating longest path first (usually,
+ * archives walk through directories in a reasonable order). If that
+ * fails, prune the last element and recursively try again.
+ */
+static int
+mkdirpath_recursive(char *path)
+{
+ char * p;
+ int r;
+
+ p = strrchr(path, '/');
+ if (!p) return (0);
+
+ *p = 0; /* Terminate path name. */
+ r = mksubdir(path); /* Try building path. */
+ *p = '/'; /* Restore the '/' we just overwrote. */
+ return (r);
+}
+
+static int
+mksubdir(char *path)
+{
+ int mode = 0755;
+
+ if (mkdir(path, mode) == 0) return (0);
+
+ if (errno == EEXIST) /* TODO: stat() here to verify it is dir */
+ return (0);
+ if (mkdirpath_recursive(path))
+ return (ARCHIVE_WARN);
+ if (mkdir(path, mode) == 0)
+ return (0);
+ return (ARCHIVE_WARN); /* Still failed. Harumph. */
+}
+
+/*
+ * Note that I only inspect entry->ae_uid and entry->ae_gid here; if
+ * the client wants POSIX compat, they'll need to do uname/gname
+ * lookups themselves. I don't do it here because of the potential
+ * performance issues: if uname/gname lookup is expensive, then the
+ * results should be aggressively cached; if they're cheap, then we
+ * shouldn't waste memory on cache tables.
+ *
+ * Returns 0 if UID/GID successfully restored; ARCHIVE_WARN otherwise.
+ */
+static int
+set_ownership(struct archive *a, struct archive_entry *entry, int flags)
+{
+ /* If UID/GID are already correct, return 0. */
+ /* TODO: Fix this; need to stat() to find on-disk GID <sigh> */
+ if (a->user_uid == archive_entry_stat(entry)->st_uid)
+ return (0);
+
+ /* Not changed. */
+ if ((flags & ARCHIVE_EXTRACT_OWNER) == 0)
+ return (ARCHIVE_WARN);
+
+ /*
+ * Root can change owner/group; owner can change group;
+ * otherwise, bail out now.
+ */
+ if ((a->user_uid != 0)
+ && (a->user_uid != archive_entry_stat(entry)->st_uid))
+ return (ARCHIVE_WARN);
+
+ if (lchown(archive_entry_pathname(entry),
+ archive_entry_stat(entry)->st_uid,
+ archive_entry_stat(entry)->st_gid)) {
+ archive_set_error(a, errno,
+ "Can't set user=%d/group=%d for %s",
+ archive_entry_stat(entry)->st_uid,
+ archive_entry_stat(entry)->st_gid,
+ archive_entry_pathname(entry));
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_time(struct archive *a, struct archive_entry *entry, int flags)
+{
+ const struct stat *st;
+ struct timeval times[2];
+
+ (void)a; /* UNUSED */
+ st = archive_entry_stat(entry);
+
+ if ((flags & ARCHIVE_EXTRACT_TIME) == 0)
+ return (ARCHIVE_OK);
+
+ times[1].tv_sec = st->st_mtime;
+ times[1].tv_usec = st->st_mtimespec.tv_nsec / 1000;
+
+ times[0].tv_sec = st->st_atime;
+ times[0].tv_usec = st->st_atimespec.tv_nsec / 1000;
+
+ if (lutimes(archive_entry_pathname(entry), times) != 0) {
+ archive_set_error(a, errno, "Can't update time for %s",
+ archive_entry_pathname(entry));
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * Note: POSIX does not provide a portable way to restore ctime.
+ * So, any restoration of ctime will necessarily be OS-specific.
+ */
+
+ /* TODO: Can FreeBSD restore ctime? */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+set_perm(struct archive *a, struct archive_entry *entry, int mode, int flags)
+{
+ const char *name;
+
+ if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
+ return (ARCHIVE_OK);
+
+ name = archive_entry_pathname(entry);
+ if (lchmod(name, mode) != 0) {
+ archive_set_error(a, errno, "Can't set permissions");
+ return (ARCHIVE_WARN);
+ }
+ return (0);
+}
+
+static int
+set_extended_perm(struct archive *a, struct archive_entry *entry, int flags)
+{
+ int ret;
+
+ if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
+ return (ARCHIVE_OK);
+
+ ret = set_fflags(a, entry);
+ if (ret == ARCHIVE_OK)
+ ret = set_acls(a, entry);
+ return (ret);
+}
+
+static int
+set_fflags(struct archive *a, struct archive_entry *entry)
+{
+ char *fflags;
+ const char *fflagsc;
+ char *fflags_p;
+ const char *name;
+ int ret;
+ unsigned long set, clear;
+ struct stat st;
+
+ name = archive_entry_pathname(entry);
+
+ ret = ARCHIVE_OK;
+ fflagsc = archive_entry_fflags(entry);
+ if (fflagsc == NULL)
+ return (ARCHIVE_OK);
+
+ fflags = strdup(fflagsc);
+ if (fflags == NULL)
+ return (ARCHIVE_WARN);
+
+ fflags_p = fflags;
+ if (strtofflags(&fflags_p, &set, &clear) != 0 &&
+ stat(name, &st) == 0) {
+ st.st_flags &= ~clear;
+ st.st_flags |= set;
+ if (chflags(name, st.st_flags) != 0) {
+ archive_set_error(a, errno,
+ "Failed to set file flags");
+ ret = ARCHIVE_WARN;
+ }
+ }
+ free(fflags);
+ return (ret);
+}
+
+/*
+ * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
+ */
+static int
+set_acls(struct archive *a, struct archive_entry *entry)
+{
+ const char *acldesc;
+ acl_t acl;
+ const char *name;
+ int ret;
+
+ ret = ARCHIVE_OK;
+ name = archive_entry_pathname(entry);
+ acldesc = archive_entry_acl(entry);
+ if (acldesc != NULL) {
+ acl = acl_from_text(acldesc);
+ if (acl == NULL) {
+ archive_set_error(a, errno, "Error parsing acl '%s'",
+ acldesc);
+ ret = ARCHIVE_WARN;
+ } else {
+ if (acl_set_file(name, ACL_TYPE_ACCESS, acl) != 0) {
+ archive_set_error(a, errno,
+ "Failed to set acl");
+ ret = ARCHIVE_WARN;
+ }
+ acl_free(acl);
+ }
+ }
+
+ acldesc = archive_entry_acl_default(entry);
+ if (acldesc != NULL) {
+ acl = acl_from_text(acldesc);
+ if (acl == NULL) {
+ archive_set_error(a, errno, "error parsing acl '%s'",
+ acldesc);
+ ret = ARCHIVE_WARN;
+ } else {
+ if (acl_set_file(name, ACL_TYPE_DEFAULT, acl) != 0) {
+ archive_set_error(a, errno,
+ "Failed to set acl");
+ ret = ARCHIVE_WARN;
+ }
+ acl_free(acl);
+ }
+ }
+
+ return (ret);
+}
OpenPOWER on IntegriCloud