summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/libarchive/archive_read_extract.c31
1 files changed, 27 insertions, 4 deletions
diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c
index f20a75b..c473980 100644
--- a/lib/libarchive/archive_read_extract.c
+++ b/lib/libarchive/archive_read_extract.c
@@ -70,6 +70,7 @@ struct fixup_entry {
#define FIXUP_FFLAGS 4
struct extract {
+ mode_t umask;
struct archive_string mkdirpath;
struct fixup_entry *fixup_list;
};
@@ -149,6 +150,7 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags)
memset(a->extract, 0, sizeof(*a->extract));
}
extract = a->extract;
+ umask(extract->umask = umask(0)); /* Read the current umask. */
restore_pwd = -1;
@@ -221,12 +223,10 @@ void archive_extract_cleanup(struct archive *a)
{
struct fixup_entry *next, *p;
struct extract *extract;
- mode_t mask;
/* Sort dir list so directories are fixed up in depth-first order. */
extract = a->extract;
p = sort_dir_list(extract->fixup_list);
- umask(mask = umask(0)); /* Read the current umask. */
while (p != NULL) {
if (p->fixup & FIXUP_TIMES) {
@@ -238,7 +238,7 @@ void archive_extract_cleanup(struct archive *a)
utimes(p->name, times);
}
if (p->fixup & FIXUP_MODE)
- chmod(p->name, p->mode & ~mask);
+ chmod(p->name, p->mode);
if (p->fixup & FIXUP_FFLAGS)
set_fflags(a, p->name, p->mode, p->fflags_set, 0);
@@ -453,7 +453,7 @@ mkdirpath_recursive(struct archive *a, char *path, const struct stat *st,
le->name = strdup(path);
if (mode != writable_mode) {
- le->mode = mode;
+ le->mode = mode & ~extract->umask;
le->fixup |= FIXUP_MODE;
mode = writable_mode;
}
@@ -466,6 +466,11 @@ mkdirpath_recursive(struct archive *a, char *path, const struct stat *st,
}
}
+ /*
+ * Try to make the longest dir first. Most archives are
+ * written in a reasonable order, so this will almost always
+ * save us from having to inspect the parent dirs.
+ */
if (mkdir(path, mode) == 0)
return (ARCHIVE_OK);
/*
@@ -509,8 +514,23 @@ mkdirpath_recursive(struct archive *a, char *path, const struct stat *st,
*p = '/'; /* Restore the '/' we just overwrote. */
if (r != ARCHIVE_OK)
return (r);
+ /* Parent exists now; let's create the last component. */
+ p++;
+ /* Of course, "", ".", and ".." are easy. */
+ if (p[0] == '\0')
+ return (ARCHIVE_OK);
+ if (p[0] == '.' && p[1] == '\0')
+ return (ARCHIVE_OK);
+ if (p[0] == '.' && p[1] == '.' && p[2] == '\0')
+ return (ARCHIVE_OK);
if (mkdir(path, mode) == 0)
return (ARCHIVE_OK);
+ /*
+ * Without the following check, a/b/../b/c/d fails at
+ * the second visit to 'b', so 'd' can't be created.
+ */
+ if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
+ return (ARCHIVE_OK);
}
archive_set_error(a, errno, "Failed to create dir '%s'", path);
return (ARCHIVE_WARN);
@@ -762,11 +782,14 @@ set_time(struct archive *a, struct archive_entry *entry, int flags)
static int
set_perm(struct archive *a, struct archive_entry *entry, int mode, int flags)
{
+ struct extract *extract;
const char *name;
if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
return (ARCHIVE_OK);
+ extract = a->extract;
+ mode &= ~extract->umask; /* Enforce umask. */
name = archive_entry_pathname(entry);
#ifdef HAVE_LCHMOD
if (lchmod(name, mode) != 0) {
OpenPOWER on IntegriCloud