Add code to create WIMBoot pointer files
[wimlib] / src / wimboot.c
1 /*
2  * wimboot.c
3  *
4  * Support for creating WIMBoot pointer files.
5  *
6  * See http://technet.microsoft.com/en-us/library/dn594399.aspx for general
7  * information about WIMBoot.
8  *
9  * Note that WIMBoot pointer files are actually implemented on top of the
10  * Windows Overlay File System Filter (WOF).  See wof.h for more info.
11  */
12
13 /*
14  * Copyright (C) 2014 Eric Biggers
15  *
16  * This file is part of wimlib, a library for working with WIM files.
17  *
18  * wimlib is free software; you can redistribute it and/or modify it under the
19  * terms of the GNU General Public License as published by the Free
20  * Software Foundation; either version 3 of the License, or (at your option)
21  * any later version.
22  *
23  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
24  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
25  * A PARTICULAR PURPOSE. See the GNU General Public License for more
26  * details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with wimlib; if not, see http://www.gnu.org/licenses/.
30  */
31
32 #ifndef __WIN32__
33 #  error "This file contains Windows code!"
34 #endif
35
36 #ifdef HAVE_CONFIG_H
37 #  include "config.h"
38 #endif
39
40 #include "wimlib/win32_common.h"
41 #include "wimlib/win32.h"
42 #include "wimlib/assert.h"
43 #include "wimlib/error.h"
44 #include "wimlib/util.h"
45 #include "wimlib/wimboot.h"
46 #include "wimlib/wof.h"
47
48 static int
49 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
50 {
51         tchar *file_abspath;
52
53         file_abspath = realpath(file_path, NULL);
54         if (!file_abspath)
55                 return WIMLIB_ERR_NOMEM;
56
57         if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
58                 ERROR("\"%ls\": Path format not recognized", file_abspath);
59                 FREE(file_abspath);
60                 return WIMLIB_ERR_UNSUPPORTED;
61         }
62
63         wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
64         FREE(file_abspath);
65         return 0;
66 }
67
68 /*
69  * Allocate a WOF data source ID for a WIM file.
70  *
71  * @wim_path
72  *      Absolute path to the WIM file.  This must include a drive letter and use
73  *      backslash path separators.
74  * @image
75  *      Index of the image in the WIM being applied.
76  * @target
77  *      Path to the target drive.
78  * @data_source_id_ret
79  *      On success, an identifier for the backing WIM file will be returned
80  *      here.
81  *
82  * Returns 0 on success, or a positive error code on failure.
83  */
84 int
85 wimboot_alloc_data_source_id(const wchar_t *wim_path, int image,
86                              const wchar_t *target, u64 *data_source_id_ret)
87 {
88         tchar drive_path[7];
89         size_t wim_path_nchars;
90         size_t wim_file_name_length;
91         void *in;
92         size_t insize;
93         struct wof_external_info *wof_info;
94         struct wim_provider_add_overlay_input *wim_info;
95         HANDLE h;
96         u64 data_source_id;
97         DWORD bytes_returned;
98         int ret;
99         const wchar_t *prefix = L"\\??\\";
100         const size_t prefix_nchars = 4;
101
102         ret = win32_get_drive_path(target, drive_path);
103         if (ret)
104                 return ret;
105
106         wimlib_assert(!wcschr(wim_path, L'/'));
107         wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':');
108
109         wim_path_nchars = wcslen(wim_path);
110         wim_file_name_length = sizeof(wchar_t) *
111                                (wim_path_nchars + prefix_nchars);
112
113         insize = sizeof(struct wof_external_info) +
114                  sizeof(struct wim_provider_add_overlay_input) +
115                  wim_file_name_length;
116
117         in = MALLOC(insize);
118         if (!in) {
119                 ret = WIMLIB_ERR_NOMEM;
120                 goto out;
121         }
122
123         wof_info = (struct wof_external_info *)in;
124         wof_info->version = WOF_CURRENT_VERSION;
125         wof_info->provider = WOF_PROVIDER_WIM;
126
127         wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
128         wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
129         wim_info->wim_index = image;
130         wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
131                                                   wim_file_name);
132         wim_info->wim_file_name_length = wim_file_name_length;
133         wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
134         wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
135
136         h = CreateFile(drive_path, GENERIC_WRITE,
137                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
138                        NULL, OPEN_EXISTING, 0, NULL);
139
140         if (h == INVALID_HANDLE_VALUE) {
141                 set_errno_from_GetLastError();
142                 ERROR_WITH_ERRNO("Failed to open \"%ls\"", drive_path);
143                 ret = WIMLIB_ERR_OPEN;
144                 goto out_free_in;
145         }
146
147         if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY,
148                              in, insize,
149                              &data_source_id, sizeof(data_source_id),
150                              &bytes_returned, NULL))
151         {
152                 set_errno_from_GetLastError();
153                 ERROR_WITH_ERRNO("Failed to add overlay source \"%ls\" "
154                                  "to volume \"%ls\"", wim_path, drive_path);
155                 ret = WIMLIB_ERR_WIMBOOT;
156                 goto out_close_handle;
157         }
158
159         if (bytes_returned != sizeof(data_source_id)) {
160                 set_errno_from_win32_error(ERROR_INVALID_DATA);
161                 ret = WIMLIB_ERR_WIMBOOT;
162                 ERROR("Unexpected result size when adding "
163                       "overlay source \"%ls\" to volume \"%ls\"",
164                       wim_path, drive_path);
165                 goto out_close_handle;
166         }
167
168         *data_source_id_ret = data_source_id;
169         ret = 0;
170
171 out_close_handle:
172         CloseHandle(h);
173 out_free_in:
174         FREE(in);
175 out:
176         return ret;
177 }
178
179
180 /*
181  * Set WIMBoot information on the specified file.
182  *
183  * @path
184  *      Path to extracted file (already created).
185  * @data_source_id
186  *      Identifier for backing WIM file.
187  * @hash
188  *      SHA-1 message digest of the file's unnamed data stream.
189  *
190  * Returns 0 on success, or a positive error code on failure.
191  */
192 int
193 wimboot_set_pointer(const wchar_t *path, u64 data_source_id,
194                     const u8 hash[20])
195 {
196         struct {
197                 struct wof_external_info wof_info;
198                 struct wim_provider_external_info wim_info;
199         } in;
200         HANDLE h;
201         DWORD bytes_returned;
202         int ret;
203
204         in.wof_info.version = WOF_CURRENT_VERSION;
205         in.wof_info.provider = WOF_PROVIDER_WIM;
206
207         in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
208         in.wim_info.flags = 0;
209         in.wim_info.data_source_id = data_source_id;
210         memcpy(in.wim_info.resource_hash, hash, 20);
211
212         h = win32_open_existing_file(path, GENERIC_WRITE);
213         if (h == INVALID_HANDLE_VALUE) {
214                 set_errno_from_GetLastError();
215                 ret = WIMLIB_ERR_OPEN;
216                 goto out;
217         }
218
219         if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING,
220                              &in, sizeof(in), NULL, 0, &bytes_returned, NULL))
221         {
222                 set_errno_from_GetLastError();
223                 ret = WIMLIB_ERR_WIMBOOT;
224                 goto out_close_handle;
225         }
226         ret = 0;
227 out_close_handle:
228         CloseHandle(h);
229 out:
230         return ret;
231 }