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