]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
load_prepopulate_pats(): Fix free
[wimlib] / src / win32_apply.c
index 9eb8bad005f9fa6b2ef23d4fcee6e5be835d2f25..c182862ea42af8e4483a687df00d665222184216 100644 (file)
 #include "wimlib/win32_common.h"
 
 #include "wimlib/apply.h"
+#include "wimlib/capture.h"
+#include "wimlib/dentry.h"
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
+#include "wimlib/paths.h"
+#include "wimlib/textfile.h"
+#include "wimlib/xml.h"
+#include "wimlib/wim.h"
+#include "wimlib/wimboot.h"
+
+static void
+ctx_save_data_source_id(struct apply_ctx *ctx, u64 data_source_id)
+{
+       ctx->private[0] = data_source_id & 0xFFFFFFFF;
+       ctx->private[1] = data_source_id >> 32;
+}
+
+static u64
+ctx_get_data_source_id(const struct apply_ctx *ctx)
+{
+       return (u32)ctx->private[0] | ((u64)(u32)ctx->private[1] << 32);
+}
+
+static void
+set_prepopulate_pats(struct apply_ctx *ctx, struct string_set *s)
+{
+       ctx->private[2] = (intptr_t)s;
+}
+
+static struct string_set *
+get_prepopulate_pats(struct apply_ctx *ctx)
+{
+       return (struct string_set *)(ctx->private[2]);
+}
+
+static void
+free_prepopulate_pats(struct apply_ctx *ctx)
+{
+       struct string_set *s;
+
+       s = get_prepopulate_pats(ctx);
+       if (s) {
+               FREE(s->strings);
+               FREE(s);
+       }
+       set_prepopulate_pats(ctx, NULL);
+
+       FREE((void *)ctx->private[3]);
+       ctx->private[3] = (intptr_t)NULL;
+}
+
+static int
+load_prepopulate_pats(struct apply_ctx *ctx)
+{
+       int ret;
+       struct wim_dentry *dentry;
+       struct wim_lookup_table_entry *lte;
+       struct string_set *s;
+       const tchar *path = WIMLIB_WIM_PATH_SEPARATOR_STRING T("Windows")
+                           WIMLIB_WIM_PATH_SEPARATOR_STRING T("System32")
+                           WIMLIB_WIM_PATH_SEPARATOR_STRING T("WimBootCompress.ini");
+       void *buf;
+       void *mem;
+       struct text_file_section sec;
+
+       dentry = get_dentry(ctx->wim, path, WIMLIB_CASE_INSENSITIVE);
+       if (!dentry ||
+           (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+                                             FILE_ATTRIBUTE_REPARSE_POINT |
+                                             FILE_ATTRIBUTE_ENCRYPTED)) ||
+           !(lte = inode_unnamed_lte(dentry->d_inode, ctx->wim->lookup_table)))
+       {
+               WARNING("%"TS" does not exist in WIM image!", path);
+               return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+       }
+
+       ret = read_full_stream_into_alloc_buf(lte, &buf);
+       if (ret)
+               return ret;
+
+       s = CALLOC(1, sizeof(struct string_set));
+       if (!s) {
+               FREE(buf);
+               return WIMLIB_ERR_NOMEM;
+       }
+
+       sec.name = T("PrepopulateList");
+       sec.strings = s;
+
+       ret = do_load_text_file(path, buf, lte->size, &mem, &sec, 1,
+                               LOAD_TEXT_FILE_REMOVE_QUOTES |
+                                       LOAD_TEXT_FILE_NO_WARNINGS,
+                               mangle_pat);
+       FREE(buf);
+       if (ret) {
+               FREE(s);
+               return ret;
+       }
+       set_prepopulate_pats(ctx, s);
+       ctx->private[3] = (intptr_t)mem;
+       return 0;
+}
+
+static bool
+in_prepopulate_list(struct wim_dentry *dentry,
+                   struct apply_ctx *ctx)
+{
+       struct string_set *pats;
+       const tchar *path;
+
+       pats = get_prepopulate_pats(ctx);
+       if (!pats)
+               return false;
+       path = dentry_full_path(dentry);
+       if (!path)
+               return false;
+
+       return match_pattern(path, path_basename(path), pats);
+}
 
 static int
 win32_start_extract(const wchar_t *path, struct apply_ctx *ctx)
@@ -78,95 +195,55 @@ win32_start_extract(const wchar_t *path, struct apply_ctx *ctx)
 
        if (supports_SetFileShortName)
                ctx->supported_features.short_names = 1;
-       return 0;
-}
 
-/* Create a normal file, overwriting one already present.  */
-static int
-win32_create_file(const wchar_t *path, struct apply_ctx *ctx, u64 *cookie_ret)
-{
-       HANDLE h;
-       unsigned retry_count = 0;
-       DWORD dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS;
+       if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
 
-retry:
-       /* WRITE_OWNER and WRITE_DAC privileges are required for some reason,
-        * even through we're creating a new file.  */
-       h = CreateFile(path, WRITE_OWNER | WRITE_DAC, 0, NULL,
-                      CREATE_ALWAYS, dwFlagsAndAttributes, NULL);
-       if (h == INVALID_HANDLE_VALUE) {
-               /* File couldn't be created.  */
-               DWORD err = GetLastError();
-               if (err == ERROR_ACCESS_DENIED && retry_count == 0) {
+               ret = load_prepopulate_pats(ctx);
+               if (ret == WIMLIB_ERR_NOMEM)
+                       return ret;
 
-                       /* Access denied error for the first time.  Try
-                        * adjusting file attributes.  */
+               u64 data_source_id;
 
-                       /* Get attributes of the existing file.  */
-                       DWORD attribs = GetFileAttributes(path);
-                       if (attribs != INVALID_FILE_ATTRIBUTES &&
-                           (attribs & (FILE_ATTRIBUTE_HIDDEN |
-                                       FILE_ATTRIBUTE_SYSTEM |
-                                       FILE_ATTRIBUTE_READONLY)))
-                       {
-                               /* If the existing file has
-                                * FILE_ATTRIBUTE_HIDDEN and/or
-                                * FILE_ATTRIBUTE_SYSTEM, they must be set in
-                                * the call to CreateFile().  This is true even
-                                * when FILE_ATTRIBUTE_NORMAL was not specified,
-                                * contrary to the MS "documentation".  */
-                               dwFlagsAndAttributes |= (attribs &
-                                                        (FILE_ATTRIBUTE_HIDDEN |
-                                                         FILE_ATTRIBUTE_SYSTEM));
-                               /* If the existing file has
-                                * FILE_ATTRIBUTE_READONLY, it must be cleared
-                                * before attempting to create a new file over
-                                * it.  This is true even when the process has
-                                * the SE_RESTORE_NAME privilege and requested
-                                * the FILE_FLAG_BACKUP_SEMANTICS flag to
-                                * CreateFile().  */
-                               if (attribs & FILE_ATTRIBUTE_READONLY) {
-                                       SetFileAttributes(path,
-                                                         attribs & ~FILE_ATTRIBUTE_READONLY);
-                               }
-                               retry_count++;
-                               goto retry;
-                       }
+               if (!wim_info_get_wimboot(ctx->wim->wim_info,
+                                         ctx->wim->current_image))
+                       WARNING("Image is not marked as WIMBoot compatible!");
+
+               ret = wimboot_alloc_data_source_id(ctx->wim->filename,
+                                                  ctx->wim->current_image,
+                                                  path, &data_source_id);
+               if (ret) {
+                       free_prepopulate_pats(ctx);
+                       return ret;
                }
-               set_errno_from_win32_error(err);
-               return WIMLIB_ERR_OPEN;
+
+               ctx_save_data_source_id(ctx, data_source_id);
        }
-       CloseHandle(h);
+
        return 0;
 }
 
 static int
-win32_create_directory(const wchar_t *path, struct apply_ctx *ctx,
-                      u64 *cookie_ret)
+win32_finish_extract(struct apply_ctx *ctx)
 {
-       if (!CreateDirectory(path, NULL))
-               if (GetLastError() != ERROR_ALREADY_EXISTS)
-                       goto error;
+       free_prepopulate_pats(ctx);
        return 0;
-
-error:
-       set_errno_from_GetLastError();
-       return WIMLIB_ERR_MKDIR;
 }
 
 /* Delete a non-directory file, working around Windows quirks.  */
 static BOOL
 win32_delete_file_wrapper(const wchar_t *path)
 {
-       DWORD attrib;
        DWORD err;
+       DWORD attrib;
 
        if (DeleteFile(path))
                return TRUE;
 
        err = GetLastError();
        attrib = GetFileAttributes(path);
-       if (attrib & FILE_ATTRIBUTE_READONLY) {
+       if ((attrib != INVALID_FILE_ATTRIBUTES) &&
+           (attrib & FILE_ATTRIBUTE_READONLY))
+       {
                /* Try again with FILE_ATTRIBUTE_READONLY cleared.  */
                attrib &= ~FILE_ATTRIBUTE_READONLY;
                if (SetFileAttributes(path, attrib)) {
@@ -181,6 +258,60 @@ win32_delete_file_wrapper(const wchar_t *path)
        return FALSE;
 }
 
+
+/* Create a normal file, overwriting one already present.  */
+static int
+win32_create_file(const wchar_t *path, struct apply_ctx *ctx, u64 *cookie_ret)
+{
+       HANDLE h;
+
+       /* Notes:
+        *
+        * WRITE_OWNER and WRITE_DAC privileges are required for some reason,
+        * even through we're creating a new file.
+        *
+        * FILE_FLAG_OPEN_REPARSE_POINT is required to prevent an existing
+        * reparse point from redirecting the creation of the new file
+        * (potentially to an arbitrary location).
+        *
+        * CREATE_ALWAYS could be used instead of CREATE_NEW.  However, there
+        * are quirks that would need to be handled (e.g. having to set
+        * FILE_ATTRIBUTE_HIDDEN and/or FILE_ATTRIBUTE_SYSTEM if the existing
+        * file had them specified, and/or having to clear
+        * FILE_ATTRIBUTE_READONLY on the existing file).  It's simpler to just
+        * call win32_delete_file_wrapper() to delete the existing file in such
+        * a way that already handles the FILE_ATTRIBUTE_READONLY quirk.
+        */
+retry:
+       h = CreateFile(path, WRITE_OWNER | WRITE_DAC, 0, NULL, CREATE_NEW,
+                      FILE_FLAG_BACKUP_SEMANTICS |
+                               FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+       if (h == INVALID_HANDLE_VALUE) {
+               DWORD err = GetLastError();
+
+               if (err == ERROR_FILE_EXISTS && win32_delete_file_wrapper(path))
+                       goto retry;
+               set_errno_from_win32_error(err);
+               return WIMLIB_ERR_OPEN;
+       }
+       CloseHandle(h);
+       return 0;
+}
+
+static int
+win32_create_directory(const wchar_t *path, struct apply_ctx *ctx,
+                      u64 *cookie_ret)
+{
+       if (!CreateDirectory(path, NULL))
+               if (GetLastError() != ERROR_ALREADY_EXISTS)
+                       goto error;
+       return 0;
+
+error:
+       set_errno_from_GetLastError();
+       return WIMLIB_ERR_MKDIR;
+}
+
 static int
 win32_create_hardlink(const wchar_t *oldpath, const wchar_t *newpath,
                      struct apply_ctx *ctx)
@@ -280,8 +411,20 @@ error:
 static int
 win32_extract_unnamed_stream(file_spec_t file,
                             struct wim_lookup_table_entry *lte,
-                            struct apply_ctx *ctx)
+                            struct apply_ctx *ctx,
+                            struct wim_dentry *dentry)
 {
+       if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT
+           && lte
+           && lte->resource_location == RESOURCE_IN_WIM
+           && lte->rspec->wim == ctx->wim
+           && !in_prepopulate_list(dentry, ctx))
+       {
+               return wimboot_set_pointer(file.path,
+                                          ctx_get_data_source_id(ctx),
+                                          lte->hash);
+       }
+
        return win32_extract_stream(file.path, NULL, 0, lte, ctx);
 }
 
@@ -740,6 +883,8 @@ const struct apply_operations win32_apply_ops = {
 
        .target_is_root           = win32_path_is_root_of_drive,
        .start_extract            = win32_start_extract,
+       .finish_extract           = win32_finish_extract,
+       .abort_extract            = win32_finish_extract,
        .create_file              = win32_create_file,
        .create_directory         = win32_create_directory,
        .create_hardlink          = win32_create_hardlink,