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