Capitalize "Windows Overlay Filesystem" consistently
[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 http://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
278 retry:
279         n = vsnwprintf(buf, buflen, format, va);
280
281         if (n >= buflen)
282                 goto realloc;
283
284         n += snwprintf(&buf[n], buflen - n,
285                        (is_ntstatus ?
286                         L" (status=%08"PRIx32"): " :
287                         L" (err=%"PRIu32"): "),
288                        code);
289
290         if (n >= buflen)
291                 goto realloc;
292
293         ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
294                                 FORMAT_MESSAGE_IGNORE_INSERTS |
295                                 (is_ntstatus ? FORMAT_MESSAGE_FROM_HMODULE : 0),
296                             (is_ntstatus ? ntdll_handle : NULL),
297                             code,
298                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
299                             &buf[n],
300                             buflen - n,
301                             NULL);
302         n += ret;
303
304         if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
305                 goto realloc;
306
307         if (buf[n - 1] == L'\n')
308                 buf[--n] = L'\0';
309         if (buf[n - 1] == L'\r')
310                 buf[--n] = L'\0';
311         if (buf[n - 1] == L'.')
312                 buf[--n] = L'\0';
313
314         if (is_error)
315                 ERROR("%ls", buf);
316         else
317                 WARNING("%ls", buf);
318         if (buf != _buf)
319                 FREE(buf);
320         return;
321
322 realloc:
323         if (buf != _buf)
324                 FREE(buf);
325         buflen *= 2;
326         buf = MALLOC(buflen * sizeof(buf[0]));
327         if (buf)
328                 goto retry;
329         ERROR("Ran out of memory while building error message!!!");
330 }
331
332 void
333 win32_warning(DWORD err, const wchar_t *format, ...)
334 {
335         va_list va;
336
337         va_start(va, format);
338         windows_msg(err, format, va, false, false);
339         va_end(va);
340 }
341
342 void
343 win32_error(DWORD err, const wchar_t *format, ...)
344 {
345         va_list va;
346
347         va_start(va, format);
348         windows_msg(err, format, va, false, true);
349         va_end(va);
350 }
351
352 void
353 winnt_warning(NTSTATUS status, const wchar_t *format, ...)
354 {
355         va_list va;
356
357         va_start(va, format);
358         windows_msg(status, format, va, true, false);
359         va_end(va);
360 }
361
362 void
363 winnt_error(NTSTATUS status, const wchar_t *format, ...)
364 {
365         va_list va;
366
367         va_start(va, format);
368         windows_msg(status, format, va, true, true);
369         va_end(va);
370 }
371
372 /*
373  * Synchronously execute a filesystem control method.  This is a wrapper around
374  * NtFsControlFile() that handles STATUS_PENDING.  Note that SYNCHRONIZE
375  * permission is, in general, required on the handle.
376  */
377 NTSTATUS
378 winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size,
379             void *out, u32 out_size_avail, u32 *actual_out_size_ret)
380 {
381         IO_STATUS_BLOCK iosb;
382         NTSTATUS status;
383
384         status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, code,
385                                  (void *)in, in_size, out, out_size_avail);
386         if (status == STATUS_PENDING) {
387                 /* Beware: this case is often encountered with remote
388                  * filesystems, but rarely with local filesystems.  */
389
390                 status = NtWaitForSingleObject(h, FALSE, NULL);
391                 if (NT_SUCCESS(status)) {
392                         status = iosb.Status;
393                 } else {
394                         /* We shouldn't be issuing ioctls on a handle to which
395                          * we don't have SYNCHRONIZE access.  Otherwise we have
396                          * no way to wait for them to complete.  */
397                         wimlib_assert(status != STATUS_ACCESS_DENIED);
398                 }
399         }
400
401         if (NT_SUCCESS(status) && actual_out_size_ret != NULL)
402                 *actual_out_size_ret = (u32)iosb.Information;
403
404         return status;
405 }
406
407 #endif /* __WIN32__ */