summaryrefslogtreecommitdiffstats
path: root/lib/libarchive/archive_write_disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libarchive/archive_write_disk.c')
-rw-r--r--lib/libarchive/archive_write_disk.c112
1 files changed, 92 insertions, 20 deletions
diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c
index 86426f1..9bf7d33 100644
--- a/lib/libarchive/archive_write_disk.c
+++ b/lib/libarchive/archive_write_disk.c
@@ -204,6 +204,7 @@ static void edit_deep_directories(struct archive_write_disk *ad);
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 *);
+static int older(struct stat *, struct archive_entry *);
static int restore_entry(struct archive_write_disk *);
#ifdef HAVE_POSIX_ACL
static int set_acl(struct archive_write_disk *, int fd, struct archive_entry *,
@@ -292,7 +293,11 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
a->pst = NULL;
a->current_fixup = NULL;
a->deferred = 0;
- a->entry = entry;
+ if (a->entry) {
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ }
+ a->entry = archive_entry_clone(entry);
a->fd = -1;
a->offset = 0;
a->uid = a->user_uid;
@@ -544,6 +549,11 @@ _archive_write_finish_entry(struct archive *_a)
close(a->fd);
a->fd = -1;
}
+ /* If there's an entry, we can release it now. */
+ if (a->entry) {
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ }
a->archive.state = ARCHIVE_STATE_HEADER;
return (ret);
}
@@ -682,21 +692,42 @@ restore_entry(struct archive_write_disk *a)
/* Try creating it first; if this fails, we'll try to recover. */
en = create_filesystem_object(a);
- if (en == ENOTDIR || en == ENOENT) {
+ if ((en == ENOTDIR || en == ENOENT)
+ && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
/* If the parent dir doesn't exist, try creating it. */
create_parent_dir(a, a->name);
/* Now try to create the object again. */
en = create_filesystem_object(a);
}
- if (en == EEXIST) {
+ if ((en == EISDIR || en == EEXIST)
+ && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
/* If we're not overwriting, we're done. */
- if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE) {
- archive_set_error(&a->archive, en, "Already exists");
+ archive_set_error(&a->archive, en, "Already exists");
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * Some platforms return EISDIR if you call
+ * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+ * return EEXIST. POSIX is ambiguous, requiring EISDIR
+ * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+ * on an existing item.
+ */
+ if (en == EISDIR) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
return (ARCHIVE_WARN);
}
-
- /* Find out what's in the way before we go any further. */
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else if (en == EEXIST) {
+ /*
+ * We know something is in the way, but we don't know what;
+ * we need to find out before we go any further.
+ */
if (lstat(a->name, &a->st) != 0) {
archive_set_error(&a->archive, errno,
"Can't stat existing object");
@@ -705,6 +736,14 @@ restore_entry(struct archive_write_disk *a)
/* TODO: if it's a symlink... */
+ if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) {
+ if (!older(&(a->st), a->entry)) {
+ archive_set_error(&a->archive, 0,
+ "File on disk is not older; skipping.");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
/* If it's our archive, we're done. */
if (a->skip_file_dev > 0 &&
a->skip_file_ino > 0 &&
@@ -1405,16 +1444,13 @@ success:
static int
set_time(struct archive_write_disk *a)
{
- const struct stat *st;
struct timeval times[2];
- st = archive_entry_stat(a->entry);
+ times[1].tv_sec = archive_entry_mtime(a->entry);
+ times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
- times[1].tv_sec = st->st_mtime;
- times[1].tv_usec = ARCHIVE_STAT_MTIME_NANOS(st) / 1000;
-
- times[0].tv_sec = st->st_atime;
- times[0].tv_usec = ARCHIVE_STAT_ATIME_NANOS(st) / 1000;
+ times[0].tv_sec = archive_entry_atime(a->entry);
+ times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
#ifdef HAVE_FUTIMES
if (a->fd >= 0 && futimes(a->fd, times) == 0) {
@@ -1450,11 +1486,10 @@ set_time(struct archive_write_disk *a)
static int
set_time(struct archive_write_disk *a)
{
- const struct stat *st = archive_entry_stat(a->entry);
struct utimbuf times;
- times.modtime = st->st_mtime;
- times.actime = st->st_atime;
+ times.modtime = archive_entry_mtime(a->entry);
+ times.actime = archive_entry_atime(a->entry);
if (!S_ISLNK(a->mode) && utime(a->name, &times) != 0) {
archive_set_error(&a->archive, errno,
"Can't update time for %s", a->name);
@@ -1479,6 +1514,7 @@ static int
set_mode(struct archive_write_disk *a, int mode)
{
int r = ARCHIVE_OK;
+ mode &= 07777; /* Strip off file type bits. */
if (a->todo & TODO_SGID_CHECK) {
/*
@@ -1539,7 +1575,8 @@ set_mode(struct archive_write_disk *a, int mode)
* impact.
*/
if (lchmod(a->name, mode) != 0) {
- archive_set_error(&a->archive, errno, "Can't set permissions");
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
r = ARCHIVE_WARN;
}
#endif
@@ -1554,7 +1591,7 @@ set_mode(struct archive_write_disk *a, int mode)
if (a->fd >= 0) {
if (fchmod(a->fd, mode) != 0) {
archive_set_error(&a->archive, errno,
- "Can't set permissions");
+ "Can't set permissions to 0%o", (int)mode);
r = ARCHIVE_WARN;
}
} else
@@ -1563,7 +1600,7 @@ set_mode(struct archive_write_disk *a, int mode)
* we'll just use chmod(). */
if (chmod(a->name, mode) != 0) {
archive_set_error(&a->archive, errno,
- "Can't set permissions");
+ "Can't set permissions to 0%o", (int)mode);
r = ARCHIVE_WARN;
}
}
@@ -1998,3 +2035,38 @@ trivial_lookup_uid(void *private_data, const char *uname, uid_t uid)
(void)uname; /* UNUSED */
return (uid);
}
+
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+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))
+ return (1);
+ /* Definitely younger. */
+ if (st->st_mtime > archive_entry_mtime(entry))
+ return (0);
+ /* If this platform supports fractional seconds, try those. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ /* Definitely older. */
+ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+ /* Definitely younger. */
+ if (st->st_mtimespec.tv_nsec > archive_entry_mtime_nsec(entry))
+ return (0);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ /* Definitely older. */
+ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+ /* Definitely older. */
+ if (st->st_mtim.tv_nsec > archive_entry_mtime_nsec(entry))
+ return (0);
+#else
+ /* This system doesn't have high-res timestamps. */
+#endif
+ /* Same age, so not older. */
+ return (0);
+}
OpenPOWER on IntegriCloud