]> wimlib.net Git - wimlib/blobdiff - src/win32.c
overwrite_wim_inplace(): cleanup
[wimlib] / src / win32.c
index 57f6fdb9e0a3bcb842391badc4df6ced18f9c4f1..f2e7eeb08988464c43db05f4eb3f9850a381d150 100644 (file)
@@ -39,6 +39,7 @@
 #include "lookup_table.h"
 #include "security.h"
 #include "endianness.h"
+#include <pthread.h>
 
 #include <errno.h>
 
@@ -52,6 +53,28 @@ struct win32_capture_state {
 #define MAX_SET_SD_ACCESS_DENIED_WARNINGS 1
 #define MAX_SET_SACL_PRIV_NOTHELD_WARNINGS 1
 
+#ifdef ENABLE_ERROR_MESSAGES
+static void
+win32_error(u32 err_code)
+{
+       wchar_t *buffer;
+       DWORD nchars;
+       nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
+                                   FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                               NULL, err_code, 0,
+                               (wchar_t*)&buffer, 0, NULL);
+       if (nchars == 0) {
+               ERROR("Error printing error message! "
+                     "Computer will self-destruct in 3 seconds.");
+       } else {
+               ERROR("Win32 error: %ls", buffer);
+               LocalFree(buffer);
+       }
+}
+#else /* ENABLE_ERROR_MESSAGES */
+#  define win32_error(err_code)
+#endif /* !ENABLE_ERROR_MESSAGES */
+
 /* Pointers to functions that are not available on all targetted versions of
  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
  * assume it specifies a certain calling convention. */
@@ -127,32 +150,6 @@ L"If you are not running this program as the administrator, you may\n"
  "          `wimlib-imagex apply' with the --no-acls flag instead.\n"
  ;
 
-#ifdef ENABLE_ERROR_MESSAGES
-void
-win32_error(u32 err_code)
-{
-       wchar_t *buffer;
-       DWORD nchars;
-       nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
-                                   FORMAT_MESSAGE_ALLOCATE_BUFFER,
-                               NULL, err_code, 0,
-                               (wchar_t*)&buffer, 0, NULL);
-       if (nchars == 0) {
-               ERROR("Error printing error message! "
-                     "Computer will self-destruct in 3 seconds.");
-       } else {
-               ERROR("Win32 error: %ls", buffer);
-               LocalFree(buffer);
-       }
-}
-
-void
-win32_error_last()
-{
-       win32_error(GetLastError());
-}
-#endif
-
 static HANDLE
 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
 {
@@ -173,29 +170,58 @@ win32_open_file_data_only(const wchar_t *path)
 }
 
 int
-win32_read_file(const wchar_t *filename,
-               void *handle, u64 offset, size_t size, void *buf)
+read_win32_file_prefix(const struct lookup_table_entry *lte,
+                      u64 size,
+                      consume_data_callback_t cb,
+                      void *ctx_or_buf,
+                      int _ignored_flags)
 {
-       HANDLE h = handle;
+       int ret;
+       void *out_buf;
        DWORD err;
-       DWORD bytesRead;
-       LARGE_INTEGER liOffset = {.QuadPart = offset};
+       u64 bytes_remaining;
 
-       wimlib_assert(size <= 0xffffffff);
+       HANDLE hFile = win32_open_file_data_only(lte->file_on_disk);
+       if (hFile == INVALID_HANDLE_VALUE) {
+               err = GetLastError();
+               ERROR("Failed to open \"%ls\"", lte->file_on_disk);
+               win32_error(err);
+               ret = WIMLIB_ERR_OPEN;
+               goto out;
+       }
 
-       if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
-               if (ReadFile(h, buf, size, &bytesRead, NULL) && bytesRead == size)
-                       return 0;
-       err = GetLastError();
-       ERROR("Error reading \"%ls\"", filename);
-       win32_error(err);
-       return WIMLIB_ERR_READ;
-}
+       if (cb)
+               out_buf = alloca(WIM_CHUNK_SIZE);
+       else
+               out_buf = ctx_or_buf;
 
-void
-win32_close_file(void *handle)
-{
-       CloseHandle((HANDLE)handle);
+       bytes_remaining = size;
+       while (bytes_remaining) {
+               DWORD bytesToRead, bytesRead;
+
+               bytesToRead = min(WIM_CHUNK_SIZE, bytes_remaining);
+               if (!ReadFile(h, out_buf, bytesToRead, &bytesRead, NULL) ||
+                   bytesRead != bytesToRead)
+               {
+                       err = GetLastError();
+                       ERROR("Failed to read data from \"%ls\"", lte->file_on_disk);
+                       win32_error(err);
+                       ret = WIMLIB_ERR_READ;
+                       goto out_close_handle;
+               }
+               bytes_remaining -= bytesRead;
+               if (cb) {
+                       ret = cb(out_buf, bytesRead, ctx_or_buf);
+                       if (ret)
+                               goto out_close_handle;
+               } else {
+                       out_buf += bytesRead;
+               }
+       }
+out_close_handle:
+       CloseHandle(hFile);
+out:
+       return ret;
 }
 
 static u64
@@ -309,8 +335,9 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                  wchar_t *path,
                                  size_t path_num_chars,
                                  struct wim_lookup_table *lookup_table,
+                                 struct wim_inode_table *inode_table,
                                  struct sd_set *sd_set,
-                                 const struct capture_config *config,
+                                 const struct wimlib_capture_config *config,
                                  int add_image_flags,
                                  wimlib_progress_func_t progress_func,
                                  struct win32_capture_state *state);
@@ -322,8 +349,9 @@ win32_recurse_directory(struct wim_dentry *root,
                        wchar_t *dir_path,
                        size_t dir_path_num_chars,
                        struct wim_lookup_table *lookup_table,
+                       struct wim_inode_table *inode_table,
                        struct sd_set *sd_set,
-                       const struct capture_config *config,
+                       const struct wimlib_capture_config *config,
                        int add_image_flags,
                        wimlib_progress_func_t progress_func,
                        struct win32_capture_state *state)
@@ -374,6 +402,7 @@ win32_recurse_directory(struct wim_dentry *root,
                                                        dir_path,
                                                        path_len,
                                                        lookup_table,
+                                                       inode_table,
                                                        sd_set,
                                                        config,
                                                        add_image_flags,
@@ -447,47 +476,6 @@ win32_capture_reparse_point(HANDLE hFile,
                                       bytesReturned - 8, lookup_table);
 }
 
-/* Calculate the SHA1 message digest of a Win32 data stream, which may be either
- * an unnamed or named data stream.
- *
- * @path:      Path to the file, with the stream noted at the end for named
- *              streams.  UTF-16LE encoding.
- *
- * @hash:       On success, the SHA1 message digest of the stream is written to
- *              this location.
- *
- * Returns 0 on success; nonzero on failure.
- */
-static int
-win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE])
-{
-       HANDLE hFile;
-       SHA_CTX ctx;
-       u8 buf[32768];
-       DWORD bytesRead;
-       int ret;
-
-       hFile = win32_open_file_data_only(path);
-       if (hFile == INVALID_HANDLE_VALUE)
-               return WIMLIB_ERR_OPEN;
-
-       sha1_init(&ctx);
-       for (;;) {
-               if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) {
-                       ret = WIMLIB_ERR_READ;
-                       goto out_close_handle;
-               }
-               if (bytesRead == 0)
-                       break;
-               sha1_update(&ctx, buf, bytesRead);
-       }
-       ret = 0;
-       sha1_final(hash, &ctx);
-out_close_handle:
-       CloseHandle(hFile);
-       return ret;
-}
-
 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
  * stream); calculates its SHA1 message digest and either creates a `struct
  * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
@@ -587,39 +575,27 @@ win32_capture_stream(const wchar_t *path,
        swprintf(spath, L"%ls%ls%ls%ls",
                 relpath_prefix, path, colonchar, stream_name);
 
-       ret = win32_sha1sum(spath, hash);
-       if (ret) {
-               err = GetLastError();
-               ERROR("Failed to read \"%ls\" to calculate SHA1sum", spath);
-               win32_error(err);
+       /* Make a new wim_lookup_table_entry */
+       lte = new_lookup_table_entry();
+       if (!lte) {
+               ret = WIMLIB_ERR_NOMEM;
                goto out_free_spath;
        }
+       lte->file_on_disk = spath;
+       spath = NULL;
+       lte->resource_location = RESOURCE_WIN32;
+       lte->resource_entry.original_size = (u64)dat->StreamSize.QuadPart;
 
-       lte = __lookup_resource(lookup_table, hash);
-       if (lte) {
-               /* Use existing wim_lookup_table_entry that has the same SHA1
-                * message digest */
-               lte->refcnt++;
-       } else {
-               /* Make a new wim_lookup_table_entry */
-               lte = new_lookup_table_entry();
-               if (!lte) {
-                       ret = WIMLIB_ERR_NOMEM;
-                       goto out_free_spath;
-               }
-               lte->file_on_disk = spath;
-               lte->win32_file_on_disk_fp = INVALID_HANDLE_VALUE;
-               spath = NULL;
-               lte->resource_location = RESOURCE_WIN32;
-               lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart;
-               lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart;
-               copy_hash(lte->hash, hash);
-               lookup_table_insert(lookup_table, lte);
-       }
-       if (is_named_stream)
+       u32 stream_id;
+       if (is_named_stream) {
+               stream_id = ads_entry->stream_id;
                ads_entry->lte = lte;
-       else
+       } else {
+               stream_id = 0;
                inode->i_lte = lte;
+       }
+
+       lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id);
 out_free_spath:
        FREE(spath);
 out:
@@ -733,8 +709,9 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                  wchar_t *path,
                                  size_t path_num_chars,
                                  struct wim_lookup_table *lookup_table,
+                                 struct wim_inode_table *inode_table,
                                  struct sd_set *sd_set,
-                                 const struct capture_config *config,
+                                 const struct wimlib_capture_config *config,
                                  int add_image_flags,
                                  wimlib_progress_func_t progress_func,
                                  struct win32_capture_state *state)
@@ -791,29 +768,32 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                goto out_close_handle;
        }
 
-       /* Create a WIM dentry */
-       ret = new_dentry_with_timeless_inode(path_basename_with_len(path, path_num_chars),
-                                            &root);
+       /* Create a WIM dentry with an associated inode, which may be shared */
+       ret = inode_table_new_dentry(inode_table,
+                                    path_basename_with_len(path, path_num_chars),
+                                    ((u64)file_info.nFileIndexHigh << 32) |
+                                        (u64)file_info.nFileIndexLow,
+                                    file_info.dwVolumeSerialNumber,
+                                    &root);
+       if (ret)
+               goto out_close_handle;
+
+       ret = win32_get_short_name(root, path);
        if (ret)
                goto out_close_handle;
 
-       /* Start preparing the associated WIM inode */
        inode = root->d_inode;
 
+       if (inode->i_nlink > 1) /* Shared inode; nothing more to do */
+               goto out_close_handle;
+
        inode->i_attributes = file_info.dwFileAttributes;
        inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
        inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
        inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
-       inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) |
-                       (u64)file_info.nFileIndexLow;
-
        inode->i_resolved = 1;
-       add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE);
 
-       /* Get DOS name and security descriptor (if any). */
-       ret = win32_get_short_name(root, path);
-       if (ret)
-               goto out_close_handle;
+       add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE);
 
        if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)) {
                ret = win32_get_security_descriptor(root, sd_set, path, state,
@@ -841,6 +821,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                              path,
                                              path_num_chars,
                                              lookup_table,
+                                             inode_table,
                                              sd_set,
                                              config,
                                              add_image_flags,
@@ -910,8 +891,9 @@ int
 win32_build_dentry_tree(struct wim_dentry **root_ret,
                        const wchar_t *root_disk_path,
                        struct wim_lookup_table *lookup_table,
+                       struct wim_inode_table *inode_table,
                        struct sd_set *sd_set,
-                       const struct capture_config *config,
+                       const struct wimlib_capture_config *config,
                        int add_image_flags,
                        wimlib_progress_func_t progress_func,
                        void *extra_arg)
@@ -940,6 +922,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
                                                path,
                                                path_nchars,
                                                lookup_table,
+                                               inode_table,
                                                sd_set,
                                                config,
                                                add_image_flags,
@@ -1018,6 +1001,41 @@ win32_set_reparse_data(HANDLE h,
        return 0;
 }
 
+static int
+win32_set_compressed(HANDLE hFile, const wchar_t *path)
+{
+       USHORT format = COMPRESSION_FORMAT_DEFAULT;
+       DWORD bytesReturned = 0;
+       if (!DeviceIoControl(hFile, FSCTL_SET_COMPRESSION,
+                            &format, sizeof(USHORT),
+                            NULL, 0,
+                            &bytesReturned, NULL))
+       {
+               /* Warning only */
+               DWORD err = GetLastError();
+               WARNING("Failed to set compression flag on \"%ls\"", path);
+               win32_error(err);
+       }
+       return 0;
+}
+
+static int
+win32_set_sparse(HANDLE hFile, const wchar_t *path)
+{
+       DWORD bytesReturned = 0;
+       if (!DeviceIoControl(hFile, FSCTL_SET_SPARSE,
+                            NULL, 0,
+                            NULL, 0,
+                            &bytesReturned, NULL))
+       {
+               /* Warning only */
+               DWORD err = GetLastError();
+               WARNING("Failed to set sparse flag on \"%ls\"", path);
+               win32_error(err);
+       }
+       return 0;
+}
+
 /*
  * Sets the security descriptor on an extracted file.
  */
@@ -1134,6 +1152,91 @@ path_is_root_of_drive(const wchar_t *path)
        return (*path == L'\0');
 }
 
+static DWORD
+win32_get_create_flags_and_attributes(DWORD i_attributes)
+{
+       DWORD attributes;
+
+       /*
+        * Some attributes cannot be set by passing them to CreateFile().  In
+        * particular:
+        *
+        * FILE_ATTRIBUTE_DIRECTORY:
+        *   CreateDirectory() must be called instead of CreateFile().
+        *
+        * FILE_ATTRIBUTE_SPARSE_FILE:
+        *   Needs an ioctl.
+        *   See: win32_set_sparse().
+        *
+        * FILE_ATTRIBUTE_COMPRESSED:
+        *   Not clear from the documentation, but apparently this needs an
+        *   ioctl as well.
+        *   See: win32_set_compressed().
+        *
+        * FILE_ATTRIBUTE_REPARSE_POINT:
+        *   Needs an ioctl, with the reparse data specified.
+        *   See: win32_set_reparse_data().
+        *
+        * In addition, clear any file flags in the attributes that we don't
+        * want, but also specify FILE_FLAG_OPEN_REPARSE_POINT and
+        * FILE_FLAG_BACKUP_SEMANTICS as we are a backup application.
+        */
+       attributes = i_attributes & ~(FILE_ATTRIBUTE_SPARSE_FILE |
+                                     FILE_ATTRIBUTE_COMPRESSED |
+                                     FILE_ATTRIBUTE_REPARSE_POINT |
+                                     FILE_ATTRIBUTE_DIRECTORY |
+                                     FILE_FLAG_DELETE_ON_CLOSE |
+                                     FILE_FLAG_NO_BUFFERING |
+                                     FILE_FLAG_OPEN_NO_RECALL |
+                                     FILE_FLAG_OVERLAPPED |
+                                     FILE_FLAG_RANDOM_ACCESS |
+                                     /*FILE_FLAG_SESSION_AWARE |*/
+                                     FILE_FLAG_SEQUENTIAL_SCAN |
+                                     FILE_FLAG_WRITE_THROUGH);
+       return attributes |
+              FILE_FLAG_OPEN_REPARSE_POINT |
+              FILE_FLAG_BACKUP_SEMANTICS;
+}
+
+static bool
+inode_has_special_attributes(const struct wim_inode *inode)
+{
+       return (inode->i_attributes & (FILE_ATTRIBUTE_COMPRESSED |
+                                      FILE_ATTRIBUTE_REPARSE_POINT |
+                                      FILE_ATTRIBUTE_SPARSE_FILE)) != 0;
+}
+
+static int
+win32_set_special_attributes(HANDLE hFile, const struct wim_inode *inode,
+                            struct wim_lookup_table_entry *unnamed_stream_lte,
+                            const wchar_t *path)
+{
+       int ret;
+
+       if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+               DEBUG("Setting reparse data on \"%ls\"", path);
+               ret = win32_set_reparse_data(hFile, inode->i_reparse_tag,
+                                            unnamed_stream_lte, path);
+               if (ret)
+                       return ret;
+       }
+
+       if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) {
+               DEBUG("Setting compression flag on \"%ls\"", path);
+               ret = win32_set_compressed(hFile, path);
+               if (ret)
+                       return ret;
+       }
+
+       if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) {
+               DEBUG("Setting sparse flag on \"%ls\"", path);
+               ret = win32_set_sparse(hFile, path);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
 static int
 win32_extract_stream(const struct wim_inode *inode,
                     const wchar_t *path,
@@ -1202,7 +1305,7 @@ win32_extract_stream(const struct wim_inode *inode,
                                }
                        }
                        DEBUG("Created directory \"%ls\"", stream_path);
-                       if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                       if (!inode_has_special_attributes(inode)) {
                                ret = 0;
                                goto out;
                        }
@@ -1212,13 +1315,11 @@ win32_extract_stream(const struct wim_inode *inode,
 
        DEBUG("Opening \"%ls\"", stream_path);
        h = CreateFileW(stream_path,
-                       GENERIC_WRITE,
+                       GENERIC_READ | GENERIC_WRITE,
                        0,
                        NULL,
                        creationDisposition,
-                       FILE_FLAG_OPEN_REPARSE_POINT |
-                           FILE_FLAG_BACKUP_SEMANTICS |
-                           inode->i_attributes,
+                       win32_get_create_flags_and_attributes(inode->i_attributes),
                        NULL);
        if (h == INVALID_HANDLE_VALUE) {
                err = GetLastError();
@@ -1228,14 +1329,13 @@ win32_extract_stream(const struct wim_inode *inode,
                goto fail;
        }
 
-       if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
-           stream_name_utf16 == NULL)
-       {
-               DEBUG("Setting reparse data on \"%ls\"", path);
-               ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path);
+       if (stream_name_utf16 == NULL && inode_has_special_attributes(inode)) {
+               ret = win32_set_special_attributes(h, inode, lte, path);
                if (ret)
                        goto fail_close_handle;
-       } else {
+       }
+
+       if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
                if (lte) {
                        DEBUG("Extracting \"%ls\" (len = %"PRIu64")",
                              stream_path, wim_resource_size(lte));
@@ -1534,4 +1634,20 @@ fail:
        return -1;
 }
 
+
+/* This really could be replaced with _wcserror_s, but this doesn't seem to
+ * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
+ * linked in by Visual Studio...?). */
+extern int
+win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
+{
+       static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
+
+       pthread_mutex_lock(&strerror_lock);
+       mbstowcs(buf, strerror(errnum), buflen);
+       buf[buflen - 1] = '\0';
+       pthread_mutex_unlock(&strerror_lock);
+       return 0;
+}
+
 #endif /* __WIN32__ */