Win32: Improvements on checking for Windows version and FS features
[wimlib] / src / win32_common.c
1 /*
2  * win32_common.c - Windows code common to applying and capturing images, as
3  * well as replacements for various functions not available on Windows, such as
4  * fsync().
5  */
6
7 /*
8  * Copyright (C) 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #ifdef __WIN32__
27
28 #include <shlwapi.h> /* for PathMatchSpecW() */
29 #include <errno.h>
30
31 #include "win32_common.h"
32
33 #ifdef ENABLE_ERROR_MESSAGES
34 void
35 win32_error(DWORD err_code)
36 {
37         wchar_t *buffer;
38         DWORD nchars;
39         nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
40                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
41                                 NULL, err_code, 0,
42                                 (wchar_t*)&buffer, 0, NULL);
43         if (nchars == 0) {
44                 ERROR("Error printing error message! "
45                       "Computer will self-destruct in 3 seconds.");
46         } else {
47                 ERROR("Win32 error: %ls", buffer);
48                 LocalFree(buffer);
49         }
50 }
51 #endif /* ENABLE_ERROR_MESSAGES */
52
53 int
54 win32_error_to_errno(DWORD err_code)
55 {
56         /* This mapping is that used in Cygwin.
57          * Some of these choices are arbitrary. */
58         switch (err_code) {
59         case ERROR_ACCESS_DENIED:
60                 return EACCES;
61         case ERROR_ACTIVE_CONNECTIONS:
62                 return EAGAIN;
63         case ERROR_ALREADY_EXISTS:
64                 return EEXIST;
65         case ERROR_BAD_DEVICE:
66                 return ENODEV;
67         case ERROR_BAD_EXE_FORMAT:
68                 return ENOEXEC;
69         case ERROR_BAD_NETPATH:
70                 return ENOENT;
71         case ERROR_BAD_NET_NAME:
72                 return ENOENT;
73         case ERROR_BAD_NET_RESP:
74                 return ENOSYS;
75         case ERROR_BAD_PATHNAME:
76                 return ENOENT;
77         case ERROR_BAD_PIPE:
78                 return EINVAL;
79         case ERROR_BAD_UNIT:
80                 return ENODEV;
81         case ERROR_BAD_USERNAME:
82                 return EINVAL;
83         case ERROR_BEGINNING_OF_MEDIA:
84                 return EIO;
85         case ERROR_BROKEN_PIPE:
86                 return EPIPE;
87         case ERROR_BUSY:
88                 return EBUSY;
89         case ERROR_BUS_RESET:
90                 return EIO;
91         case ERROR_CALL_NOT_IMPLEMENTED:
92                 return ENOSYS;
93         case ERROR_CANNOT_MAKE:
94                 return EPERM;
95         case ERROR_CHILD_NOT_COMPLETE:
96                 return EBUSY;
97         case ERROR_COMMITMENT_LIMIT:
98                 return EAGAIN;
99         case ERROR_CRC:
100                 return EIO;
101         case ERROR_DEVICE_DOOR_OPEN:
102                 return EIO;
103         case ERROR_DEVICE_IN_USE:
104                 return EAGAIN;
105         case ERROR_DEVICE_REQUIRES_CLEANING:
106                 return EIO;
107         case ERROR_DIRECTORY:
108                 return ENOTDIR;
109         case ERROR_DIR_NOT_EMPTY:
110                 return ENOTEMPTY;
111         case ERROR_DISK_CORRUPT:
112                 return EIO;
113         case ERROR_DISK_FULL:
114                 return ENOSPC;
115 #ifdef ENOTUNIQ
116         case ERROR_DUP_NAME:
117                 return ENOTUNIQ;
118 #endif
119         case ERROR_EAS_DIDNT_FIT:
120                 return ENOSPC;
121 #ifdef ENOTSUP
122         case ERROR_EAS_NOT_SUPPORTED:
123                 return ENOTSUP;
124 #endif
125         case ERROR_EA_LIST_INCONSISTENT:
126                 return EINVAL;
127         case ERROR_EA_TABLE_FULL:
128                 return ENOSPC;
129         case ERROR_END_OF_MEDIA:
130                 return ENOSPC;
131         case ERROR_EOM_OVERFLOW:
132                 return EIO;
133         case ERROR_EXE_MACHINE_TYPE_MISMATCH:
134                 return ENOEXEC;
135         case ERROR_EXE_MARKED_INVALID:
136                 return ENOEXEC;
137         case ERROR_FILEMARK_DETECTED:
138                 return EIO;
139         case ERROR_FILENAME_EXCED_RANGE:
140                 return ENAMETOOLONG;
141         case ERROR_FILE_CORRUPT:
142                 return EEXIST;
143         case ERROR_FILE_EXISTS:
144                 return EEXIST;
145         case ERROR_FILE_INVALID:
146                 return ENXIO;
147         case ERROR_FILE_NOT_FOUND:
148                 return ENOENT;
149         case ERROR_HANDLE_DISK_FULL:
150                 return ENOSPC;
151 #ifdef ENODATA
152         case ERROR_HANDLE_EOF:
153                 return ENODATA;
154 #endif
155         case ERROR_INVALID_ADDRESS:
156                 return EINVAL;
157         case ERROR_INVALID_AT_INTERRUPT_TIME:
158                 return EINTR;
159         case ERROR_INVALID_BLOCK_LENGTH:
160                 return EIO;
161         case ERROR_INVALID_DATA:
162                 return EINVAL;
163         case ERROR_INVALID_DRIVE:
164                 return ENODEV;
165         case ERROR_INVALID_EA_NAME:
166                 return EINVAL;
167         case ERROR_INVALID_EXE_SIGNATURE:
168                 return ENOEXEC;
169 #ifdef EBADRQC
170         case ERROR_INVALID_FUNCTION:
171                 return EBADRQC;
172 #endif
173         case ERROR_INVALID_HANDLE:
174                 return EBADF;
175         case ERROR_INVALID_NAME:
176                 return ENOENT;
177         case ERROR_INVALID_PARAMETER:
178                 return EINVAL;
179         case ERROR_INVALID_SIGNAL_NUMBER:
180                 return EINVAL;
181         case ERROR_IOPL_NOT_ENABLED:
182                 return ENOEXEC;
183         case ERROR_IO_DEVICE:
184                 return EIO;
185         case ERROR_IO_INCOMPLETE:
186                 return EAGAIN;
187         case ERROR_IO_PENDING:
188                 return EAGAIN;
189         case ERROR_LOCK_VIOLATION:
190                 return EBUSY;
191         case ERROR_MAX_THRDS_REACHED:
192                 return EAGAIN;
193         case ERROR_META_EXPANSION_TOO_LONG:
194                 return EINVAL;
195         case ERROR_MOD_NOT_FOUND:
196                 return ENOENT;
197 #ifdef EMSGSIZE
198         case ERROR_MORE_DATA:
199                 return EMSGSIZE;
200 #endif
201         case ERROR_NEGATIVE_SEEK:
202                 return EINVAL;
203         case ERROR_NETNAME_DELETED:
204                 return ENOENT;
205         case ERROR_NOACCESS:
206                 return EFAULT;
207         case ERROR_NONE_MAPPED:
208                 return EINVAL;
209         case ERROR_NONPAGED_SYSTEM_RESOURCES:
210                 return EAGAIN;
211 #ifdef ENOLINK
212         case ERROR_NOT_CONNECTED:
213                 return ENOLINK;
214 #endif
215         case ERROR_NOT_ENOUGH_MEMORY:
216                 return ENOMEM;
217         case ERROR_NOT_OWNER:
218                 return EPERM;
219 #ifdef ENOMEDIUM
220         case ERROR_NOT_READY:
221                 return ENOMEDIUM;
222 #endif
223         case ERROR_NOT_SAME_DEVICE:
224                 return EXDEV;
225         case ERROR_NOT_SUPPORTED:
226                 return ENOSYS;
227         case ERROR_NO_DATA:
228                 return EPIPE;
229         case ERROR_NO_DATA_DETECTED:
230                 return EIO;
231 #ifdef ENOMEDIUM
232         case ERROR_NO_MEDIA_IN_DRIVE:
233                 return ENOMEDIUM;
234 #endif
235 #ifdef ENMFILE
236         case ERROR_NO_MORE_FILES:
237                 return ENMFILE;
238 #endif
239 #ifdef ENMFILE
240         case ERROR_NO_MORE_ITEMS:
241                 return ENMFILE;
242 #endif
243         case ERROR_NO_MORE_SEARCH_HANDLES:
244                 return ENFILE;
245         case ERROR_NO_PROC_SLOTS:
246                 return EAGAIN;
247         case ERROR_NO_SIGNAL_SENT:
248                 return EIO;
249         case ERROR_NO_SYSTEM_RESOURCES:
250                 return EFBIG;
251         case ERROR_NO_TOKEN:
252                 return EINVAL;
253         case ERROR_OPEN_FAILED:
254                 return EIO;
255         case ERROR_OPEN_FILES:
256                 return EAGAIN;
257         case ERROR_OUTOFMEMORY:
258                 return ENOMEM;
259         case ERROR_PAGED_SYSTEM_RESOURCES:
260                 return EAGAIN;
261         case ERROR_PAGEFILE_QUOTA:
262                 return EAGAIN;
263         case ERROR_PATH_NOT_FOUND:
264                 return ENOENT;
265         case ERROR_PIPE_BUSY:
266                 return EBUSY;
267         case ERROR_PIPE_CONNECTED:
268                 return EBUSY;
269 #ifdef ECOMM
270         case ERROR_PIPE_LISTENING:
271                 return ECOMM;
272         case ERROR_PIPE_NOT_CONNECTED:
273                 return ECOMM;
274 #endif
275         case ERROR_POSSIBLE_DEADLOCK:
276                 return EDEADLOCK;
277         case ERROR_PRIVILEGE_NOT_HELD:
278                 return EPERM;
279         case ERROR_PROCESS_ABORTED:
280                 return EFAULT;
281         case ERROR_PROC_NOT_FOUND:
282                 return ESRCH;
283 #ifdef ENONET
284         case ERROR_REM_NOT_LIST:
285                 return ENONET;
286 #endif
287         case ERROR_SECTOR_NOT_FOUND:
288                 return EINVAL;
289         case ERROR_SEEK:
290                 return EINVAL;
291         case ERROR_SETMARK_DETECTED:
292                 return EIO;
293         case ERROR_SHARING_BUFFER_EXCEEDED:
294                 return ENOLCK;
295         case ERROR_SHARING_VIOLATION:
296                 return EBUSY;
297         case ERROR_SIGNAL_PENDING:
298                 return EBUSY;
299         case ERROR_SIGNAL_REFUSED:
300                 return EIO;
301 #ifdef ELIBBAD
302         case ERROR_SXS_CANT_GEN_ACTCTX:
303                 return ELIBBAD;
304 #endif
305         case ERROR_THREAD_1_INACTIVE:
306                 return EINVAL;
307         case ERROR_TOO_MANY_LINKS:
308                 return EMLINK;
309         case ERROR_TOO_MANY_OPEN_FILES:
310                 return EMFILE;
311         case ERROR_WAIT_NO_CHILDREN:
312                 return ECHILD;
313         case ERROR_WORKING_SET_QUOTA:
314                 return EAGAIN;
315         case ERROR_WRITE_PROTECT:
316                 return EROFS;
317         default:
318                 return -1;
319         }
320 }
321
322 void
323 set_errno_from_GetLastError()
324 {
325         errno = win32_error_to_errno(GetLastError());
326 }
327
328 /* Replacement for POSIX fsync() */
329 int
330 fsync(int fd)
331 {
332         HANDLE h;
333
334         h = (HANDLE)_get_osfhandle(fd);
335         if (h == INVALID_HANDLE_VALUE)
336                 goto err;
337         if (!FlushFileBuffers(h))
338                 goto err_set_errno;
339         return 0;
340 err_set_errno:
341         set_errno_from_GetLastError();
342 err:
343         return -1;
344 }
345
346 /* Use the Win32 API to get the number of processors */
347 unsigned
348 win32_get_number_of_processors()
349 {
350         SYSTEM_INFO sysinfo;
351         GetSystemInfo(&sysinfo);
352         return sysinfo.dwNumberOfProcessors;
353 }
354
355 /* Replacement for POSIX-2008 realpath().  Warning: partial functionality only
356  * (resolved_path must be NULL).   Also I highly doubt that GetFullPathName
357  * really does the right thing under all circumstances. */
358 wchar_t *
359 realpath(const wchar_t *path, wchar_t *resolved_path)
360 {
361         DWORD ret;
362         DWORD err;
363         wimlib_assert(resolved_path == NULL);
364
365         ret = GetFullPathNameW(path, 0, NULL, NULL);
366         if (!ret) {
367                 err = GetLastError();
368                 goto fail_win32;
369         }
370
371         resolved_path = TMALLOC(ret);
372         if (!resolved_path)
373                 goto out;
374         ret = GetFullPathNameW(path, ret, resolved_path, NULL);
375         if (!ret) {
376                 err = GetLastError();
377                 free(resolved_path);
378                 resolved_path = NULL;
379                 goto fail_win32;
380         }
381         goto out;
382 fail_win32:
383         errno = win32_error_to_errno(err);
384 out:
385         return resolved_path;
386 }
387
388 /* rename() on Windows fails if the destination file exists.  And we need to
389  * make it work on wide characters.  Fix it. */
390 int
391 win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath)
392 {
393         if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) {
394                 return 0;
395         } else {
396                 set_errno_from_GetLastError();
397                 return -1;
398         }
399 }
400
401 /* Replacement for POSIX fnmatch() (partial functionality only) */
402 int
403 fnmatch(const wchar_t *pattern, const wchar_t *string, int flags)
404 {
405         if (PathMatchSpecW(string, pattern))
406                 return 0;
407         else
408                 return FNM_NOMATCH;
409 }
410
411 /* truncate() replacement */
412 int
413 win32_truncate_replacement(const wchar_t *path, off_t size)
414 {
415         DWORD err = NO_ERROR;
416         LARGE_INTEGER liOffset;
417
418         HANDLE h = win32_open_existing_file(path, GENERIC_WRITE);
419         if (h == INVALID_HANDLE_VALUE)
420                 goto fail;
421
422         liOffset.QuadPart = size;
423         if (!SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
424                 goto fail_close_handle;
425
426         if (!SetEndOfFile(h))
427                 goto fail_close_handle;
428         CloseHandle(h);
429         return 0;
430
431 fail_close_handle:
432         err = GetLastError();
433         CloseHandle(h);
434 fail:
435         if (err == NO_ERROR)
436                 err = GetLastError();
437         errno = win32_error_to_errno(err);
438         return -1;
439 }
440
441
442 /* This really could be replaced with _wcserror_s, but this doesn't seem to
443  * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
444  * linked in by Visual Studio...?). */
445 extern int
446 win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
447 {
448         static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
449
450         pthread_mutex_lock(&strerror_lock);
451         mbstowcs(buf, strerror(errnum), buflen);
452         buf[buflen - 1] = '\0';
453         pthread_mutex_unlock(&strerror_lock);
454         return 0;
455 }
456
457 static int
458 do_pread_or_pwrite(int fd, void *buf, size_t count, off_t offset,
459                    bool is_pwrite)
460 {
461         HANDLE h;
462         LARGE_INTEGER orig_offset;
463         DWORD bytes_read_or_written;
464         LARGE_INTEGER relative_offset;
465         OVERLAPPED overlapped;
466         BOOL bret;
467
468         wimlib_assert(count <= 0xffffffff);
469
470         h = (HANDLE)_get_osfhandle(fd);
471         if (h == INVALID_HANDLE_VALUE)
472                 goto err;
473
474         /* Get original position */
475         relative_offset.QuadPart = 0;
476         if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT))
477                 goto err_set_errno;
478
479         memset(&overlapped, 0, sizeof(overlapped));
480         overlapped.Offset = offset;
481         overlapped.OffsetHigh = offset >> 32;
482
483         /* Do the read or write at the specified offset */
484         if (is_pwrite)
485                 bret = WriteFile(h, buf, count, &bytes_read_or_written, &overlapped);
486         else
487                 bret = ReadFile(h, buf, count, &bytes_read_or_written, &overlapped);
488         if (!bret)
489                 goto err_set_errno;
490
491         /* Restore the original position */
492         if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN))
493                 goto err_set_errno;
494
495         return bytes_read_or_written;
496 err_set_errno:
497         set_errno_from_GetLastError();
498 err:
499         return -1;
500 }
501
502 /* Dumb Windows implementation of pread().  It temporarily changes the file
503  * offset, so it is not safe to use with readers/writers on the same file
504  * descriptor.  */
505 extern ssize_t
506 win32_pread(int fd, void *buf, size_t count, off_t offset)
507 {
508         return do_pread_or_pwrite(fd, buf, count, offset, false);
509 }
510
511 /* Dumb Windows implementation of pwrite().  It temporarily changes the file
512  * offset, so it is not safe to use with readers/writers on the same file
513  * descriptor. */
514 extern ssize_t
515 win32_pwrite(int fd, const void *buf, size_t count, off_t offset)
516 {
517         return do_pread_or_pwrite(fd, (void*)buf, count, offset, true);
518 }
519
520 /* Dumb Windows implementation of writev().  It writes the vectors one at a
521  * time. */
522 extern ssize_t
523 win32_writev(int fd, const struct iovec *iov, int iovcnt)
524 {
525         ssize_t total_bytes_written = 0;
526
527         if (iovcnt <= 0) {
528                 errno = EINVAL;
529                 return -1;
530         }
531         for (int i = 0; i < iovcnt; i++) {
532                 ssize_t bytes_written;
533
534                 bytes_written = write(fd, iov[i].iov_base, iov[i].iov_len);
535                 if (bytes_written >= 0)
536                         total_bytes_written += bytes_written;
537                 if (bytes_written != iov[i].iov_len) {
538                         if (total_bytes_written == 0)
539                                 total_bytes_written = -1;
540                         break;
541                 }
542         }
543         return total_bytes_written;
544 }
545
546 /* Given a path, which may not yet exist, get a set of flags that describe the
547  * features of the volume the path is on. */
548 int
549 win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret)
550 {
551         wchar_t *volume;
552         BOOL bret;
553         DWORD vol_flags;
554
555         if (path[0] != L'\0' && path[0] != L'\\' &&
556             path[0] != L'/' && path[1] == L':')
557         {
558                 /* Path starts with a drive letter; use it. */
559                 volume = alloca(4 * sizeof(wchar_t));
560                 volume[0] = path[0];
561                 volume[1] = path[1];
562                 volume[2] = L'\\';
563                 volume[3] = L'\0';
564         } else {
565                 /* Path does not start with a drive letter; use the volume of
566                  * the current working directory. */
567                 volume = NULL;
568         }
569         bret = GetVolumeInformationW(volume, /* lpRootPathName */
570                                      NULL,  /* lpVolumeNameBuffer */
571                                      0,     /* nVolumeNameSize */
572                                      NULL,  /* lpVolumeSerialNumber */
573                                      NULL,  /* lpMaximumComponentLength */
574                                      &vol_flags, /* lpFileSystemFlags */
575                                      NULL,  /* lpFileSystemNameBuffer */
576                                      0);    /* nFileSystemNameSize */
577         if (!bret) {
578                 DWORD err = GetLastError();
579                 WARNING("Failed to get volume information for path \"%ls\"", path);
580                 win32_error(err);
581                 vol_flags = 0xffffffff;
582         }
583
584         DEBUG("using vol_flags = %x", vol_flags);
585         *vol_flags_ret = vol_flags;
586         return 0;
587 }
588
589 HANDLE
590 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
591 {
592         return CreateFileW(path,
593                            dwDesiredAccess,
594                            FILE_SHARE_READ,
595                            NULL, /* lpSecurityAttributes */
596                            OPEN_EXISTING,
597                            FILE_FLAG_BACKUP_SEMANTICS |
598                                FILE_FLAG_OPEN_REPARSE_POINT,
599                            NULL /* hTemplateFile */);
600 }
601
602 HANDLE
603 win32_open_file_data_only(const wchar_t *path)
604 {
605         return win32_open_existing_file(path, FILE_READ_DATA);
606 }
607
608 /* Pointers to functions that are not available on all targetted versions of
609  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
610  * assume it specifies a certain calling convention. */
611
612 /* Vista and later */
613 HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
614                                             STREAM_INFO_LEVELS InfoLevel,
615                                             LPVOID lpFindStreamData,
616                                             DWORD dwFlags) = NULL;
617
618 /* Vista and later */
619 BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
620                                          LPVOID lpFindStreamData) = NULL;
621
622 static OSVERSIONINFO windows_version_info = {
623         .dwOSVersionInfoSize = sizeof(OSVERSIONINFO),
624 };
625
626 static HMODULE hKernel32 = NULL;
627
628 bool
629 windows_version_is_at_least(unsigned major, unsigned minor)
630 {
631         return windows_version_info.dwMajorVersion > major ||
632                 (windows_version_info.dwMajorVersion == major &&
633                  windows_version_info.dwMinorVersion >= minor);
634 }
635
636 /* Try to dynamically load some functions */
637 void
638 win32_global_init()
639 {
640         DWORD err;
641
642         if (hKernel32 == NULL) {
643                 DEBUG("Loading Kernel32.dll");
644                 hKernel32 = LoadLibraryW(L"Kernel32.dll");
645                 if (hKernel32 == NULL) {
646                         err = GetLastError();
647                         WARNING("Can't load Kernel32.dll");
648                         win32_error(err);
649                 }
650         }
651
652         if (hKernel32) {
653                 win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32,
654                                                                    "FindFirstStreamW");
655                 if (win32func_FindFirstStreamW) {
656                         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32,
657                                                                           "FindNextStreamW");
658                         if (!win32func_FindNextStreamW)
659                                 win32func_FindFirstStreamW = NULL;
660                 }
661         }
662
663         GetVersionEx(&windows_version_info);
664 }
665
666 void
667 win32_global_cleanup()
668 {
669         if (hKernel32 != NULL) {
670                 DEBUG("Closing Kernel32.dll");
671                 FreeLibrary(hKernel32);
672                 hKernel32 = NULL;
673         }
674 }
675
676 #endif /* __WIN32__ */