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