]> wimlib.net Git - wimlib/blobdiff - src/win32_capture.c
win32_capture.c: experimental fixup of WIM-backed files
[wimlib] / src / win32_capture.c
index 8937891e75ee205376142a627264470d8e00fb07..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;
                }
 
@@ -628,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.
         *
@@ -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)
@@ -1383,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)) {