]> wimlib.net Git - wimlib/blob - src/win32_common.c
v1.14.4
[wimlib] / src / win32_common.c
1 /*
2  * win32_common.c - Windows code common to applying and capturing images.
3  */
4
5 /*
6  * Copyright (C) 2013-2016 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see https://www.gnu.org/licenses/.
20  */
21
22 #ifdef _WIN32
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib/win32_common.h"
29
30 #include "wimlib/assert.h"
31 #include "wimlib/error.h"
32 #include "wimlib/util.h"
33 #include "wimlib/win32_vss.h"
34
35 static bool
36 win32_modify_privilege(const wchar_t *privilege, bool enable)
37 {
38         HANDLE hToken;
39         LUID luid;
40         TOKEN_PRIVILEGES newState;
41         bool ret = FALSE;
42
43         if (!OpenProcessToken(GetCurrentProcess(),
44                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
45                               &hToken))
46                 goto out;
47
48         if (!LookupPrivilegeValue(NULL, privilege, &luid))
49                 goto out_close_handle;
50
51         newState.PrivilegeCount = 1;
52         newState.Privileges[0].Luid = luid;
53         newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
54         SetLastError(ERROR_SUCCESS);
55         ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
56         if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED)
57                 ret = FALSE;
58 out_close_handle:
59         CloseHandle(hToken);
60 out:
61         return ret;
62 }
63
64 static bool
65 win32_modify_capture_privileges(bool enable)
66 {
67         bool ok = true;
68         ok &= win32_modify_privilege(SE_BACKUP_NAME, enable);
69         ok &= win32_modify_privilege(SE_SECURITY_NAME, enable);
70         return ok;
71 }
72
73 static bool
74 win32_modify_apply_privileges(bool enable)
75 {
76         bool ok = true;
77         ok &= win32_modify_privilege(SE_RESTORE_NAME, enable);
78         ok &= win32_modify_privilege(SE_SECURITY_NAME, enable);
79         ok &= win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable);
80         ok &= win32_modify_privilege(SE_MANAGE_VOLUME_NAME, enable);
81         return ok;
82 }
83
84 static void
85 win32_release_capture_and_apply_privileges(void)
86 {
87         win32_modify_capture_privileges(false);
88         win32_modify_apply_privileges(false);
89 }
90
91 /* Pointers to dynamically loaded functions  */
92
93 NTSTATUS (WINAPI *func_RtlDosPathNameToNtPathName_U_WithStatus)
94                 (IN PCWSTR DosName,
95                  OUT PUNICODE_STRING NtName,
96                  OUT PCWSTR *PartName,
97                  OUT PRTL_RELATIVE_NAME_U RelativeName);
98
99 NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
100                 (PCUNICODE_STRING VolumeRootPath);
101
102 static bool acquired_privileges = false;
103
104 static HMODULE ntdll_handle = NULL;
105
106 static int
107 init_ntdll(void)
108 {
109         ntdll_handle = LoadLibrary(L"ntdll.dll");
110
111         if (!ntdll_handle) {
112                 ERROR("Unable to load ntdll.dll");
113                 return WIMLIB_ERR_UNSUPPORTED;
114         }
115
116         func_RtlDosPathNameToNtPathName_U_WithStatus =
117                 (void *)GetProcAddress(ntdll_handle,
118                                        "RtlDosPathNameToNtPathName_U_WithStatus");
119
120         func_RtlCreateSystemVolumeInformationFolder =
121                 (void *)GetProcAddress(ntdll_handle,
122                                        "RtlCreateSystemVolumeInformationFolder");
123         return 0;
124 }
125
126 /* One-time initialization for Windows capture/apply code.  */
127 int
128 win32_global_init(int init_flags)
129 {
130         int ret;
131
132         /* Try to acquire useful privileges.  */
133         if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) {
134                 ret = WIMLIB_ERR_INSUFFICIENT_PRIVILEGES;
135                 if (!win32_modify_capture_privileges(true))
136                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES)
137                                 goto out_drop_privs;
138                 if (!win32_modify_apply_privileges(true))
139                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES)
140                                 goto out_drop_privs;
141                 acquired_privileges = true;
142         }
143
144         ret = init_ntdll();
145         if (ret)
146                 goto out_drop_privs;
147
148         return 0;
149
150 out_drop_privs:
151         win32_release_capture_and_apply_privileges();
152         return ret;
153 }
154
155 void
156 win32_global_cleanup(void)
157 {
158         vss_global_cleanup();
159
160         if (acquired_privileges)
161                 win32_release_capture_and_apply_privileges();
162
163         FreeLibrary(ntdll_handle);
164         ntdll_handle = NULL;
165 }
166
167 /*
168  * Translates a Win32-namespace path into an NT-namespace path.
169  *
170  * On success, returns 0.  The NT-namespace path will be stored in the
171  * UNICODE_STRING structure pointed to by nt_path.  nt_path->Buffer will be set
172  * to a new buffer that must later be freed with HeapFree().  (Really
173  * RtlHeapFree(), but HeapFree() seems to be the same thing.)
174  *
175  * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM.
176  */
177 int
178 win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path)
179 {
180         NTSTATUS status;
181
182         if (func_RtlDosPathNameToNtPathName_U_WithStatus) {
183                 status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path,
184                                                                          nt_path,
185                                                                          NULL, NULL);
186         } else {
187                 if (RtlDosPathNameToNtPathName_U(win32_path, nt_path, NULL, NULL))
188                         status = STATUS_SUCCESS;
189                 else
190                         status = STATUS_NO_MEMORY;
191         }
192
193         if (likely(NT_SUCCESS(status)))
194                 return 0;
195
196         if (status == STATUS_NO_MEMORY)
197                 return WIMLIB_ERR_NOMEM;
198
199         winnt_error(status, L"\"%ls\": invalid path name", win32_path);
200         return WIMLIB_ERR_INVALID_PARAM;
201 }
202
203 int
204 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
205 {
206         tchar *file_abspath;
207
208         file_abspath = realpath(file_path, NULL);
209         if (!file_abspath)
210                 return WIMLIB_ERR_NOMEM;
211
212         if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
213                 ERROR("\"%ls\": Path format not recognized", file_abspath);
214                 FREE(file_abspath);
215                 return WIMLIB_ERR_UNSUPPORTED;
216         }
217
218         wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
219         FREE(file_abspath);
220         return 0;
221 }
222
223 /* Try to attach an instance of the Windows Overlay Filesystem filter driver to
224  * the specified drive (such as C:)  */
225 bool
226 win32_try_to_attach_wof(const wchar_t *drive)
227 {
228         HMODULE fltlib;
229         bool retval = false;
230
231         /* Use FilterAttach() from Fltlib.dll.  */
232
233         fltlib = LoadLibrary(L"Fltlib.dll");
234
235         if (!fltlib) {
236                 WARNING("Failed to load Fltlib.dll");
237                 return retval;
238         }
239
240         HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
241                                             LPCWSTR lpVolumeName,
242                                             LPCWSTR lpInstanceName,
243                                             DWORD dwCreatedInstanceNameLength,
244                                             LPWSTR lpCreatedInstanceName);
245
246         func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
247
248         if (func_FilterAttach) {
249                 HRESULT res;
250
251                 res = (*func_FilterAttach)(L"wof", drive, NULL, 0, NULL);
252
253                 if (res != S_OK)
254                         res = (*func_FilterAttach)(L"wofadk", drive, NULL, 0, NULL);
255
256                 if (res == S_OK)
257                         retval = true;
258         } else {
259                 WARNING("FilterAttach() does not exist in Fltlib.dll");
260         }
261
262         FreeLibrary(fltlib);
263
264         return retval;
265 }
266
267
268 static void
269 windows_msg(u32 code, const wchar_t *format, va_list va,
270             bool is_ntstatus, bool is_error)
271 {
272         wchar_t _buf[STACK_MAX / 8];
273         wchar_t *buf = _buf;
274         size_t buflen = ARRAY_LEN(_buf);
275         size_t ret;
276         size_t n;
277         va_list va2;
278
279 retry:
280         va_copy(va2, va);
281         n = vsnwprintf(buf, buflen, format, va2);
282         va_end(va2);
283
284         if (n >= buflen)
285                 goto realloc;
286
287         n += snwprintf(&buf[n], buflen - n,
288                        (is_ntstatus ?
289                         L" (status=%08"PRIx32"): " :
290                         L" (err=%"PRIu32"): "),
291                        code);
292
293         if (n >= buflen)
294                 goto realloc;
295
296         ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
297                                 FORMAT_MESSAGE_IGNORE_INSERTS |
298                                 (is_ntstatus ? FORMAT_MESSAGE_FROM_HMODULE : 0),
299                             (is_ntstatus ? ntdll_handle : NULL),
300                             code,
301                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
302                             &buf[n],
303                             buflen - n,
304                             NULL);
305         n += ret;
306
307         if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
308                 goto realloc;
309
310         if (buf[n - 1] == L'\n')
311                 buf[--n] = L'\0';
312         if (buf[n - 1] == L'\r')
313                 buf[--n] = L'\0';
314         if (buf[n - 1] == L'.')
315                 buf[--n] = L'\0';
316
317         if (is_error)
318                 ERROR("%ls", buf);
319         else
320                 WARNING("%ls", buf);
321         if (buf != _buf)
322                 FREE(buf);
323         return;
324
325 realloc:
326         if (buf != _buf)
327                 FREE(buf);
328         buflen *= 2;
329         buf = MALLOC(buflen * sizeof(buf[0]));
330         if (buf)
331                 goto retry;
332         ERROR("Ran out of memory while building error message!!!");
333 }
334
335 void
336 win32_warning(DWORD err, const wchar_t *format, ...)
337 {
338         va_list va;
339
340         va_start(va, format);
341         windows_msg(err, format, va, false, false);
342         va_end(va);
343 }
344
345 void
346 win32_error(DWORD err, const wchar_t *format, ...)
347 {
348         va_list va;
349
350         va_start(va, format);
351         windows_msg(err, format, va, false, true);
352         va_end(va);
353 }
354
355 void
356 winnt_warning(NTSTATUS status, const wchar_t *format, ...)
357 {
358         va_list va;
359
360         va_start(va, format);
361         windows_msg(status, format, va, true, false);
362         va_end(va);
363 }
364
365 void
366 winnt_error(NTSTATUS status, const wchar_t *format, ...)
367 {
368         va_list va;
369
370         va_start(va, format);
371         windows_msg(status, format, va, true, true);
372         va_end(va);
373 }
374
375 /*
376  * Synchronously execute a filesystem control method.  This is a wrapper around
377  * NtFsControlFile() that handles STATUS_PENDING.  Note that SYNCHRONIZE
378  * permission is, in general, required on the handle.
379  */
380 NTSTATUS
381 winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size,
382             void *out, u32 out_size_avail, u32 *actual_out_size_ret)
383 {
384         IO_STATUS_BLOCK iosb;
385         NTSTATUS status;
386
387         status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, code,
388                                  (void *)in, in_size, out, out_size_avail);
389         if (status == STATUS_PENDING) {
390                 /* Beware: this case is often encountered with remote
391                  * filesystems, but rarely with local filesystems.  */
392
393                 status = NtWaitForSingleObject(h, FALSE, NULL);
394                 if (NT_SUCCESS(status)) {
395                         status = iosb.Status;
396                 } else {
397                         /* We shouldn't be issuing ioctls on a handle to which
398                          * we don't have SYNCHRONIZE access.  Otherwise we have
399                          * no way to wait for them to complete.  */
400                         wimlib_assert(status != STATUS_ACCESS_DENIED);
401                 }
402         }
403
404         if (NT_SUCCESS(status) && actual_out_size_ret != NULL)
405                 *actual_out_size_ret = (u32)iosb.Information;
406
407         return status;
408 }
409
410 #endif /* _WIN32 */