Refactor headers
[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
37 #ifdef ENABLE_ERROR_MESSAGES
38 void
39 win32_error(DWORD err_code)
40 {
41         wchar_t *buffer;
42         DWORD nchars;
43         nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
44                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
45                                 NULL, err_code, 0,
46                                 (wchar_t*)&buffer, 0, NULL);
47         if (nchars == 0) {
48                 ERROR("Error printing error message! "
49                       "Computer will self-destruct in 3 seconds.");
50         } else {
51                 ERROR("Win32 error: %ls", buffer);
52                 LocalFree(buffer);
53         }
54 }
55 #endif /* ENABLE_ERROR_MESSAGES */
56
57 int
58 win32_error_to_errno(DWORD err_code)
59 {
60         /* This mapping is that used in Cygwin.
61          * Some of these choices are arbitrary. */
62         switch (err_code) {
63         case ERROR_ACCESS_DENIED:
64                 return EACCES;
65         case ERROR_ACTIVE_CONNECTIONS:
66                 return EAGAIN;
67         case ERROR_ALREADY_EXISTS:
68                 return EEXIST;
69         case ERROR_BAD_DEVICE:
70                 return ENODEV;
71         case ERROR_BAD_EXE_FORMAT:
72                 return ENOEXEC;
73         case ERROR_BAD_NETPATH:
74                 return ENOENT;
75         case ERROR_BAD_NET_NAME:
76                 return ENOENT;
77         case ERROR_BAD_NET_RESP:
78                 return ENOSYS;
79         case ERROR_BAD_PATHNAME:
80                 return ENOENT;
81         case ERROR_BAD_PIPE:
82                 return EINVAL;
83         case ERROR_BAD_UNIT:
84                 return ENODEV;
85         case ERROR_BAD_USERNAME:
86                 return EINVAL;
87         case ERROR_BEGINNING_OF_MEDIA:
88                 return EIO;
89         case ERROR_BROKEN_PIPE:
90                 return EPIPE;
91         case ERROR_BUSY:
92                 return EBUSY;
93         case ERROR_BUS_RESET:
94                 return EIO;
95         case ERROR_CALL_NOT_IMPLEMENTED:
96                 return ENOSYS;
97         case ERROR_CANNOT_MAKE:
98                 return EPERM;
99         case ERROR_CHILD_NOT_COMPLETE:
100                 return EBUSY;
101         case ERROR_COMMITMENT_LIMIT:
102                 return EAGAIN;
103         case ERROR_CRC:
104                 return EIO;
105         case ERROR_DEVICE_DOOR_OPEN:
106                 return EIO;
107         case ERROR_DEVICE_IN_USE:
108                 return EAGAIN;
109         case ERROR_DEVICE_REQUIRES_CLEANING:
110                 return EIO;
111         case ERROR_DIRECTORY:
112                 return ENOTDIR;
113         case ERROR_DIR_NOT_EMPTY:
114                 return ENOTEMPTY;
115         case ERROR_DISK_CORRUPT:
116                 return EIO;
117         case ERROR_DISK_FULL:
118                 return ENOSPC;
119 #ifdef ENOTUNIQ
120         case ERROR_DUP_NAME:
121                 return ENOTUNIQ;
122 #endif
123         case ERROR_EAS_DIDNT_FIT:
124                 return ENOSPC;
125 #ifdef ENOTSUP
126         case ERROR_EAS_NOT_SUPPORTED:
127                 return ENOTSUP;
128 #endif
129         case ERROR_EA_LIST_INCONSISTENT:
130                 return EINVAL;
131         case ERROR_EA_TABLE_FULL:
132                 return ENOSPC;
133         case ERROR_END_OF_MEDIA:
134                 return ENOSPC;
135         case ERROR_EOM_OVERFLOW:
136                 return EIO;
137         case ERROR_EXE_MACHINE_TYPE_MISMATCH:
138                 return ENOEXEC;
139         case ERROR_EXE_MARKED_INVALID:
140                 return ENOEXEC;
141         case ERROR_FILEMARK_DETECTED:
142                 return EIO;
143         case ERROR_FILENAME_EXCED_RANGE:
144                 return ENAMETOOLONG;
145         case ERROR_FILE_CORRUPT:
146                 return EEXIST;
147         case ERROR_FILE_EXISTS:
148                 return EEXIST;
149         case ERROR_FILE_INVALID:
150                 return ENXIO;
151         case ERROR_FILE_NOT_FOUND:
152                 return ENOENT;
153         case ERROR_HANDLE_DISK_FULL:
154                 return ENOSPC;
155 #ifdef ENODATA
156         case ERROR_HANDLE_EOF:
157                 return ENODATA;
158 #endif
159         case ERROR_INVALID_ADDRESS:
160                 return EINVAL;
161         case ERROR_INVALID_AT_INTERRUPT_TIME:
162                 return EINTR;
163         case ERROR_INVALID_BLOCK_LENGTH:
164                 return EIO;
165         case ERROR_INVALID_DATA:
166                 return EINVAL;
167         case ERROR_INVALID_DRIVE:
168                 return ENODEV;
169         case ERROR_INVALID_EA_NAME:
170                 return EINVAL;
171         case ERROR_INVALID_EXE_SIGNATURE:
172                 return ENOEXEC;
173 #ifdef EBADRQC
174         case ERROR_INVALID_FUNCTION:
175                 return EBADRQC;
176 #endif
177         case ERROR_INVALID_HANDLE:
178                 return EBADF;
179         case ERROR_INVALID_NAME:
180                 return ENOENT;
181         case ERROR_INVALID_PARAMETER:
182                 return EINVAL;
183         case ERROR_INVALID_SIGNAL_NUMBER:
184                 return EINVAL;
185         case ERROR_IOPL_NOT_ENABLED:
186                 return ENOEXEC;
187         case ERROR_IO_DEVICE:
188                 return EIO;
189         case ERROR_IO_INCOMPLETE:
190                 return EAGAIN;
191         case ERROR_IO_PENDING:
192                 return EAGAIN;
193         case ERROR_LOCK_VIOLATION:
194                 return EBUSY;
195         case ERROR_MAX_THRDS_REACHED:
196                 return EAGAIN;
197         case ERROR_META_EXPANSION_TOO_LONG:
198                 return EINVAL;
199         case ERROR_MOD_NOT_FOUND:
200                 return ENOENT;
201 #ifdef EMSGSIZE
202         case ERROR_MORE_DATA:
203                 return EMSGSIZE;
204 #endif
205         case ERROR_NEGATIVE_SEEK:
206                 return EINVAL;
207         case ERROR_NETNAME_DELETED:
208                 return ENOENT;
209         case ERROR_NOACCESS:
210                 return EFAULT;
211         case ERROR_NONE_MAPPED:
212                 return EINVAL;
213         case ERROR_NONPAGED_SYSTEM_RESOURCES:
214                 return EAGAIN;
215 #ifdef ENOLINK
216         case ERROR_NOT_CONNECTED:
217                 return ENOLINK;
218 #endif
219         case ERROR_NOT_ENOUGH_MEMORY:
220                 return ENOMEM;
221         case ERROR_NOT_OWNER:
222                 return EPERM;
223 #ifdef ENOMEDIUM
224         case ERROR_NOT_READY:
225                 return ENOMEDIUM;
226 #endif
227         case ERROR_NOT_SAME_DEVICE:
228                 return EXDEV;
229         case ERROR_NOT_SUPPORTED:
230                 return ENOSYS;
231         case ERROR_NO_DATA:
232                 return EPIPE;
233         case ERROR_NO_DATA_DETECTED:
234                 return EIO;
235 #ifdef ENOMEDIUM
236         case ERROR_NO_MEDIA_IN_DRIVE:
237                 return ENOMEDIUM;
238 #endif
239 #ifdef ENMFILE
240         case ERROR_NO_MORE_FILES:
241                 return ENMFILE;
242 #endif
243 #ifdef ENMFILE
244         case ERROR_NO_MORE_ITEMS:
245                 return ENMFILE;
246 #endif
247         case ERROR_NO_MORE_SEARCH_HANDLES:
248                 return ENFILE;
249         case ERROR_NO_PROC_SLOTS:
250                 return EAGAIN;
251         case ERROR_NO_SIGNAL_SENT:
252                 return EIO;
253         case ERROR_NO_SYSTEM_RESOURCES:
254                 return EFBIG;
255         case ERROR_NO_TOKEN:
256                 return EINVAL;
257         case ERROR_OPEN_FAILED:
258                 return EIO;
259         case ERROR_OPEN_FILES:
260                 return EAGAIN;
261         case ERROR_OUTOFMEMORY:
262                 return ENOMEM;
263         case ERROR_PAGED_SYSTEM_RESOURCES:
264                 return EAGAIN;
265         case ERROR_PAGEFILE_QUOTA:
266                 return EAGAIN;
267         case ERROR_PATH_NOT_FOUND:
268                 return ENOENT;
269         case ERROR_PIPE_BUSY:
270                 return EBUSY;
271         case ERROR_PIPE_CONNECTED:
272                 return EBUSY;
273 #ifdef ECOMM
274         case ERROR_PIPE_LISTENING:
275                 return ECOMM;
276         case ERROR_PIPE_NOT_CONNECTED:
277                 return ECOMM;
278 #endif
279         case ERROR_POSSIBLE_DEADLOCK:
280                 return EDEADLOCK;
281         case ERROR_PRIVILEGE_NOT_HELD:
282                 return EPERM;
283         case ERROR_PROCESS_ABORTED:
284                 return EFAULT;
285         case ERROR_PROC_NOT_FOUND:
286                 return ESRCH;
287 #ifdef ENONET
288         case ERROR_REM_NOT_LIST:
289                 return ENONET;
290 #endif
291         case ERROR_SECTOR_NOT_FOUND:
292                 return EINVAL;
293         case ERROR_SEEK:
294                 return EINVAL;
295         case ERROR_SETMARK_DETECTED:
296                 return EIO;
297         case ERROR_SHARING_BUFFER_EXCEEDED:
298                 return ENOLCK;
299         case ERROR_SHARING_VIOLATION:
300                 return EBUSY;
301         case ERROR_SIGNAL_PENDING:
302                 return EBUSY;
303         case ERROR_SIGNAL_REFUSED:
304                 return EIO;
305 #ifdef ELIBBAD
306         case ERROR_SXS_CANT_GEN_ACTCTX:
307                 return ELIBBAD;
308 #endif
309         case ERROR_THREAD_1_INACTIVE:
310                 return EINVAL;
311         case ERROR_TOO_MANY_LINKS:
312                 return EMLINK;
313         case ERROR_TOO_MANY_OPEN_FILES:
314                 return EMFILE;
315         case ERROR_WAIT_NO_CHILDREN:
316                 return ECHILD;
317         case ERROR_WORKING_SET_QUOTA:
318                 return EAGAIN;
319         case ERROR_WRITE_PROTECT:
320                 return EROFS;
321         default:
322                 return -1;
323         }
324 }
325
326 void
327 set_errno_from_GetLastError(void)
328 {
329         errno = win32_error_to_errno(GetLastError());
330 }
331
332 /* Given a path, which may not yet exist, get a set of flags that describe the
333  * features of the volume the path is on. */
334 int
335 win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret)
336 {
337         wchar_t *volume;
338         BOOL bret;
339         DWORD vol_flags;
340
341         if (path[0] != L'\0' && path[0] != L'\\' &&
342             path[0] != L'/' && path[1] == L':')
343         {
344                 /* Path starts with a drive letter; use it. */
345                 volume = alloca(4 * sizeof(wchar_t));
346                 volume[0] = path[0];
347                 volume[1] = path[1];
348                 volume[2] = L'\\';
349                 volume[3] = L'\0';
350         } else {
351                 /* Path does not start with a drive letter; use the volume of
352                  * the current working directory. */
353                 volume = NULL;
354         }
355         bret = GetVolumeInformationW(volume, /* lpRootPathName */
356                                      NULL,  /* lpVolumeNameBuffer */
357                                      0,     /* nVolumeNameSize */
358                                      NULL,  /* lpVolumeSerialNumber */
359                                      NULL,  /* lpMaximumComponentLength */
360                                      &vol_flags, /* lpFileSystemFlags */
361                                      NULL,  /* lpFileSystemNameBuffer */
362                                      0);    /* nFileSystemNameSize */
363         if (!bret) {
364                 DWORD err = GetLastError();
365                 WARNING("Failed to get volume information for path \"%ls\"", path);
366                 win32_error(err);
367                 vol_flags = 0xffffffff;
368         }
369
370         DEBUG("using vol_flags = %x", vol_flags);
371         *vol_flags_ret = vol_flags;
372         return 0;
373 }
374
375 HANDLE
376 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
377 {
378         return CreateFileW(path,
379                            dwDesiredAccess,
380                            FILE_SHARE_READ,
381                            NULL, /* lpSecurityAttributes */
382                            OPEN_EXISTING,
383                            FILE_FLAG_BACKUP_SEMANTICS |
384                                FILE_FLAG_OPEN_REPARSE_POINT,
385                            NULL /* hTemplateFile */);
386 }
387
388 HANDLE
389 win32_open_file_data_only(const wchar_t *path)
390 {
391         return win32_open_existing_file(path, FILE_READ_DATA);
392 }
393
394 /* Pointers to functions that are not available on all targetted versions of
395  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
396  * assume it specifies a certain calling convention. */
397
398 /* Vista and later */
399 HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
400                                             STREAM_INFO_LEVELS InfoLevel,
401                                             LPVOID lpFindStreamData,
402                                             DWORD dwFlags) = NULL;
403
404 /* Vista and later */
405 BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
406                                          LPVOID lpFindStreamData) = NULL;
407
408 static OSVERSIONINFO windows_version_info = {
409         .dwOSVersionInfoSize = sizeof(OSVERSIONINFO),
410 };
411
412 static HMODULE hKernel32 = NULL;
413
414 bool
415 windows_version_is_at_least(unsigned major, unsigned minor)
416 {
417         return windows_version_info.dwMajorVersion > major ||
418                 (windows_version_info.dwMajorVersion == major &&
419                  windows_version_info.dwMinorVersion >= minor);
420 }
421
422 /* Try to dynamically load some functions */
423 void
424 win32_global_init(void)
425 {
426         DWORD err;
427
428         if (hKernel32 == NULL) {
429                 DEBUG("Loading Kernel32.dll");
430                 hKernel32 = LoadLibraryW(L"Kernel32.dll");
431                 if (hKernel32 == NULL) {
432                         err = GetLastError();
433                         WARNING("Can't load Kernel32.dll");
434                         win32_error(err);
435                 }
436         }
437
438         if (hKernel32) {
439                 win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32,
440                                                                    "FindFirstStreamW");
441                 if (win32func_FindFirstStreamW) {
442                         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32,
443                                                                           "FindNextStreamW");
444                         if (!win32func_FindNextStreamW)
445                                 win32func_FindFirstStreamW = NULL;
446                 }
447         }
448
449         GetVersionEx(&windows_version_info);
450 }
451
452 void
453 win32_global_cleanup(void)
454 {
455         if (hKernel32 != NULL) {
456                 DEBUG("Closing Kernel32.dll");
457                 FreeLibrary(hKernel32);
458                 hKernel32 = NULL;
459         }
460 }
461
462 #endif /* __WIN32__ */