]> wimlib.net Git - wimlib/blob - src/win32_common.c
win32_common: add winnt_fsctl() helper method
[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 /* ntdll.dll  */
94
95 NTSTATUS (WINAPI *func_NtCreateFile)(PHANDLE FileHandle,
96                                      ACCESS_MASK DesiredAccess,
97                                      POBJECT_ATTRIBUTES ObjectAttributes,
98                                      PIO_STATUS_BLOCK IoStatusBlock,
99                                      PLARGE_INTEGER AllocationSize,
100                                      ULONG FileAttributes,
101                                      ULONG ShareAccess,
102                                      ULONG CreateDisposition,
103                                      ULONG CreateOptions,
104                                      PVOID EaBuffer,
105                                      ULONG EaLength);
106
107 NTSTATUS (WINAPI *func_NtOpenFile) (PHANDLE FileHandle,
108                                     ACCESS_MASK DesiredAccess,
109                                     POBJECT_ATTRIBUTES ObjectAttributes,
110                                     PIO_STATUS_BLOCK IoStatusBlock,
111                                     ULONG ShareAccess,
112                                     ULONG OpenOptions);
113
114 NTSTATUS (WINAPI *func_NtReadFile) (HANDLE FileHandle,
115                                     HANDLE Event,
116                                     PIO_APC_ROUTINE ApcRoutine,
117                                     PVOID ApcContext,
118                                     PIO_STATUS_BLOCK IoStatusBlock,
119                                     PVOID Buffer,
120                                     ULONG Length,
121                                     PLARGE_INTEGER ByteOffset,
122                                     PULONG Key);
123
124 NTSTATUS (WINAPI *func_NtWriteFile) (HANDLE FileHandle,
125                                      HANDLE Event,
126                                      PIO_APC_ROUTINE ApcRoutine,
127                                      PVOID ApcContext,
128                                      PIO_STATUS_BLOCK IoStatusBlock,
129                                      PVOID Buffer,
130                                      ULONG Length,
131                                      PLARGE_INTEGER ByteOffset,
132                                      PULONG Key);
133
134 NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle,
135                                                PIO_STATUS_BLOCK IoStatusBlock,
136                                                PVOID FileInformation,
137                                                ULONG Length,
138                                                FILE_INFORMATION_CLASS FileInformationClass);
139
140 NTSTATUS (WINAPI *func_NtQuerySecurityObject)(HANDLE handle,
141                                               SECURITY_INFORMATION SecurityInformation,
142                                               PSECURITY_DESCRIPTOR SecurityDescriptor,
143                                               ULONG Length,
144                                               PULONG LengthNeeded);
145
146 NTSTATUS (WINAPI *func_NtQueryDirectoryFile) (HANDLE FileHandle,
147                                               HANDLE Event,
148                                               PIO_APC_ROUTINE ApcRoutine,
149                                               PVOID ApcContext,
150                                               PIO_STATUS_BLOCK IoStatusBlock,
151                                               PVOID FileInformation,
152                                               ULONG Length,
153                                               FILE_INFORMATION_CLASS FileInformationClass,
154                                               BOOLEAN ReturnSingleEntry,
155                                               PUNICODE_STRING FileName,
156                                               BOOLEAN RestartScan);
157
158 NTSTATUS (WINAPI *func_NtQueryVolumeInformationFile) (HANDLE FileHandle,
159                                                       PIO_STATUS_BLOCK IoStatusBlock,
160                                                       PVOID FsInformation,
161                                                       ULONG Length,
162                                                       FS_INFORMATION_CLASS FsInformationClass);
163
164 NTSTATUS (WINAPI *func_NtSetInformationFile)(HANDLE FileHandle,
165                                              PIO_STATUS_BLOCK IoStatusBlock,
166                                              PVOID FileInformation,
167                                              ULONG Length,
168                                              FILE_INFORMATION_CLASS FileInformationClass);
169
170 NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
171                                             SECURITY_INFORMATION SecurityInformation,
172                                             PSECURITY_DESCRIPTOR SecurityDescriptor);
173
174 static NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle,
175                                                 HANDLE Event,
176                                                 PIO_APC_ROUTINE ApcRoutine,
177                                                 PVOID ApcContext,
178                                                 PIO_STATUS_BLOCK IoStatusBlock,
179                                                 ULONG FsControlCode,
180                                                 PVOID InputBuffer,
181                                                 ULONG InputBufferLength,
182                                                 PVOID OutputBuffer,
183                                                 ULONG OutputBufferLength);
184
185 static NTSTATUS (WINAPI *func_NtWaitForSingleObject) (HANDLE Handle,
186                                                       BOOLEAN Alertable,
187                                                       PLARGE_INTEGER Timeout);
188
189 NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle);
190
191 DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
192
193 BOOLEAN (WINAPI *func_RtlDosPathNameToNtPathName_U)
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_RtlDosPathNameToNtPathName_U_WithStatus)
200                 (IN PCWSTR DosName,
201                  OUT PUNICODE_STRING NtName,
202                  OUT PCWSTR *PartName,
203                  OUT PRTL_RELATIVE_NAME_U RelativeName);
204
205 NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
206                 (PCUNICODE_STRING VolumeRootPath);
207
208 static bool acquired_privileges = false;
209
210 struct dll_sym {
211         void **func_ptr;
212         const char *name;
213         bool required;
214 };
215
216 #define DLL_SYM(name, required) { (void **)&func_##name, #name, required }
217
218 #define for_each_sym(sym, spec) \
219         for ((sym) = (spec)->syms; (sym)->name; (sym)++)
220
221 struct dll_spec {
222         const wchar_t *name;
223         HMODULE handle;
224         const struct dll_sym syms[];
225 };
226
227 struct dll_spec ntdll_spec = {
228         .name = L"ntdll.dll",
229         .syms = {
230                 DLL_SYM(NtCreateFile, true),
231                 DLL_SYM(NtOpenFile, true),
232                 DLL_SYM(NtReadFile, true),
233                 DLL_SYM(NtWriteFile, true),
234                 DLL_SYM(NtQueryInformationFile, true),
235                 DLL_SYM(NtQuerySecurityObject, true),
236                 DLL_SYM(NtQueryDirectoryFile, true),
237                 DLL_SYM(NtQueryVolumeInformationFile, true),
238                 DLL_SYM(NtSetInformationFile, true),
239                 DLL_SYM(NtSetSecurityObject, true),
240                 DLL_SYM(NtFsControlFile, true),
241                 DLL_SYM(NtWaitForSingleObject, true),
242                 DLL_SYM(NtClose, true),
243                 DLL_SYM(RtlNtStatusToDosError, true),
244                 DLL_SYM(RtlCreateSystemVolumeInformationFolder, false),
245                 DLL_SYM(RtlDosPathNameToNtPathName_U, true),
246                 DLL_SYM(RtlDosPathNameToNtPathName_U_WithStatus, false), /* Not present on XP  */
247                 {NULL, NULL},
248         },
249 };
250
251 static int
252 init_dll(struct dll_spec *spec)
253 {
254         const struct dll_sym *sym;
255         void *addr;
256
257         if (!spec->handle)
258                 spec->handle = LoadLibrary(spec->name);
259         if (!spec->handle) {
260                 for_each_sym(sym, spec) {
261                         if (sym->required) {
262                                 ERROR("%ls could not be loaded!", spec->name);
263                                 return WIMLIB_ERR_UNSUPPORTED;
264                         }
265                 }
266                 return 0;
267         }
268         for_each_sym(sym, spec) {
269                 addr = (void *)GetProcAddress(spec->handle, sym->name);
270                 if (addr) {
271                         *(sym->func_ptr) = addr;
272                 } else if (sym->required) {
273                         ERROR("Can't find %s in %ls", sym->name, spec->name);
274                         return WIMLIB_ERR_UNSUPPORTED;
275                 }
276         }
277         return 0;
278 }
279
280 static void
281 cleanup_dll(struct dll_spec *spec)
282 {
283         const struct dll_sym *sym;
284
285         if (spec->handle) {
286                 FreeLibrary(spec->handle);
287                 spec->handle = NULL;
288
289                 for_each_sym(sym, spec)
290                         *(sym->func_ptr) = NULL;
291         }
292 }
293
294 /* One-time initialization for Windows capture/apply code.  */
295 int
296 win32_global_init(int init_flags)
297 {
298         int ret;
299
300         /* Try to acquire useful privileges.  */
301         if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) {
302                 ret = WIMLIB_ERR_INSUFFICIENT_PRIVILEGES;
303                 if (!win32_modify_capture_privileges(true))
304                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES)
305                                 goto out_drop_privs;
306                 if (!win32_modify_apply_privileges(true))
307                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES)
308                                 goto out_drop_privs;
309                 acquired_privileges = true;
310         }
311
312         ret = init_dll(&ntdll_spec);
313         if (ret)
314                 goto out_drop_privs;
315
316         return 0;
317
318 out_drop_privs:
319         win32_release_capture_and_apply_privileges();
320         return ret;
321 }
322
323 void
324 win32_global_cleanup(void)
325 {
326         vss_global_cleanup();
327
328         if (acquired_privileges)
329                 win32_release_capture_and_apply_privileges();
330
331         cleanup_dll(&ntdll_spec);
332 }
333
334 /*
335  * Translates a Win32-namespace path into an NT-namespace path.
336  *
337  * On success, returns 0.  The NT-namespace path will be stored in the
338  * UNICODE_STRING structure pointed to by nt_path.  nt_path->Buffer will be set
339  * to a new buffer that must later be freed with HeapFree().  (Really
340  * RtlHeapFree(), but HeapFree() seems to be the same thing.)
341  *
342  * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM.
343  */
344 int
345 win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path)
346 {
347         NTSTATUS status;
348
349         if (func_RtlDosPathNameToNtPathName_U_WithStatus) {
350                 status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path,
351                                                                          nt_path,
352                                                                          NULL, NULL);
353         } else {
354                 if ((*func_RtlDosPathNameToNtPathName_U)(win32_path, nt_path,
355                                                          NULL, NULL))
356                         status = STATUS_SUCCESS;
357                 else
358                         status = STATUS_NO_MEMORY;
359         }
360
361         if (likely(NT_SUCCESS(status)))
362                 return 0;
363
364         if (status == STATUS_NO_MEMORY)
365                 return WIMLIB_ERR_NOMEM;
366
367         winnt_error(status, L"\"%ls\": invalid path name", win32_path);
368         return WIMLIB_ERR_INVALID_PARAM;
369 }
370
371 int
372 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
373 {
374         tchar *file_abspath;
375
376         file_abspath = realpath(file_path, NULL);
377         if (!file_abspath)
378                 return WIMLIB_ERR_NOMEM;
379
380         if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
381                 ERROR("\"%ls\": Path format not recognized", file_abspath);
382                 FREE(file_abspath);
383                 return WIMLIB_ERR_UNSUPPORTED;
384         }
385
386         wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
387         FREE(file_abspath);
388         return 0;
389 }
390
391 /* Try to attach an instance of the Windows Overlay File System Filter Driver to
392  * the specified drive (such as C:)  */
393 bool
394 win32_try_to_attach_wof(const wchar_t *drive)
395 {
396         HMODULE fltlib;
397         bool retval = false;
398
399         /* Use FilterAttach() from Fltlib.dll.  */
400
401         fltlib = LoadLibrary(L"Fltlib.dll");
402
403         if (!fltlib) {
404                 WARNING("Failed to load Fltlib.dll");
405                 return retval;
406         }
407
408         HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName,
409                                             LPCWSTR lpVolumeName,
410                                             LPCWSTR lpInstanceName,
411                                             DWORD dwCreatedInstanceNameLength,
412                                             LPWSTR lpCreatedInstanceName);
413
414         func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach");
415
416         if (func_FilterAttach) {
417                 HRESULT res;
418
419                 res = (*func_FilterAttach)(L"wof", drive, NULL, 0, NULL);
420
421                 if (res != S_OK)
422                         res = (*func_FilterAttach)(L"wofadk", drive, NULL, 0, NULL);
423
424                 if (res == S_OK)
425                         retval = true;
426         } else {
427                 WARNING("FilterAttach() does not exist in Fltlib.dll");
428         }
429
430         FreeLibrary(fltlib);
431
432         return retval;
433 }
434
435
436 static void
437 windows_msg(u32 code, const wchar_t *format, va_list va,
438             bool is_ntstatus, bool is_error)
439 {
440         wchar_t _buf[STACK_MAX / 8];
441         wchar_t *buf = _buf;
442         size_t buflen = ARRAY_LEN(_buf);
443         size_t ret;
444         size_t n;
445
446 retry:
447         n = vsnwprintf(buf, buflen, format, va);
448
449         if (n >= buflen)
450                 goto realloc;
451
452         n += snwprintf(&buf[n], buflen - n,
453                        (is_ntstatus ?
454                         L" (status=%08"PRIx32"): " :
455                         L" (err=%"PRIu32"): "),
456                        code);
457
458         if (n >= buflen)
459                 goto realloc;
460
461         ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
462                                 FORMAT_MESSAGE_IGNORE_INSERTS |
463                                 (is_ntstatus ? FORMAT_MESSAGE_FROM_HMODULE : 0),
464                             (is_ntstatus ? ntdll_spec.handle : NULL),
465                             code,
466                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
467                             &buf[n],
468                             buflen - n,
469                             NULL);
470         n += ret;
471
472         if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
473                 goto realloc;
474
475         if (buf[n - 1] == L'\n')
476                 buf[--n] = L'\0';
477         if (buf[n - 1] == L'\r')
478                 buf[--n] = L'\0';
479         if (buf[n - 1] == L'.')
480                 buf[--n] = L'\0';
481
482         if (is_error)
483                 ERROR("%ls", buf);
484         else
485                 WARNING("%ls", buf);
486         if (buf != _buf)
487                 FREE(buf);
488         return;
489
490 realloc:
491         if (buf != _buf)
492                 FREE(buf);
493         buflen *= 2;
494         buf = MALLOC(buflen * sizeof(buf[0]));
495         if (buf)
496                 goto retry;
497         ERROR("Ran out of memory while building error message!!!");
498 }
499
500 void
501 win32_warning(DWORD err, const wchar_t *format, ...)
502 {
503         va_list va;
504
505         va_start(va, format);
506         windows_msg(err, format, va, false, false);
507         va_end(va);
508 }
509
510 void
511 win32_error(DWORD err, const wchar_t *format, ...)
512 {
513         va_list va;
514
515         va_start(va, format);
516         windows_msg(err, format, va, false, true);
517         va_end(va);
518 }
519
520 void
521 winnt_warning(NTSTATUS status, const wchar_t *format, ...)
522 {
523         va_list va;
524
525         va_start(va, format);
526         windows_msg(status, format, va, true, false);
527         va_end(va);
528 }
529
530 void
531 winnt_error(NTSTATUS status, const wchar_t *format, ...)
532 {
533         va_list va;
534
535         va_start(va, format);
536         windows_msg(status, format, va, true, true);
537         va_end(va);
538 }
539
540 /*
541  * Synchronously execute a filesystem control method.  This is a wrapper around
542  * NtFsControlFile() that handles STATUS_PENDING.  Note that SYNCHRONIZE
543  * permission is, in general, required on the handle.
544  */
545 NTSTATUS
546 winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size,
547             void *out, u32 out_size_avail, u32 *actual_out_size_ret)
548 {
549         IO_STATUS_BLOCK iosb;
550         NTSTATUS status;
551
552         status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb, code,
553                                          (void *)in, in_size,
554                                          out, out_size_avail);
555         if (status == STATUS_PENDING) {
556                 /* Beware: this case is often encountered with remote
557                  * filesystems, but rarely with local filesystems.  */
558
559                 status = (*func_NtWaitForSingleObject)(h, FALSE, NULL);
560                 if (NT_SUCCESS(status)) {
561                         status = iosb.Status;
562                 } else {
563                         /* We shouldn't be issuing ioctls on a handle to which
564                          * we don't have SYNCHRONIZE access.  Otherwise we have
565                          * no way to wait for them to complete.  */
566                         wimlib_assert(status != STATUS_ACCESS_DENIED);
567                 }
568         }
569
570         if (NT_SUCCESS(status) && actual_out_size_ret != NULL)
571                 *actual_out_size_ret = (u32)iosb.Information;
572
573         return status;
574 }
575
576 #endif /* __WIN32__ */