]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add a new archive_write_disk option:ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS
authorBrooks Davis <brooks@one-eyed-alien.net>
Tue, 10 Feb 2015 17:39:51 +0000 (17:39 +0000)
committerBrooks Davis <brooks@one-eyed-alien.net>
Thu, 14 May 2015 18:53:06 +0000 (18:53 +0000)
that clears platform-specific file flags that might prevent the removal
of a file system object before attempting to remove it.

Sponsored by: DARPA, AFRL

libarchive/archive.h
libarchive/archive_write_disk.3
libarchive/archive_write_disk_posix.c

index f0b1c24806128fcb3ffabb76ecdc5afb7a6c791a..55c84a50820c197094473a111b923149445fa732 100644 (file)
@@ -651,6 +651,8 @@ __LA_DECL int archive_read_set_passphrase_callback(struct archive *,
 #define        ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED  (0x8000)
 /* Default: Do not reject entries with absolute paths */
 #define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
+/* Default: Do not clear no-change flags when unlinking object */
+#define        ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS   (0x20000)
 
 __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
                     int flags);
index a2e7afaa06d8909ab28ebda6f4058b61c707e43c..06f83cb2e05522ce94634a1d26c345d4bbd2258c 100644 (file)
@@ -184,6 +184,9 @@ The default is to not refuse such paths.
 Scan data for blocks of NUL bytes and try to recreate them with holes.
 This results in sparse files, independent of whether the archive format
 supports or uses them.
+.It Cm ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS
+Before removing a file system object prior to replacing it, clear
+platform-specific file flags which might prevent its removal.
 .El
 .It Xo
 .Fn archive_write_disk_set_group_lookup ,
index e14d6942eaec454a7f8b47c1e4826e6a4e4965db..102bab9568ab3dc2044679c9abfa4fdaf4bf2525 100644 (file)
@@ -343,6 +343,7 @@ static int  restore_entry(struct archive_write_disk *);
 static int     set_mac_metadata(struct archive_write_disk *, const char *,
                                 const void *, size_t);
 static int     set_xattrs(struct archive_write_disk *);
+static int     clear_nochange_fflags(struct archive_write_disk *);
 static int     set_fflags(struct archive_write_disk *);
 static int     set_fflags_platform(struct archive_write_disk *, int fd,
                    const char *name, mode_t mode,
@@ -1846,6 +1847,8 @@ restore_entry(struct archive_write_disk *a)
                 * object is a dir, but that doesn't mean the old
                 * object isn't a dir.
                 */
+               if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+                       (void)clear_nochange_fflags(a);
                if (unlink(a->name) == 0) {
                        /* We removed it, reset cached stat. */
                        a->pst = NULL;
@@ -1944,6 +1947,8 @@ restore_entry(struct archive_write_disk *a)
 
                if (!S_ISDIR(a->st.st_mode)) {
                        /* A non-dir is in the way, unlink it. */
+                       if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+                               (void)clear_nochange_fflags(a);
                        if (unlink(a->name) != 0) {
                                archive_set_error(&a->archive, errno,
                                    "Can't unlink already-existing object");
@@ -1954,6 +1959,8 @@ restore_entry(struct archive_write_disk *a)
                        en = create_filesystem_object(a);
                } else if (!S_ISDIR(a->mode)) {
                        /* A dir is in the way of a non-dir, rmdir it. */
+                       if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+                               (void)clear_nochange_fflags(a);
                        if (rmdir(a->name) != 0) {
                                archive_set_error(&a->archive, errno,
                                    "Can't replace existing directory with non-directory");
@@ -3185,6 +3192,36 @@ set_fflags(struct archive_write_disk *a)
        return (ARCHIVE_OK);
 }
 
+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;
+#ifdef SF_IMMUTABLE
+       nochange_flags |= SF_IMMUTABLE;
+#endif
+#ifdef UF_IMMUTABLE
+       nochange_flags |= UF_IMMUTABLE;
+#endif
+#ifdef SF_APPEND
+       nochange_flags |= SF_APPEND;
+#endif
+#ifdef UF_APPEND
+       nochange_flags |= UF_APPEND;
+#endif
+#ifdef EXT2_APPEND_FL
+       nochange_flags |= EXT2_APPEND_FL;
+#endif
+#ifdef EXT2_IMMUTABLE_FL
+       nochange_flags |= EXT2_IMMUTABLE_FL;
+#endif
+
+       return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags));
+}
+
 
 #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS)
 /*