bcd5c55037b8ab12edf0001eb8689be78511f833
[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 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifdef __WIN32__
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <errno.h>
31
32 #ifdef WITH_NTDLL
33 #  include <winternl.h>
34 #endif
35
36 #include "wimlib/win32_common.h"
37 #include "wimlib/assert.h"
38 #include "wimlib/error.h"
39 #include "wimlib/util.h"
40
41 static int
42 win32_error_to_errno(DWORD err_code)
43 {
44         /* This mapping is that used in Cygwin.
45          * Some of these choices are arbitrary. */
46         switch (err_code) {
47         case ERROR_ACCESS_DENIED:
48                 return EACCES;
49         case ERROR_ACTIVE_CONNECTIONS:
50                 return EAGAIN;
51         case ERROR_ALREADY_EXISTS:
52                 return EEXIST;
53         case ERROR_BAD_DEVICE:
54                 return ENODEV;
55         case ERROR_BAD_EXE_FORMAT:
56                 return ENOEXEC;
57         case ERROR_BAD_NETPATH:
58                 return ENOENT;
59         case ERROR_BAD_NET_NAME:
60                 return ENOENT;
61         case ERROR_BAD_NET_RESP:
62                 return ENOSYS;
63         case ERROR_BAD_PATHNAME:
64                 return ENOENT;
65         case ERROR_BAD_PIPE:
66                 return EINVAL;
67         case ERROR_BAD_UNIT:
68                 return ENODEV;
69         case ERROR_BAD_USERNAME:
70                 return EINVAL;
71         case ERROR_BEGINNING_OF_MEDIA:
72                 return EIO;
73         case ERROR_BROKEN_PIPE:
74                 return EPIPE;
75         case ERROR_BUSY:
76                 return EBUSY;
77         case ERROR_BUS_RESET:
78                 return EIO;
79         case ERROR_CALL_NOT_IMPLEMENTED:
80                 return ENOSYS;
81         case ERROR_CANNOT_MAKE:
82                 return EPERM;
83         case ERROR_CHILD_NOT_COMPLETE:
84                 return EBUSY;
85         case ERROR_COMMITMENT_LIMIT:
86                 return EAGAIN;
87         case ERROR_CRC:
88                 return EIO;
89         case ERROR_DEVICE_DOOR_OPEN:
90                 return EIO;
91         case ERROR_DEVICE_IN_USE:
92                 return EAGAIN;
93         case ERROR_DEVICE_REQUIRES_CLEANING:
94                 return EIO;
95         case ERROR_DIRECTORY:
96                 return ENOTDIR;
97         case ERROR_DIR_NOT_EMPTY:
98                 return ENOTEMPTY;
99         case ERROR_DISK_CORRUPT:
100                 return EIO;
101         case ERROR_DISK_FULL:
102                 return ENOSPC;
103 #ifdef ENOTUNIQ
104         case ERROR_DUP_NAME:
105                 return ENOTUNIQ;
106 #endif
107         case ERROR_EAS_DIDNT_FIT:
108                 return ENOSPC;
109 #ifdef ENOTSUP
110         case ERROR_EAS_NOT_SUPPORTED:
111                 return ENOTSUP;
112 #endif
113         case ERROR_EA_LIST_INCONSISTENT:
114                 return EINVAL;
115         case ERROR_EA_TABLE_FULL:
116                 return ENOSPC;
117         case ERROR_END_OF_MEDIA:
118                 return ENOSPC;
119         case ERROR_EOM_OVERFLOW:
120                 return EIO;
121         case ERROR_EXE_MACHINE_TYPE_MISMATCH:
122                 return ENOEXEC;
123         case ERROR_EXE_MARKED_INVALID:
124                 return ENOEXEC;
125         case ERROR_FILEMARK_DETECTED:
126                 return EIO;
127         case ERROR_FILENAME_EXCED_RANGE:
128                 return ENAMETOOLONG;
129         case ERROR_FILE_CORRUPT:
130                 return EEXIST;
131         case ERROR_FILE_EXISTS:
132                 return EEXIST;
133         case ERROR_FILE_INVALID:
134                 return ENXIO;
135         case ERROR_FILE_NOT_FOUND:
136                 return ENOENT;
137         case ERROR_HANDLE_DISK_FULL:
138                 return ENOSPC;
139 #ifdef ENODATA
140         case ERROR_HANDLE_EOF:
141                 return ENODATA;
142 #endif
143         case ERROR_INVALID_ADDRESS:
144                 return EINVAL;
145         case ERROR_INVALID_AT_INTERRUPT_TIME:
146                 return EINTR;
147         case ERROR_INVALID_BLOCK_LENGTH:
148                 return EIO;
149         case ERROR_INVALID_DATA:
150                 return EINVAL;
151         case ERROR_INVALID_DRIVE:
152                 return ENODEV;
153         case ERROR_INVALID_EA_NAME:
154                 return EINVAL;
155         case ERROR_INVALID_EXE_SIGNATURE:
156                 return ENOEXEC;
157 #ifdef EBADRQC
158         case ERROR_INVALID_FUNCTION:
159                 return EBADRQC;
160 #endif
161         case ERROR_INVALID_HANDLE:
162                 return EBADF;
163         case ERROR_INVALID_NAME:
164                 return ENOENT;
165         case ERROR_INVALID_PARAMETER:
166                 return EINVAL;
167         case ERROR_INVALID_SIGNAL_NUMBER:
168                 return EINVAL;
169         case ERROR_IOPL_NOT_ENABLED:
170                 return ENOEXEC;
171         case ERROR_IO_DEVICE:
172                 return EIO;
173         case ERROR_IO_INCOMPLETE:
174                 return EAGAIN;
175         case ERROR_IO_PENDING:
176                 return EAGAIN;
177         case ERROR_LOCK_VIOLATION:
178                 return EBUSY;
179         case ERROR_MAX_THRDS_REACHED:
180                 return EAGAIN;
181         case ERROR_META_EXPANSION_TOO_LONG:
182                 return EINVAL;
183         case ERROR_MOD_NOT_FOUND:
184                 return ENOENT;
185 #ifdef EMSGSIZE
186         case ERROR_MORE_DATA:
187                 return EMSGSIZE;
188 #endif
189         case ERROR_NEGATIVE_SEEK:
190                 return EINVAL;
191         case ERROR_NETNAME_DELETED:
192                 return ENOENT;
193         case ERROR_NOACCESS:
194                 return EFAULT;
195         case ERROR_NONE_MAPPED:
196                 return EINVAL;
197         case ERROR_NONPAGED_SYSTEM_RESOURCES:
198                 return EAGAIN;
199 #ifdef ENOLINK
200         case ERROR_NOT_CONNECTED:
201                 return ENOLINK;
202 #endif
203         case ERROR_NOT_ENOUGH_MEMORY:
204                 return ENOMEM;
205         case ERROR_NOT_OWNER:
206                 return EPERM;
207 #ifdef ENOMEDIUM
208         case ERROR_NOT_READY:
209                 return ENOMEDIUM;
210 #endif
211         case ERROR_NOT_SAME_DEVICE:
212                 return EXDEV;
213         case ERROR_NOT_SUPPORTED:
214                 return ENOSYS;
215         case ERROR_NO_DATA:
216                 return EPIPE;
217         case ERROR_NO_DATA_DETECTED:
218                 return EIO;
219 #ifdef ENOMEDIUM
220         case ERROR_NO_MEDIA_IN_DRIVE:
221                 return ENOMEDIUM;
222 #endif
223 #ifdef ENMFILE
224         case ERROR_NO_MORE_FILES:
225                 return ENMFILE;
226 #endif
227 #ifdef ENMFILE
228         case ERROR_NO_MORE_ITEMS:
229                 return ENMFILE;
230 #endif
231         case ERROR_NO_MORE_SEARCH_HANDLES:
232                 return ENFILE;
233         case ERROR_NO_PROC_SLOTS:
234                 return EAGAIN;
235         case ERROR_NO_SIGNAL_SENT:
236                 return EIO;
237         case ERROR_NO_SYSTEM_RESOURCES:
238                 return EFBIG;
239         case ERROR_NO_TOKEN:
240                 return EINVAL;
241         case ERROR_OPEN_FAILED:
242                 return EIO;
243         case ERROR_OPEN_FILES:
244                 return EAGAIN;
245         case ERROR_OUTOFMEMORY:
246                 return ENOMEM;
247         case ERROR_PAGED_SYSTEM_RESOURCES:
248                 return EAGAIN;
249         case ERROR_PAGEFILE_QUOTA:
250                 return EAGAIN;
251         case ERROR_PATH_NOT_FOUND:
252                 return ENOENT;
253         case ERROR_PIPE_BUSY:
254                 return EBUSY;
255         case ERROR_PIPE_CONNECTED:
256                 return EBUSY;
257 #ifdef ECOMM
258         case ERROR_PIPE_LISTENING:
259                 return ECOMM;
260         case ERROR_PIPE_NOT_CONNECTED:
261                 return ECOMM;
262 #endif
263         case ERROR_POSSIBLE_DEADLOCK:
264                 return EDEADLOCK;
265         case ERROR_PRIVILEGE_NOT_HELD:
266                 return EPERM;
267         case ERROR_PROCESS_ABORTED:
268                 return EFAULT;
269         case ERROR_PROC_NOT_FOUND:
270                 return ESRCH;
271 #ifdef ENONET
272         case ERROR_REM_NOT_LIST:
273                 return ENONET;
274 #endif
275         case ERROR_SECTOR_NOT_FOUND:
276                 return EINVAL;
277         case ERROR_SEEK:
278                 return EINVAL;
279         case ERROR_SETMARK_DETECTED:
280                 return EIO;
281         case ERROR_SHARING_BUFFER_EXCEEDED:
282                 return ENOLCK;
283         case ERROR_SHARING_VIOLATION:
284                 return EBUSY;
285         case ERROR_SIGNAL_PENDING:
286                 return EBUSY;
287         case ERROR_SIGNAL_REFUSED:
288                 return EIO;
289 #ifdef ELIBBAD
290         case ERROR_SXS_CANT_GEN_ACTCTX:
291                 return ELIBBAD;
292 #endif
293         case ERROR_THREAD_1_INACTIVE:
294                 return EINVAL;
295         case ERROR_TOO_MANY_LINKS:
296                 return EMLINK;
297         case ERROR_TOO_MANY_OPEN_FILES:
298                 return EMFILE;
299         case ERROR_WAIT_NO_CHILDREN:
300                 return ECHILD;
301         case ERROR_WORKING_SET_QUOTA:
302                 return EAGAIN;
303         case ERROR_WRITE_PROTECT:
304                 return EROFS;
305         default:
306                 return -1;
307         }
308 }
309
310
311 void
312 set_errno_from_win32_error(DWORD err)
313 {
314         errno = win32_error_to_errno(err);
315 }
316
317 void
318 set_errno_from_GetLastError(void)
319 {
320         set_errno_from_win32_error(GetLastError());
321 }
322
323 #ifdef WITH_NTDLL
324 void
325 set_errno_from_nt_status(NTSTATUS status)
326 {
327         set_errno_from_win32_error((*func_RtlNtStatusToDosError)(status));
328 }
329 #endif
330
331 /* Given a Windows-style path, return the number of characters of the prefix
332  * that specify the path to the root directory of a drive, or return 0 if the
333  * drive is relative (or at least on the current drive, in the case of
334  * absolute-but-not-really-absolute paths like \Windows\System32) */
335 static size_t
336 win32_path_drive_spec_len(const wchar_t *path)
337 {
338         size_t n = 0;
339
340         if (!wcsncmp(path, L"\\\\?\\", 4)) {
341                 /* \\?\-prefixed path.  Check for following drive letter and
342                  * path separator. */
343                 if (path[4] != L'\0' && path[5] == L':' &&
344                     is_any_path_separator(path[6]))
345                         n = 7;
346         } else {
347                 /* Not a \\?\-prefixed path.  Check for an initial drive letter
348                  * and path separator. */
349                 if (path[0] != L'\0' && path[1] == L':' &&
350                     is_any_path_separator(path[2]))
351                         n = 3;
352         }
353         /* Include any additional path separators.*/
354         if (n > 0)
355                 while (is_any_path_separator(path[n]))
356                         n++;
357         return n;
358 }
359
360 bool
361 win32_path_is_root_of_drive(const wchar_t *path)
362 {
363         size_t drive_spec_len;
364         wchar_t full_path[32768];
365         DWORD ret;
366
367         ret = GetFullPathName(path, ARRAY_LEN(full_path), full_path, NULL);
368         if (ret > 0 && ret < ARRAY_LEN(full_path))
369                 path = full_path;
370
371         /* Explicit drive letter and path separator? */
372         drive_spec_len = win32_path_drive_spec_len(path);
373         if (drive_spec_len > 0 && path[drive_spec_len] == L'\0')
374                 return true;
375
376         /* All path separators? */
377         for (const wchar_t *p = path; *p != L'\0'; p++)
378                 if (!is_any_path_separator(*p))
379                         return false;
380         return true;
381 }
382
383
384 /* Given a path, which may not yet exist, get a set of flags that describe the
385  * features of the volume the path is on. */
386 int
387 win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret,
388                     bool *supports_SetFileShortName_ret)
389 {
390         wchar_t *volume;
391         BOOL bret;
392         DWORD vol_flags;
393         size_t drive_spec_len;
394         wchar_t filesystem_name[MAX_PATH + 1];
395
396         if (supports_SetFileShortName_ret)
397                 *supports_SetFileShortName_ret = false;
398
399         drive_spec_len = win32_path_drive_spec_len(path);
400
401         if (drive_spec_len == 0)
402                 if (path[0] != L'\0' && path[1] == L':') /* Drive-relative path? */
403                         drive_spec_len = 2;
404
405         if (drive_spec_len == 0) {
406                 /* Path does not start with a drive letter; use the volume of
407                  * the current working directory. */
408                 volume = NULL;
409         } else {
410                 /* Path starts with a drive letter (or \\?\ followed by a drive
411                  * letter); use it. */
412                 volume = alloca((drive_spec_len + 2) * sizeof(wchar_t));
413                 wmemcpy(volume, path, drive_spec_len);
414                 /* Add trailing backslash in case this was a drive-relative
415                  * path. */
416                 volume[drive_spec_len] = L'\\';
417                 volume[drive_spec_len + 1] = L'\0';
418         }
419         bret = GetVolumeInformation(
420                         volume,                         /* lpRootPathName */
421                         NULL,                           /* lpVolumeNameBuffer */
422                         0,                              /* nVolumeNameSize */
423                         NULL,                           /* lpVolumeSerialNumber */
424                         NULL,                           /* lpMaximumComponentLength */
425                         &vol_flags,                     /* lpFileSystemFlags */
426                         filesystem_name,                /* lpFileSystemNameBuffer */
427                         ARRAY_LEN(filesystem_name));    /* nFileSystemNameSize */
428         if (!bret) {
429                 set_errno_from_GetLastError();
430                 WARNING_WITH_ERRNO("Failed to get volume information for "
431                                    "path \"%ls\"", path);
432                 vol_flags = 0xffffffff;
433                 goto out;
434         }
435
436         if (wcsstr(filesystem_name, L"NTFS")) {
437                 /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and later.
438                  * Force it on anyway if filesystem is NTFS.  */
439                 vol_flags |= FILE_SUPPORTS_HARD_LINKS;
440
441                 if (supports_SetFileShortName_ret)
442                         *supports_SetFileShortName_ret = true;
443         }
444
445 out:
446         DEBUG("using vol_flags = %x", vol_flags);
447         *vol_flags_ret = vol_flags;
448         return 0;
449 }
450
451 static bool
452 win32_modify_privilege(const wchar_t *privilege, bool enable)
453 {
454         HANDLE hToken;
455         LUID luid;
456         TOKEN_PRIVILEGES newState;
457         bool ret = FALSE;
458
459         if (!OpenProcessToken(GetCurrentProcess(),
460                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
461                               &hToken))
462                 goto out;
463
464         if (!LookupPrivilegeValue(NULL, privilege, &luid))
465                 goto out_close_handle;
466
467         newState.PrivilegeCount = 1;
468         newState.Privileges[0].Luid = luid;
469         newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
470         SetLastError(ERROR_SUCCESS);
471         ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
472         if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED)
473                 ret = FALSE;
474 out_close_handle:
475         CloseHandle(hToken);
476 out:
477         return ret;
478 }
479
480 static bool
481 win32_modify_capture_privileges(bool enable)
482 {
483         return win32_modify_privilege(SE_BACKUP_NAME, enable)
484             && win32_modify_privilege(SE_SECURITY_NAME, enable);
485 }
486
487 static bool
488 win32_modify_apply_privileges(bool enable)
489 {
490         return win32_modify_privilege(SE_RESTORE_NAME, enable)
491             && win32_modify_privilege(SE_SECURITY_NAME, enable)
492             && win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable);
493 }
494
495 static void
496 win32_release_capture_and_apply_privileges(void)
497 {
498         win32_modify_capture_privileges(false);
499         win32_modify_apply_privileges(false);
500 }
501
502 HANDLE
503 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
504 {
505         return CreateFile(path,
506                           dwDesiredAccess,
507                           FILE_SHARE_READ,
508                           NULL, /* lpSecurityAttributes */
509                           OPEN_EXISTING,
510                           FILE_FLAG_BACKUP_SEMANTICS |
511                                 FILE_FLAG_OPEN_REPARSE_POINT,
512                           NULL /* hTemplateFile */);
513 }
514
515 /* Pointers to functions that are not available on all targetted versions of
516  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
517  * assume it specifies a certain calling convention. */
518
519 /* Vista and later */
520 HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
521                                             STREAM_INFO_LEVELS InfoLevel,
522                                             LPVOID lpFindStreamData,
523                                             DWORD dwFlags) = NULL;
524
525 /* Vista and later */
526 BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
527                                          LPVOID lpFindStreamData) = NULL;
528
529 /* Vista and later */
530 BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName,
531                                              const wchar_t *lpTargetFileName,
532                                              DWORD dwFlags) = NULL;
533
534 #ifdef WITH_NTDLL
535
536 DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
537
538 NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle,
539                                                PIO_STATUS_BLOCK IoStatusBlock,
540                                                PVOID FileInformation,
541                                                ULONG Length,
542                                                FILE_INFORMATION_CLASS FileInformationClass);
543
544 NTSTATUS (WINAPI *func_NtQuerySecurityObject)(HANDLE handle,
545                                               SECURITY_INFORMATION SecurityInformation,
546                                               PSECURITY_DESCRIPTOR SecurityDescriptor,
547                                               ULONG Length,
548                                               PULONG LengthNeeded);
549
550 NTSTATUS (WINAPI *func_NtQueryDirectoryFile) (HANDLE FileHandle,
551                                               HANDLE Event,
552                                               PIO_APC_ROUTINE ApcRoutine,
553                                               PVOID ApcContext,
554                                               PIO_STATUS_BLOCK IoStatusBlock,
555                                               PVOID FileInformation,
556                                               ULONG Length,
557                                               FILE_INFORMATION_CLASS FileInformationClass,
558                                               BOOLEAN ReturnSingleEntry,
559                                               PUNICODE_STRING FileName,
560                                               BOOLEAN RestartScan);
561
562 NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
563                                             SECURITY_INFORMATION SecurityInformation,
564                                             PSECURITY_DESCRIPTOR SecurityDescriptor);
565
566 #endif /* WITH_NTDLL */
567
568 static OSVERSIONINFO windows_version_info = {
569         .dwOSVersionInfoSize = sizeof(OSVERSIONINFO),
570 };
571
572 static HMODULE hKernel32 = NULL;
573
574 #ifdef WITH_NTDLL
575 static HMODULE hNtdll = NULL;
576 #endif
577
578 static bool acquired_privileges = false;
579
580 bool
581 windows_version_is_at_least(unsigned major, unsigned minor)
582 {
583         return windows_version_info.dwMajorVersion > major ||
584                 (windows_version_info.dwMajorVersion == major &&
585                  windows_version_info.dwMinorVersion >= minor);
586 }
587
588 /* One-time initialization for Windows capture/apply code.  */
589 int
590 win32_global_init(int init_flags)
591 {
592         /* Try to acquire useful privileges.  */
593         if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) {
594                 if (!win32_modify_capture_privileges(true))
595                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES)
596                                 goto insufficient_privileges;
597                 if (!win32_modify_apply_privileges(true))
598                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES)
599                                 goto insufficient_privileges;
600                 acquired_privileges = true;
601         }
602
603         /* Get Windows version information.  */
604         GetVersionEx(&windows_version_info);
605
606         /* Try to dynamically load some functions.  */
607         if (hKernel32 == NULL)
608                 hKernel32 = LoadLibrary(L"Kernel32.dll");
609
610         if (hKernel32) {
611                 win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32,
612                                                                    "FindFirstStreamW");
613                 if (win32func_FindFirstStreamW) {
614                         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32,
615                                                                           "FindNextStreamW");
616                         if (!win32func_FindNextStreamW)
617                                 win32func_FindFirstStreamW = NULL;
618                 }
619                 win32func_CreateSymbolicLinkW = (void*)GetProcAddress(hKernel32,
620                                                                       "CreateSymbolicLinkW");
621         }
622
623 #ifdef WITH_NTDLL
624         if (hNtdll == NULL)
625                 hNtdll = LoadLibrary(L"ntdll.dll");
626
627         if (hNtdll) {
628                 func_RtlNtStatusToDosError  =
629                         (void*)GetProcAddress(hNtdll, "RtlNtStatusToDosError");
630                 if (func_RtlNtStatusToDosError) {
631
632                         func_NtQuerySecurityObject  =
633                                 (void*)GetProcAddress(hNtdll, "NtQuerySecurityObject");
634
635                         func_NtQueryDirectoryFile   =
636                                 (void*)GetProcAddress(hNtdll, "NtQueryDirectoryFile");
637
638                         func_NtQueryInformationFile =
639                                 (void*)GetProcAddress(hNtdll, "NtQueryInformationFile");
640
641                         func_NtSetSecurityObject    =
642                                 (void*)GetProcAddress(hNtdll, "NtSetSecurityObject");
643                 }
644         }
645
646         DEBUG("FindFirstStreamW       @ %p", win32func_FindFirstStreamW);
647         DEBUG("FindNextStreamW        @ %p", win32func_FindNextStreamW);
648         DEBUG("CreateSymbolicLinkW    @ %p", win32func_CreateSymbolicLinkW);
649         DEBUG("RtlNtStatusToDosError  @ %p", func_RtlNtStatusToDosError);
650         DEBUG("NtQuerySecurityObject  @ %p", func_NtQuerySecurityObject);
651         DEBUG("NtQueryDirectoryFile   @ %p", func_NtQueryDirectoryFile);
652         DEBUG("NtQueryInformationFile @ %p", func_NtQueryInformationFile);
653         DEBUG("NtSetSecurityObject    @ %p", func_NtSetSecurityObject);
654 #endif
655
656         return 0;
657
658 insufficient_privileges:
659         win32_release_capture_and_apply_privileges();
660         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES;
661 }
662
663 void
664 win32_global_cleanup(void)
665 {
666         if (acquired_privileges)
667                 win32_release_capture_and_apply_privileges();
668         if (hKernel32 != NULL) {
669                 FreeLibrary(hKernel32);
670                 hKernel32 = NULL;
671         }
672 #ifdef WITH_NTDLL
673         if (hNtdll != NULL) {
674                 FreeLibrary(hNtdll);
675                 hNtdll = NULL;
676         }
677 #endif
678 }
679
680 #endif /* __WIN32__ */