]> wimlib.net Git - wimlib/blobdiff - src/win32.c
Win32 apply: Set short names
[wimlib] / src / win32.c
index aec728fe17e4f319b6ee50daf945a5f137f588d8..b6e28e75ad4211d1a7030ef6060aa46b479a8035 100644 (file)
@@ -678,6 +678,8 @@ win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
        if (ret < 0)
                return RP_NOT_FIXED;
        stripped_chars = ret;
+       if (stripped_chars)
+               stripped_chars -= 2;
        target[target_nchars] = L'\0';
        orig_target = target;
        target = capture_fixup_absolute_symlink(target + stripped_chars,
@@ -688,7 +690,7 @@ win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
        wmemmove(orig_target + stripped_chars, target, target_nchars + 1);
        *target_nbytes_p = (target_nchars + stripped_chars) * sizeof(wchar_t);
        DEBUG("Fixed reparse point (new target: \"%ls\")", orig_target);
-       if (stripped_chars == 6)
+       if (stripped_chars)
                return RP_FIXED_FULLPATH;
        else
                return RP_FIXED_ABSPATH;
@@ -1314,6 +1316,14 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        if (path_nchars > 32767)
                return WIMLIB_ERR_INVALID_PARAM;
 
+       if (GetFileAttributesW(root_disk_path) == INVALID_FILE_ATTRIBUTES &&
+           GetLastError() == ERROR_FILE_NOT_FOUND)
+       {
+               ERROR("Capture directory \"%ls\" does not exist!",
+                     root_disk_path);
+               return WIMLIB_ERR_OPENDIR;
+       }
+
        ret = win32_get_file_and_vol_ids(root_disk_path,
                                         &params->capture_root_ino,
                                         &params->capture_root_dev);
@@ -1399,7 +1409,7 @@ win32_extract_try_rpfix(u8 *rpbuf,
 
        /* Print name excludes the \??\ if present. */
        new_print_name = p;
-       if (target_nchars - stripped_target_nchars != 0) {
+       if (stripped_nchars != 0) {
                /* Get drive letter from real path to extract root, if a drive
                 * letter was present before. */
                *p++ = extract_root_realpath[0];
@@ -1685,13 +1695,8 @@ do_win32_extract_stream(HANDLE hStream, const struct wim_lookup_table_entry *lte
 }
 
 struct win32_encrypted_extract_ctx {
-       void *file_ctx;
-       int wimlib_err_code;
-       bool done;
-       pthread_cond_t cond;
-       pthread_mutex_t mutex;
-       u8 buf[WIM_CHUNK_SIZE];
-       size_t buf_filled;
+       const struct wim_lookup_table_entry *lte;
+       u64 offset;
 };
 
 static DWORD WINAPI
@@ -1700,78 +1705,16 @@ win32_encrypted_import_cb(unsigned char *data, void *_ctx,
 {
        struct win32_encrypted_extract_ctx *ctx = _ctx;
        unsigned long len = *len_p;
+       const struct wim_lookup_table_entry *lte = ctx->lte;
 
-       pthread_mutex_lock(&ctx->mutex);
-       while (len) {
-               size_t bytes_to_copy;
+       len = min(len, wim_resource_size(lte) - ctx->offset);
 
-               DEBUG("Importing up to %lu more bytes of raw encrypted data", len);
-               while (ctx->buf_filled == 0) {
-                       if (ctx->done)
-                               goto out;
-                       pthread_cond_wait(&ctx->cond, &ctx->mutex);
-               }
-               bytes_to_copy = min(len, ctx->buf_filled);
-               memcpy(data, ctx->buf, bytes_to_copy);
-               len -= bytes_to_copy;
-               data += bytes_to_copy;
-               ctx->buf_filled -= bytes_to_copy;
-               memmove(ctx->buf, ctx->buf + bytes_to_copy, ctx->buf_filled);
-               pthread_cond_signal(&ctx->cond);
-       }
-out:
-       *len_p -= len;
-       pthread_mutex_unlock(&ctx->mutex);
-       return ERROR_SUCCESS;
-}
+       if (read_partial_wim_resource_into_buf(lte, len, ctx->offset, data, false))
+               return ERROR_READ_FAULT;
 
-/* Extract ("Import") an encrypted file in a different thread. */
-static void *
-win32_encrypted_import_proc(void *arg)
-{
-       struct win32_encrypted_extract_ctx *ctx = arg;
-       DWORD ret;
-       ret = WriteEncryptedFileRaw(win32_encrypted_import_cb, ctx,
-                                   ctx->file_ctx);
-       pthread_mutex_lock(&ctx->mutex);
-       if (ret == ERROR_SUCCESS) {
-               ctx->wimlib_err_code = 0;
-       } else {
-               win32_error(ret);
-               ctx->wimlib_err_code = WIMLIB_ERR_WRITE;
-       }
-       ctx->done = true;
-       pthread_cond_signal(&ctx->cond);
-       pthread_mutex_unlock(&ctx->mutex);
-       return NULL;
-}
-
-
-static int
-win32_extract_raw_encrypted_chunk(const void *buf, size_t len, void *arg)
-{
-       struct win32_encrypted_extract_ctx *ctx = arg;
-       size_t bytes_to_copy;
-
-       while (len) {
-               DEBUG("Extracting up to %zu more bytes of encrypted data", len);
-               pthread_mutex_lock(&ctx->mutex);
-               while (ctx->buf_filled == WIM_CHUNK_SIZE) {
-                       if (ctx->done) {
-                               pthread_mutex_unlock(&ctx->mutex);
-                               return ctx->wimlib_err_code;
-                       }
-                       pthread_cond_wait(&ctx->cond, &ctx->mutex);
-               }
-               bytes_to_copy = min(len, WIM_CHUNK_SIZE - ctx->buf_filled);
-               memcpy(&ctx->buf[ctx->buf_filled], buf, bytes_to_copy);
-               len -= bytes_to_copy;
-               buf += bytes_to_copy;
-               ctx->buf_filled += bytes_to_copy;
-               pthread_cond_signal(&ctx->cond);
-               pthread_mutex_unlock(&ctx->mutex);
-       }
-       return 0;
+       ctx->offset += len;
+       *len_p = len;
+       return ERROR_SUCCESS;
 }
 
 /* Create an encrypted file and extract the raw encrypted data to it.
@@ -1790,11 +1733,8 @@ static int
 do_win32_extract_encrypted_stream(const wchar_t *path,
                                  const struct wim_lookup_table_entry *lte)
 {
-       struct win32_encrypted_extract_ctx ctx;
        void *file_ctx;
-       pthread_t import_thread;
        int ret;
-       int ret2;
 
        DEBUG("Opening file \"%ls\" to extract raw encrypted data", path);
 
@@ -1805,61 +1745,20 @@ do_win32_extract_encrypted_stream(const wchar_t *path,
                return WIMLIB_ERR_OPEN;
        }
 
-       if (!lte)
-               goto out_close;
-
-       /* Hack alert:  WriteEncryptedFileRaw() requires the callback function
-        * to work with a buffer whose size we cannot control.  This doesn't
-        * play well with our read_resource_prefix() function, which itself uses
-        * a callback function to extract WIM_CHUNK_SIZE chunks of data.  We
-        * work around this problem by calling WriteEncryptedFileRaw() in a
-        * different thread and feeding it the data as needed.  */
-       ctx.file_ctx = file_ctx;
-       ctx.buf_filled = 0;
-       ctx.done = false;
-       ctx.wimlib_err_code = 0;
-       if (pthread_mutex_init(&ctx.mutex, NULL)) {
-               ERROR_WITH_ERRNO("Can't create mutex");
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_close;
-       }
-       if (pthread_cond_init(&ctx.cond, NULL)) {
-               ERROR_WITH_ERRNO("Can't create condition variable");
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_pthread_mutex_destroy;
-       }
-       ret = pthread_create(&import_thread, NULL,
-                            win32_encrypted_import_proc, &ctx);
-       if (ret) {
-               errno = ret;
-               ERROR_WITH_ERRNO("Failed to create thread");
-               ret = WIMLIB_ERR_FORK;
-               goto out_pthread_cond_destroy;
-       }
-
-       ret = extract_wim_resource(lte, wim_resource_size(lte),
-                                  win32_extract_raw_encrypted_chunk, &ctx);
-       pthread_mutex_lock(&ctx.mutex);
-       ctx.done = true;
-       pthread_cond_signal(&ctx.cond);
-       pthread_mutex_unlock(&ctx.mutex);
-       ret2 = pthread_join(import_thread, NULL);
-       if (ret2) {
-               errno = ret2;
-               ERROR_WITH_ERRNO("Failed to join encrypted import thread");
-               if (ret == 0)
+       if (lte) {
+               struct win32_encrypted_extract_ctx ctx;
+
+               ctx.lte = lte;
+               ctx.offset = 0;
+               ret = WriteEncryptedFileRaw(win32_encrypted_import_cb, &ctx, file_ctx);
+               if (ret == ERROR_SUCCESS) {
+                       ret = 0;
+               } else {
                        ret = WIMLIB_ERR_WRITE;
+                       ERROR("Failed to extract encrypted file \"%ls\"", path);
+               }
        }
-       if (ret == 0)
-               ret = ctx.wimlib_err_code;
-out_pthread_cond_destroy:
-       pthread_cond_destroy(&ctx.cond);
-out_pthread_mutex_destroy:
-       pthread_mutex_destroy(&ctx.mutex);
-out_close:
        CloseEncryptedFileRaw(file_ctx);
-       if (ret)
-               ERROR("Failed to extract encrypted file \"%ls\"", path);
        return ret;
 }
 
@@ -2000,12 +1899,23 @@ win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
            vol_flags & FILE_SUPPORTS_ENCRYPTION)
        {
                if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
-                       if (!EncryptFile(path)) {
-                               err = GetLastError();
-                               ERROR("Failed to encrypt directory \"%ls\"",
-                                     path);
-                               win32_error(err);
-                               return WIMLIB_ERR_WRITE;
+                       unsigned remaining_sharing_violations = 100;
+                       while (!EncryptFile(path)) {
+                               if (remaining_sharing_violations &&
+                                   err == ERROR_SHARING_VIOLATION)
+                               {
+                                       WARNING("Couldn't encrypt directory \"%ls\" "
+                                               "due to sharing violation; re-trying "
+                                               "after 100 ms", path);
+                                       Sleep(100);
+                                       remaining_sharing_violations--;
+                               } else {
+                                       err = GetLastError();
+                                       ERROR("Failed to encrypt directory \"%ls\"",
+                                             path);
+                                       win32_error(err);
+                                       return WIMLIB_ERR_WRITE;
+                               }
                        }
                } else {
                        ret = do_win32_extract_encrypted_stream(path, lte);
@@ -2041,13 +1951,15 @@ win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
 /* Set security descriptor and extract stream data or reparse data (skip the
  * unnamed data stream of encrypted files, which was already extracted). */
 static int
-win32_finish_extract_stream(HANDLE h, const struct wim_inode *inode,
+win32_finish_extract_stream(HANDLE h, const struct wim_dentry *dentry,
                            const struct wim_lookup_table_entry *lte,
                            const wchar_t *stream_path,
                            const wchar_t *stream_name_utf16,
                            struct apply_args *args)
 {
        int ret = 0;
+       const struct wim_inode *inode = dentry->d_inode;
+       const wchar_t *short_name;
        if (stream_name_utf16 == NULL) {
                /* Unnamed stream. */
 
@@ -2098,6 +2010,21 @@ win32_finish_extract_stream(HANDLE h, const struct wim_inode *inode,
                         * the stream is encrypted and therefore was already
                         * extracted as a special case. */
                        ret = do_win32_extract_stream(h, lte);
+                       if (ret)
+                               return ret;
+               }
+
+               if (dentry_has_short_name(dentry))
+                       short_name = dentry->short_name;
+               else
+                       short_name = L"";
+               /* Set short name */
+               if (!SetFileShortNameW(h, short_name)) {
+               #if 0
+                       DWORD err = GetLastError();
+                       ERROR("Could not set short name on \"%ls\"", stream_path);
+                       win32_error(err);
+               #endif
                }
        } else {
                /* Extract the data for a named data stream. */
@@ -2138,7 +2065,7 @@ win32_decrypt_file(HANDLE open_handle, const wchar_t *path)
  * This handles reparse points, directories, alternate data streams, encrypted
  * files, compressed files, etc.
  *
- * @inode: WIM inode containing the stream.
+ * @dentry: WIM dentry for the file or directory being extracted.
  *
  * @path:  Path to extract the file to.
  *
@@ -2156,7 +2083,7 @@ win32_decrypt_file(HANDLE open_handle, const wchar_t *path)
  * Returns 0 on success; nonzero on failure.
  */
 static int
-win32_extract_stream(const struct wim_inode *inode,
+win32_extract_stream(const struct wim_dentry *dentry,
                     const wchar_t *path,
                     const wchar_t *stream_name_utf16,
                     struct wim_lookup_table_entry *lte,
@@ -2169,6 +2096,8 @@ win32_extract_stream(const struct wim_inode *inode,
        DWORD creationDisposition = CREATE_ALWAYS;
        DWORD requestedAccess;
        BY_HANDLE_FILE_INFORMATION file_info;
+       unsigned remaining_sharing_violations = 1000;
+       const struct wim_inode *inode = dentry->d_inode;
 
        if (stream_name_utf16) {
                /* Named stream.  Create a buffer that contains the UTF-16LE
@@ -2212,7 +2141,8 @@ win32_extract_stream(const struct wim_inode *inode,
        }
 
        DEBUG("Opening \"%ls\"", stream_path);
-       requestedAccess = GENERIC_READ | GENERIC_WRITE |
+       /* DELETE access is needed for SetFileShortNameW(), for some reason. */
+       requestedAccess = GENERIC_READ | GENERIC_WRITE | DELETE |
                          ACCESS_SYSTEM_SECURITY;
 try_open_again:
        /* Open the stream to be extracted.  Depending on what we have set
@@ -2221,7 +2151,7 @@ try_open_again:
         * CreateDirectoryW() or OpenEncryptedFileRawW(). */
        h = CreateFileW(stream_path,
                        requestedAccess,
-                       0,
+                       FILE_SHARE_READ,
                        NULL,
                        creationDisposition,
                        win32_get_create_flags_and_attributes(inode->i_attributes),
@@ -2243,8 +2173,25 @@ try_open_again:
                        requestedAccess &= ~ACCESS_SYSTEM_SECURITY;
                        goto try_open_again;
                }
-               ERROR("Failed to create \"%ls\"", stream_path);
-               win32_error(err);
+               if (err == ERROR_SHARING_VIOLATION) {
+                       if (remaining_sharing_violations) {
+                               --remaining_sharing_violations;
+                               /* This can happen when restoring encrypted directories
+                                * for some reason.  Probably a bug in EncryptFile(). */
+                               WARNING("Couldn't open \"%ls\" due to sharing violation; "
+                                       "re-trying after 100ms", stream_path);
+                               Sleep(100);
+                               goto 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);
+               }
                ret = WIMLIB_ERR_OPEN;
                goto fail;
        }
@@ -2303,7 +2250,7 @@ try_open_again:
         * descriptor and actually extract the stream data (other than for
         * extracted files, which were already extracted).
         * win32_finish_extract_stream() handles these additional steps. */
-       ret = win32_finish_extract_stream(h, inode, lte, stream_path,
+       ret = win32_finish_extract_stream(h, dentry, lte, stream_path,
                                          stream_name_utf16, args);
        if (ret)
                goto fail_close_handle;
@@ -2332,23 +2279,24 @@ out:
  * (unnamed data stream and/or reparse point stream, plus any alternate data
  * streams).  Handles sparse, compressed, and/or encrypted files.
  *
- * @inode:     WIM inode for this file or directory.
+ * @dentry:    WIM dentry for this file or directory.
  * @path:      UTF-16LE external path to extract the inode to.
  * @args:      Additional extraction context.
  *
  * Returns 0 on success; nonzero on failure.
  */
 static int
-win32_extract_streams(const struct wim_inode *inode,
+win32_extract_streams(const struct wim_dentry *dentry,
                      const wchar_t *path, struct apply_args *args)
 {
        struct wim_lookup_table_entry *unnamed_lte;
        int ret;
+       const struct wim_inode *inode = dentry->d_inode;
 
        /* First extract the unnamed stream. */
 
        unnamed_lte = inode_unnamed_lte_resolved(inode);
-       ret = win32_extract_stream(inode, path, NULL, unnamed_lte, args);
+       ret = win32_extract_stream(dentry, path, NULL, unnamed_lte, args);
        if (ret)
                goto out;
 
@@ -2373,7 +2321,7 @@ win32_extract_streams(const struct wim_inode *inode,
                        continue;
 
                /* Extract the named stream */
-               ret = win32_extract_stream(inode,
+               ret = win32_extract_stream(dentry,
                                           path,
                                           ads_entry->stream_name,
                                           ads_entry->lte,
@@ -2520,7 +2468,7 @@ win32_do_apply_dentry(const wchar_t *output_path,
        } else {
                /* Create the file, directory, or reparse point, and extract the
                 * data streams. */
-               ret = win32_extract_streams(inode, output_path, args);
+               ret = win32_extract_streams(dentry, output_path, args);
                if (ret)
                        return ret;
        }