]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
archive_write_disk_posix: fix writing fflags broken in 8a1bd5c
authorMartin Matuska <martin@matuska.org>
Wed, 17 Nov 2021 20:06:00 +0000 (21:06 +0100)
committerMartin Matuska <martin@matuska.org>
Wed, 17 Nov 2021 20:20:09 +0000 (21:20 +0100)
The fixup list was erroneously assumed to be directories only.
Only in the case of critical file flags modification (e.g. SF_IMMUTABLE
on BSD systems), other file types (e.g. regular files or symbolic links)
may be added to the fixup list. We still need to verify that we are writing
to the correct file type, so compare the archive entry file type with
the file type of the file to be modified.

Fixes #1617

libarchive/archive_write_disk_posix.c

index aadc58718f9b69c7d47de74e667076ab8dc83c94..7e57aac2e922cd8e10707752510ada3739021fc6 100644 (file)
@@ -173,6 +173,7 @@ struct fixup_entry {
        struct fixup_entry      *next;
        struct archive_acl       acl;
        mode_t                   mode;
+       __LA_MODE_T              filetype;
        int64_t                  atime;
        int64_t                  birthtime;
        int64_t                  mtime;
@@ -357,6 +358,7 @@ struct archive_write_disk {
 
 static int     la_opendirat(int, const char *);
 static int     la_mktemp(struct archive_write_disk *);
+static int     la_verify_filetype(mode_t, __LA_MODE_T);
 static void    fsobj_error(int *, struct archive_string *, int, const char *,
                    const char *);
 static int     check_symlinks_fsobj(char *, int *, struct archive_string *,
@@ -464,6 +466,39 @@ la_opendirat(int fd, const char *path) {
 #endif
 }
 
+static int
+la_verify_filetype(mode_t mode, __LA_MODE_T filetype) {
+       int ret = 0;
+
+       switch (filetype) {
+       case AE_IFREG:
+               ret = (S_ISREG(mode));
+               break;
+       case AE_IFDIR:
+               ret = (S_ISDIR(mode));
+               break;
+       case AE_IFLNK:
+               ret = (S_ISLNK(mode));
+               break;
+       case AE_IFSOCK:
+               ret = (S_ISSOCK(mode));
+               break;
+       case AE_IFCHR:
+               ret = (S_ISCHR(mode));
+               break;
+       case AE_IFBLK:
+               ret = (S_ISBLK(mode));
+               break;
+       case AE_IFIFO:
+               ret = (S_ISFIFO(mode));
+               break;
+       default:
+               break;
+       }
+
+       return (ret);
+}
+
 static int
 lazy_stat(struct archive_write_disk *a)
 {
@@ -822,6 +857,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
                fe = current_fixup(a, archive_entry_pathname(entry));
                if (fe == NULL)
                        return (ARCHIVE_FATAL);
+               fe->filetype = archive_entry_filetype(entry);
                fe->fixup |= TODO_MODE_BASE;
                fe->mode = a->mode;
        }
@@ -832,6 +868,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
                fe = current_fixup(a, archive_entry_pathname(entry));
                if (fe == NULL)
                        return (ARCHIVE_FATAL);
+               fe->filetype = archive_entry_filetype(entry);
                fe->mode = a->mode;
                fe->fixup |= TODO_TIMES;
                if (archive_entry_atime_is_set(entry)) {
@@ -865,6 +902,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
                fe = current_fixup(a, archive_entry_pathname(entry));
                if (fe == NULL)
                        return (ARCHIVE_FATAL);
+               fe->filetype = archive_entry_filetype(entry);
                fe->fixup |= TODO_ACLS;
                archive_acl_copy(&fe->acl, archive_entry_acl(entry));
        }
@@ -877,6 +915,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
                        fe = current_fixup(a, archive_entry_pathname(entry));
                        if (fe == NULL)
                                return (ARCHIVE_FATAL);
+                       fe->filetype = archive_entry_filetype(entry);
                        fe->mac_metadata = malloc(metadata_size);
                        if (fe->mac_metadata != NULL) {
                                memcpy(fe->mac_metadata, metadata,
@@ -891,6 +930,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
                fe = current_fixup(a, archive_entry_pathname(entry));
                if (fe == NULL)
                        return (ARCHIVE_FATAL);
+               fe->filetype = archive_entry_filetype(entry);
                fe->fixup |= TODO_FFLAGS;
                /* TODO: Complete this.. defer fflags from below. */
        }
@@ -2463,7 +2503,7 @@ _archive_write_disk_close(struct archive *_a)
        struct fixup_entry *next, *p;
        struct stat st;
        char *c;
-       int fd, ret;
+       int fd, ret, openflags;
 
        archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
            ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
@@ -2490,32 +2530,53 @@ _archive_write_disk_close(struct archive *_a)
                if (p->fixup == 0)
                        goto skip_fixup_entry;
                else {
-                       fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY
+                       /*
+                        * We need to verify if the type of the file
+                        * we are going to open matches the file type
+                        * of the fixup entry.
+                        */
+                       openflags = O_BINARY | O_NOFOLLOW | O_RDONLY
+                           | O_CLOEXEC;
 #if defined(O_DIRECTORY)
-                           | O_DIRECTORY
+                       if (p->filetype == AE_IFDIR)
+                               openflags |= O_DIRECTORY;
 #endif
-                           | O_CLOEXEC);
+                       fd = open(p->name, openflags);
+
+#if defined(O_DIRECTORY)
                        /*
-                `       * If we don't support O_DIRECTORY,
-                        * or open() has failed, we must stat()
-                        * to verify that we are opening a directory
+                        * If we support O_DIRECTORY and open was
+                        * successful we can skip the file type check
+                        * for directories. For other file types
+                        * we need to verify via fstat() or lstat()
                         */
-#if defined(O_DIRECTORY)
-                       if (fd == -1) {
+                       if (fd == -1 || p->filetype != AE_IFDIR) {
+#if HAVE_FSTAT
+                               if (fd > 0 && (
+                                   fstat(fd, &st) != 0 ||
+                                   la_verify_filetype(st.st_mode,
+                                   p->filetype) == 0)) {
+                                       goto skip_fixup_entry;
+                               } else
+#endif
                                if (lstat(p->name, &st) != 0 ||
-                                   !S_ISDIR(st.st_mode)) {
+                                   la_verify_filetype(st.st_mode,
+                                   p->filetype) == 0) {
                                        goto skip_fixup_entry;
                                }
                        }
 #else
 #if HAVE_FSTAT
                        if (fd > 0 && (
-                           fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) {
+                           fstat(fd, &st) != 0 ||
+                           la_verify_filetype(st.st_mode,
+                           p->filetype) == 0)) {
                                goto skip_fixup_entry;
                        } else
 #endif
                        if (lstat(p->name, &st) != 0 ||
-                           !S_ISDIR(st.st_mode)) {
+                           la_verify_filetype(st.st_mode,
+                           p->filetype) == 0) {
                                goto skip_fixup_entry;
                        }
 #endif
@@ -2689,6 +2750,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
        fe->next = a->fixup_list;
        a->fixup_list = fe;
        fe->fixup = 0;
+       fe->filetype = 0;
        fe->name = strdup(pathname);
        return (fe);
 }
@@ -3811,6 +3873,7 @@ set_fflags(struct archive_write_disk *a)
                        le = current_fixup(a, a->name);
                        if (le == NULL)
                                return (ARCHIVE_FATAL);
+                       le->filetype = archive_entry_filetype(a->entry);
                        le->fixup |= TODO_FFLAGS;
                        le->fflags_set = set;
                        /* Store the mode if it's not already there. */