aboutsummaryrefslogtreecommitdiffstats
path: root/libarchive/archive_write_disk_posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/archive_write_disk_posix.c')
-rw-r--r--libarchive/archive_write_disk_posix.c126
1 files changed, 74 insertions, 52 deletions
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
index 3ed281df23f3..ed15451762bf 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -2588,8 +2588,11 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
*/
restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
__archive_ensure_cloexec_flag(restore_pwd);
- if (restore_pwd < 0)
+ if (restore_pwd < 0) {
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not open ", path);
return (ARCHIVE_FATAL);
+ }
head = path;
tail = path;
last = 0;
@@ -3128,12 +3131,14 @@ create_dir(struct archive_write_disk *a, char *path)
static int
set_ownership(struct archive_write_disk *a)
{
-#ifndef __CYGWIN__
-/* unfortunately, on win32 there is no 'root' user with uid 0,
- so we just have to try the chown and see if it works */
-
- /* If we know we can't change it, don't bother trying. */
- if (a->user_uid != 0 && a->user_uid != a->uid) {
+#if !defined(__CYGWIN__) && !defined(__linux__)
+/*
+ * On Linux, a process may have the CAP_CHOWN capability.
+ * On Windows there is no 'root' user with uid 0.
+ * Elsewhere we can skip calling chown if we are not root and the desired
+ * user id does not match the current user.
+ */
+ if (a->user_uid != 0 && a->user_uid != a->uid) {
archive_set_error(&a->archive, errno,
"Can't set UID=%jd", (intmax_t)a->uid);
return (ARCHIVE_WARN);
@@ -3500,9 +3505,7 @@ set_fflags(struct archive_write_disk *a)
struct fixup_entry *le;
unsigned long set, clear;
int r;
- int critical_flags;
mode_t mode = archive_entry_mode(a->entry);
-
/*
* Make 'critical_flags' hold all file flags that can't be
* immediately restored. For example, on BSD systems,
@@ -3518,33 +3521,33 @@ set_fflags(struct archive_write_disk *a)
* other programs that might try to muck with files as they're
* being restored.
*/
- /* Hopefully, the compiler will optimize this mess into a constant. */
- critical_flags = 0;
+ const int critical_flags = 0
#ifdef SF_IMMUTABLE
- critical_flags |= SF_IMMUTABLE;
+ | SF_IMMUTABLE
#endif
#ifdef UF_IMMUTABLE
- critical_flags |= UF_IMMUTABLE;
+ | UF_IMMUTABLE
#endif
#ifdef SF_APPEND
- critical_flags |= SF_APPEND;
+ | SF_APPEND
#endif
#ifdef UF_APPEND
- critical_flags |= UF_APPEND;
+ | UF_APPEND
#endif
#if defined(FS_APPEND_FL)
- critical_flags |= FS_APPEND_FL;
+ | FS_APPEND_FL
#elif defined(EXT2_APPEND_FL)
- critical_flags |= EXT2_APPEND_FL;
+ | EXT2_APPEND_FL
#endif
#if defined(FS_IMMUTABLE_FL)
- critical_flags |= FS_IMMUTABLE_FL;
+ | FS_IMMUTABLE_FL
#elif defined(EXT2_IMMUTABLE_FL)
- critical_flags |= EXT2_IMMUTABLE_FL;
+ | EXT2_IMMUTABLE_FL
#endif
#ifdef FS_JOURNAL_DATA_FL
- critical_flags |= FS_JOURNAL_DATA_FL;
+ | FS_JOURNAL_DATA_FL
#endif
+ ;
if (a->todo & TODO_FFLAGS) {
archive_entry_fflags(a->entry, &set, &clear);
@@ -3575,29 +3578,27 @@ set_fflags(struct archive_write_disk *a)
static int
clear_nochange_fflags(struct archive_write_disk *a)
{
- int nochange_flags;
mode_t mode = archive_entry_mode(a->entry);
-
- /* Hopefully, the compiler will optimize this mess into a constant. */
- nochange_flags = 0;
+ const int nochange_flags = 0
#ifdef SF_IMMUTABLE
- nochange_flags |= SF_IMMUTABLE;
+ | SF_IMMUTABLE
#endif
#ifdef UF_IMMUTABLE
- nochange_flags |= UF_IMMUTABLE;
+ | UF_IMMUTABLE
#endif
#ifdef SF_APPEND
- nochange_flags |= SF_APPEND;
+ | SF_APPEND
#endif
#ifdef UF_APPEND
- nochange_flags |= UF_APPEND;
+ | UF_APPEND
#endif
#ifdef EXT2_APPEND_FL
- nochange_flags |= EXT2_APPEND_FL;
+ | EXT2_APPEND_FL
#endif
#ifdef EXT2_IMMUTABLE_FL
- nochange_flags |= EXT2_IMMUTABLE_FL;
+ | EXT2_IMMUTABLE_FL
#endif
+ ;
return (set_fflags_platform(a, a->fd, a->name, mode, 0,
nochange_flags));
@@ -3613,8 +3614,22 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
mode_t mode, unsigned long set, unsigned long clear)
{
int r;
-
+ const int sf_mask = 0
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef SF_ARCHIVED
+ | SF_ARCHIVED
+#endif
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef SF_NOUNLINK
+ | SF_NOUNLINK
+#endif
+ ;
(void)mode; /* UNUSED */
+
if (set == 0 && clear == 0)
return (ARCHIVE_OK);
@@ -3629,6 +3644,12 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
a->st.st_flags &= ~clear;
a->st.st_flags |= set;
+
+ /* Only super-user may change SF_* flags */
+
+ if (a->user_uid != 0)
+ a->st.st_flags &= ~sf_mask;
+
#ifdef HAVE_FCHFLAGS
/* If platform has fchflags() and we were given an fd, use it. */
if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0)
@@ -3670,22 +3691,6 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
int ret;
int myfd = fd;
int newflags, oldflags;
- int sf_mask = 0;
-
- if (set == 0 && clear == 0)
- return (ARCHIVE_OK);
- /* Only regular files and dirs can have flags. */
- if (!S_ISREG(mode) && !S_ISDIR(mode))
- return (ARCHIVE_OK);
-
- /* If we weren't given an fd, open it ourselves. */
- if (myfd < 0) {
- myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);
- __archive_ensure_cloexec_flag(myfd);
- }
- if (myfd < 0)
- return (ARCHIVE_OK);
-
/*
* Linux has no define for the flags that are only settable by
* the root user. This code may seem a little complex, but
@@ -3693,19 +3698,36 @@ 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.
*/
+ const int sf_mask = 0
#if defined(FS_IMMUTABLE_FL)
- sf_mask |= FS_IMMUTABLE_FL;
+ | FS_IMMUTABLE_FL
#elif defined(EXT2_IMMUTABLE_FL)
- sf_mask |= EXT2_IMMUTABLE_FL;
+ | EXT2_IMMUTABLE_FL
#endif
#if defined(FS_APPEND_FL)
- sf_mask |= FS_APPEND_FL;
+ | FS_APPEND_FL
#elif defined(EXT2_APPEND_FL)
- sf_mask |= EXT2_APPEND_FL;
+ | EXT2_APPEND_FL
#endif
#if defined(FS_JOURNAL_DATA_FL)
- sf_mask |= FS_JOURNAL_DATA_FL;
+ | FS_JOURNAL_DATA_FL
#endif
+ ;
+
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+ /* Only regular files and dirs can have flags. */
+ if (!S_ISREG(mode) && !S_ISDIR(mode))
+ return (ARCHIVE_OK);
+
+ /* If we weren't given an fd, open it ourselves. */
+ if (myfd < 0) {
+ myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(myfd);
+ }
+ if (myfd < 0)
+ return (ARCHIVE_OK);
+
/*
* XXX As above, this would be way simpler if we didn't have
* to read the current flags from disk. XXX