Win32: Fix drive root detection with \\?\-style paths
[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
367         /* Explicit drive letter and path separator? */
368         drive_spec_len = win32_path_drive_spec_len(path);
369         if (drive_spec_len > 0 && path[drive_spec_len] == L'\0')
370                 return true;
371
372         /* All path separators? */
373         for (const wchar_t *p = path; *p != L'\0'; p++)
374                 if (!is_any_path_separator(*p))
375                         return false;
376         return true;
377
378         /* XXX This function does not handle paths like "c:" where the working
379          * directory on "c:" is actually "c:\", or weird paths like "\.".  But
380          * currently the capture and apply code always prefixes the paths with
381          * \\?\ anyway so this is irrelevant... */
382 }
383
384
385 /* Given a path, which may not yet exist, get a set of flags that describe the
386  * features of the volume the path is on. */
387 int
388 win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret)
389 {
390         wchar_t *volume;
391         BOOL bret;
392         DWORD vol_flags;
393         size_t drive_spec_len;
394
395         drive_spec_len = win32_path_drive_spec_len(path);
396
397         if (drive_spec_len == 0)
398                 if (path[0] != L'\0' && path[1] == L':') /* Drive-relative path? */
399                         drive_spec_len = 2;
400
401         if (drive_spec_len == 0) {
402                 /* Path does not start with a drive letter; use the volume of
403                  * the current working directory. */
404                 volume = NULL;
405         } else {
406                 /* Path starts with a drive letter (or \\?\ followed by a drive
407                  * letter); use it. */
408                 volume = alloca((drive_spec_len + 2) * sizeof(wchar_t));
409                 wmemcpy(volume, path, drive_spec_len);
410                 /* Add trailing backslash in case this was a drive-relative
411                  * path. */
412                 volume[drive_spec_len] = L'\\';
413                 volume[drive_spec_len + 1] = L'\0';
414         }
415         bret = GetVolumeInformationW(volume, /* lpRootPathName */
416                                      NULL,  /* lpVolumeNameBuffer */
417                                      0,     /* nVolumeNameSize */
418                                      NULL,  /* lpVolumeSerialNumber */
419                                      NULL,  /* lpMaximumComponentLength */
420                                      &vol_flags, /* lpFileSystemFlags */
421                                      NULL,  /* lpFileSystemNameBuffer */
422                                      0);    /* nFileSystemNameSize */
423         if (!bret) {
424                 DWORD err = GetLastError();
425                 WARNING("Failed to get volume information for path \"%ls\"", path);
426                 win32_error(err);
427                 vol_flags = 0xffffffff;
428         }
429
430         DEBUG("using vol_flags = %x", vol_flags);
431         *vol_flags_ret = vol_flags;
432         return 0;
433 }
434
435 HANDLE
436 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
437 {
438         return CreateFileW(path,
439                            dwDesiredAccess,
440                            FILE_SHARE_READ,
441                            NULL, /* lpSecurityAttributes */
442                            OPEN_EXISTING,
443                            FILE_FLAG_BACKUP_SEMANTICS |
444                                FILE_FLAG_OPEN_REPARSE_POINT,
445                            NULL /* hTemplateFile */);
446 }
447
448 HANDLE
449 win32_open_file_data_only(const wchar_t *path)
450 {
451         return win32_open_existing_file(path, FILE_READ_DATA);
452 }
453
454 /* Pointers to functions that are not available on all targetted versions of
455  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
456  * assume it specifies a certain calling convention. */
457
458 /* Vista and later */
459 HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
460                                             STREAM_INFO_LEVELS InfoLevel,
461                                             LPVOID lpFindStreamData,
462                                             DWORD dwFlags) = NULL;
463
464 /* Vista and later */
465 BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
466                                          LPVOID lpFindStreamData) = NULL;
467
468 static OSVERSIONINFO windows_version_info = {
469         .dwOSVersionInfoSize = sizeof(OSVERSIONINFO),
470 };
471
472 static HMODULE hKernel32 = NULL;
473
474 bool
475 windows_version_is_at_least(unsigned major, unsigned minor)
476 {
477         return windows_version_info.dwMajorVersion > major ||
478                 (windows_version_info.dwMajorVersion == major &&
479                  windows_version_info.dwMinorVersion >= minor);
480 }
481
482 /* Try to dynamically load some functions */
483 void
484 win32_global_init(void)
485 {
486         DWORD err;
487
488         if (hKernel32 == NULL) {
489                 DEBUG("Loading Kernel32.dll");
490                 hKernel32 = LoadLibraryW(L"Kernel32.dll");
491                 if (hKernel32 == NULL) {
492                         err = GetLastError();
493                         WARNING("Can't load Kernel32.dll");
494                         win32_error(err);
495                 }
496         }
497
498         if (hKernel32) {
499                 win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32,
500                                                                    "FindFirstStreamW");
501                 if (win32func_FindFirstStreamW) {
502                         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32,
503                                                                           "FindNextStreamW");
504                         if (!win32func_FindNextStreamW)
505                                 win32func_FindFirstStreamW = NULL;
506                 }
507         }
508
509         GetVersionEx(&windows_version_info);
510 }
511
512 void
513 win32_global_cleanup(void)
514 {
515         if (hKernel32 != NULL) {
516                 DEBUG("Closing Kernel32.dll");
517                 FreeLibrary(hKernel32);
518                 hKernel32 = NULL;
519         }
520 }
521
522 #endif /* __WIN32__ */