]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
win32_apply.c: Set security descriptors correctly
[wimlib] / src / win32_apply.c
index 120ec210a6e4c94efdc4d0f2d55c02848c93e247..1ae84b9f70ceea432ee0b6dc5ac147d9878b9739 100644 (file)
@@ -27,8 +27,6 @@
 #  include "config.h"
 #endif
 
-#include <aclapi.h> /* for SetSecurityInfo() */
-
 #include "wimlib/win32_common.h"
 
 #include "wimlib/apply.h"
@@ -287,47 +285,19 @@ win32_set_security_data(const struct wim_inode *inode,
        unsigned long n;
        DWORD err;
        const struct wim_security_data *sd;
+       SECURITY_INFORMATION securityInformation;
 
-       SECURITY_INFORMATION securityInformation = 0;
-
-       void *owner = NULL;
-       void *group = NULL;
-       ACL *dacl = NULL;
-       ACL *sacl = NULL;
+       securityInformation = OWNER_SECURITY_INFORMATION |
+                             GROUP_SECURITY_INFORMATION |
+                             DACL_SECURITY_INFORMATION  |
+                             SACL_SECURITY_INFORMATION;
 
-       BOOL owner_defaulted;
-       BOOL group_defaulted;
-       BOOL dacl_present;
-       BOOL dacl_defaulted;
-       BOOL sacl_present;
-       BOOL sacl_defaulted;
-
-       sd = wim_const_security_data(args->w);
+       sd = wim_const_security_data(args->wim);
        descriptor = sd->descriptors[inode->i_security_id];
 
-       GetSecurityDescriptorOwner(descriptor, &owner, &owner_defaulted);
-       if (owner)
-               securityInformation |= OWNER_SECURITY_INFORMATION;
-
-       GetSecurityDescriptorGroup(descriptor, &group, &group_defaulted);
-       if (group)
-               securityInformation |= GROUP_SECURITY_INFORMATION;
-
-       GetSecurityDescriptorDacl(descriptor, &dacl_present,
-                                 &dacl, &dacl_defaulted);
-       if (dacl)
-               securityInformation |= DACL_SECURITY_INFORMATION;
-
-       GetSecurityDescriptorSacl(descriptor, &sacl_present,
-                                 &sacl, &sacl_defaulted);
-       if (sacl)
-               securityInformation |= SACL_SECURITY_INFORMATION;
-
 again:
-       if (securityInformation == 0)
-               return 0;
-       if (SetSecurityInfo(hFile, SE_FILE_OBJECT,
-                           securityInformation, owner, group, dacl, sacl))
+       /* We could use SetSecurityInfo() here if it wasn't horribly broken.  */
+       if (SetFileSecurity(path, securityInformation, descriptor))
                return 0;
        err = GetLastError();
        if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
@@ -337,7 +307,6 @@ again:
                if (securityInformation & SACL_SECURITY_INFORMATION) {
                        n = args->num_set_sacl_priv_notheld++;
                        securityInformation &= ~SACL_SECURITY_INFORMATION;
-                       sacl = NULL;
                        if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
                                WARNING(
 "We don't have enough privileges to set the full security\n"
@@ -479,23 +448,6 @@ do_win32_extract_encrypted_stream(const wchar_t *path,
        return ret;
 }
 
-static bool
-path_is_root_of_drive(const wchar_t *path)
-{
-       if (!*path)
-               return false;
-
-       if (*path != L'/' && *path != L'\\') {
-               if (*(path + 1) == L':')
-                       path += 2;
-               else
-                       return false;
-       }
-       while (*path == L'/' || *path == L'\\')
-               path++;
-       return (*path == L'\0');
-}
-
 static inline DWORD
 win32_mask_attributes(DWORD i_attributes)
 {
@@ -598,18 +550,19 @@ win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
        /* Directories must be created with CreateDirectoryW().  Then the call
         * to CreateFileW() will merely open the directory that was already
         * created rather than creating a new file. */
-       if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY &&
-           !path_is_root_of_drive(path)) {
-               if (!CreateDirectoryW(path, NULL)) {
-                       err = GetLastError();
-                       if (err != ERROR_ALREADY_EXISTS) {
-                               ERROR("Failed to create directory \"%ls\"",
-                                     path);
-                               win32_error(err);
-                               return WIMLIB_ERR_MKDIR;
+       if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+               if (!win32_path_is_root_of_drive(path)) {
+                       if (!CreateDirectoryW(path, NULL)) {
+                               err = GetLastError();
+                               if (err != ERROR_ALREADY_EXISTS) {
+                                       ERROR("Failed to create directory \"%ls\"",
+                                             path);
+                                       win32_error(err);
+                                       return WIMLIB_ERR_MKDIR;
+                               }
                        }
+                       DEBUG("Created directory \"%ls\"", path);
                }
-               DEBUG("Created directory \"%ls\"", path);
                *creationDisposition_ret = OPEN_EXISTING;
        }
        if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
@@ -651,7 +604,7 @@ win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
         * directory, so treat that as a special case and do not set attributes.
         * */
        if (*creationDisposition_ret == OPEN_EXISTING &&
-           !path_is_root_of_drive(path))
+           !win32_path_is_root_of_drive(path))
        {
                if (!SetFileAttributesW(path,
                                        win32_mask_attributes(inode->i_attributes)))
@@ -679,19 +632,6 @@ win32_finish_extract_stream(HANDLE h, const struct wim_dentry *dentry,
        if (stream_name_utf16 == NULL) {
                /* Unnamed stream. */
 
-               /* Set security descriptor, unless the extract_flags indicate
-                * not to or the volume does not supported it.  Note that this
-                * is only done when the unnamed stream is being extracted, as
-                * security descriptors are per-file and not per-stream. */
-               if (inode->i_security_id >= 0 &&
-                   !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
-                   && (args->vol_flags & FILE_PERSISTENT_ACLS))
-               {
-                       ret = win32_set_security_data(inode, h, stream_path, args);
-                       if (ret)
-                               return ret;
-               }
-
                /* Handle reparse points.  The data for them needs to be set
                 * using a special ioctl.  Note that the reparse point may have
                 * been created using CreateFileW() in the case of
@@ -730,10 +670,23 @@ win32_finish_extract_stream(HANDLE h, const struct wim_dentry *dentry,
                                return ret;
                }
 
-               if (dentry_has_short_name(dentry))
+               if (dentry_has_short_name(dentry) && !dentry->dos_name_invalid)
                        SetFileShortNameW(h, dentry->short_name);
                else if (running_on_windows_7_or_later())
                        SetFileShortNameW(h, L"");
+
+               /* Set security descriptor, unless the extract_flags indicate
+                * not to or the volume does not supported it.  Note that this
+                * is only done when the unnamed stream is being extracted, as
+                * security descriptors are per-file and not per-stream. */
+               if (inode->i_security_id >= 0 &&
+                   !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
+                   && (args->vol_flags & FILE_PERSISTENT_ACLS))
+               {
+                       ret = win32_set_security_data(inode, h, stream_path, args);
+                       if (ret)
+                               return ret;
+               }
        } else {
                /* Extract the data for a named data stream. */
                if (lte != NULL) {
@@ -809,12 +762,12 @@ win32_extract_stream(const struct wim_dentry *dentry,
 
        if (stream_name_utf16) {
                /* Named stream.  Create a buffer that contains the UTF-16LE
-                * string [./]path:stream_name_utf16.  This is needed to
+                * string [.\]path:stream_name_utf16.  This is needed to
                 * create and open the stream using CreateFileW().  I'm not
                 * aware of any other APIs to do this.  Note: the '$DATA' suffix
-                * seems to be unneeded.  Additional note: a "./" prefix needs
-                * to be added when the path is not absolute to avoid ambiguity
-                * with drive letters. */
+                * seems to be unneeded.  Additional note: a ".\" prefix needs
+                * to be added when the path is a 1-character long relative path
+                * to avoid ambiguity with drive letters. */
                size_t stream_path_nchars;
                size_t path_nchars;
                size_t stream_name_nchars;
@@ -823,12 +776,10 @@ win32_extract_stream(const struct wim_dentry *dentry,
                path_nchars = wcslen(path);
                stream_name_nchars = wcslen(stream_name_utf16);
                stream_path_nchars = path_nchars + 1 + stream_name_nchars;
-               if (path[0] != cpu_to_le16(L'\0') &&
-                   path[0] != cpu_to_le16(L'/') &&
-                   path[0] != cpu_to_le16(L'\\') &&
-                   path[1] != cpu_to_le16(L':'))
-               {
-                       prefix = L"./";
+               if (path_nchars == 1 && !is_any_path_separator(path[0])) {
+                       static const wchar_t _prefix[] =
+                               {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
+                       prefix = _prefix;
                        stream_path_nchars += 2;
                } else {
                        prefix = L"";
@@ -849,9 +800,33 @@ win32_extract_stream(const struct wim_dentry *dentry,
        }
 
        DEBUG("Opening \"%ls\"", stream_path);
-       /* DELETE access is needed for SetFileShortNameW(), for some reason. */
-       requestedAccess = GENERIC_READ | GENERIC_WRITE | DELETE |
-                         ACCESS_SYSTEM_SECURITY;
+       requestedAccess = GENERIC_READ | GENERIC_WRITE;
+       /* DELETE access is needed for SetFileShortNameW(), for some reason.
+        * But don't request it for the extraction root, for the following
+        * reasons:
+        *
+        * - Requesting DELETE access on the extraction root will cause a
+        *   sharing violation if the extraction root is the current working
+        *   directory (".").
+        * - The extraction root may be extracted to a different name than given
+        *   in the WIM file, in which case the DOS name, if given, would not be
+        *   meaningful.
+        * - For full-image extractions, the root dentry is supposed to be
+        *   unnamed anyway.
+        * - Microsoft's ImageX does not extract the root directory.
+        */
+       if (dentry != args->extract_root)
+               requestedAccess |= DELETE;
+
+       /* SetFileSecurity() opens its own handle so doesn't need the following
+        * access rights.  */
+#if 0
+       /* If file has a security descriptor, set extra permissions needed for
+        * SetSecurityInfo() not implied by GENERIC_WRITE.  */
+       if (inode->i_security_id != -1)
+               requestedAccess |= WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY;
+#endif
+
 try_open_again:
        /* Open the stream to be extracted.  Depending on what we have set
         * creationDisposition to, we may be creating this for the first time,
@@ -867,7 +842,7 @@ try_open_again:
        if (h == INVALID_HANDLE_VALUE) {
                err = GetLastError();
                if (err == ERROR_ACCESS_DENIED &&
-                   path_is_root_of_drive(stream_path))
+                   win32_path_is_root_of_drive(stream_path))
                {
                        ret = 0;
                        goto out;
@@ -881,7 +856,11 @@ try_open_again:
                        requestedAccess &= ~ACCESS_SYSTEM_SECURITY;
                        goto try_open_again;
                }
-               if (err == ERROR_SHARING_VIOLATION) {
+               if (err == ERROR_SHARING_VIOLATION &&
+                   (inode->i_attributes & (FILE_ATTRIBUTE_ENCRYPTED |
+                                           FILE_ATTRIBUTE_DIRECTORY)) ==
+                       (FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_DIRECTORY))
+               {
                        if (remaining_sharing_violations) {
                                --remaining_sharing_violations;
                                /* This can happen when restoring encrypted directories
@@ -893,13 +872,12 @@ try_open_again:
                        } else {
                                ERROR("Too many sharing violations; giving up...");
                        }
-               } else {
-                       if (creationDisposition == OPEN_EXISTING)
-                               ERROR("Failed to open \"%ls\"", stream_path);
-                       else
-                               ERROR("Failed to create \"%ls\"", stream_path);
-                       win32_error(err);
                }
+               if (creationDisposition == OPEN_EXISTING)
+                       ERROR("Failed to open \"%ls\"", stream_path);
+               else
+                       ERROR("Failed to create \"%ls\"", stream_path);
+               win32_error(err);
                ret = WIMLIB_ERR_OPEN;
                goto fail;
        }
@@ -1235,6 +1213,7 @@ win32_do_apply_dentry(const wchar_t *output_path,
            !(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
        {
                WARNING("Not extracting reparse point \"%ls\"", output_path);
+               dentry->not_extracted = 1;
        } else {
                /* Create the file, directory, or reparse point, and extract the
                 * data streams. */
@@ -1258,7 +1237,7 @@ win32_do_apply_dentry(const wchar_t *output_path,
                        /* Save extracted path for a later call to
                         * CreateHardLinkW() if this inode has multiple links.
                         * */
-                       inode->i_extracted_file = WSTRDUP(output_path);
+                       inode->i_extracted_file = WCSDUP(output_path);
                        if (!inode->i_extracted_file)
                                return WIMLIB_ERR_NOMEM;
                }
@@ -1277,17 +1256,10 @@ win32_do_apply_dentry_timestamps(const wchar_t *path,
        HANDLE h;
        const struct wim_inode *inode = dentry->d_inode;
 
-       if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
-           !(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
-       {
-               /* Skip reparse points not extracted */
-               return 0;
-       }
-
        /* Windows doesn't let you change the timestamps of the root directory
         * (at least on FAT, which is dumb but expected since FAT doesn't store
         * any metadata about the root directory...) */
-       if (path_is_root_of_drive(path))
+       if (win32_path_is_root_of_drive(path))
                return 0;
 
        DEBUG("Opening \"%ls\" to set timestamps", path);