]> wimlib.net Git - wimlib/blobdiff - src/win32.c
overwrite_wim_inplace(): cleanup
[wimlib] / src / win32.c
index 638380908b62091fe7b91dbb164adbecb152271d..f2e7eeb08988464c43db05f4eb3f9850a381d150 100644 (file)
@@ -53,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. */
@@ -128,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)
 {
@@ -174,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
@@ -451,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
@@ -591,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:
@@ -1029,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.
  */
@@ -1145,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,
@@ -1213,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;
                        }
@@ -1223,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();
@@ -1239,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));