summaryrefslogtreecommitdiff
path: root/archivers/libarchive/files/libarchive/archive_write_disk_posix.c
diff options
context:
space:
mode:
authorjoerg <joerg@pkgsrc.org>2017-02-25 20:53:51 +0000
committerjoerg <joerg@pkgsrc.org>2017-02-25 20:53:51 +0000
commitc8fcd52665c6781876ed54832bd7f6ea46348688 (patch)
treeb5165a0dff599b0ae48d70b5104febf7f8732828 /archivers/libarchive/files/libarchive/archive_write_disk_posix.c
parentf67a2b170587775e96627a49fc615267312586d3 (diff)
downloadpkgsrc-c8fcd52665c6781876ed54832bd7f6ea46348688.tar.gz
Import libarchive-3.3.1.
Diffstat (limited to 'archivers/libarchive/files/libarchive/archive_write_disk_posix.c')
-rw-r--r--archivers/libarchive/files/libarchive/archive_write_disk_posix.c588
1 files changed, 464 insertions, 124 deletions
diff --git a/archivers/libarchive/files/libarchive/archive_write_disk_posix.c b/archivers/libarchive/files/libarchive/archive_write_disk_posix.c
index 6737cd755b2..5a01e8450ce 100644
--- a/archivers/libarchive/files/libarchive/archive_write_disk_posix.c
+++ b/archivers/libarchive/files/libarchive/archive_write_disk_posix.c
@@ -110,6 +110,18 @@ __FBSDID("$FreeBSD$");
#include <sys/fcntl1.h>
#endif
+/*
+ * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared.
+ *
+ * It assumes that the input is an integer type of no more than 64 bits.
+ * If the number is less than zero, t must be a signed type, so it fits in
+ * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t
+ * without loss. But it could be a large unsigned value, so we have to clip it
+ * to INT64_MAX.*
+ */
+#define to_int64_time(t) \
+ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t))
+
#if __APPLE__
#include <TargetConditionals.h>
#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H
@@ -140,7 +152,17 @@ __FBSDID("$FreeBSD$");
#define O_BINARY 0
#endif
#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
+#define O_CLOEXEC 0
+#endif
+
+/* Ignore non-int O_NOFOLLOW constant. */
+/* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */
+#if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)
+#undef O_NOFOLLOW
+#endif
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
#endif
struct fixup_entry {
@@ -298,7 +320,7 @@ struct archive_write_disk {
#define MAXIMUM_DIR_MODE 0775
/*
- * Maxinum uncompressed size of a decmpfs block.
+ * Maximum uncompressed size of a decmpfs block.
*/
#define MAX_DECMPFS_BLOCK_SIZE (64 * 1024)
/*
@@ -313,7 +335,7 @@ struct archive_write_disk {
#define RSRC_F_SIZE 50 /* Size of Resource fork footer. */
/* Size to write compressed data to resource fork. */
#define COMPRESSED_W_SIZE (64 * 1024)
-/* decmpfs difinitions. */
+/* decmpfs definitions. */
#define MAX_DECMPFS_XATTR_SIZE 3802
#ifndef DECMPFS_XATTR_NAME
#define DECMPFS_XATTR_NAME "com.apple.decmpfs"
@@ -326,12 +348,19 @@ struct archive_write_disk {
#define HFS_BLOCKS(s) ((s) >> 12)
+static void fsobj_error(int *, struct archive_string *, int, const char *,
+ const char *);
+static int check_symlinks_fsobj(char *, int *, struct archive_string *,
+ int);
static int check_symlinks(struct archive_write_disk *);
static int create_filesystem_object(struct archive_write_disk *);
-static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+ const char *pathname);
#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
static void edit_deep_directories(struct archive_write_disk *ad);
#endif
+static int cleanup_pathname_fsobj(char *, int *, struct archive_string *,
+ int);
static int cleanup_pathname(struct archive_write_disk *);
static int create_dir(struct archive_write_disk *, char *);
static int create_parent_dir(struct archive_write_disk *, char *);
@@ -362,11 +391,14 @@ static struct archive_vtable *archive_write_disk_vtable(void);
static int _archive_write_disk_close(struct archive *);
static int _archive_write_disk_free(struct archive *);
-static int _archive_write_disk_header(struct archive *, struct archive_entry *);
+static int _archive_write_disk_header(struct archive *,
+ struct archive_entry *);
static int64_t _archive_write_disk_filter_bytes(struct archive *, int);
static int _archive_write_disk_finish_entry(struct archive *);
-static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t);
-static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t);
+static ssize_t _archive_write_disk_data(struct archive *, const void *,
+ size_t);
+static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
+ size_t, int64_t);
static int
lazy_stat(struct archive_write_disk *a)
@@ -612,9 +644,9 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
/*
* NOTE: UF_COMPRESSED is ignored even if the filesystem
* supports HFS+ Compression because the file should
- * have at least an extended attriute "com.apple.decmpfs"
+ * have at least an extended attribute "com.apple.decmpfs"
* before the flag is set to indicate that the file have
- * been compressed. If hte filesystem does not support
+ * been compressed. If the filesystem does not support
* HFS+ Compression the system call will fail.
*/
if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)
@@ -637,7 +669,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
if (a->restore_pwd >= 0) {
r = fchdir(a->restore_pwd);
if (r != 0) {
- archive_set_error(&a->archive, errno, "chdir() failure");
+ archive_set_error(&a->archive, errno,
+ "chdir() failure");
ret = ARCHIVE_FATAL;
}
close(a->restore_pwd);
@@ -685,7 +718,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
}
if (archive_entry_birthtime_is_set(entry)) {
fe->birthtime = archive_entry_birthtime(entry);
- fe->birthtime_nanos = archive_entry_birthtime_nsec(entry);
+ fe->birthtime_nanos = archive_entry_birthtime_nsec(
+ entry);
} else {
/* If birthtime is unset, use mtime. */
fe->birthtime = fe->mtime;
@@ -711,7 +745,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
return (ARCHIVE_FATAL);
fe->mac_metadata = malloc(metadata_size);
if (fe->mac_metadata != NULL) {
- memcpy(fe->mac_metadata, metadata, metadata_size);
+ memcpy(fe->mac_metadata, metadata,
+ metadata_size);
fe->mac_metadata_size = metadata_size;
fe->fixup |= TODO_MAC_METADATA;
}
@@ -1224,7 +1259,7 @@ hfs_drive_compressor(struct archive_write_disk *a, const char *buff,
ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);
a->compressed_buffer_remaining = a->compressed_buffer_size;
- /* If the compressed size is not enouph smaller than
+ /* If the compressed size is not enough smaller than
* the uncompressed size. cancel HFS+ compression.
* TODO: study a behavior of ditto utility and improve
* the condition to fall back into no HFS+ compression. */
@@ -1329,7 +1364,7 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
(uint32_t *)(a->resource_fork + RSRC_H_SIZE);
/* Set the block count to the resource fork. */
archive_le32enc(a->decmpfs_block_info++, block_count);
- /* Get the position where we are goint to set compressed
+ /* Get the position where we are going to set compressed
* data. */
a->compressed_rsrc_position =
RSRC_H_SIZE + 4 + (block_count * 8);
@@ -1402,7 +1437,7 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff,
bytes_to_write = size;
/* Seek if necessary to the specified offset. */
if (a->offset < a->fd_offset) {
- /* Can't support backword move. */
+ /* Can't support backward move. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Seek failed");
return (ARCHIVE_FATAL);
@@ -1468,7 +1503,8 @@ _archive_write_disk_data_block(struct archive *_a,
return (r);
if ((size_t)r < size) {
archive_set_error(&a->archive, 0,
- "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize);
+ "Too much data: Truncating file at %ju bytes",
+ (uintmax_t)a->filesize);
return (ARCHIVE_WARN);
}
#if ARCHIVE_VERSION_NUMBER < 3999000
@@ -1666,10 +1702,26 @@ _archive_write_disk_finish_entry(struct archive *_a)
* ACLs that prevent attribute changes (including time).
*/
if (a->todo & TODO_ACLS) {
- int r2 = archive_write_disk_set_acls(&a->archive, a->fd,
- archive_entry_pathname(a->entry),
- archive_entry_acl(a->entry));
+ int r2;
+#ifdef HAVE_DARWIN_ACL
+ /*
+ * On Mac OS, platform ACLs are stored also in mac_metadata by
+ * the operating system. If mac_metadata is present it takes
+ * precedence and we skip extracting libarchive NFSv4 ACLs
+ */
+ const void *metadata;
+ size_t metadata_size;
+ metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+ if ((a->todo & TODO_MAC_METADATA) == 0 ||
+ metadata == NULL || metadata_size == 0) {
+#endif
+ r2 = archive_write_disk_set_acls(&a->archive, a->fd,
+ archive_entry_pathname(a->entry),
+ archive_entry_acl(a->entry));
if (r2 < ret) ret = r2;
+#ifdef HAVE_DARWIN_ACL
+ }
+#endif
}
finish_metadata:
@@ -1755,10 +1807,9 @@ archive_write_disk_new(void)
{
struct archive_write_disk *a;
- a = (struct archive_write_disk *)malloc(sizeof(*a));
+ a = (struct archive_write_disk *)calloc(1, sizeof(*a));
if (a == NULL)
return (NULL);
- memset(a, 0, sizeof(*a));
a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
/* We're ready to write a header immediately. */
a->archive.state = ARCHIVE_STATE_HEADER;
@@ -1796,7 +1847,7 @@ edit_deep_directories(struct archive_write_disk *a)
char *tail = a->name;
/* If path is short, avoid the open() below. */
- if (strlen(tail) <= PATH_MAX)
+ if (strlen(tail) < PATH_MAX)
return;
/* Try to record our starting dir. */
@@ -1806,7 +1857,7 @@ edit_deep_directories(struct archive_write_disk *a)
return;
/* As long as the path is too long... */
- while (strlen(tail) > PATH_MAX) {
+ while (strlen(tail) >= PATH_MAX) {
/* Locate a dir prefix shorter than PATH_MAX. */
tail += PATH_MAX - 8;
while (tail > a->name && *tail != '/')
@@ -1993,8 +2044,9 @@ 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);
+ if ((&a->archive)->error == NULL)
+ archive_set_error(&a->archive, en, "Can't create '%s'",
+ a->name);
return (ARCHIVE_FAILED);
}
@@ -2014,6 +2066,11 @@ create_filesystem_object(struct archive_write_disk *a)
const char *linkname;
mode_t final_mode, mode;
int r;
+ /* these for check_symlinks_fsobj */
+ char *linkname_copy; /* non-const copy of linkname */
+ struct stat st;
+ struct archive_string error_string;
+ int error_number;
/* We identify hard/symlinks according to the link names. */
/* Since link(2) and symlink(2) don't handle modes, we're done here. */
@@ -2022,6 +2079,43 @@ create_filesystem_object(struct archive_write_disk *a)
#if !HAVE_LINK
return (EPERM);
#else
+ archive_string_init(&error_string);
+ linkname_copy = strdup(linkname);
+ if (linkname_copy == NULL) {
+ return (EPERM);
+ }
+ /*
+ * TODO: consider using the cleaned-up path as the link
+ * target?
+ */
+ r = cleanup_pathname_fsobj(linkname_copy, &error_number,
+ &error_string, a->flags);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * EPERM is more appropriate than error_number for our
+ * callers
+ */
+ return (EPERM);
+ }
+ r = check_symlinks_fsobj(linkname_copy, &error_number,
+ &error_string, a->flags);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * EPERM is more appropriate than error_number for our
+ * callers
+ */
+ return (EPERM);
+ }
+ free(linkname_copy);
+ archive_string_free(&error_string);
r = link(linkname, a->name) ? errno : 0;
/*
* New cpio and pax formats allow hardlink entries
@@ -2039,11 +2133,20 @@ create_filesystem_object(struct archive_write_disk *a)
a->todo = 0;
a->deferred = 0;
} else if (r == 0 && a->filesize > 0) {
- a->fd = open(a->name,
- O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC);
- __archive_ensure_cloexec_flag(a->fd);
- if (a->fd < 0)
+#ifdef HAVE_LSTAT
+ r = lstat(a->name, &st);
+#else
+ r = stat(a->name, &st);
+#endif
+ if (r != 0)
r = errno;
+ else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+ a->fd = open(a->name, O_WRONLY | O_TRUNC |
+ O_BINARY | O_CLOEXEC | O_NOFOLLOW);
+ __archive_ensure_cloexec_flag(a->fd);
+ if (a->fd < 0)
+ r = errno;
+ }
}
return (r);
#endif
@@ -2190,8 +2293,13 @@ _archive_write_disk_close(struct archive *_a)
if (p->fixup & TODO_MODE_BASE)
chmod(p->name, p->mode);
if (p->fixup & TODO_ACLS)
- archive_write_disk_set_acls(&a->archive,
- -1, p->name, &p->acl);
+#ifdef HAVE_DARWIN_ACL
+ if ((p->fixup & TODO_MAC_METADATA) == 0 ||
+ p->mac_metadata == NULL ||
+ p->mac_metadata_size == 0)
+#endif
+ archive_write_disk_set_acls(&a->archive,
+ -1, p->name, &p->acl);
if (p->fixup & TODO_FFLAGS)
set_fflags_platform(a, -1, p->name,
p->mode, p->fflags_set, 0);
@@ -2351,116 +2459,283 @@ current_fixup(struct archive_write_disk *a, const char *pathname)
return (a->current_fixup);
}
-/* TODO: Make this work. */
-/*
- * TODO: The deep-directory support bypasses this; disable deep directory
- * support if we're doing symlink checks.
- */
+/* Error helper for new *_fsobj functions */
+static void
+fsobj_error(int *a_eno, struct archive_string *a_estr,
+ int err, const char *errstr, const char *path)
+{
+ if (a_eno)
+ *a_eno = err;
+ if (a_estr)
+ archive_string_sprintf(a_estr, errstr, path);
+}
+
/*
* TODO: Someday, integrate this with the deep dir support; they both
* scan the path and both can be optimized by comparing against other
* recent paths.
*/
/* TODO: Extend this to support symlinks on Windows Vista and later. */
+
+/*
+ * Checks the given path to see if any elements along it are symlinks. Returns
+ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
+ */
static int
-check_symlinks(struct archive_write_disk *a)
+check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+ int flags)
{
#if !defined(HAVE_LSTAT)
/* Platform doesn't have lstat, so we can't look for symlinks. */
- (void)a; /* UNUSED */
+ (void)path; /* UNUSED */
+ (void)error_number; /* UNUSED */
+ (void)error_string; /* UNUSED */
+ (void)flags; /* UNUSED */
return (ARCHIVE_OK);
#else
- char *pn;
+ int res = ARCHIVE_OK;
+ char *tail;
+ char *head;
+ int last;
char c;
int r;
struct stat st;
+ int restore_pwd;
+
+ /* Nothing to do here if name is empty */
+ if(path[0] == '\0')
+ return (ARCHIVE_OK);
/*
* Guard against symlink tricks. Reject any archive entry whose
* destination would be altered by a symlink.
+ *
+ * Walk the filename in chunks separated by '/'. For each segment:
+ * - if it doesn't exist, continue
+ * - if it's symlink, abort or remove it
+ * - if it's a directory and it's not the last chunk, cd into it
+ * As we go:
+ * head points to the current (relative) path
+ * tail points to the temporary \0 terminating the segment we're
+ * currently examining
+ * c holds what used to be in *tail
+ * last is 1 if this is the last tail
*/
- /* Whatever we checked last time doesn't need to be re-checked. */
- pn = a->name;
- if (archive_strlen(&(a->path_safe)) > 0) {
- char *p = a->path_safe.s;
- while ((*pn != '\0') && (*p == *pn))
- ++p, ++pn;
- }
+ restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(restore_pwd);
+ if (restore_pwd < 0)
+ return (ARCHIVE_FATAL);
+ head = path;
+ tail = path;
+ last = 0;
+ /* TODO: reintroduce a safe cache here? */
/* Skip the root directory if the path is absolute. */
- if(pn == a->name && pn[0] == '/')
- ++pn;
- c = pn[0];
- /* Keep going until we've checked the entire name. */
- while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) {
+ if(tail == path && tail[0] == '/')
+ ++tail;
+ /* Keep going until we've checked the entire name.
+ * head, tail, path all alias the same string, which is
+ * temporarily zeroed at tail, so be careful restoring the
+ * stashed (c=tail[0]) for error messages.
+ * Exiting the loop with break is okay; continue is not.
+ */
+ while (!last) {
+ /*
+ * Skip the separator we just consumed, plus any adjacent ones
+ */
+ while (*tail == '/')
+ ++tail;
/* Skip the next path element. */
- while (*pn != '\0' && *pn != '/')
- ++pn;
- c = pn[0];
- pn[0] = '\0';
+ while (*tail != '\0' && *tail != '/')
+ ++tail;
+ /* is this the last path component? */
+ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');
+ /* temporarily truncate the string here */
+ c = tail[0];
+ tail[0] = '\0';
/* Check that we haven't hit a symlink. */
- r = lstat(a->name, &st);
+ r = lstat(head, &st);
if (r != 0) {
+ tail[0] = c;
/* We've hit a dir that doesn't exist; stop now. */
- if (errno == ENOENT)
+ if (errno == ENOENT) {
+ break;
+ } else {
+ /*
+ * Treat any other error as fatal - best to be
+ * paranoid here.
+ * Note: This effectively disables deep
+ * directory support when security checks are
+ * enabled. Otherwise, very long pathnames that
+ * trigger an error here could evade the
+ * sandbox.
+ * TODO: We could do better, but it would
+ * probably require merging the symlink checks
+ * with the deep-directory editing.
+ */
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not stat %s", path);
+ res = ARCHIVE_FAILED;
break;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+ if (!last) {
+ if (chdir(head) != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not chdir %s", path);
+ res = (ARCHIVE_FATAL);
+ break;
+ }
+ /* Our view is now from inside this dir: */
+ head = tail + 1;
+ }
} else if (S_ISLNK(st.st_mode)) {
- if (c == '\0') {
+ if (last) {
/*
* Last element is symlink; remove it
* so we can overwrite it with the
* item being extracted.
*/
- if (unlink(a->name)) {
- archive_set_error(&a->archive, errno,
+ if (unlink(head)) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
"Could not remove symlink %s",
- a->name);
- pn[0] = c;
- return (ARCHIVE_FAILED);
+ path);
+ res = ARCHIVE_FAILED;
+ break;
}
- a->pst = NULL;
/*
* Even if we did remove it, a warning
* is in order. The warning is silly,
* though, if we're just replacing one
* symlink with another symlink.
*/
- if (!S_ISLNK(a->mode)) {
- archive_set_error(&a->archive, 0,
- "Removing symlink %s",
- a->name);
+ tail[0] = c;
+ /*
+ * FIXME: not sure how important this is to
+ * restore
+ */
+ /*
+ if (!S_ISLNK(path)) {
+ fsobj_error(a_eno, a_estr, 0,
+ "Removing symlink %s", path);
}
+ */
/* Symlink gone. No more problem! */
- pn[0] = c;
- return (0);
- } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
+ res = ARCHIVE_OK;
+ break;
+ } else if (flags & ARCHIVE_EXTRACT_UNLINK) {
/* User asked us to remove problems. */
- if (unlink(a->name) != 0) {
- archive_set_error(&a->archive, 0,
- "Cannot remove intervening symlink %s",
- a->name);
- pn[0] = c;
- return (ARCHIVE_FAILED);
+ if (unlink(head) != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot remove intervening "
+ "symlink %s", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ tail[0] = c;
+ } else if ((flags &
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+ /*
+ * We are not the last element and we want to
+ * follow symlinks if they are a directory.
+ *
+ * This is needed to extract hardlinks over
+ * symlinks.
+ */
+ r = stat(head, &st);
+ if (r != 0) {
+ tail[0] = c;
+ if (errno == ENOENT) {
+ break;
+ } else {
+ fsobj_error(a_eno, a_estr,
+ errno,
+ "Could not stat %s", path);
+ res = (ARCHIVE_FAILED);
+ break;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+ if (chdir(head) != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr,
+ errno,
+ "Could not chdir %s", path);
+ res = (ARCHIVE_FATAL);
+ break;
+ }
+ /*
+ * Our view is now from inside
+ * this dir:
+ */
+ head = tail + 1;
+ } else {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot extract through "
+ "symlink %s", path);
+ res = ARCHIVE_FAILED;
+ break;
}
- a->pst = NULL;
} else {
- archive_set_error(&a->archive, 0,
- "Cannot extract through symlink %s",
- a->name);
- pn[0] = c;
- return (ARCHIVE_FAILED);
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot extract through symlink %s", path);
+ res = ARCHIVE_FAILED;
+ break;
}
}
- pn[0] = c;
- if (pn[0] != '\0')
- pn++; /* Advance to the next segment. */
+ /* be sure to always maintain this */
+ tail[0] = c;
+ if (tail[0] != '\0')
+ tail++; /* Advance to the next segment. */
+ }
+ /* Catches loop exits via break */
+ tail[0] = c;
+#ifdef HAVE_FCHDIR
+ /* If we changed directory above, restore it here. */
+ if (restore_pwd >= 0) {
+ r = fchdir(restore_pwd);
+ if (r != 0) {
+ fsobj_error(a_eno, a_estr, errno,
+ "chdir() failure", "");
+ }
+ close(restore_pwd);
+ restore_pwd = -1;
+ if (r != 0) {
+ res = (ARCHIVE_FATAL);
+ }
}
- pn[0] = c;
- /* We've checked and/or cleaned the whole path, so remember it. */
- archive_strcpy(&a->path_safe, a->name);
- return (ARCHIVE_OK);
#endif
+ /* TODO: reintroduce a safe cache here? */
+ return res;
+#endif
+}
+
+/*
+ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
+ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+ struct archive_string error_string;
+ int error_number;
+ int rc;
+ archive_string_init(&error_string);
+ rc = check_symlinks_fsobj(a->name, &error_number, &error_string,
+ a->flags);
+ if (rc != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ }
+ archive_string_free(&error_string);
+ a->pst = NULL; /* to be safe */
+ return rc;
}
+
#if defined(__CYGWIN__)
/*
* 1. Convert a path separator from '\' to '/' .
@@ -2471,7 +2746,7 @@ check_symlinks(struct archive_write_disk *a)
* See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx
*/
static void
-cleanup_pathname_win(struct archive_write_disk *a)
+cleanup_pathname_win(char *path)
{
wchar_t wc;
char *p;
@@ -2482,7 +2757,7 @@ cleanup_pathname_win(struct archive_write_disk *a)
mb = 0;
complete = 1;
utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0;
- for (p = a->name; *p != '\0'; p++) {
+ for (p = path; *p != '\0'; p++) {
++alen;
if (*p == '\\') {
/* If previous byte is smaller than 128,
@@ -2507,7 +2782,7 @@ cleanup_pathname_win(struct archive_write_disk *a)
/*
* Convert path separator in wide-character.
*/
- p = a->name;
+ p = path;
while (*p != '\0' && alen) {
l = mbtowc(&wc, p, alen);
if (l == (size_t)-1) {
@@ -2534,26 +2809,27 @@ cleanup_pathname_win(struct archive_write_disk *a)
* is set) if the path is absolute.
*/
static int
-cleanup_pathname(struct archive_write_disk *a)
+cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+ int flags)
{
char *dest, *src;
char separator = '\0';
- dest = src = a->name;
+ dest = src = path;
if (*src == '\0') {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Invalid empty pathname");
+ fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+ "Invalid empty ", "pathname");
return (ARCHIVE_FAILED);
}
#if defined(__CYGWIN__)
- cleanup_pathname_win(a);
+ cleanup_pathname_win(path);
#endif
/* Skip leading '/'. */
if (*src == '/') {
- if (a->flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Path is absolute");
+ if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
+ fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+ "Path is ", "absolute");
return (ARCHIVE_FAILED);
}
@@ -2580,10 +2856,11 @@ cleanup_pathname(struct archive_write_disk *a)
} else if (src[1] == '.') {
if (src[2] == '/' || src[2] == '\0') {
/* Conditionally warn about '..' */
- if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
- archive_set_error(&a->archive,
+ if (flags
+ & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+ fsobj_error(a_eno, a_estr,
ARCHIVE_ERRNO_MISC,
- "Path contains '..'");
+ "Path contains ", "'..'");
return (ARCHIVE_FAILED);
}
}
@@ -2614,7 +2891,7 @@ cleanup_pathname(struct archive_write_disk *a)
* We've just copied zero or more path elements, not including the
* final '/'.
*/
- if (dest == a->name) {
+ if (dest == path) {
/*
* Nothing got copied. The path must have been something
* like '.' or '/' or './' or '/././././/./'.
@@ -2629,6 +2906,23 @@ cleanup_pathname(struct archive_write_disk *a)
return (ARCHIVE_OK);
}
+static int
+cleanup_pathname(struct archive_write_disk *a)
+{
+ struct archive_string error_string;
+ int error_number;
+ int rc;
+ archive_string_init(&error_string);
+ rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string,
+ a->flags);
+ if (rc != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ }
+ archive_string_free(&error_string);
+ return rc;
+}
+
/*
* Create the parent directory of the specified path, assuming path
* is already in mutable storage.
@@ -2707,7 +3001,8 @@ create_dir(struct archive_write_disk *a, char *path)
}
} else if (errno != ENOENT && errno != ENOTDIR) {
/* Stat failed? */
- archive_set_error(&a->archive, errno, "Can't test directory '%s'", path);
+ archive_set_error(&a->archive, errno,
+ "Can't test directory '%s'", path);
return (ARCHIVE_FAILED);
} else if (slash != NULL) {
*slash = '\0';
@@ -3172,12 +3467,19 @@ set_fflags(struct archive_write_disk *a)
#ifdef UF_APPEND
critical_flags |= UF_APPEND;
#endif
-#ifdef EXT2_APPEND_FL
+#if defined(FS_APPEND_FL)
+ critical_flags |= FS_APPEND_FL;
+#elif defined(EXT2_APPEND_FL)
critical_flags |= EXT2_APPEND_FL;
#endif
-#ifdef EXT2_IMMUTABLE_FL
+#if defined(FS_IMMUTABLE_FL)
+ critical_flags |= FS_IMMUTABLE_FL;
+#elif defined(EXT2_IMMUTABLE_FL)
critical_flags |= EXT2_IMMUTABLE_FL;
#endif
+#ifdef FS_JOURNAL_DATA_FL
+ critical_flags |= FS_JOURNAL_DATA_FL;
+#endif
if (a->todo & TODO_FFLAGS) {
archive_entry_fflags(a->entry, &set, &clear);
@@ -3232,7 +3534,8 @@ clear_nochange_fflags(struct archive_write_disk *a)
nochange_flags |= EXT2_IMMUTABLE_FL;
#endif
- return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags));
+ return (set_fflags_platform(a, a->fd, a->name, mode, 0,
+ nochange_flags));
}
@@ -3288,7 +3591,10 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
return (ARCHIVE_WARN);
}
-#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \
+ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \
+ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
/*
* Linux uses ioctl() to read and write file flags.
*/
@@ -3301,7 +3607,7 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
int newflags, oldflags;
int sf_mask = 0;
- if (set == 0 && clear == 0)
+ if (set == 0 && clear == 0)
return (ARCHIVE_OK);
/* Only regular files and dirs can have flags. */
if (!S_ISREG(mode) && !S_ISDIR(mode))
@@ -3322,12 +3628,19 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
* defines. (?) The code below degrades reasonably gracefully
* if sf_mask is incomplete.
*/
-#ifdef EXT2_IMMUTABLE_FL
+#if defined(FS_IMMUTABLE_FL)
+ sf_mask |= FS_IMMUTABLE_FL;
+#elif defined(EXT2_IMMUTABLE_FL)
sf_mask |= EXT2_IMMUTABLE_FL;
#endif
-#ifdef EXT2_APPEND_FL
+#if defined(FS_APPEND_FL)
+ sf_mask |= FS_APPEND_FL;
+#elif defined(EXT2_APPEND_FL)
sf_mask |= EXT2_APPEND_FL;
#endif
+#if defined(FS_JOURNAL_DATA_FL)
+ sf_mask |= FS_JOURNAL_DATA_FL;
+#endif
/*
* XXX As above, this would be way simpler if we didn't have
* to read the current flags from disk. XXX
@@ -3335,12 +3648,24 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
ret = ARCHIVE_OK;
/* Read the current file flags. */
- if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) < 0)
+ if (ioctl(myfd,
+#ifdef FS_IOC_GETFLAGS
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &oldflags) < 0)
goto fail;
/* Try setting the flags as given. */
newflags = (oldflags & ~clear) | set;
- if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+ if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+ FS_IOC_SETFLAGS,
+#else
+ EXT2_IOC_SETFLAGS,
+#endif
+ &newflags) >= 0)
goto cleanup;
if (errno != EPERM)
goto fail;
@@ -3349,7 +3674,13 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
newflags &= ~sf_mask;
oldflags &= sf_mask;
newflags |= oldflags;
- if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+ if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+ FS_IOC_SETFLAGS,
+#else
+ EXT2_IOC_SETFLAGS,
+#endif
+ &newflags) >= 0)
goto cleanup;
/* We couldn't set the flags, so report the failure. */
@@ -3487,6 +3818,9 @@ exit_xattr:
static int
copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)
{
+#ifndef HAVE_SYS_ACL_H
+ return 0;
+#else
acl_t acl, dfacl = NULL;
int acl_r, ret = ARCHIVE_OK;
@@ -3514,6 +3848,7 @@ exit_acl:
if (dfacl)
acl_free(dfacl);
return (ret);
+#endif
}
static int
@@ -3753,7 +4088,8 @@ set_xattrs(struct archive_write_disk *a)
if (errno == ENOTSUP || errno == ENOSYS) {
if (!warning_done) {
warning_done = 1;
- archive_set_error(&a->archive, errno,
+ archive_set_error(&a->archive,
+ errno,
"Cannot restore extended "
"attributes on this file "
"system");
@@ -3764,7 +4100,8 @@ set_xattrs(struct archive_write_disk *a)
ret = ARCHIVE_WARN;
}
} else {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid extended attribute encountered");
ret = ARCHIVE_WARN;
}
@@ -3808,19 +4145,22 @@ set_xattrs(struct archive_write_disk *a)
errno = 0;
#if HAVE_EXTATTR_SET_FD
if (a->fd >= 0)
- e = extattr_set_fd(a->fd, namespace, name, value, size);
+ 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);
+ e = extattr_set_file(
+ archive_entry_pathname(entry), namespace,
+ name, value, size);
}
if (e != (int)size) {
if (errno == ENOTSUP || errno == ENOSYS) {
if (!warning_done) {
warning_done = 1;
- archive_set_error(&a->archive, errno,
+ archive_set_error(&a->archive,
+ errno,
"Cannot restore extended "
"attributes on this file "
"system");
@@ -3866,10 +4206,10 @@ older(struct stat *st, struct archive_entry *entry)
{
/* First, test the seconds and return if we have a definite answer. */
/* Definitely older. */
- if (st->st_mtime < archive_entry_mtime(entry))
+ if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))
return (1);
/* Definitely younger. */
- if (st->st_mtime > archive_entry_mtime(entry))
+ if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))
return (0);
/* If this platform supports fractional seconds, try those. */
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC