Add experimental support for Windows VSS
[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, 2014, 2015 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/error.h"
31 #include "wimlib/util.h"
32 #include "wimlib/win32_vss.h"
33
34 static bool
35 win32_modify_privilege(const wchar_t *privilege, bool enable)
36 {
37         HANDLE hToken;
38         LUID luid;
39         TOKEN_PRIVILEGES newState;
40         bool ret = FALSE;
41
42         if (!OpenProcessToken(GetCurrentProcess(),
43                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
44                               &hToken))
45                 goto out;
46
47         if (!LookupPrivilegeValue(NULL, privilege, &luid))
48                 goto out_close_handle;
49
50         newState.PrivilegeCount = 1;
51         newState.Privileges[0].Luid = luid;
52         newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
53         SetLastError(ERROR_SUCCESS);
54         ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
55         if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED)
56                 ret = FALSE;
57 out_close_handle:
58         CloseHandle(hToken);
59 out:
60         return ret;
61 }
62
63 static bool
64 win32_modify_capture_privileges(bool enable)
65 {
66         bool ok = true;
67         ok &= win32_modify_privilege(SE_BACKUP_NAME, enable);
68         ok &= win32_modify_privilege(SE_SECURITY_NAME, enable);
69         return ok;
70 }
71
72 static bool
73 win32_modify_apply_privileges(bool enable)
74 {
75         bool ok = true;
76         ok &= win32_modify_privilege(SE_RESTORE_NAME, enable);
77         ok &= win32_modify_privilege(SE_SECURITY_NAME, enable);
78         ok &= win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable);
79         ok &= win32_modify_privilege(SE_MANAGE_VOLUME_NAME, enable);
80         return ok;
81 }
82
83 static void
84 win32_release_capture_and_apply_privileges(void)
85 {
86         win32_modify_capture_privileges(false);
87         win32_modify_apply_privileges(false);
88 }
89
90 /* Pointers to dynamically loaded functions  */
91
92 /* ntdll.dll  */
93
94 NTSTATUS (WINAPI *func_NtCreateFile)(PHANDLE FileHandle,
95                                      ACCESS_MASK DesiredAccess,
96                                      POBJECT_ATTRIBUTES ObjectAttributes,
97                                      PIO_STATUS_BLOCK IoStatusBlock,
98                                      PLARGE_INTEGER AllocationSize,
99                                      ULONG FileAttributes,
100                                      ULONG ShareAccess,
101                                      ULONG CreateDisposition,
102                                      ULONG CreateOptions,
103                                      PVOID EaBuffer,
104                                      ULONG EaLength);
105
106 NTSTATUS (WINAPI *func_NtOpenFile) (PHANDLE FileHandle,
107                                     ACCESS_MASK DesiredAccess,
108                                     POBJECT_ATTRIBUTES ObjectAttributes,
109                                     PIO_STATUS_BLOCK IoStatusBlock,
110                                     ULONG ShareAccess,
111                                     ULONG OpenOptions);
112
113 NTSTATUS (WINAPI *func_NtReadFile) (HANDLE FileHandle,
114                                     HANDLE Event,
115                                     PIO_APC_ROUTINE ApcRoutine,
116                                     PVOID ApcContext,
117                                     PIO_STATUS_BLOCK IoStatusBlock,
118                                     PVOID Buffer,
119                                     ULONG Length,
120                                     PLARGE_INTEGER ByteOffset,
121                                     PULONG Key);
122
123 NTSTATUS (WINAPI *func_NtWriteFile) (HANDLE FileHandle,
124                                      HANDLE Event,
125                                      PIO_APC_ROUTINE ApcRoutine,
126                                      PVOID ApcContext,
127                                      PIO_STATUS_BLOCK IoStatusBlock,
128                                      PVOID Buffer,
129                                      ULONG Length,
130                                      PLARGE_INTEGER ByteOffset,
131                                      PULONG Key);
132
133 NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle,
134                                                PIO_STATUS_BLOCK IoStatusBlock,
135                                                PVOID FileInformation,
136                                                ULONG Length,
137                                                FILE_INFORMATION_CLASS FileInformationClass);
138
139 NTSTATUS (WINAPI *func_NtQuerySecurityObject)(HANDLE handle,
140                                               SECURITY_INFORMATION SecurityInformation,
141                                               PSECURITY_DESCRIPTOR SecurityDescriptor,
142                                               ULONG Length,
143                                               PULONG LengthNeeded);
144
145 NTSTATUS (WINAPI *func_NtQueryDirectoryFile) (HANDLE FileHandle,
146                                               HANDLE Event,
147                                               PIO_APC_ROUTINE ApcRoutine,
148                                               PVOID ApcContext,
149                                               PIO_STATUS_BLOCK IoStatusBlock,
150                                               PVOID FileInformation,
151                                               ULONG Length,
152                                               FILE_INFORMATION_CLASS FileInformationClass,
153                                               BOOLEAN ReturnSingleEntry,
154                                               PUNICODE_STRING FileName,
155                                               BOOLEAN RestartScan);
156
157 NTSTATUS (WINAPI *func_NtQueryVolumeInformationFile) (HANDLE FileHandle,
158                                                       PIO_STATUS_BLOCK IoStatusBlock,
159                                                       PVOID FsInformation,
160                                                       ULONG Length,
161                                                       FS_INFORMATION_CLASS FsInformationClass);
162
163 NTSTATUS (WINAPI *func_NtSetInformationFile)(HANDLE FileHandle,
164                                              PIO_STATUS_BLOCK IoStatusBlock,
165                                              PVOID FileInformation,
166                                              ULONG Length,
167                                              FILE_INFORMATION_CLASS FileInformationClass);
168
169 NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
170                                             SECURITY_INFORMATION SecurityInformation,
171                                             PSECURITY_DESCRIPTOR SecurityDescriptor);
172
173 NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle,
174                                          HANDLE Event,
175                                          PIO_APC_ROUTINE ApcRoutine,
176                                          PVOID ApcContext,
177                                          PIO_STATUS_BLOCK IoStatusBlock,
178                                          ULONG FsControlCode,
179                                          PVOID InputBuffer,
180                                          ULONG InputBufferLength,
181                                          PVOID OutputBuffer,
182                                          ULONG OutputBufferLength);
183
184 NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle);
185
186 DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
187
188 BOOLEAN (WINAPI *func_RtlDosPathNameToNtPathName_U)
189                   (IN PCWSTR DosName,
190                    OUT PUNICODE_STRING NtName,
191                    OUT PCWSTR *PartName,
192                    OUT PRTL_RELATIVE_NAME_U RelativeName);
193
194 NTSTATUS (WINAPI *func_RtlDosPathNameToNtPathName_U_WithStatus)
195                 (IN PCWSTR DosName,
196                  OUT PUNICODE_STRING NtName,
197                  OUT PCWSTR *PartName,
198                  OUT PRTL_RELATIVE_NAME_U RelativeName);
199
200 NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
201                 (PCUNICODE_STRING VolumeRootPath);
202
203 static bool acquired_privileges = false;
204
205 struct dll_sym {
206         void **func_ptr;
207         const char *name;
208         bool required;
209 };
210
211 #define DLL_SYM(name, required) { (void **)&func_##name, #name, required }
212
213 #define for_each_sym(sym, spec) \
214         for ((sym) = (spec)->syms; (sym)->name; (sym)++)
215
216 struct dll_spec {
217         const wchar_t *name;
218         HMODULE handle;
219         const struct dll_sym syms[];
220 };
221
222 struct dll_spec ntdll_spec = {
223         .name = L"ntdll.dll",
224         .syms = {
225                 DLL_SYM(NtCreateFile, true),
226                 DLL_SYM(NtOpenFile, true),
227                 DLL_SYM(NtReadFile, true),
228                 DLL_SYM(NtWriteFile, true),
229                 DLL_SYM(NtQueryInformationFile, true),
230                 DLL_SYM(NtQuerySecurityObject, true),
231                 DLL_SYM(NtQueryDirectoryFile, true),
232                 DLL_SYM(NtQueryVolumeInformationFile, true),
233                 DLL_SYM(NtSetInformationFile, true),
234                 DLL_SYM(NtSetSecurityObject, true),
235                 DLL_SYM(NtFsControlFile, true),
236                 DLL_SYM(NtClose, true),
237                 DLL_SYM(RtlNtStatusToDosError, true),
238                 DLL_SYM(RtlCreateSystemVolumeInformationFolder, false),
239                 DLL_SYM(RtlDosPathNameToNtPathName_U, true),
240                 DLL_SYM(RtlDosPathNameToNtPathName_U_WithStatus, false), /* Not present on XP  */
241                 {NULL, NULL},
242         },
243 };
244
245 static int
246 init_dll(struct dll_spec *spec)
247 {
248         const struct dll_sym *sym;
249         void *addr;
250
251         if (!spec->handle)
252                 spec->handle = LoadLibrary(spec->name);
253         if (!spec->handle) {
254                 for_each_sym(sym, spec) {
255                         if (sym->required) {
256                                 ERROR("%ls could not be loaded!", spec->name);
257                                 return WIMLIB_ERR_UNSUPPORTED;
258                         }
259                 }
260                 return 0;
261         }
262         for_each_sym(sym, spec) {
263                 addr = (void *)GetProcAddress(spec->handle, sym->name);
264                 if (addr) {
265                         *(sym->func_ptr) = addr;
266                 } else if (sym->required) {
267                         ERROR("Can't find %s in %ls", sym->name, spec->name);
268                         return WIMLIB_ERR_UNSUPPORTED;
269                 }
270         }
271         return 0;
272 }
273
274 static void
275 cleanup_dll(struct dll_spec *spec)
276 {
277         const struct dll_sym *sym;
278
279         if (spec->handle) {
280                 FreeLibrary(spec->handle);
281                 spec->handle = NULL;
282
283                 for_each_sym(sym, spec)
284                         *(sym->func_ptr) = NULL;
285         }
286 }
287
288 /* One-time initialization for Windows capture/apply code.  */
289 int
290 win32_global_init(int init_flags)
291 {
292         int ret;
293
294         /* Try to acquire useful privileges.  */
295         if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) {
296                 ret = WIMLIB_ERR_INSUFFICIENT_PRIVILEGES;
297                 if (!win32_modify_capture_privileges(true))
298                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES)
299                                 goto out_drop_privs;
300                 if (!win32_modify_apply_privileges(true))
301                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES)
302                                 goto out_drop_privs;
303                 acquired_privileges = true;
304         }
305
306         ret = init_dll(&ntdll_spec);
307         if (ret)
308                 goto out_drop_privs;
309
310         return 0;
311
312 out_drop_privs:
313         win32_release_capture_and_apply_privileges();
314         return ret;
315 }
316
317 void
318 win32_global_cleanup(void)
319 {
320         vss_global_cleanup();
321
322         if (acquired_privileges)
323                 win32_release_capture_and_apply_privileges();
324
325         cleanup_dll(&ntdll_spec);
326 }
327
328 /*
329  * Translates a Win32-namespace path into an NT-namespace path.
330  *
331  * On success, returns 0.  The NT-namespace path will be stored in the
332  * UNICODE_STRING structure pointed to by nt_path.  nt_path->Buffer will be set
333  * to a new buffer that must later be freed with HeapFree().  (Really
334  * RtlHeapFree(), but HeapFree() seems to be the same thing.)
335  *
336  * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM.
337  */
338 int
339 win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path)
340 {
341         NTSTATUS status;
342
343         if (func_RtlDosPathNameToNtPathName_U_WithStatus) {
344                 status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path,
345                                                                          nt_path,
346                                                                          NULL, NULL);
347         } else {
348                 if ((*func_RtlDosPathNameToNtPathName_U)(win32_path, nt_path,
349                                                          NULL, NULL))
350                         status = STATUS_SUCCESS;
351                 else
352                         status = STATUS_NO_MEMORY;
353         }
354
355         if (likely(NT_SUCCESS(status)))
356                 return 0;
357
358         if (status == STATUS_NO_MEMORY)
359                 return WIMLIB_ERR_NOMEM;
360
361         winnt_error(status, L"\"%ls\": invalid path name", win32_path);
362         return WIMLIB_ERR_INVALID_PARAM;
363 }
364
365 int
366 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
367 {
368         tchar *file_abspath;
369
370         file_abspath = realpath(file_path, NULL);
371         if (!file_abspath)
372                 return WIMLIB_ERR_NOMEM;
373
374         if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
375                 ERROR("\"%ls\": Path format not recognized", file_abspath);
376                 FREE(file_abspath);
377                 return WIMLIB_ERR_UNSUPPORTED;
378         }
379
380         wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
381         FREE(file_abspath);
382         return 0;
383 }
384
385 /* Try to attach an instance of the Windows Overlay File System Filter Driver to
386  * the specified drive (such as C:)  */
387 bool
388 win32_try_to_attach_wof(const wchar_t *drive)
389 {
390         HMODULE fltlib;
391         bool retval = false;
392
393         /* Use FilterAttach() from Fltlib.dll.  */
394
395         fltlib = LoadLibrary(L"Fltlib.dll");
396
397         if (!fltlib) {
398                 WARNING("Failed to load Fltlib.dll");
399                 return retval;
400         }
401
402         HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
403                                             LPCWSTR lpVolumeName,
404                                             LPCWSTR lpInstanceName,
405                                             DWORD dwCreatedInstanceNameLength,
406                                             LPWSTR lpCreatedInstanceName);
407
408         func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
409
410         if (func_FilterAttach) {
411                 HRESULT res;
412
413                 res = (*func_FilterAttach)(L"wof", drive, NULL, 0, NULL);
414
415                 if (res != S_OK)
416                         res = (*func_FilterAttach)(L"wofadk", drive, NULL, 0, NULL);
417
418                 if (res == S_OK)
419                         retval = true;
420         } else {
421                 WARNING("FilterAttach() does not exist in Fltlib.dll");
422         }
423
424         FreeLibrary(fltlib);
425
426         return retval;
427 }
428
429
430 static void
431 windows_msg(u32 code, const wchar_t *format, va_list va,
432             bool is_ntstatus, bool is_error)
433 {
434         wchar_t _buf[STACK_MAX / 8];
435         wchar_t *buf = _buf;
436         size_t buflen = ARRAY_LEN(_buf);
437         size_t ret;
438         size_t n;
439
440 retry:
441         n = vsnwprintf(buf, buflen, format, va);
442
443         if (n >= buflen)
444                 goto realloc;
445
446         n += snwprintf(&buf[n], buflen - n,
447                        (is_ntstatus ?
448                         L" (status=%08"PRIx32"): " :
449                         L" (err=%"PRIu32"): "),
450                        code);
451
452         if (n >= buflen)
453                 goto realloc;
454
455         ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
456                                 FORMAT_MESSAGE_IGNORE_INSERTS |
457                                 (is_ntstatus ? FORMAT_MESSAGE_FROM_HMODULE : 0),
458                             (is_ntstatus ? ntdll_spec.handle : NULL),
459                             code,
460                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
461                             &buf[n],
462                             buflen - n,
463                             NULL);
464         n += ret;
465
466         if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
467                 goto realloc;
468
469         if (buf[n - 1] == L'\n')
470                 buf[--n] = L'\0';
471         if (buf[n - 1] == L'\r')
472                 buf[--n] = L'\0';
473         if (buf[n - 1] == L'.')
474                 buf[--n] = L'\0';
475
476         if (is_error)
477                 ERROR("%ls", buf);
478         else
479                 WARNING("%ls", buf);
480         if (buf != _buf)
481                 FREE(buf);
482         return;
483
484 realloc:
485         if (buf != _buf)
486                 FREE(buf);
487         buflen *= 2;
488         buf = MALLOC(buflen * sizeof(buf[0]));
489         if (buf)
490                 goto retry;
491         ERROR("Ran out of memory while building error message!!!");
492 }
493
494 void
495 win32_warning(DWORD err, const wchar_t *format, ...)
496 {
497         va_list va;
498
499         va_start(va, format);
500         windows_msg(err, format, va, false, false);
501         va_end(va);
502 }
503
504 void
505 win32_error(DWORD err, const wchar_t *format, ...)
506 {
507         va_list va;
508
509         va_start(va, format);
510         windows_msg(err, format, va, false, true);
511         va_end(va);
512 }
513
514 void
515 winnt_warning(NTSTATUS status, const wchar_t *format, ...)
516 {
517         va_list va;
518
519         va_start(va, format);
520         windows_msg(status, format, va, true, false);
521         va_end(va);
522 }
523
524 void
525 winnt_error(NTSTATUS status, const wchar_t *format, ...)
526 {
527         va_list va;
528
529         va_start(va, format);
530         windows_msg(status, format, va, true, true);
531         va_end(va);
532 }
533
534 #endif /* __WIN32__ */