]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
win32_apply.c: fix extraction of empty reparse points
[wimlib] / src / win32_apply.c
index 7593f20e23a65cfad914a255689f033b7532366e..49e25d4b50bd30d156b6a669cd0f6a9f6ab0d0a1 100644 (file)
 #include "wimlib/error.h"
 #include "wimlib/metadata.h"
 #include "wimlib/paths.h"
+#include "wimlib/pattern.h"
 #include "wimlib/reparse.h"
 #include "wimlib/textfile.h"
 #include "wimlib/xml.h"
-#include "wimlib/wildcard.h"
 #include "wimlib/wimboot.h"
 
 struct win32_apply_ctx {
@@ -122,12 +122,12 @@ struct win32_apply_ctx {
         * beginning of the array)  */
        unsigned num_open_handles;
 
-       /* List of dentries, joined by @tmp_list, that need to have reparse data
-        * extracted as soon as the whole blob has been read into @data_buffer.
-        * */
+       /* List of dentries, joined by @d_tmp_list, that need to have reparse
+        * data extracted as soon as the whole blob has been read into
+        * @data_buffer.  */
        struct list_head reparse_dentries;
 
-       /* List of dentries, joined by @tmp_list, that need to have raw
+       /* List of dentries, joined by @d_tmp_list, that need to have raw
         * encrypted data extracted as soon as the whole blob has been read into
         * @data_buffer.  */
        struct list_head encrypted_dentries;
@@ -341,28 +341,15 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
        return 0;
 }
 
-/* Returns %true if the specified absolute path to a file in the WIM image
- * matches a pattern in [PrepopulateList] of WimBootCompress.ini.  Otherwise
- * returns %false.  */
-static bool
-in_prepopulate_list(const wchar_t *path, size_t path_nchars,
-                   const struct win32_apply_ctx *ctx)
-{
-       const struct string_set *pats = ctx->wimboot.prepopulate_pats;
-
-       if (!pats || !pats->num_strings)
-               return false;
-
-       return match_pattern_list(path, path_nchars, pats);
-}
-
 /* Returns %true if the specified absolute path to a file in the WIM image can
  * be subject to external backing when extracted.  Otherwise returns %false.  */
 static bool
-can_externally_back_path(const wchar_t *path, size_t path_nchars,
-                        const struct win32_apply_ctx *ctx)
+can_externally_back_path(const wchar_t *path, const struct win32_apply_ctx *ctx)
 {
-       if (in_prepopulate_list(path, path_nchars, ctx))
+       /* Does the path match a pattern given in the [PrepopulateList] section
+        * of WimBootCompress.ini?  */
+       if (ctx->wimboot.prepopulate_pats &&
+           match_pattern_list(path, ctx->wimboot.prepopulate_pats))
                return false;
 
        /* Since we attempt to modify the SYSTEM registry after it's extracted
@@ -374,8 +361,7 @@ can_externally_back_path(const wchar_t *path, size_t path_nchars,
         * However, a WIM that wasn't specifically captured in "WIMBoot mode"
         * may contain SYSTEM.* files.  So to make things "just work", hard-code
         * the pattern.  */
-       if (match_path(path, path_nchars, L"\\Windows\\System32\\config\\SYSTEM*",
-                      OS_PREFERRED_PATH_SEPARATOR, false))
+       if (match_path(path, L"\\Windows\\System32\\config\\SYSTEM*", false))
                return false;
 
        return true;
@@ -492,9 +478,7 @@ will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx,
                if (ret)
                        return ret;
 
-               if (!can_externally_back_path(dentry->_full_path,
-                                             wcslen(dentry->_full_path), ctx))
-               {
+               if (!can_externally_back_path(dentry->d_full_path, ctx)) {
                        if (excluded_dentry_ret)
                                *excluded_dentry_ret = dentry;
                        return WIM_BACKING_EXCLUDED;
@@ -557,7 +541,7 @@ set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *
 
                build_extraction_path(excluded_dentry, ctx);
 
-               info.wimboot_exclude.path_in_wim = excluded_dentry->_full_path;
+               info.wimboot_exclude.path_in_wim = excluded_dentry->d_full_path;
                info.wimboot_exclude.extraction_path = current_path(ctx);
 
                return call_progress(ctx->common.progfunc,
@@ -1149,7 +1133,7 @@ remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_appl
        name = &ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)];
        while (name != ctx->pathbuf.Buffer && *(name - 1) != L'\\')
                name--;
-       end = mempcpy(name, dentry->short_name, dentry->short_name_nbytes);
+       end = mempcpy(name, dentry->d_short_name, dentry->d_short_name_nbytes);
        ctx->pathbuf.Length = ((u8 *)end - (u8 *)ctx->pathbuf.Buffer);
 
        /* Open the conflicting file (by short name).  */
@@ -1226,7 +1210,7 @@ set_short_name(HANDLE h, const struct wim_dentry *dentry,
         */
 
        size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
-                        max(dentry->short_name_nbytes, sizeof(wchar_t)) +
+                        max(dentry->d_short_name_nbytes, sizeof(wchar_t)) +
                         sizeof(wchar_t);
        u8 buf[bufsize] _aligned_attribute(8);
        FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
@@ -1235,8 +1219,8 @@ set_short_name(HANDLE h, const struct wim_dentry *dentry,
 
        memset(buf, 0, bufsize);
 
-       info->FileNameLength = dentry->short_name_nbytes;
-       memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
+       info->FileNameLength = dentry->d_short_name_nbytes;
+       memcpy(info->FileName, dentry->d_short_name, dentry->d_short_name_nbytes);
 
 retry:
        status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
@@ -1245,7 +1229,7 @@ retry:
                return 0;
 
        if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
-               if (dentry->short_name_nbytes == 0)
+               if (dentry->d_short_name_nbytes == 0)
                        return 0;
                if (!ctx->tried_to_enable_short_names) {
                        wchar_t volume[7];
@@ -1279,7 +1263,7 @@ retry:
         *   from files.
         */
        if (unlikely(status == STATUS_OBJECT_NAME_COLLISION) &&
-           dentry->short_name_nbytes && !tried_to_remove_existing)
+           dentry->d_short_name_nbytes && !tried_to_remove_existing)
        {
                tried_to_remove_existing = true;
                status = remove_conflicting_short_name(dentry, ctx);
@@ -1290,7 +1274,7 @@ retry:
        /* By default, failure to set short names is not an error (since short
         * names aren't too important anymore...).  */
        if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES)) {
-               if (dentry->short_name_nbytes)
+               if (dentry->d_short_name_nbytes)
                        ctx->num_set_short_name_failures++;
                else
                        ctx->num_remove_short_name_failures++;
@@ -1464,44 +1448,104 @@ retry:
        return WIMLIB_ERR_OPEN;
 }
 
+/* Set the reparse point @rpbuf of length @rpbuflen on the extracted file
+ * corresponding to the WIM dentry @dentry.  */
+static int
+do_set_reparse_point(const struct wim_dentry *dentry,
+                    const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
+                    struct win32_apply_ctx *ctx)
+{
+       NTSTATUS status;
+       HANDLE h;
+
+       status = create_file(&h, GENERIC_WRITE, NULL,
+                            0, FILE_OPEN, 0, dentry, ctx);
+       if (!NT_SUCCESS(status))
+               goto fail;
+
+       status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
+                                        &ctx->iosb, FSCTL_SET_REPARSE_POINT,
+                                        (void *)rpbuf, rpbuflen,
+                                        NULL, 0);
+       (*func_NtClose)(h);
+
+       if (NT_SUCCESS(status))
+               return 0;
+
+       /* On Windows, by default only the Administrator can create symbolic
+        * links for some reason.  By default we just issue a warning if this
+        * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
+        * to get a hard error.  */
+       if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
+           && (status == STATUS_PRIVILEGE_NOT_HELD ||
+               status == STATUS_ACCESS_DENIED)
+           && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+               dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
+       {
+               WARNING("Can't create symbolic link \"%ls\"!              \n"
+                       "          (Need Administrator rights, or at least "
+                       "the\n"
+                       "          SeCreateSymbolicLink privilege.)",
+                       current_path(ctx));
+               return 0;
+       }
+
+fail:
+       winnt_error(status, L"Can't set reparse data on \"%ls\"",
+                   current_path(ctx));
+       return WIMLIB_ERR_SET_REPARSE_DATA;
+}
+
 /*
- * Create empty named data streams for the specified file, if there are any.
+ * Create empty named data streams and potentially a reparse point for the
+ * specified file, if any.
  *
  * Since these won't have blob descriptors, they won't show up in the call to
  * extract_blob_list().  Hence the need for the special case.
  */
 static int
-create_empty_named_data_streams(const struct wim_dentry *dentry,
-                               struct win32_apply_ctx *ctx)
+create_empty_streams(const struct wim_dentry *dentry,
+                    struct win32_apply_ctx *ctx)
 {
        const struct wim_inode *inode = dentry->d_inode;
-       bool path_modified = false;
-       int ret = 0;
-
-       if (!ctx->common.supported_features.named_data_streams)
-               return 0;
+       int ret;
 
        for (unsigned i = 0; i < inode->i_num_streams; i++) {
                const struct wim_inode_stream *strm = &inode->i_streams[i];
-               HANDLE h;
 
-               if (!stream_is_named_data_stream(strm) ||
-                   stream_blob_resolved(strm) != NULL)
+               if (stream_blob_resolved(strm) != NULL)
                        continue;
 
-               build_extraction_path_with_ads(dentry, ctx,
-                                              strm->stream_name,
-                                              utf16le_len_chars(strm->stream_name));
-               path_modified = true;
-               ret = supersede_file_or_stream(ctx, &h);
-               if (ret)
-                       break;
-               (*func_NtClose)(h);
+               if (strm->stream_type == STREAM_TYPE_REPARSE_POINT &&
+                   ctx->common.supported_features.reparse_points)
+               {
+                       u8 buf[REPARSE_DATA_OFFSET] _aligned_attribute(8);
+                       struct reparse_buffer_disk *rpbuf =
+                               (struct reparse_buffer_disk *)buf;
+                       complete_reparse_point(rpbuf, inode, 0);
+                       ret = do_set_reparse_point(dentry, rpbuf,
+                                                  REPARSE_DATA_OFFSET, ctx);
+                       if (ret)
+                               return ret;
+               } else if (stream_is_named_data_stream(strm) &&
+                          ctx->common.supported_features.named_data_streams)
+               {
+                       HANDLE h;
+
+                       build_extraction_path_with_ads(dentry, ctx,
+                                                      strm->stream_name,
+                                                      utf16le_len_chars(strm->stream_name));
+                       ret = supersede_file_or_stream(ctx, &h);
+
+                       build_extraction_path(dentry, ctx);
+
+                       if (ret)
+                               return ret;
+                       (*func_NtClose)(h);
+               }
        }
-       /* Restore the path to the dentry itself  */
-       if (path_modified)
-               build_extraction_path(dentry, ctx);
-       return ret;
+
+       return 0;
 }
 
 /*
@@ -1587,7 +1631,7 @@ create_directories(struct list_head *dentry_list,
                ret = create_directory(dentry, ctx);
 
                if (!ret)
-                       ret = create_empty_named_data_streams(dentry, ctx);
+                       ret = create_empty_streams(dentry, ctx);
 
                ret = check_apply_error(dentry, ctx, ret);
                if (ret)
@@ -1624,7 +1668,7 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry,
        if (ret)
                goto out_close;
 
-       ret = create_empty_named_data_streams(dentry, ctx);
+       ret = create_empty_streams(dentry, ctx);
        if (ret)
                goto out_close;
 
@@ -1815,7 +1859,7 @@ begin_extract_blob_instance(const struct blob_descriptor *blob,
                 * data be available.  So, stage the data in a buffer.  */
                if (!prepare_data_buffer(ctx, blob->size))
                        return WIMLIB_ERR_NOMEM;
-               list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
+               list_add_tail(&dentry->d_tmp_list, &ctx->reparse_dentries);
                return 0;
        }
 
@@ -1833,7 +1877,7 @@ begin_extract_blob_instance(const struct blob_descriptor *blob,
                 * such files...  */
                if (!prepare_data_buffer(ctx, blob->size))
                        return WIMLIB_ERR_NOMEM;
-               list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
+               list_add_tail(&dentry->d_tmp_list, &ctx->encrypted_dentries);
                return 0;
        }
 
@@ -1881,54 +1925,6 @@ begin_extract_blob_instance(const struct blob_descriptor *blob,
        return 0;
 }
 
-/* Set the reparse point @rpbuf of length @rpbuflen on the extracted file
- * corresponding to the WIM dentry @dentry.  */
-static int
-do_set_reparse_point(const struct wim_dentry *dentry,
-                    const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
-                    struct win32_apply_ctx *ctx)
-{
-       NTSTATUS status;
-       HANDLE h;
-
-       status = create_file(&h, GENERIC_WRITE, NULL,
-                            0, FILE_OPEN, 0, dentry, ctx);
-       if (!NT_SUCCESS(status))
-               goto fail;
-
-       status = (*func_NtFsControlFile)(h, NULL, NULL, NULL,
-                                        &ctx->iosb, FSCTL_SET_REPARSE_POINT,
-                                        (void *)rpbuf, rpbuflen,
-                                        NULL, 0);
-       (*func_NtClose)(h);
-
-       if (NT_SUCCESS(status))
-               return 0;
-
-       /* On Windows, by default only the Administrator can create symbolic
-        * links for some reason.  By default we just issue a warning if this
-        * appears to be the problem.  Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS
-        * to get a hard error.  */
-       if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS)
-           && (status == STATUS_PRIVILEGE_NOT_HELD ||
-               status == STATUS_ACCESS_DENIED)
-           && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
-               dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
-       {
-               WARNING("Can't create symbolic link \"%ls\"!              \n"
-                       "          (Need Administrator rights, or at least "
-                       "the\n"
-                       "          SeCreateSymbolicLink privilege.)",
-                       current_path(ctx));
-               return 0;
-       }
-
-fail:
-       winnt_error(status, L"Can't set reparse data on \"%ls\"",
-                   current_path(ctx));
-       return WIMLIB_ERR_SET_REPARSE_DATA;
-}
-
 /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a
  * pointer to the suffix of the path that begins with the device directly, such
  * as e:\Windows\System32.  */
@@ -2233,7 +2229,7 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
        if (!list_empty(&ctx->reparse_dentries)) {
                if (blob->size > REPARSE_DATA_MAX_SIZE) {
                        dentry = list_first_entry(&ctx->reparse_dentries,
-                                                 struct wim_dentry, tmp_list);
+                                                 struct wim_dentry, d_tmp_list);
                        build_extraction_path(dentry, ctx);
                        ERROR("Reparse data of \"%ls\" has size "
                              "%"PRIu64" bytes (exceeds %u bytes)",
@@ -2245,7 +2241,7 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
                /* Reparse data  */
                memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, blob->size);
 
-               list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
+               list_for_each_entry(dentry, &ctx->reparse_dentries, d_tmp_list) {
 
                        /* Reparse point header  */
                        complete_reparse_point(&ctx->rpbuf, dentry->d_inode,
@@ -2262,7 +2258,7 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
 
        if (!list_empty(&ctx->encrypted_dentries)) {
                ctx->encrypted_size = blob->size;
-               list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
+               list_for_each_entry(dentry, &ctx->encrypted_dentries, d_tmp_list) {
                        ret = extract_encrypted_file(dentry, ctx);
                        ret = check_apply_error(dentry, ctx, ret);
                        if (ret)