]> wimlib.net Git - wimlib/blob - src/win32_common.c
Merge branch with pipable WIM support
[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 HANDLE
454 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
455 {
456         return CreateFileW(path,
457                            dwDesiredAccess,
458                            FILE_SHARE_READ,
459                            NULL, /* lpSecurityAttributes */
460                            OPEN_EXISTING,
461                            FILE_FLAG_BACKUP_SEMANTICS |
462                                FILE_FLAG_OPEN_REPARSE_POINT,
463                            NULL /* hTemplateFile */);
464 }
465
466 HANDLE
467 win32_open_file_data_only(const wchar_t *path)
468 {
469         return win32_open_existing_file(path, FILE_READ_DATA);
470 }
471
472 /* Pointers to functions that are not available on all targetted versions of
473  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
474  * assume it specifies a certain calling convention. */
475
476 /* Vista and later */
477 HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
478                                             STREAM_INFO_LEVELS InfoLevel,
479                                             LPVOID lpFindStreamData,
480                                             DWORD dwFlags) = NULL;
481
482 /* Vista and later */
483 BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
484                                          LPVOID lpFindStreamData) = NULL;
485
486 static OSVERSIONINFO windows_version_info = {
487         .dwOSVersionInfoSize = sizeof(OSVERSIONINFO),
488 };
489
490 static HMODULE hKernel32 = NULL;
491
492 bool
493 windows_version_is_at_least(unsigned major, unsigned minor)
494 {
495         return windows_version_info.dwMajorVersion > major ||
496                 (windows_version_info.dwMajorVersion == major &&
497                  windows_version_info.dwMinorVersion >= minor);
498 }
499
500 /* Try to dynamically load some functions */
501 void
502 win32_global_init(void)
503 {
504         DWORD err;
505
506         if (hKernel32 == NULL) {
507                 DEBUG("Loading Kernel32.dll");
508                 hKernel32 = LoadLibraryW(L"Kernel32.dll");
509                 if (hKernel32 == NULL) {
510                         err = GetLastError();
511                         WARNING("Can't load Kernel32.dll");
512                         win32_error(err);
513                 }
514         }
515
516         if (hKernel32) {
517                 win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32,
518                                                                    "FindFirstStreamW");
519                 if (win32func_FindFirstStreamW) {
520                         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32,
521                                                                           "FindNextStreamW");
522                         if (!win32func_FindNextStreamW)
523                                 win32func_FindFirstStreamW = NULL;
524                 }
525         }
526
527         GetVersionEx(&windows_version_info);
528 }
529
530 void
531 win32_global_cleanup(void)
532 {
533         if (hKernel32 != NULL) {
534                 DEBUG("Closing Kernel32.dll");
535                 FreeLibrary(hKernel32);
536                 hKernel32 = NULL;
537         }
538 }
539
540 #endif /* __WIN32__ */