]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add support for Windows file attributes readonly, hidden and system
authorMartin Matuska <martin@matuska.org>
Wed, 8 May 2019 22:06:46 +0000 (00:06 +0200)
committerMartin Matuska <martin@matuska.org>
Thu, 9 May 2019 20:15:25 +0000 (22:15 +0200)
libarchive/archive_entry.c
libarchive/archive_read_disk_windows.c
libarchive/archive_read_support_format_rar5.c
libarchive/archive_write_disk_windows.c

index d9d54b870e8fe5a4594bf7014c8f31238411f8c4..72c644e6079b06946a210e2afe033853a1c22d93 100644 (file)
@@ -1766,6 +1766,10 @@ static const struct flag {
        { "nohidden",   L"nohidden",            UF_HIDDEN,      0},
        { "nouhidden",  L"nouhidden",           UF_HIDDEN,      0},
 #endif
+#ifdef FILE_ATTRIBUTE_HIDDEN
+       { "nohidden",   L"nohidden",    FILE_ATTRIBUTE_HIDDEN,  0},
+       { "nouhidden",  L"nouhidden",   FILE_ATTRIBUTE_HIDDEN,  0},
+#endif
 #ifdef UF_OFFLINE
        { "nooffline",  L"nooffline",           UF_OFFLINE,     0},
        { "nouoffline", L"nouoffline",          UF_OFFLINE,     0},
@@ -1775,6 +1779,11 @@ static const struct flag {
        { "nourdonly",  L"nourdonly",           UF_READONLY,    0},
        { "noreadonly", L"noreadonly",          UF_READONLY,    0},
 #endif
+#ifdef FILE_ATTRIBUTE_READONLY
+       { "nordonly",   L"nordonly",    FILE_ATTRIBUTE_READONLY,        0},
+       { "nourdonly",  L"nourdonly",   FILE_ATTRIBUTE_READONLY,        0},
+       { "noreadonly", L"noreadonly",  FILE_ATTRIBUTE_READONLY,        0},
+#endif
 #ifdef UF_SPARSE
        { "nosparse",   L"nosparse",            UF_SPARSE,      0},
        { "nousparse",  L"nousparse",           UF_SPARSE,      0},
@@ -1787,6 +1796,10 @@ static const struct flag {
        { "nosystem",   L"nosystem",            UF_SYSTEM,      0},
        { "nousystem",  L"nousystem",           UF_SYSTEM,      0},
 #endif
+#ifdef FILE_ATTRIBUTE_SYSTEM
+       { "nosystem",   L"nosystem",    FILE_ATTRIBUTE_SYSTEM,  0},
+       { "nousystem",  L"nousystem",   FILE_ATTRIBUTE_SYSTEM,  0},
+#endif
 #if defined(FS_UNRM_FL)                /* 'u' */
        { "noundel",    L"noundel",             FS_UNRM_FL,     0},
 #elif defined(EXT2_UNRM_FL)
index ff79cc05521d1d60367f59e72021a9aa25cf0150..4a5421f8f30effb8ec5c77345ed247ff2d35bc68 100644 (file)
@@ -1046,6 +1046,19 @@ next_entry(struct archive_read_disk *a, struct tree *t,
                }
        }
 
+       /*
+        * File attributes
+        */
+       if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+               const int supported_attrs =
+                   FILE_ATTRIBUTE_READONLY |
+                   FILE_ATTRIBUTE_HIDDEN |
+                   FILE_ATTRIBUTE_SYSTEM;
+               DWORD file_attrs = st->dwFileAttributes & supported_attrs;
+               if (file_attrs != 0)
+                       archive_entry_set_fflags(entry, file_attrs, 0);
+       }
+
        /*
         * Invoke a meta data filter callback.
         */
@@ -2300,6 +2313,19 @@ archive_read_disk_entry_from_file(struct archive *_a,
        if (name != NULL)
                archive_entry_copy_gname(entry, name);
 
+       /*
+        * File attributes
+        */
+       if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+               const int supported_attrs =
+                   FILE_ATTRIBUTE_READONLY |
+                   FILE_ATTRIBUTE_HIDDEN |
+                   FILE_ATTRIBUTE_SYSTEM;
+               DWORD file_attrs = fileAttributes & supported_attrs;
+               if (file_attrs != 0)
+                       archive_entry_set_fflags(entry, file_attrs, 0);
+       }
+
        /*
         * Can this file be sparse file ?
         */
index 06b340f8d16e62021e8374eac95cf647692b9fa8..619f94a0a3977f712841cb25cedf2672a2430899 100644 (file)
@@ -1673,6 +1673,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
                /* Host OS is Windows */
 
                __LA_MODE_T mode;
+               unsigned long fflags = 0;
 
                if(file_attr & ATTR_DIRECTORY) {
                        mode = 0755 | AE_IFDIR;
@@ -1686,11 +1687,38 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
 
                archive_entry_set_mode(entry, mode);
 
-               /*
-                * TODO: implement attribute support (READONLY, HIDDEN, SYSTEM)
-                * This requires a platform-independent extended attribute
-                * handling
-                */
+#if defined(FILE_ATTRIBUTE_READONLY) || defined(UF_READONLY)
+               if (file_attr & ATTR_READONLY) {
+                       fflags |=
+#if defined(FILE_ATTRIBUTE_READONLY)
+                               FILE_ATTRIBUTE_READONLY;
+#else
+                               UF_READONLY;
+#endif
+               }
+#endif
+#if defined(FILE_ATTRIBUTE_HIDDEN) || defined(UF_HIDDEN)
+               if (file_attr & ATTR_HIDDEN) {
+                       fflags |=
+#if defined(FILE_ATTRIBUTE_HIDDEN)
+                               FILE_ATTRIBUTE_HIDDEN;
+#else
+                               UF_HIDDEN;      
+#endif
+               }
+#endif
+#if defined(FILE_ATTRIBUTE_SYSTEM) || defined(UF_SYSTEM)
+               if (file_attr & ATTR_SYSTEM) {
+                       fflags |=
+#if defined(FILE_ATTRIBUTE_SYSTEM)
+                               FILE_ATTRIBUTE_SYSTEM;
+#else
+                               UF_SYSTEM;
+#endif
+               }
+#endif
+               if (fflags != 0)
+                       archive_entry_set_fflags(entry, fflags, 0);
        } else if(host_os == HOST_UNIX) {
                /* Host OS is Unix */
                archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
index e3e65ba18a2b1eb87483b5f4c2657aae520a390a..bc10a619e8a145ea7fbd11cecf536b7da3edf684 100644 (file)
@@ -221,7 +221,10 @@ static int restore_entry(struct archive_write_disk *);
 static int     set_acls(struct archive_write_disk *, HANDLE h,
                    const wchar_t *, struct archive_acl *);
 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(const wchar_t *, unsigned long,
+                   unsigned long);
 static int     set_ownership(struct archive_write_disk *);
 static int     set_mode(struct archive_write_disk *, int mode);
 static int     set_times(struct archive_write_disk *, HANDLE, int,
@@ -964,9 +967,11 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
        }
 
        if (a->deferred & TODO_FFLAGS) {
+               unsigned long set, clear;
+
                fe = current_fixup(a, archive_entry_pathname_w(entry));
-               fe->fixup |= TODO_FFLAGS;
-               /* TODO: Complete this.. defer fflags from below. */
+               archive_entry_fflags(entry, &set, &clear);
+               fe->fflags_set = set;
        }
 
        /*
@@ -1387,6 +1392,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 (disk_unlink(a->name) == 0) {
                        /* We removed it, reset cached stat. */
                        a->pst = NULL;
@@ -1524,6 +1531,10 @@ restore_entry(struct archive_write_disk *a)
 
                if (!S_ISDIR(st_mode)) {
                        /* Edge case: a directory symlink pointing to a file */
+                       if (a->flags &
+                           ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+                               (void)clear_nochange_fflags(a);
+                       }
                        if (dirlnk) {
                                if (disk_rmdir(a->name) != 0) {
                                        archive_set_error(&a->archive, errno,
@@ -1541,6 +1552,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 (disk_rmdir(a->name) != 0) {
                                archive_set_error(&a->archive, errno,
                                    "Can't remove already-existing dir");
@@ -1798,10 +1811,15 @@ _archive_write_disk_close(struct archive *_a)
                            p->mtime, p->mtime_nanos,
                            p->ctime, p->ctime_nanos);
                }
-               if (p->fixup & TODO_MODE_BASE)
-                       la_chmod(p->name, p->mode);
+               if (p->fixup & TODO_MODE_BASE) {
+                       /* fflags have higher priority than mode */
+                       if ((p->fflags_set & FILE_ATTRIBUTE_READONLY) == 0)
+                               la_chmod(p->name, p->mode);
+               }
                if (p->fixup & TODO_ACLS)
                        set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl);
+               if (p->fixup & TODO_FFLAGS)
+                       set_fflags_platform(p->name, p->fflags_set, 0);
                next = p->next;
                archive_acl_clear(&p->acl);
                free(p->name);
@@ -1918,6 +1936,7 @@ new_fixup(struct archive_write_disk *a, const wchar_t *pathname)
        a->fixup_list = fe;
        fe->fixup = 0;
        fe->name = _wcsdup(pathname);
+       fe->fflags_set = 0;
        return (fe);
 }
 
@@ -1985,6 +2004,10 @@ check_symlinks(struct archive_write_disk *a)
                                 * Remove it so we can overwrite it with the
                                 * item being extracted.
                                 */
+                               if (a->flags &
+                                   ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+                                       (void)clear_nochange_fflags(a);
+                               }
                                if (st.dwFileAttributes &
                                    FILE_ATTRIBUTE_DIRECTORY) {
                                        r = disk_rmdir(a->name);
@@ -2015,6 +2038,10 @@ check_symlinks(struct archive_write_disk *a)
                                return (0);
                        } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
                                /* User asked us to remove problems. */
+                               if (a->flags &
+                                   ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+                                       (void)clear_nochange_fflags(a);
+                               }
                                if (st.dwFileAttributes &
                                    FILE_ATTRIBUTE_DIRECTORY) {
                                        r = disk_rmdir(a->name);
@@ -2589,10 +2616,56 @@ set_mode(struct archive_write_disk *a, int mode)
        return (r);
 }
 
+static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set,
+    unsigned long fflags_clear)
+{
+       DWORD oldflags, newflags;
+       wchar_t *fullname;
+
+       const DWORD settable_flags =
+           FILE_ATTRIBUTE_ARCHIVE |
+           FILE_ATTRIBUTE_HIDDEN |
+           FILE_ATTRIBUTE_NORMAL |
+           FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
+           FILE_ATTRIBUTE_OFFLINE |
+           FILE_ATTRIBUTE_READONLY |
+           FILE_ATTRIBUTE_SYSTEM |
+           FILE_ATTRIBUTE_TEMPORARY;
+
+       oldflags = GetFileAttributesW(name);
+       if (oldflags == (DWORD)-1 &&
+           GetLastError() == ERROR_INVALID_NAME) {
+               fullname = __la_win_permissive_name_w(name);
+               oldflags = GetFileAttributesW(fullname);
+       }
+       if (oldflags == (DWORD)-1) {
+               la_dosmaperr(GetLastError());
+               return (ARCHIVE_WARN);
+       }
+       newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags;
+       if(SetFileAttributesW(name, newflags) == 0)
+               return (ARCHIVE_WARN);
+       return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+       return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY));
+}
+
 static int
 set_fflags(struct archive_write_disk *a)
 {
-       (void)a; /* UNUSED */
+       unsigned long   set, clear;
+
+       if (a->todo & TODO_FFLAGS) {
+               archive_entry_fflags(a->entry, &set, &clear);
+               if (set == 0  && clear == 0)
+                       return (ARCHIVE_OK);
+               return (set_fflags_platform(a->name, set, clear));
+
+        }
        return (ARCHIVE_OK);
 }