Add code to create WIMBoot pointer files
authorEric Biggers <ebiggers3@gmail.com>
Sun, 13 Apr 2014 06:59:37 +0000 (01:59 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Fri, 18 Apr 2014 06:23:04 +0000 (01:23 -0500)
Makefile.am
include/wimlib.h
include/wimlib/wimboot.h [new file with mode: 0644]
include/wimlib/wof.h [new file with mode: 0644]
src/wimboot.c [new file with mode: 0644]

index 783821e..23ffcd9 100644 (file)
@@ -135,8 +135,11 @@ libwim_la_SOURCES += src/win32_common.c                    \
                     src/win32_apply.c                  \
                     src/win32_capture.c                \
                     src/win32_replacements.c           \
+                    src/wimboot.c                      \
                     include/wimlib/win32_common.h      \
-                    include/wimlib/win32.h
+                    include/wimlib/win32.h             \
+                    include/wimlib/wimboot.h           \
+                    include/wimlib/wof.h
 else
 libwim_la_SOURCES += src/unix_apply.c          \
                     src/unix_capture.c
index 799f66b..17d76b5 100644 (file)
@@ -1227,6 +1227,7 @@ struct wimlib_dir_entry {
 #define WIMLIB_REPARSE_TAG_DFS                 0x8000000A
 #define WIMLIB_REPARSE_TAG_DFSR                        0x80000012
 #define WIMLIB_REPARSE_TAG_FILTER_MANAGER      0x8000000B
+#define WIMLIB_REPARSE_TAG_WIMBOOT             0x80000017
 #define WIMLIB_REPARSE_TAG_SYMLINK             0xA000000C
        /** If the file is a reparse point (FILE_ATTRIBUTE_DIRECTORY set in the
         * attributes), this will give the reparse tag.  This tells you whether
diff --git a/include/wimlib/wimboot.h b/include/wimlib/wimboot.h
new file mode 100644 (file)
index 0000000..35d9626
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _WIMBOOT_H_
+#define _WIMBOOT_H_
+
+#include "wimlib/types.h"
+
+extern int
+wimboot_set_pointer(const wchar_t *path, u64 data_source_id,
+                   const u8 hash[20]);
+
+extern int
+wimboot_alloc_data_source_id(const wchar_t *wim_path, int image,
+                            const wchar_t *target, u64 *data_source_id_ret);
+
+#endif /* _WIMBOOT_H_ */
diff --git a/include/wimlib/wof.h b/include/wimlib/wof.h
new file mode 100644 (file)
index 0000000..a588a95
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * wof.h
+ *
+ * Definitions for Windows Overlay File System Filter (WOF) ioctls.  See
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff540367(v=vs.85).aspx
+ * for more information.
+ */
+
+#ifndef _WOF_H_
+#define _WOF_H_
+
+#include "wimlib/types.h"
+
+#define WOF_CURRENT_VERSION    1
+#define WOF_PROVIDER_WIM       1
+#define WIM_PROVIDER_CURRENT_VERSION 1
+
+/* Identifies a backing provider for a specific overlay service version.  */
+struct wof_external_info {
+
+       /* Version of the overlay service supported by the backing provider.
+        * Set to WOF_CURRENT_VERSION.  */
+       u32 version;
+
+       /* Identifier for the backing provider.  Example value:
+        * WOF_PROVIDER_WIM.  */
+       u32 provider;
+};
+
+/* WOF reparse points can't be directly manipulated on Windows; setting the
+ * reparse data doesn't seem to work, and the WOF driver hides the reparse
+ * points so their data can't be read from Windows 8.1 and later.  Use the
+ * ioctls (FSCTL_SET_EXTERNAL_BACKING, FSCTL_GET_EXTERNAL_BACKING,
+ * FSCTL_DELETE_EXTERNAL_BACKING) instead.  */
+#if 0
+/*
+ * Format of the reparse data of WoF (Windows Overlay File System Filter)
+ * reparse points.  These include WIMBoot "pointer files".
+ *
+ * Notes:
+ *     - Reparse tag is 0x80000017
+ *     - Don't make these if the file has no unnamed data stream, has an empty
+ *       unnamed data stream, or already is a reparse point.
+ *     - There is nowhere to put named data streams.  They have to copied
+ *       literally to the reparse point file.
+ */
+struct wof_rpdata_disk {
+       struct wof_external_info info;
+       union {
+               struct {
+                       /* (Guess) Version of this structure --- set to 2.  */
+                       u64 version;
+
+                       /* Integer ID that identifies the WIM.  */
+                       u64 data_source_id;
+
+                       /* SHA1 message digest of the file's unnamed data
+                        * stream.  */
+                       u8 stream_sha1[20];
+
+                       /* SHA1 message digest of the WIM's lookup table.  */
+                       u8 wim_lookup_table_sha1[20];
+
+                       /* Uncompressed size of the file's unnamed data stream,
+                        * in bytes.  */
+                       u64 stream_uncompressed_size;
+
+                       /* Compressed size of the file's unnamed data stream, in
+                        * bytes.  If stream is stored uncompressed, set this
+                        * the same as the uncompressed size.  */
+                       u64 stream_compressed_size;
+
+                       /* Byte offset of the file's unnamed data stream in the
+                        * WIM.  */
+                       u64 stream_offset_in_wim;
+               } wim;
+       } provider_data;
+};
+#endif
+
+/*****************************************************************************
+ *
+ * --- FSCTL_SET_EXTERNAL_BACKING ---
+ *
+ * Sets the backing source of a file.
+ *
+ * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
+ * Access:     0 (FILE_ANY_ACCESS)
+ * Function:   195
+ * Method:     0 (METHOD_BUFFERED)
+ *
+ * Input buffer:  'struct wof_external_info' followed by provider-specific data
+ * ('struct wim_provider_external_info' in the case of WIM).
+ *
+ * Output buffer: None
+ */
+#define FSCTL_SET_EXTERNAL_BACKING 0x9030C
+
+struct wim_provider_external_info {
+
+       /* Set to WIM_PROVIDER_CURRENT_VERSION.  */
+       u32 version;
+
+       /* 0 when WIM provider active, otherwise
+        * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or
+        * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED.  */
+       u32 flags;
+
+       /* Integer ID that identifies the WIM.  Get this with the
+        * FSCTL_ADD_OVERLAY ioctl.  */
+       u64 data_source_id;
+
+       /* SHA1 message digest of the file's unnamed data stream.  */
+       u8 resource_hash[20];
+};
+
+/*****************************************************************************
+ *
+ * --- FSCTL_ADD_OVERLAY ---
+ *
+ * Adds a new external backing source to a volume.
+ *
+ * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
+ * Access:     2 (FILE_WRITE_ACCESS)
+ * Function:   204
+ * Method:     0 (METHOD_BUFFERED)
+ *
+ * Input buffer:  'struct wof_external_info' followed by provider-specific data
+ * ('struct wim_provider_add_overlay_input' in the case of WIM).
+ *
+ * Output buffer:  Buffer large enough to receive any information resulting from
+ * the add operation.  For the WIM provider, this must be an 8 byte buffer that
+ * receives the 64-bit WIM file ID.
+ */
+#define FSCTL_ADD_OVERLAY 0x98330
+
+struct wim_provider_add_overlay_input {
+
+       /* Type of WIM file.  */
+       u32 wim_type;
+#define WIM_BOOT_OS_WIM                0
+#define WIM_BOOT_NOT_OS_WIM    1
+
+       /* Index of the image in the WIM to use??? (This doesn't really make
+        * sense, since WIM files combine streams for all images into a single
+        * table.  Set to 1 if unsure...)  */
+       u32 wim_index;
+
+       /* Byte offset of wim_file_name in this buffer, not including the
+        * preceding 'struct wof_external_info' (should be 16).  */
+       u32 wim_file_name_offset;
+
+       /* Number of bytes in wim_file_name.  */
+       u32 wim_file_name_length;
+
+       /* Full path to the WIM, e.g. "\??\d:\test-wimboot.wim".
+        * Does NOT need to be null terminated (MS docs claim otherwise).  */
+       wchar_t wim_file_name[];
+};
+
+#endif /* _WOF_H_ */
diff --git a/src/wimboot.c b/src/wimboot.c
new file mode 100644 (file)
index 0000000..caefb65
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * wimboot.c
+ *
+ * Support for creating WIMBoot pointer files.
+ *
+ * See http://technet.microsoft.com/en-us/library/dn594399.aspx for general
+ * information about WIMBoot.
+ *
+ * Note that WIMBoot pointer files are actually implemented on top of the
+ * Windows Overlay File System Filter (WOF).  See wof.h for more info.
+ */
+
+/*
+ * Copyright (C) 2014 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * wimlib is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifndef __WIN32__
+#  error "This file contains Windows code!"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib/win32_common.h"
+#include "wimlib/win32.h"
+#include "wimlib/assert.h"
+#include "wimlib/error.h"
+#include "wimlib/util.h"
+#include "wimlib/wimboot.h"
+#include "wimlib/wof.h"
+
+static int
+win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
+{
+       tchar *file_abspath;
+
+       file_abspath = realpath(file_path, NULL);
+       if (!file_abspath)
+               return WIMLIB_ERR_NOMEM;
+
+       if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
+               ERROR("\"%ls\": Path format not recognized", file_abspath);
+               FREE(file_abspath);
+               return WIMLIB_ERR_UNSUPPORTED;
+       }
+
+       wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
+       FREE(file_abspath);
+       return 0;
+}
+
+/*
+ * Allocate a WOF data source ID for a WIM file.
+ *
+ * @wim_path
+ *     Absolute path to the WIM file.  This must include a drive letter and use
+ *     backslash path separators.
+ * @image
+ *     Index of the image in the WIM being applied.
+ * @target
+ *     Path to the target drive.
+ * @data_source_id_ret
+ *     On success, an identifier for the backing WIM file will be returned
+ *     here.
+ *
+ * Returns 0 on success, or a positive error code on failure.
+ */
+int
+wimboot_alloc_data_source_id(const wchar_t *wim_path, int image,
+                            const wchar_t *target, u64 *data_source_id_ret)
+{
+       tchar drive_path[7];
+       size_t wim_path_nchars;
+       size_t wim_file_name_length;
+       void *in;
+       size_t insize;
+       struct wof_external_info *wof_info;
+       struct wim_provider_add_overlay_input *wim_info;
+       HANDLE h;
+       u64 data_source_id;
+       DWORD bytes_returned;
+       int ret;
+       const wchar_t *prefix = L"\\??\\";
+       const size_t prefix_nchars = 4;
+
+       ret = win32_get_drive_path(target, drive_path);
+       if (ret)
+               return ret;
+
+       wimlib_assert(!wcschr(wim_path, L'/'));
+       wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':');
+
+       wim_path_nchars = wcslen(wim_path);
+       wim_file_name_length = sizeof(wchar_t) *
+                              (wim_path_nchars + prefix_nchars);
+
+       insize = sizeof(struct wof_external_info) +
+                sizeof(struct wim_provider_add_overlay_input) +
+                wim_file_name_length;
+
+       in = MALLOC(insize);
+       if (!in) {
+               ret = WIMLIB_ERR_NOMEM;
+               goto out;
+       }
+
+       wof_info = (struct wof_external_info *)in;
+       wof_info->version = WOF_CURRENT_VERSION;
+       wof_info->provider = WOF_PROVIDER_WIM;
+
+       wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
+       wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
+       wim_info->wim_index = image;
+       wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
+                                                 wim_file_name);
+       wim_info->wim_file_name_length = wim_file_name_length;
+       wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
+       wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
+
+       h = CreateFile(drive_path, GENERIC_WRITE,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                      NULL, OPEN_EXISTING, 0, NULL);
+
+       if (h == INVALID_HANDLE_VALUE) {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("Failed to open \"%ls\"", drive_path);
+               ret = WIMLIB_ERR_OPEN;
+               goto out_free_in;
+       }
+
+       if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY,
+                            in, insize,
+                            &data_source_id, sizeof(data_source_id),
+                            &bytes_returned, NULL))
+       {
+               set_errno_from_GetLastError();
+               ERROR_WITH_ERRNO("Failed to add overlay source \"%ls\" "
+                                "to volume \"%ls\"", wim_path, drive_path);
+               ret = WIMLIB_ERR_WIMBOOT;
+               goto out_close_handle;
+       }
+
+       if (bytes_returned != sizeof(data_source_id)) {
+               set_errno_from_win32_error(ERROR_INVALID_DATA);
+               ret = WIMLIB_ERR_WIMBOOT;
+               ERROR("Unexpected result size when adding "
+                     "overlay source \"%ls\" to volume \"%ls\"",
+                     wim_path, drive_path);
+               goto out_close_handle;
+       }
+
+       *data_source_id_ret = data_source_id;
+       ret = 0;
+
+out_close_handle:
+       CloseHandle(h);
+out_free_in:
+       FREE(in);
+out:
+       return ret;
+}
+
+
+/*
+ * Set WIMBoot information on the specified file.
+ *
+ * @path
+ *     Path to extracted file (already created).
+ * @data_source_id
+ *     Identifier for backing WIM file.
+ * @hash
+ *     SHA-1 message digest of the file's unnamed data stream.
+ *
+ * Returns 0 on success, or a positive error code on failure.
+ */
+int
+wimboot_set_pointer(const wchar_t *path, u64 data_source_id,
+                   const u8 hash[20])
+{
+       struct {
+               struct wof_external_info wof_info;
+               struct wim_provider_external_info wim_info;
+       } in;
+       HANDLE h;
+       DWORD bytes_returned;
+       int ret;
+
+       in.wof_info.version = WOF_CURRENT_VERSION;
+       in.wof_info.provider = WOF_PROVIDER_WIM;
+
+       in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
+       in.wim_info.flags = 0;
+       in.wim_info.data_source_id = data_source_id;
+       memcpy(in.wim_info.resource_hash, hash, 20);
+
+       h = win32_open_existing_file(path, GENERIC_WRITE);
+       if (h == INVALID_HANDLE_VALUE) {
+               set_errno_from_GetLastError();
+               ret = WIMLIB_ERR_OPEN;
+               goto out;
+       }
+
+       if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
+                            &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
+       {
+               set_errno_from_GetLastError();
+               ret = WIMLIB_ERR_WIMBOOT;
+               goto out_close_handle;
+       }
+       ret = 0;
+out_close_handle:
+       CloseHandle(h);
+out:
+       return ret;
+}