From: Brooks Davis Date: Tue, 10 Feb 2015 17:39:51 +0000 (+0000) Subject: Add a new archive_write_disk option:ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS X-Git-Tag: v3.1.900a~76^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6a595ef611d0365ae741d8a2758462213babb851;p=thirdparty%2Flibarchive.git Add a new archive_write_disk option:ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS 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 --- diff --git a/libarchive/archive.h b/libarchive/archive.h index f0b1c2480..55c84a508 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -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); diff --git a/libarchive/archive_write_disk.3 b/libarchive/archive_write_disk.3 index a2e7afaa0..06f83cb2e 100644 --- a/libarchive/archive_write_disk.3 +++ b/libarchive/archive_write_disk.3 @@ -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 , diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index e14d6942e..102bab956 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -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) /*