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