]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
win32_capture.c: experimental fixup of WIM-backed files
[wimlib] / src / win32_capture.c
index 96b373602747c94808ed62b7da4fdfcc8ddc44d3..60d212d13ce102f62de70ebd711e045bb68e1db7 100644 (file)
@@ -38,6 +38,7 @@
 #include "wimlib/error.h"
 #include "wimlib/paths.h"
 #include "wimlib/reparse.h"
+#include "wimlib/wof.h"
 
 struct winnt_scan_stats {
        unsigned long num_get_sd_access_denied;
@@ -137,10 +138,16 @@ read_winnt_stream_prefix(const struct blob_descriptor *blob, u64 size,
 
                status = (*func_NtReadFile)(h, NULL, NULL, NULL,
                                            &iosb, buf, count, NULL, NULL);
-               if (!NT_SUCCESS(status)) {
-                       winnt_error(status, L"\"%ls\": Error reading data",
-                                   printable_path(path));
-                       ret = WIMLIB_ERR_READ;
+               if (unlikely(!NT_SUCCESS(status))) {
+                       if (status == STATUS_END_OF_FILE) {
+                               ERROR("\"%ls\": File was concurrently truncated",
+                                     printable_path(path));
+                               ret = WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED;
+                       } else {
+                               winnt_error(status, L"\"%ls\": Error reading data",
+                                           printable_path(path));
+                               ret = WIMLIB_ERR_READ;
+                       }
                        break;
                }
 
@@ -415,9 +422,8 @@ winnt_recurse_directory(HANDLE h,
        {
                const FILE_NAMES_INFORMATION *info = buf;
                for (;;) {
-                       if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') &&
-                           !(info->FileNameLength == 4 && info->FileName[0] == L'.' &&
-                                                          info->FileName[1] == L'.'))
+                       if (!should_ignore_filename(info->FileName,
+                                                   info->FileNameLength / 2))
                        {
                                wchar_t *p;
                                wchar_t *filename;
@@ -450,8 +456,7 @@ winnt_recurse_directory(HANDLE h,
 
                                if (ret)
                                        goto out_free_buf;
-                               if (child)
-                                       dentry_add_child(parent, child);
+                               attach_scanned_tree(parent, child, params->blob_table);
                        }
                        if (info->NextEntryOffset == 0)
                                break;
@@ -630,7 +635,7 @@ winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p,
         * SYMBOLIC_LINK_RELATIVE.  We will use this information, although this
         * may not always do what the user expects, since drive-relative
         * symbolic links such as "\Users\Public" have SYMBOLIC_LINK_RELATIVE
-        * set, in addition to truely relative symbolic links such as "Users" or
+        * set, in addition to truly relative symbolic links such as "Users" or
         * "Users\Public".  However, WIMGAPI (as of Windows 8.1) has this same
         * behavior.
         *
@@ -744,6 +749,8 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode,
        }
 
        inode->i_reparse_tag = le32_to_cpu(rpbuf.rptag);
+       inode->i_rp_reserved = le16_to_cpu(rpbuf.rpreserved);
+
        if (!inode_add_stream_with_data(inode,
                                        STREAM_TYPE_REPARSE_POINT,
                                        NO_STREAM_NAME,
@@ -1106,6 +1113,55 @@ set_sort_key(struct wim_inode *inode, u64 sort_key)
        }
 }
 
+/* Special case to better support in-place updates of backing WIM files: in
+ * WIMBOOT mode, if the file is backed by a WIM (perhaps the WIM being updated)
+ * and WOF is not attached to the volume, then capture the file as the
+ * "dereferenced file" rather than as the "pointer file".  Note that this only
+ * requires stream manipulation --- there is no special handling of metadata
+ * such as security descriptors required.  */
+static noinline_for_stack int
+fixup_wim_backed_file(struct wim_inode *inode, struct blob_table *blob_table)
+{
+       struct wim_inode_stream *strm;
+       struct blob_descriptor *blob;
+       u8 _rpdata[REPARSE_DATA_MAX_SIZE] _aligned_attribute(8);
+       struct {
+               struct wof_external_info wof_info;
+               struct wim_provider_rpdata wim_info;
+       } *rpdata = (void *)_rpdata;
+       int ret;
+
+       strm = inode_get_unnamed_stream(inode, STREAM_TYPE_REPARSE_POINT);
+       blob = stream_blob_resolved(strm);
+       if (!blob || blob->size < sizeof(*rpdata))
+               return 0;
+
+       ret = read_blob_into_buf(blob, rpdata);
+       if (ret)
+               return ret;
+
+       if (rpdata->wof_info.version != WOF_CURRENT_VERSION ||
+           rpdata->wof_info.provider != WOF_PROVIDER_WIM ||
+           rpdata->wim_info.version != 2)
+               return 0;
+
+       blob = lookup_blob(blob_table, rpdata->wim_info.unnamed_data_stream_hash);
+       if (!blob)
+               return 0;
+
+       /* All okay --- remove the reparse point and redirect the unnamed data
+        * stream to the "dereferenced" one.  */
+       inode_remove_stream(inode, strm, blob_table);
+       strm = inode_get_unnamed_data_stream(inode);
+       wimlib_assert(strm != NULL);
+       inode_replace_stream_blob(inode, strm, blob, blob_table);
+       inode->i_attributes &= ~(FILE_ATTRIBUTE_REPARSE_POINT |
+                                FILE_ATTRIBUTE_SPARSE_FILE);
+       if (inode->i_attributes == 0)
+               inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
+       return 0;
+}
+
 static noinline_for_stack u32
 get_volume_information(HANDLE h, const wchar_t *full_path,
                       struct capture_params *params)
@@ -1290,8 +1346,7 @@ retry_open:
                                     filename,
                                     file_info.ino,
                                     params->capture_root_dev,
-                                    (file_info.num_links <= 1 ||
-                                       (file_info.attributes & FILE_ATTRIBUTE_DIRECTORY)),
+                                    (file_info.num_links <= 1),
                                     &root);
        if (ret)
                goto out;
@@ -1384,6 +1439,15 @@ retry_open:
                        goto out;
        }
 
+       if (unlikely((params->add_flags & WIMLIB_ADD_FLAG_WIMBOOT) &&
+                    (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+                    (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_WOF)))
+       {
+               ret = fixup_wim_backed_file(inode, params->blob_table);
+               if (ret)
+                       goto out;
+       }
+
        set_sort_key(inode, sort_key);
 
        if (inode_is_directory(inode)) {