From: Eric Biggers Date: Sun, 13 Apr 2014 06:59:37 +0000 (-0500) Subject: Add code to create WIMBoot pointer files X-Git-Tag: v1.7.0~292 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=e20f8057569b65f75994e8d88c7a0be80a749888 Add code to create WIMBoot pointer files --- diff --git a/Makefile.am b/Makefile.am index 783821e4..23ffcd9c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/include/wimlib.h b/include/wimlib.h index 799f66b1..17d76b5a 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -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 index 00000000..35d96263 --- /dev/null +++ b/include/wimlib/wimboot.h @@ -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 index 00000000..a588a953 --- /dev/null +++ b/include/wimlib/wof.h @@ -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 index 00000000..caefb654 --- /dev/null +++ b/src/wimboot.c @@ -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; +}