]> wimlib.net Git - wimlib/blob - src/win32.c
Document wimlib_update_image()
[wimlib] / src / win32.c
1 /*
2  * win32.c
3  *
4  * Most of the library code specific to native Windows builds is in here.
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 "config.h"
29 #include <windows.h>
30 #include <ntdef.h>
31 #include <wchar.h>
32 #include <shlwapi.h> /* for PathMatchSpecW() */
33 #include <aclapi.h> /* for SetSecurityInfo() */
34 #ifdef ERROR /* windows.h defines this */
35 #  undef ERROR
36 #endif
37
38 #include "win32.h"
39 #include "dentry.h"
40 #include "lookup_table.h"
41 #include "security.h"
42 #include "endianness.h"
43 #include "buffer_io.h"
44 #include <pthread.h>
45
46 #include <errno.h>
47
48 #define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1
49 #define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1
50 #define MAX_CREATE_HARD_LINK_WARNINGS 5
51 #define MAX_CREATE_SOFT_LINK_WARNINGS 5
52 struct win32_capture_state {
53         unsigned long num_get_sd_access_denied;
54         unsigned long num_get_sacl_priv_notheld;
55 };
56
57 #define MAX_SET_SD_ACCESS_DENIED_WARNINGS 1
58 #define MAX_SET_SACL_PRIV_NOTHELD_WARNINGS 1
59
60 #ifdef ENABLE_ERROR_MESSAGES
61 static void
62 win32_error(u32 err_code)
63 {
64         wchar_t *buffer;
65         DWORD nchars;
66         nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
67                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
68                                 NULL, err_code, 0,
69                                 (wchar_t*)&buffer, 0, NULL);
70         if (nchars == 0) {
71                 ERROR("Error printing error message! "
72                       "Computer will self-destruct in 3 seconds.");
73         } else {
74                 ERROR("Win32 error: %ls", buffer);
75                 LocalFree(buffer);
76         }
77 }
78 #else /* ENABLE_ERROR_MESSAGES */
79 #  define win32_error(err_code)
80 #endif /* !ENABLE_ERROR_MESSAGES */
81
82 static int
83 win32_error_to_errno(DWORD err_code)
84 {
85         /* This mapping is that used in Cygwin.
86          * Some of these choices are arbitrary. */
87         switch (err_code) {
88         case ERROR_ACCESS_DENIED:
89                 return EACCES;
90         case ERROR_ACTIVE_CONNECTIONS:
91                 return EAGAIN;
92         case ERROR_ALREADY_EXISTS:
93                 return EEXIST;
94         case ERROR_BAD_DEVICE:
95                 return ENODEV;
96         case ERROR_BAD_EXE_FORMAT:
97                 return ENOEXEC;
98         case ERROR_BAD_NETPATH:
99                 return ENOENT;
100         case ERROR_BAD_NET_NAME:
101                 return ENOENT;
102         case ERROR_BAD_NET_RESP:
103                 return ENOSYS;
104         case ERROR_BAD_PATHNAME:
105                 return ENOENT;
106         case ERROR_BAD_PIPE:
107                 return EINVAL;
108         case ERROR_BAD_UNIT:
109                 return ENODEV;
110         case ERROR_BAD_USERNAME:
111                 return EINVAL;
112         case ERROR_BEGINNING_OF_MEDIA:
113                 return EIO;
114         case ERROR_BROKEN_PIPE:
115                 return EPIPE;
116         case ERROR_BUSY:
117                 return EBUSY;
118         case ERROR_BUS_RESET:
119                 return EIO;
120         case ERROR_CALL_NOT_IMPLEMENTED:
121                 return ENOSYS;
122         case ERROR_CANNOT_MAKE:
123                 return EPERM;
124         case ERROR_CHILD_NOT_COMPLETE:
125                 return EBUSY;
126         case ERROR_COMMITMENT_LIMIT:
127                 return EAGAIN;
128         case ERROR_CRC:
129                 return EIO;
130         case ERROR_DEVICE_DOOR_OPEN:
131                 return EIO;
132         case ERROR_DEVICE_IN_USE:
133                 return EAGAIN;
134         case ERROR_DEVICE_REQUIRES_CLEANING:
135                 return EIO;
136         case ERROR_DIRECTORY:
137                 return ENOTDIR;
138         case ERROR_DIR_NOT_EMPTY:
139                 return ENOTEMPTY;
140         case ERROR_DISK_CORRUPT:
141                 return EIO;
142         case ERROR_DISK_FULL:
143                 return ENOSPC;
144 #ifdef ENOTUNIQ
145         case ERROR_DUP_NAME:
146                 return ENOTUNIQ;
147 #endif
148         case ERROR_EAS_DIDNT_FIT:
149                 return ENOSPC;
150         case ERROR_EAS_NOT_SUPPORTED:
151                 return ENOTSUP;
152         case ERROR_EA_LIST_INCONSISTENT:
153                 return EINVAL;
154         case ERROR_EA_TABLE_FULL:
155                 return ENOSPC;
156         case ERROR_END_OF_MEDIA:
157                 return ENOSPC;
158         case ERROR_EOM_OVERFLOW:
159                 return EIO;
160         case ERROR_EXE_MACHINE_TYPE_MISMATCH:
161                 return ENOEXEC;
162         case ERROR_EXE_MARKED_INVALID:
163                 return ENOEXEC;
164         case ERROR_FILEMARK_DETECTED:
165                 return EIO;
166         case ERROR_FILENAME_EXCED_RANGE:
167                 return ENAMETOOLONG;
168         case ERROR_FILE_CORRUPT:
169                 return EEXIST;
170         case ERROR_FILE_EXISTS:
171                 return EEXIST;
172         case ERROR_FILE_INVALID:
173                 return ENXIO;
174         case ERROR_FILE_NOT_FOUND:
175                 return ENOENT;
176         case ERROR_HANDLE_DISK_FULL:
177                 return ENOSPC;
178 #ifdef ENODATA
179         case ERROR_HANDLE_EOF:
180                 return ENODATA;
181 #endif
182         case ERROR_INVALID_ADDRESS:
183                 return EINVAL;
184         case ERROR_INVALID_AT_INTERRUPT_TIME:
185                 return EINTR;
186         case ERROR_INVALID_BLOCK_LENGTH:
187                 return EIO;
188         case ERROR_INVALID_DATA:
189                 return EINVAL;
190         case ERROR_INVALID_DRIVE:
191                 return ENODEV;
192         case ERROR_INVALID_EA_NAME:
193                 return EINVAL;
194         case ERROR_INVALID_EXE_SIGNATURE:
195                 return ENOEXEC;
196 #ifdef EBADRQC
197         case ERROR_INVALID_FUNCTION:
198                 return EBADRQC;
199 #endif
200         case ERROR_INVALID_HANDLE:
201                 return EBADF;
202         case ERROR_INVALID_NAME:
203                 return ENOENT;
204         case ERROR_INVALID_PARAMETER:
205                 return EINVAL;
206         case ERROR_INVALID_SIGNAL_NUMBER:
207                 return EINVAL;
208         case ERROR_IOPL_NOT_ENABLED:
209                 return ENOEXEC;
210         case ERROR_IO_DEVICE:
211                 return EIO;
212         case ERROR_IO_INCOMPLETE:
213                 return EAGAIN;
214         case ERROR_IO_PENDING:
215                 return EAGAIN;
216         case ERROR_LOCK_VIOLATION:
217                 return EBUSY;
218         case ERROR_MAX_THRDS_REACHED:
219                 return EAGAIN;
220         case ERROR_META_EXPANSION_TOO_LONG:
221                 return EINVAL;
222         case ERROR_MOD_NOT_FOUND:
223                 return ENOENT;
224 #ifdef EMSGSIZE
225         case ERROR_MORE_DATA:
226                 return EMSGSIZE;
227 #endif
228         case ERROR_NEGATIVE_SEEK:
229                 return EINVAL;
230         case ERROR_NETNAME_DELETED:
231                 return ENOENT;
232         case ERROR_NOACCESS:
233                 return EFAULT;
234         case ERROR_NONE_MAPPED:
235                 return EINVAL;
236         case ERROR_NONPAGED_SYSTEM_RESOURCES:
237                 return EAGAIN;
238 #ifdef ENOLINK
239         case ERROR_NOT_CONNECTED:
240                 return ENOLINK;
241 #endif
242         case ERROR_NOT_ENOUGH_MEMORY:
243                 return ENOMEM;
244         case ERROR_NOT_OWNER:
245                 return EPERM;
246 #ifdef ENOMEDIUM
247         case ERROR_NOT_READY:
248                 return ENOMEDIUM;
249 #endif
250         case ERROR_NOT_SAME_DEVICE:
251                 return EXDEV;
252         case ERROR_NOT_SUPPORTED:
253                 return ENOSYS;
254         case ERROR_NO_DATA:
255                 return EPIPE;
256         case ERROR_NO_DATA_DETECTED:
257                 return EIO;
258 #ifdef ENOMEDIUM
259         case ERROR_NO_MEDIA_IN_DRIVE:
260                 return ENOMEDIUM;
261 #endif
262 #ifdef ENMFILE
263         case ERROR_NO_MORE_FILES:
264                 return ENMFILE;
265 #endif
266 #ifdef ENMFILE
267         case ERROR_NO_MORE_ITEMS:
268                 return ENMFILE;
269 #endif
270         case ERROR_NO_MORE_SEARCH_HANDLES:
271                 return ENFILE;
272         case ERROR_NO_PROC_SLOTS:
273                 return EAGAIN;
274         case ERROR_NO_SIGNAL_SENT:
275                 return EIO;
276         case ERROR_NO_SYSTEM_RESOURCES:
277                 return EFBIG;
278         case ERROR_NO_TOKEN:
279                 return EINVAL;
280         case ERROR_OPEN_FAILED:
281                 return EIO;
282         case ERROR_OPEN_FILES:
283                 return EAGAIN;
284         case ERROR_OUTOFMEMORY:
285                 return ENOMEM;
286         case ERROR_PAGED_SYSTEM_RESOURCES:
287                 return EAGAIN;
288         case ERROR_PAGEFILE_QUOTA:
289                 return EAGAIN;
290         case ERROR_PATH_NOT_FOUND:
291                 return ENOENT;
292         case ERROR_PIPE_BUSY:
293                 return EBUSY;
294         case ERROR_PIPE_CONNECTED:
295                 return EBUSY;
296 #ifdef ECOMM
297         case ERROR_PIPE_LISTENING:
298                 return ECOMM;
299         case ERROR_PIPE_NOT_CONNECTED:
300                 return ECOMM;
301 #endif
302         case ERROR_POSSIBLE_DEADLOCK:
303                 return EDEADLOCK;
304         case ERROR_PRIVILEGE_NOT_HELD:
305                 return EPERM;
306         case ERROR_PROCESS_ABORTED:
307                 return EFAULT;
308         case ERROR_PROC_NOT_FOUND:
309                 return ESRCH;
310 #ifdef ENONET
311         case ERROR_REM_NOT_LIST:
312                 return ENONET;
313 #endif
314         case ERROR_SECTOR_NOT_FOUND:
315                 return EINVAL;
316         case ERROR_SEEK:
317                 return EINVAL;
318         case ERROR_SETMARK_DETECTED:
319                 return EIO;
320         case ERROR_SHARING_BUFFER_EXCEEDED:
321                 return ENOLCK;
322         case ERROR_SHARING_VIOLATION:
323                 return EBUSY;
324         case ERROR_SIGNAL_PENDING:
325                 return EBUSY;
326         case ERROR_SIGNAL_REFUSED:
327                 return EIO;
328 #ifdef ELIBBAD
329         case ERROR_SXS_CANT_GEN_ACTCTX:
330                 return ELIBBAD;
331 #endif
332         case ERROR_THREAD_1_INACTIVE:
333                 return EINVAL;
334         case ERROR_TOO_MANY_LINKS:
335                 return EMLINK;
336         case ERROR_TOO_MANY_OPEN_FILES:
337                 return EMFILE;
338         case ERROR_WAIT_NO_CHILDREN:
339                 return ECHILD;
340         case ERROR_WORKING_SET_QUOTA:
341                 return EAGAIN;
342         case ERROR_WRITE_PROTECT:
343                 return EROFS;
344         default:
345                 return -1;
346         }
347 }
348
349 static void
350 set_errno_from_GetLastError()
351 {
352         errno = win32_error_to_errno(GetLastError());
353 }
354
355 /* Pointers to functions that are not available on all targetted versions of
356  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
357  * assume it specifies a certain calling convention. */
358
359 /* Vista and later */
360 static HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
361                                             STREAM_INFO_LEVELS InfoLevel,
362                                             LPVOID lpFindStreamData,
363                                             DWORD dwFlags) = NULL;
364
365 /* Vista and later */
366 static BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
367                                          LPVOID lpFindStreamData) = NULL;
368
369 static HMODULE hKernel32 = NULL;
370
371 /* Try to dynamically load some functions */
372 void
373 win32_global_init()
374 {
375         DWORD err;
376
377         if (hKernel32 == NULL) {
378                 DEBUG("Loading Kernel32.dll");
379                 hKernel32 = LoadLibraryW(L"Kernel32.dll");
380                 if (hKernel32 == NULL) {
381                         err = GetLastError();
382                         WARNING("Can't load Kernel32.dll");
383                         win32_error(err);
384                         return;
385                 }
386         }
387
388         DEBUG("Looking for FindFirstStreamW");
389         win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, "FindFirstStreamW");
390         if (!win32func_FindFirstStreamW) {
391                 WARNING("Could not find function FindFirstStreamW() in Kernel32.dll!");
392                 WARNING("Capturing alternate data streams will not be supported.");
393                 return;
394         }
395
396         DEBUG("Looking for FindNextStreamW");
397         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32, "FindNextStreamW");
398         if (!win32func_FindNextStreamW) {
399                 WARNING("Could not find function FindNextStreamW() in Kernel32.dll!");
400                 WARNING("Capturing alternate data streams will not be supported.");
401                 win32func_FindFirstStreamW = NULL;
402         }
403 }
404
405 void
406 win32_global_cleanup()
407 {
408         if (hKernel32 != NULL) {
409                 DEBUG("Closing Kernel32.dll");
410                 FreeLibrary(hKernel32);
411                 hKernel32 = NULL;
412         }
413 }
414
415 static const wchar_t *capture_access_denied_msg =
416 L"         If you are not running this program as the administrator, you may\n"
417  "         need to do so, so that all data and metadata can be backed up.\n"
418  "         Otherwise, there may be no way to access the desired data or\n"
419  "         metadata without taking ownership of the file or directory.\n"
420  ;
421
422 static const wchar_t *apply_access_denied_msg =
423 L"If you are not running this program as the administrator, you may\n"
424  "          need to do so, so that all data and metadata can be extracted\n"
425  "          exactly as the origignal copy.  However, if you do not care that\n"
426  "          the security descriptors are extracted correctly, you could run\n"
427  "          `wimlib-imagex apply' with the --no-acls flag instead.\n"
428  ;
429
430 static HANDLE
431 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
432 {
433         return CreateFileW(path,
434                            dwDesiredAccess,
435                            FILE_SHARE_READ,
436                            NULL, /* lpSecurityAttributes */
437                            OPEN_EXISTING,
438                            FILE_FLAG_BACKUP_SEMANTICS |
439                                FILE_FLAG_OPEN_REPARSE_POINT,
440                            NULL /* hTemplateFile */);
441 }
442
443 HANDLE
444 win32_open_file_data_only(const wchar_t *path)
445 {
446         return win32_open_existing_file(path, FILE_READ_DATA);
447 }
448
449 int
450 read_win32_file_prefix(const struct wim_lookup_table_entry *lte,
451                        u64 size,
452                        consume_data_callback_t cb,
453                        void *ctx_or_buf,
454                        int _ignored_flags)
455 {
456         int ret = 0;
457         void *out_buf;
458         DWORD err;
459         u64 bytes_remaining;
460
461         HANDLE hFile = win32_open_file_data_only(lte->file_on_disk);
462         if (hFile == INVALID_HANDLE_VALUE) {
463                 err = GetLastError();
464                 ERROR("Failed to open \"%ls\"", lte->file_on_disk);
465                 win32_error(err);
466                 return WIMLIB_ERR_OPEN;
467         }
468
469         if (cb)
470                 out_buf = alloca(WIM_CHUNK_SIZE);
471         else
472                 out_buf = ctx_or_buf;
473
474         bytes_remaining = size;
475         while (bytes_remaining) {
476                 DWORD bytesToRead, bytesRead;
477
478                 bytesToRead = min(WIM_CHUNK_SIZE, bytes_remaining);
479                 if (!ReadFile(hFile, out_buf, bytesToRead, &bytesRead, NULL) ||
480                     bytesRead != bytesToRead)
481                 {
482                         err = GetLastError();
483                         ERROR("Failed to read data from \"%ls\"", lte->file_on_disk);
484                         win32_error(err);
485                         ret = WIMLIB_ERR_READ;
486                         break;
487                 }
488                 bytes_remaining -= bytesRead;
489                 if (cb) {
490                         ret = (*cb)(out_buf, bytesRead, ctx_or_buf);
491                         if (ret)
492                                 break;
493                 } else {
494                         out_buf += bytesRead;
495                 }
496         }
497         CloseHandle(hFile);
498         return ret;
499 }
500
501 struct win32_encrypted_read_ctx {
502         consume_data_callback_t read_prefix_cb;
503         void *read_prefix_ctx_or_buf;
504         int wimlib_err_code;
505         void *buf;
506         size_t buf_filled;
507         u64 bytes_remaining;
508 };
509
510 static DWORD WINAPI
511 win32_encrypted_export_cb(unsigned char *_data, void *_ctx, unsigned long len)
512 {
513         const void *data = _data;
514         struct win32_encrypted_read_ctx *ctx = _ctx;
515         int ret;
516
517         DEBUG("len = %lu", len);
518         if (ctx->read_prefix_cb) {
519                 /* The length of the buffer passed to the ReadEncryptedFileRaw()
520                  * export callback is undocumented, so we assume it may be of
521                  * arbitrary size. */
522                 size_t bytes_to_buffer = min(ctx->bytes_remaining - ctx->buf_filled,
523                                              len);
524                 while (bytes_to_buffer) {
525                         size_t bytes_to_copy_to_buf =
526                                 min(bytes_to_buffer, WIM_CHUNK_SIZE - ctx->buf_filled);
527
528                         memcpy(ctx->buf + ctx->buf_filled, data,
529                                bytes_to_copy_to_buf);
530                         ctx->buf_filled += bytes_to_copy_to_buf;
531                         data += bytes_to_copy_to_buf;
532                         bytes_to_buffer -= bytes_to_copy_to_buf;
533
534                         if (ctx->buf_filled == WIM_CHUNK_SIZE ||
535                             ctx->buf_filled == ctx->bytes_remaining)
536                         {
537                                 ret = (*ctx->read_prefix_cb)(ctx->buf,
538                                                              ctx->buf_filled,
539                                                              ctx->read_prefix_ctx_or_buf);
540                                 if (ret) {
541                                         ctx->wimlib_err_code = ret;
542                                         /* Shouldn't matter what error code is returned
543                                          * here, as long as it isn't ERROR_SUCCESS. */
544                                         return ERROR_READ_FAULT;
545                                 }
546                                 ctx->bytes_remaining -= ctx->buf_filled;
547                                 ctx->buf_filled = 0;
548                         }
549                 }
550         } else {
551                 size_t len_to_copy = min(len, ctx->bytes_remaining);
552                 memcpy(ctx->read_prefix_ctx_or_buf, data, len_to_copy);
553                 ctx->bytes_remaining -= len_to_copy;
554                 ctx->read_prefix_ctx_or_buf += len_to_copy;
555         }
556         return ERROR_SUCCESS;
557 }
558
559 int
560 read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte,
561                                  u64 size,
562                                  consume_data_callback_t cb,
563                                  void *ctx_or_buf,
564                                  int _ignored_flags)
565 {
566         struct win32_encrypted_read_ctx export_ctx;
567         DWORD err;
568         void *file_ctx;
569         int ret;
570
571         DEBUG("Reading %"PRIu64" bytes from encryted file \"%ls\"",
572               size, lte->file_on_disk);
573
574         export_ctx.read_prefix_cb = cb;
575         export_ctx.read_prefix_ctx_or_buf = ctx_or_buf;
576         export_ctx.wimlib_err_code = 0;
577         if (cb) {
578                 export_ctx.buf = MALLOC(WIM_CHUNK_SIZE);
579                 if (!export_ctx.buf)
580                         return WIMLIB_ERR_NOMEM;
581         } else {
582                 export_ctx.buf = NULL;
583         }
584         export_ctx.buf_filled = 0;
585         export_ctx.bytes_remaining = size;
586
587         err = OpenEncryptedFileRawW(lte->file_on_disk, 0, &file_ctx);
588         if (err != ERROR_SUCCESS) {
589                 ERROR("Failed to open encrypted file \"%ls\" for raw read",
590                       lte->file_on_disk);
591                 win32_error(err);
592                 ret = WIMLIB_ERR_OPEN;
593                 goto out_free_buf;
594         }
595         err = ReadEncryptedFileRaw(win32_encrypted_export_cb,
596                                    &export_ctx, file_ctx);
597         if (err != ERROR_SUCCESS) {
598                 ERROR("Failed to read encrypted file \"%ls\"",
599                       lte->file_on_disk);
600                 win32_error(err);
601                 ret = export_ctx.wimlib_err_code;
602                 if (ret == 0)
603                         ret = WIMLIB_ERR_READ;
604         } else if (export_ctx.bytes_remaining != 0) {
605                 ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
606                       "encryted file \"%ls\"",
607                       size - export_ctx.bytes_remaining, size,
608                       lte->file_on_disk);
609                 ret = WIMLIB_ERR_READ;
610         } else {
611                 ret = 0;
612         }
613         CloseEncryptedFileRaw(file_ctx);
614 out_free_buf:
615         FREE(export_ctx.buf);
616         return ret;
617 }
618
619 /* Given a path, which may not yet exist, get a set of flags that describe the
620  * features of the volume the path is on. */
621 static int
622 win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret)
623 {
624         wchar_t *volume;
625         BOOL bret;
626         DWORD vol_flags;
627
628         if (path[0] != L'\0' && path[0] != L'\\' &&
629             path[0] != L'/' && path[1] == L':')
630         {
631                 /* Path starts with a drive letter; use it. */
632                 volume = alloca(4 * sizeof(wchar_t));
633                 volume[0] = path[0];
634                 volume[1] = path[1];
635                 volume[2] = L'\\';
636                 volume[3] = L'\0';
637         } else {
638                 /* Path does not start with a drive letter; use the volume of
639                  * the current working directory. */
640                 volume = NULL;
641         }
642         bret = GetVolumeInformationW(volume, /* lpRootPathName */
643                                      NULL,  /* lpVolumeNameBuffer */
644                                      0,     /* nVolumeNameSize */
645                                      NULL,  /* lpVolumeSerialNumber */
646                                      NULL,  /* lpMaximumComponentLength */
647                                      &vol_flags, /* lpFileSystemFlags */
648                                      NULL,  /* lpFileSystemNameBuffer */
649                                      0);    /* nFileSystemNameSize */
650         if (!bret) {
651                 DWORD err = GetLastError();
652                 WARNING("Failed to get volume information for path \"%ls\"", path);
653                 win32_error(err);
654                 vol_flags = 0xffffffff;
655         }
656
657         DEBUG("using vol_flags = %x", vol_flags);
658         *vol_flags_ret = vol_flags;
659         return 0;
660 }
661
662
663 static u64
664 FILETIME_to_u64(const FILETIME *ft)
665 {
666         return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
667 }
668
669 static int
670 win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path)
671 {
672         WIN32_FIND_DATAW dat;
673         HANDLE hFind;
674         int ret = 0;
675
676         /* If we can't read the short filename for some reason, we just ignore
677          * the error and assume the file has no short name.  I don't think this
678          * should be an issue, since the short names are essentially obsolete
679          * anyway. */
680         hFind = FindFirstFileW(path, &dat);
681         if (hFind != INVALID_HANDLE_VALUE) {
682                 if (dat.cAlternateFileName[0] != L'\0') {
683                         DEBUG("\"%ls\": short name \"%ls\"", path, dat.cAlternateFileName);
684                         size_t short_name_nbytes = wcslen(dat.cAlternateFileName) *
685                                                    sizeof(wchar_t);
686                         size_t n = short_name_nbytes + sizeof(wchar_t);
687                         dentry->short_name = MALLOC(n);
688                         if (dentry->short_name) {
689                                 memcpy(dentry->short_name, dat.cAlternateFileName, n);
690                                 dentry->short_name_nbytes = short_name_nbytes;
691                         } else {
692                                 ret = WIMLIB_ERR_NOMEM;
693                         }
694                 }
695                 FindClose(hFind);
696         }
697         return ret;
698 }
699
700 static int
701 win32_get_security_descriptor(struct wim_dentry *dentry,
702                               struct sd_set *sd_set,
703                               const wchar_t *path,
704                               struct win32_capture_state *state,
705                               int add_flags)
706 {
707         SECURITY_INFORMATION requestedInformation;
708         DWORD lenNeeded = 0;
709         BOOL status;
710         DWORD err;
711         unsigned long n;
712
713         requestedInformation = DACL_SECURITY_INFORMATION |
714                                SACL_SECURITY_INFORMATION |
715                                OWNER_SECURITY_INFORMATION |
716                                GROUP_SECURITY_INFORMATION;
717 again:
718         /* Request length of security descriptor */
719         status = GetFileSecurityW(path, requestedInformation,
720                                   NULL, 0, &lenNeeded);
721         err = GetLastError();
722         if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
723                 DWORD len = lenNeeded;
724                 char buf[len];
725                 if (GetFileSecurityW(path, requestedInformation,
726                                      (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
727                 {
728                         int security_id = sd_set_add_sd(sd_set, buf, len);
729                         if (security_id < 0)
730                                 return WIMLIB_ERR_NOMEM;
731                         else {
732                                 dentry->d_inode->i_security_id = security_id;
733                                 return 0;
734                         }
735                 } else {
736                         err = GetLastError();
737                 }
738         }
739
740         if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
741                 goto fail;
742
743         switch (err) {
744         case ERROR_PRIVILEGE_NOT_HELD:
745                 if (requestedInformation & SACL_SECURITY_INFORMATION) {
746                         n = state->num_get_sacl_priv_notheld++;
747                         requestedInformation &= ~SACL_SECURITY_INFORMATION;
748                         if (n < MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
749                                 WARNING(
750 "We don't have enough privileges to read the full security\n"
751 "          descriptor of \"%ls\"!\n"
752 "          Re-trying with SACL omitted.\n", path);
753                         } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
754                                 WARNING(
755 "Suppressing further privileges not held error messages when reading\n"
756 "          security descriptors.");
757                         }
758                         goto again;
759                 }
760                 /* Fall through */
761         case ERROR_ACCESS_DENIED:
762                 n = state->num_get_sd_access_denied++;
763                 if (n < MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
764                         WARNING("Failed to read security descriptor of \"%ls\": "
765                                 "Access denied!\n%ls", path, capture_access_denied_msg);
766                 } else if (n == MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
767                         WARNING("Suppressing further access denied errors messages i"
768                                 "when reading security descriptors");
769                 }
770                 return 0;
771         default:
772 fail:
773                 ERROR("Failed to read security descriptor of \"%ls\"", path);
774                 win32_error(err);
775                 return WIMLIB_ERR_READ;
776         }
777 }
778
779 static int
780 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
781                                   wchar_t *path,
782                                   size_t path_num_chars,
783                                   struct add_image_params *params,
784                                   struct win32_capture_state *state,
785                                   unsigned vol_flags);
786
787 /* Reads the directory entries of directory using a Win32 API and recursively
788  * calls win32_build_dentry_tree() on them. */
789 static int
790 win32_recurse_directory(struct wim_dentry *root,
791                         wchar_t *dir_path,
792                         size_t dir_path_num_chars,
793                         struct add_image_params *params,
794                         struct win32_capture_state *state,
795                         unsigned vol_flags)
796 {
797         WIN32_FIND_DATAW dat;
798         HANDLE hFind;
799         DWORD err;
800         int ret;
801
802         DEBUG("Recurse to directory \"%ls\"", dir_path);
803
804         /* Begin reading the directory by calling FindFirstFileW.  Unlike UNIX
805          * opendir(), FindFirstFileW has file globbing built into it.  But this
806          * isn't what we actually want, so just add a dummy glob to get all
807          * entries. */
808         dir_path[dir_path_num_chars] = L'/';
809         dir_path[dir_path_num_chars + 1] = L'*';
810         dir_path[dir_path_num_chars + 2] = L'\0';
811         hFind = FindFirstFileW(dir_path, &dat);
812         dir_path[dir_path_num_chars] = L'\0';
813
814         if (hFind == INVALID_HANDLE_VALUE) {
815                 err = GetLastError();
816                 if (err == ERROR_FILE_NOT_FOUND) {
817                         return 0;
818                 } else {
819                         ERROR("Failed to read directory \"%ls\"", dir_path);
820                         win32_error(err);
821                         return WIMLIB_ERR_READ;
822                 }
823         }
824         ret = 0;
825         do {
826                 /* Skip . and .. entries */
827                 if (dat.cFileName[0] == L'.' &&
828                     (dat.cFileName[1] == L'\0' ||
829                      (dat.cFileName[1] == L'.' &&
830                       dat.cFileName[2] == L'\0')))
831                         continue;
832                 size_t filename_len = wcslen(dat.cFileName);
833
834                 dir_path[dir_path_num_chars] = L'/';
835                 wmemcpy(dir_path + dir_path_num_chars + 1,
836                         dat.cFileName,
837                         filename_len + 1);
838
839                 struct wim_dentry *child;
840                 size_t path_len = dir_path_num_chars + 1 + filename_len;
841                 ret = win32_build_dentry_tree_recursive(&child,
842                                                         dir_path,
843                                                         path_len,
844                                                         params,
845                                                         state,
846                                                         vol_flags);
847                 dir_path[dir_path_num_chars] = L'\0';
848                 if (ret)
849                         goto out_find_close;
850                 if (child)
851                         dentry_add_child(root, child);
852         } while (FindNextFileW(hFind, &dat));
853         err = GetLastError();
854         if (err != ERROR_NO_MORE_FILES) {
855                 ERROR("Failed to read directory \"%ls\"", dir_path);
856                 win32_error(err);
857                 if (ret == 0)
858                         ret = WIMLIB_ERR_READ;
859         }
860 out_find_close:
861         FindClose(hFind);
862         return ret;
863 }
864
865 int
866 win32_get_file_and_vol_ids(const wchar_t *path, u64 *ino_ret, u64 *dev_ret)
867 {
868         HANDLE hFile;
869         DWORD err;
870         BY_HANDLE_FILE_INFORMATION file_info;
871         int ret;
872
873         hFile = win32_open_existing_file(path, FILE_READ_ATTRIBUTES);
874         if (hFile == INVALID_HANDLE_VALUE) {
875                 err = GetLastError();
876                 if (err != ERROR_FILE_NOT_FOUND) {
877                         WARNING("Failed to open \"%ls\" to get file "
878                                 "and volume IDs", path);
879                         win32_error(err);
880                 }
881                 return WIMLIB_ERR_OPEN;
882         }
883
884         if (!GetFileInformationByHandle(hFile, &file_info)) {
885                 err = GetLastError();
886                 ERROR("Failed to get file information for \"%ls\"", path);
887                 win32_error(err);
888                 ret = WIMLIB_ERR_STAT;
889         } else {
890                 *ino_ret = ((u64)file_info.nFileIndexHigh << 32) |
891                             (u64)file_info.nFileIndexLow;
892                 *dev_ret = file_info.dwVolumeSerialNumber;
893                 ret = 0;
894         }
895         CloseHandle(hFile);
896         return ret;
897 }
898
899 /* Reparse point fixup status code */
900 enum rp_status {
901         /* Reparse point corresponded to an absolute symbolic link or junction
902          * point that pointed outside the directory tree being captured, and
903          * therefore was excluded. */
904         RP_EXCLUDED       = 0x0,
905
906         /* Reparse point was not fixed as it was either a relative symbolic
907          * link, a mount point, or something else we could not understand. */
908         RP_NOT_FIXED      = 0x1,
909
910         /* Reparse point corresponded to an absolute symbolic link or junction
911          * point that pointed inside the directory tree being captured, where
912          * the target was specified by a "full" \??\ prefixed path, and
913          * therefore was fixed to be relative to the root of the directory tree
914          * being captured. */
915         RP_FIXED_FULLPATH = 0x2,
916
917         /* Same as RP_FIXED_FULLPATH, except the absolute link target did not
918          * have the \??\ prefix.  It may have begun with a drive letter though.
919          * */
920         RP_FIXED_ABSPATH  = 0x4,
921
922         /* Either RP_FIXED_FULLPATH or RP_FIXED_ABSPATH. */
923         RP_FIXED          = RP_FIXED_FULLPATH | RP_FIXED_ABSPATH,
924 };
925
926 /* Given the "substitute name" target of a Windows reparse point, try doing a
927  * fixup where we change it to be absolute relative to the root of the directory
928  * tree being captured.
929  *
930  * Note that this is only executed when WIMLIB_ADD_FLAG_RPFIX has been
931  * set.
932  *
933  * @capture_root_ino and @capture_root_dev indicate the inode number and device
934  * of the root of the directory tree being captured.  They are meant to identify
935  * this directory (as an alternative to its actual path, which could potentially
936  * be reached via multiple destinations due to other symbolic links).  This may
937  * not work properly on FAT, which doesn't seem to supply proper inode numbers
938  * or file IDs.  However, FAT doesn't support reparse points so this function
939  * wouldn't even be called anyway.
940  */
941 static enum rp_status
942 win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
943                                  u64 capture_root_ino, u64 capture_root_dev,
944                                  u32 rptag)
945 {
946         u16 target_nchars = *target_nbytes_p / 2;
947         size_t stripped_chars;
948         wchar_t *orig_target;
949         int ret;
950
951         ret = parse_substitute_name(target, *target_nbytes_p, rptag);
952         if (ret < 0)
953                 return RP_NOT_FIXED;
954         stripped_chars = ret;
955         if (stripped_chars)
956                 stripped_chars -= 2;
957         target[target_nchars] = L'\0';
958         orig_target = target;
959         target = capture_fixup_absolute_symlink(target + stripped_chars,
960                                                 capture_root_ino, capture_root_dev);
961         if (!target)
962                 return RP_EXCLUDED;
963         target_nchars = wcslen(target);
964         wmemmove(orig_target + stripped_chars, target, target_nchars + 1);
965         *target_nbytes_p = (target_nchars + stripped_chars) * sizeof(wchar_t);
966         DEBUG("Fixed reparse point (new target: \"%ls\")", orig_target);
967         if (stripped_chars)
968                 return RP_FIXED_FULLPATH;
969         else
970                 return RP_FIXED_ABSPATH;
971 }
972
973 /* Returns: `enum rp_status' value on success; negative WIMLIB_ERR_* value on
974  * failure. */
975 static int
976 win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
977                         u64 capture_root_ino, u64 capture_root_dev,
978                         const wchar_t *path)
979 {
980         struct reparse_data rpdata;
981         DWORD rpbuflen;
982         int ret;
983         enum rp_status rp_status;
984
985         rpbuflen = *rpbuflen_p;
986         ret = parse_reparse_data(rpbuf, rpbuflen, &rpdata);
987         if (ret)
988                 return -ret;
989
990         rp_status = win32_capture_maybe_rpfix_target(rpdata.substitute_name,
991                                                      &rpdata.substitute_name_nbytes,
992                                                      capture_root_ino,
993                                                      capture_root_dev,
994                                                      le32_to_cpu(*(u32*)rpbuf));
995         if (rp_status & RP_FIXED) {
996                 wimlib_assert(rpdata.substitute_name_nbytes % 2 == 0);
997                 utf16lechar substitute_name_copy[rpdata.substitute_name_nbytes / 2];
998                 wmemcpy(substitute_name_copy, rpdata.substitute_name,
999                         rpdata.substitute_name_nbytes / 2);
1000                 rpdata.substitute_name = substitute_name_copy;
1001                 rpdata.print_name = substitute_name_copy;
1002                 rpdata.print_name_nbytes = rpdata.substitute_name_nbytes;
1003                 if (rp_status == RP_FIXED_FULLPATH) {
1004                         /* "full path", meaning \??\ prefixed.  We should not
1005                          * include this prefix in the print name, as it is
1006                          * apparently meant for the filesystem driver only. */
1007                         rpdata.print_name += 4;
1008                         rpdata.print_name_nbytes -= 8;
1009                 }
1010                 ret = make_reparse_buffer(&rpdata, rpbuf);
1011                 if (ret == 0)
1012                         ret = rp_status;
1013                 else
1014                         ret = -ret;
1015         } else {
1016                 if (rp_status == RP_EXCLUDED) {
1017                         size_t print_name_nchars = rpdata.print_name_nbytes / 2;
1018                         wchar_t print_name0[print_name_nchars + 1];
1019                         print_name0[print_name_nchars] = L'\0';
1020                         wmemcpy(print_name0, rpdata.print_name, print_name_nchars);
1021                         WARNING("Ignoring %ls pointing out of capture directory:\n"
1022                                 "          \"%ls\" -> \"%ls\"\n"
1023                                 "          (Use --norpfix to capture all symbolic links "
1024                                 "and junction points as-is)",
1025                                 (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK) ?
1026                                         L"absolute symbolic link" : L"junction point",
1027                                 path, print_name0);
1028                 }
1029                 ret = rp_status;
1030         }
1031         return ret;
1032 }
1033
1034 /*
1035  * Loads the reparse point data from a reparse point into memory, optionally
1036  * fixing the targets of absolute symbolic links and junction points to be
1037  * relative to the root of capture.
1038  *
1039  * @hFile:  Open handle to the reparse point.
1040  * @path:   Path to the reparse point.  Used for error messages only.
1041  * @params: Additional parameters, including whether to do reparse point fixups
1042  *          or not.
1043  * @rpbuf:  Buffer of length at least REPARSE_POINT_MAX_SIZE bytes into which
1044  *          the reparse point buffer will be loaded.
1045  * @rpbuflen_ret:  On success, the length of the reparse point buffer in bytes
1046  *                 is written to this location.
1047  *
1048  * Returns:
1049  *      On success, returns an `enum rp_status' value that indicates if and/or
1050  *      how the reparse point fixup was done.
1051  *
1052  *      On failure, returns a negative value that is a negated WIMLIB_ERR_*
1053  *      code.
1054  */
1055 static int
1056 win32_get_reparse_data(HANDLE hFile, const wchar_t *path,
1057                        struct add_image_params *params,
1058                        u8 *rpbuf, u16 *rpbuflen_ret)
1059 {
1060         DWORD bytesReturned;
1061         u32 reparse_tag;
1062         int ret;
1063         u16 rpbuflen;
1064
1065         DEBUG("Loading reparse data from \"%ls\"", path);
1066         if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
1067                              NULL, /* "Not used with this operation; set to NULL" */
1068                              0, /* "Not used with this operation; set to 0" */
1069                              rpbuf, /* "A pointer to a buffer that
1070                                                    receives the reparse point data */
1071                              REPARSE_POINT_MAX_SIZE, /* "The size of the output
1072                                                         buffer, in bytes */
1073                              &bytesReturned,
1074                              NULL))
1075         {
1076                 DWORD err = GetLastError();
1077                 ERROR("Failed to get reparse data of \"%ls\"", path);
1078                 win32_error(err);
1079                 return -WIMLIB_ERR_READ;
1080         }
1081         if (bytesReturned < 8 || bytesReturned > REPARSE_POINT_MAX_SIZE) {
1082                 ERROR("Reparse data on \"%ls\" is invalid", path);
1083                 return -WIMLIB_ERR_INVALID_REPARSE_DATA;
1084         }
1085
1086         rpbuflen = bytesReturned;
1087         reparse_tag = le32_to_cpu(*(u32*)rpbuf);
1088         if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
1089             (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1090              reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
1091         {
1092                 /* Try doing reparse point fixup */
1093                 ret = win32_capture_try_rpfix(rpbuf,
1094                                               &rpbuflen,
1095                                               params->capture_root_ino,
1096                                               params->capture_root_dev,
1097                                               path);
1098         } else {
1099                 ret = RP_NOT_FIXED;
1100         }
1101         *rpbuflen_ret = rpbuflen;
1102         return ret;
1103 }
1104
1105 static DWORD WINAPI
1106 win32_tally_encrypted_size_cb(unsigned char *_data, void *_ctx,
1107                               unsigned long len)
1108 {
1109         *(u64*)_ctx += len;
1110         return ERROR_SUCCESS;
1111 }
1112
1113 static int
1114 win32_get_encrypted_file_size(const wchar_t *path, u64 *size_ret)
1115 {
1116         DWORD err;
1117         void *file_ctx;
1118         int ret;
1119
1120         *size_ret = 0;
1121         err = OpenEncryptedFileRawW(path, 0, &file_ctx);
1122         if (err != ERROR_SUCCESS) {
1123                 ERROR("Failed to open encrypted file \"%ls\" for raw read", path);
1124                 win32_error(err);
1125                 return WIMLIB_ERR_OPEN;
1126         }
1127         err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb,
1128                                    size_ret, file_ctx);
1129         if (err != ERROR_SUCCESS) {
1130                 ERROR("Failed to read raw encrypted data from \"%ls\"", path);
1131                 win32_error(err);
1132                 ret = WIMLIB_ERR_READ;
1133         } else {
1134                 ret = 0;
1135         }
1136         CloseEncryptedFileRaw(file_ctx);
1137         return ret;
1138 }
1139
1140 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
1141  * stream); calculates its SHA1 message digest and either creates a `struct
1142  * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
1143  * wim_lookup_table_entry' for an identical stream.
1144  *
1145  * @path:               Path to the file (UTF-16LE).
1146  *
1147  * @path_num_chars:     Number of 2-byte characters in @path.
1148  *
1149  * @inode:              WIM inode to save the stream into.
1150  *
1151  * @lookup_table:       Stream lookup table for the WIM.
1152  *
1153  * @dat:                A `WIN32_FIND_STREAM_DATA' structure that specifies the
1154  *                      stream name.
1155  *
1156  * Returns 0 on success; nonzero on failure.
1157  */
1158 static int
1159 win32_capture_stream(const wchar_t *path,
1160                      size_t path_num_chars,
1161                      struct wim_inode *inode,
1162                      struct wim_lookup_table *lookup_table,
1163                      WIN32_FIND_STREAM_DATA *dat)
1164 {
1165         struct wim_ads_entry *ads_entry;
1166         struct wim_lookup_table_entry *lte;
1167         int ret;
1168         wchar_t *stream_name, *colon;
1169         size_t stream_name_nchars;
1170         bool is_named_stream;
1171         wchar_t *spath;
1172         size_t spath_nchars;
1173         size_t spath_buf_nbytes;
1174         const wchar_t *relpath_prefix;
1175         const wchar_t *colonchar;
1176
1177         DEBUG("Capture \"%ls\" stream \"%ls\"", path, dat->cStreamName);
1178
1179         /* The stream name should be returned as :NAME:TYPE */
1180         stream_name = dat->cStreamName;
1181         if (*stream_name != L':')
1182                 goto out_invalid_stream_name;
1183         stream_name += 1;
1184         colon = wcschr(stream_name, L':');
1185         if (colon == NULL)
1186                 goto out_invalid_stream_name;
1187
1188         if (wcscmp(colon + 1, L"$DATA")) {
1189                 /* Not a DATA stream */
1190                 ret = 0;
1191                 goto out;
1192         }
1193
1194         *colon = '\0';
1195
1196         stream_name_nchars = colon - stream_name;
1197         is_named_stream = (stream_name_nchars != 0);
1198
1199         if (is_named_stream) {
1200                 /* Allocate an ADS entry for the named stream. */
1201                 ads_entry = inode_add_ads_utf16le(inode, stream_name,
1202                                                   stream_name_nchars * sizeof(wchar_t));
1203                 if (!ads_entry) {
1204                         ret = WIMLIB_ERR_NOMEM;
1205                         goto out;
1206                 }
1207         }
1208
1209         /* If zero length stream, no lookup table entry needed. */
1210         if ((u64)dat->StreamSize.QuadPart == 0) {
1211                 ret = 0;
1212                 goto out;
1213         }
1214
1215         /* Create a UTF-16LE string @spath that gives the filename, then a
1216          * colon, then the stream name.  Or, if it's an unnamed stream, just the
1217          * filename.  It is MALLOC()'ed so that it can be saved in the
1218          * wim_lookup_table_entry if needed.
1219          *
1220          * As yet another special case, relative paths need to be changed to
1221          * begin with an explicit "./" so that, for example, a file t:ads, where
1222          * :ads is the part we added, is not interpreted as a file on the t:
1223          * drive. */
1224         spath_nchars = path_num_chars;
1225         relpath_prefix = L"";
1226         colonchar = L"";
1227         if (is_named_stream) {
1228                 spath_nchars += 1 + stream_name_nchars;
1229                 colonchar = L":";
1230                 if (path_num_chars == 1 &&
1231                     path[0] != L'/' &&
1232                     path[0] != L'\\')
1233                 {
1234                         spath_nchars += 2;
1235                         relpath_prefix = L"./";
1236                 }
1237         }
1238
1239         spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t);
1240         spath = MALLOC(spath_buf_nbytes);
1241
1242         swprintf(spath, L"%ls%ls%ls%ls",
1243                  relpath_prefix, path, colonchar, stream_name);
1244
1245         /* Make a new wim_lookup_table_entry */
1246         lte = new_lookup_table_entry();
1247         if (!lte) {
1248                 ret = WIMLIB_ERR_NOMEM;
1249                 goto out_free_spath;
1250         }
1251         lte->file_on_disk = spath;
1252         spath = NULL;
1253         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED && !is_named_stream) {
1254                 u64 encrypted_size;
1255                 lte->resource_location = RESOURCE_WIN32_ENCRYPTED;
1256                 ret = win32_get_encrypted_file_size(path, &encrypted_size);
1257                 if (ret)
1258                         goto out_free_spath;
1259                 lte->resource_entry.original_size = encrypted_size;
1260         } else {
1261                 lte->resource_location = RESOURCE_WIN32;
1262                 lte->resource_entry.original_size = (u64)dat->StreamSize.QuadPart;
1263         }
1264
1265         u32 stream_id;
1266         if (is_named_stream) {
1267                 stream_id = ads_entry->stream_id;
1268                 ads_entry->lte = lte;
1269         } else {
1270                 stream_id = 0;
1271                 inode->i_lte = lte;
1272         }
1273         lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id);
1274         ret = 0;
1275 out_free_spath:
1276         FREE(spath);
1277 out:
1278         return ret;
1279 out_invalid_stream_name:
1280         ERROR("Invalid stream name: \"%ls:%ls\"", path, dat->cStreamName);
1281         ret = WIMLIB_ERR_READ;
1282         goto out;
1283 }
1284
1285 /* Scans a Win32 file for unnamed and named data streams (not reparse point
1286  * streams).
1287  *
1288  * @path:               Path to the file (UTF-16LE).
1289  *
1290  * @path_num_chars:     Number of 2-byte characters in @path.
1291  *
1292  * @inode:              WIM inode to save the stream into.
1293  *
1294  * @lookup_table:       Stream lookup table for the WIM.
1295  *
1296  * @file_size:          Size of unnamed data stream.  (Used only if alternate
1297  *                      data streams API appears to be unavailable.)
1298  *
1299  * @vol_flags:          Flags that specify features of the volume being
1300  *                      captured.
1301  *
1302  * Returns 0 on success; nonzero on failure.
1303  */
1304 static int
1305 win32_capture_streams(const wchar_t *path,
1306                       size_t path_num_chars,
1307                       struct wim_inode *inode,
1308                       struct wim_lookup_table *lookup_table,
1309                       u64 file_size,
1310                       unsigned vol_flags)
1311 {
1312         WIN32_FIND_STREAM_DATA dat;
1313         int ret;
1314         HANDLE hFind;
1315         DWORD err;
1316
1317         DEBUG("Capturing streams from \"%ls\"", path);
1318
1319         if (win32func_FindFirstStreamW == NULL ||
1320             !(vol_flags & FILE_NAMED_STREAMS))
1321                 goto unnamed_only;
1322
1323         hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0);
1324         if (hFind == INVALID_HANDLE_VALUE) {
1325                 err = GetLastError();
1326                 if (err == ERROR_CALL_NOT_IMPLEMENTED)
1327                         goto unnamed_only;
1328
1329                 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
1330                  * points and directories */
1331                 if ((inode->i_attributes &
1332                     (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
1333                     && err == ERROR_HANDLE_EOF)
1334                 {
1335                         DEBUG("ERROR_HANDLE_EOF (ok)");
1336                         return 0;
1337                 } else {
1338                         if (err == ERROR_ACCESS_DENIED) {
1339                                 WARNING("Failed to look up data streams "
1340                                         "of \"%ls\": Access denied!\n%ls",
1341                                         path, capture_access_denied_msg);
1342                                 return 0;
1343                         } else {
1344                                 ERROR("Failed to look up data streams "
1345                                       "of \"%ls\"", path);
1346                                 win32_error(err);
1347                                 return WIMLIB_ERR_READ;
1348                         }
1349                 }
1350         }
1351         do {
1352                 ret = win32_capture_stream(path,
1353                                            path_num_chars,
1354                                            inode, lookup_table,
1355                                            &dat);
1356                 if (ret)
1357                         goto out_find_close;
1358         } while (win32func_FindNextStreamW(hFind, &dat));
1359         err = GetLastError();
1360         if (err != ERROR_HANDLE_EOF) {
1361                 ERROR("Win32 API: Error reading data streams from \"%ls\"", path);
1362                 win32_error(err);
1363                 ret = WIMLIB_ERR_READ;
1364         }
1365 out_find_close:
1366         FindClose(hFind);
1367         return ret;
1368 unnamed_only:
1369         /* FindFirstStreamW() API is not available, or the volume does not
1370          * support named streams.  Only capture the unnamed data stream. */
1371         DEBUG("Only capturing unnamed data stream");
1372         if (inode->i_attributes &
1373              (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
1374         {
1375                 ret = 0;
1376         } else {
1377                 /* Just create our own WIN32_FIND_STREAM_DATA for an unnamed
1378                  * stream to reduce the code to a call to the
1379                  * already-implemented win32_capture_stream() */
1380                 wcscpy(dat.cStreamName, L"::$DATA");
1381                 dat.StreamSize.QuadPart = file_size;
1382                 ret = win32_capture_stream(path,
1383                                            path_num_chars,
1384                                            inode, lookup_table,
1385                                            &dat);
1386         }
1387         return ret;
1388 }
1389
1390 static int
1391 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
1392                                   wchar_t *path,
1393                                   size_t path_num_chars,
1394                                   struct add_image_params *params,
1395                                   struct win32_capture_state *state,
1396                                   unsigned vol_flags)
1397 {
1398         struct wim_dentry *root = NULL;
1399         struct wim_inode *inode;
1400         DWORD err;
1401         u64 file_size;
1402         int ret;
1403         u8 *rpbuf;
1404         u16 rpbuflen;
1405         u16 not_rpfixed;
1406
1407         if (exclude_path(path, path_num_chars, params->config, true)) {
1408                 if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {
1409                         ERROR("Cannot exclude the root directory from capture");
1410                         ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
1411                         goto out;
1412                 }
1413                 if ((params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE)
1414                     && params->progress_func)
1415                 {
1416                         union wimlib_progress_info info;
1417                         info.scan.cur_path = path;
1418                         info.scan.excluded = true;
1419                         params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
1420                 }
1421                 ret = 0;
1422                 goto out;
1423         }
1424
1425         if ((params->add_flags & WIMLIB_ADD_FLAG_VERBOSE)
1426             && params->progress_func)
1427         {
1428                 union wimlib_progress_info info;
1429                 info.scan.cur_path = path;
1430                 info.scan.excluded = false;
1431                 params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
1432         }
1433
1434         HANDLE hFile = win32_open_existing_file(path,
1435                                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES);
1436         if (hFile == INVALID_HANDLE_VALUE) {
1437                 err = GetLastError();
1438                 ERROR("Win32 API: Failed to open \"%ls\"", path);
1439                 win32_error(err);
1440                 ret = WIMLIB_ERR_OPEN;
1441                 goto out;
1442         }
1443
1444         BY_HANDLE_FILE_INFORMATION file_info;
1445         if (!GetFileInformationByHandle(hFile, &file_info)) {
1446                 err = GetLastError();
1447                 ERROR("Win32 API: Failed to get file information for \"%ls\"",
1448                       path);
1449                 win32_error(err);
1450                 ret = WIMLIB_ERR_STAT;
1451                 goto out_close_handle;
1452         }
1453
1454         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1455                 rpbuf = alloca(REPARSE_POINT_MAX_SIZE);
1456                 ret = win32_get_reparse_data(hFile, path, params,
1457                                              rpbuf, &rpbuflen);
1458                 if (ret < 0) {
1459                         /* WIMLIB_ERR_* (inverted) */
1460                         ret = -ret;
1461                         goto out_close_handle;
1462                 } else if (ret & RP_FIXED) {
1463                         not_rpfixed = 0;
1464                 } else if (ret == RP_EXCLUDED) {
1465                         ret = 0;
1466                         goto out_close_handle;
1467                 } else {
1468                         not_rpfixed = 1;
1469                 }
1470         }
1471
1472         /* Create a WIM dentry with an associated inode, which may be shared.
1473          *
1474          * However, we need to explicitly check for directories and files with
1475          * only 1 link and refuse to hard link them.  This is because Windows
1476          * has a bug where it can return duplicate File IDs for files and
1477          * directories on the FAT filesystem. */
1478         ret = inode_table_new_dentry(params->inode_table,
1479                                      path_basename_with_len(path, path_num_chars),
1480                                      ((u64)file_info.nFileIndexHigh << 32) |
1481                                          (u64)file_info.nFileIndexLow,
1482                                      file_info.dwVolumeSerialNumber,
1483                                      (file_info.nNumberOfLinks <= 1 ||
1484                                         (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)),
1485                                      &root);
1486         if (ret)
1487                 goto out_close_handle;
1488
1489         ret = win32_get_short_name(root, path);
1490         if (ret)
1491                 goto out_close_handle;
1492
1493         inode = root->d_inode;
1494
1495         if (inode->i_nlink > 1) /* Shared inode; nothing more to do */
1496                 goto out_close_handle;
1497
1498         inode->i_attributes = file_info.dwFileAttributes;
1499         inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
1500         inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
1501         inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
1502         inode->i_resolved = 1;
1503
1504         params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
1505
1506         if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)
1507             && (vol_flags & FILE_PERSISTENT_ACLS))
1508         {
1509                 ret = win32_get_security_descriptor(root, params->sd_set,
1510                                                     path, state,
1511                                                     params->add_flags);
1512                 if (ret)
1513                         goto out_close_handle;
1514         }
1515
1516         file_size = ((u64)file_info.nFileSizeHigh << 32) |
1517                      (u64)file_info.nFileSizeLow;
1518
1519         CloseHandle(hFile);
1520
1521         /* Capture the unnamed data stream (only should be present for regular
1522          * files) and any alternate data streams. */
1523         ret = win32_capture_streams(path,
1524                                     path_num_chars,
1525                                     inode,
1526                                     params->lookup_table,
1527                                     file_size,
1528                                     vol_flags);
1529         if (ret)
1530                 goto out;
1531
1532         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1533                 /* Reparse point: set the reparse data (which we read already)
1534                  * */
1535                 inode->i_not_rpfixed = not_rpfixed;
1536                 inode->i_reparse_tag = le32_to_cpu(*(u32*)rpbuf);
1537                 ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8,
1538                                                params->lookup_table);
1539         } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
1540                 /* Directory (not a reparse point) --- recurse to children */
1541                 ret = win32_recurse_directory(root,
1542                                               path,
1543                                               path_num_chars,
1544                                               params,
1545                                               state,
1546                                               vol_flags);
1547         }
1548         goto out;
1549 out_close_handle:
1550         CloseHandle(hFile);
1551 out:
1552         if (ret == 0)
1553                 *root_ret = root;
1554         else
1555                 free_dentry_tree(root, params->lookup_table);
1556         return ret;
1557 }
1558
1559 static void
1560 win32_do_capture_warnings(const struct win32_capture_state *state,
1561                           int add_flags)
1562 {
1563         if (state->num_get_sacl_priv_notheld == 0 &&
1564             state->num_get_sd_access_denied == 0)
1565                 return;
1566
1567         WARNING("");
1568         WARNING("Built dentry tree successfully, but with the following problem(s):");
1569         if (state->num_get_sacl_priv_notheld != 0) {
1570                 WARNING("Could not capture SACL (System Access Control List)\n"
1571                         "          on %lu files or directories.",
1572                         state->num_get_sacl_priv_notheld);
1573         }
1574         if (state->num_get_sd_access_denied != 0) {
1575                 WARNING("Could not capture security descriptor at all\n"
1576                         "          on %lu files or directories.",
1577                         state->num_get_sd_access_denied);
1578         }
1579         WARNING(
1580           "Try running the program as the Administrator to make sure all the\n"
1581 "          desired metadata has been captured exactly.  However, if you\n"
1582 "          do not care about capturing security descriptors correctly, then\n"
1583 "          nothing more needs to be done%ls\n",
1584         (add_flags & WIMLIB_ADD_FLAG_NO_ACLS) ? L"." :
1585          L", although you might consider\n"
1586 "          passing the --no-acls flag to `wimlib-imagex capture' or\n"
1587 "          `wimlib-imagex append' to explicitly capture no security\n"
1588 "          descriptors.\n");
1589 }
1590
1591 /* Win32 version of capturing a directory tree */
1592 int
1593 win32_build_dentry_tree(struct wim_dentry **root_ret,
1594                         const wchar_t *root_disk_path,
1595                         struct add_image_params *params)
1596 {
1597         size_t path_nchars;
1598         wchar_t *path;
1599         int ret;
1600         struct win32_capture_state state;
1601         unsigned vol_flags;
1602
1603
1604         path_nchars = wcslen(root_disk_path);
1605         if (path_nchars > 32767)
1606                 return WIMLIB_ERR_INVALID_PARAM;
1607
1608         if (GetFileAttributesW(root_disk_path) == INVALID_FILE_ATTRIBUTES &&
1609             GetLastError() == ERROR_FILE_NOT_FOUND)
1610         {
1611                 ERROR("Capture directory \"%ls\" does not exist!",
1612                       root_disk_path);
1613                 return WIMLIB_ERR_OPENDIR;
1614         }
1615
1616         ret = win32_get_file_and_vol_ids(root_disk_path,
1617                                          &params->capture_root_ino,
1618                                          &params->capture_root_dev);
1619         if (ret)
1620                 return ret;
1621
1622         win32_get_vol_flags(root_disk_path, &vol_flags);
1623
1624         /* There is no check for overflow later when this buffer is being used!
1625          * But the max path length on NTFS is 32767 characters, and paths need
1626          * to be written specially to even go past 260 characters, so we should
1627          * be okay with 32770 characters. */
1628         path = MALLOC(32770 * sizeof(wchar_t));
1629         if (!path)
1630                 return WIMLIB_ERR_NOMEM;
1631
1632         wmemcpy(path, root_disk_path, path_nchars + 1);
1633
1634         memset(&state, 0, sizeof(state));
1635         ret = win32_build_dentry_tree_recursive(root_ret, path,
1636                                                 path_nchars, params,
1637                                                 &state, vol_flags);
1638         FREE(path);
1639         if (ret == 0)
1640                 win32_do_capture_warnings(&state, params->add_flags);
1641         return ret;
1642 }
1643
1644 static int
1645 win32_extract_try_rpfix(u8 *rpbuf,
1646                         const wchar_t *extract_root_realpath,
1647                         unsigned extract_root_realpath_nchars)
1648 {
1649         struct reparse_data rpdata;
1650         wchar_t *target;
1651         size_t target_nchars;
1652         size_t stripped_nchars;
1653         wchar_t *stripped_target;
1654         wchar_t stripped_target_nchars;
1655         int ret;
1656
1657         utf16lechar *new_target;
1658         utf16lechar *new_print_name;
1659         size_t new_target_nchars;
1660         size_t new_print_name_nchars;
1661         utf16lechar *p;
1662
1663         ret = parse_reparse_data(rpbuf, 8 + le16_to_cpu(*(u16*)(rpbuf + 4)),
1664                                  &rpdata);
1665         if (ret)
1666                 return ret;
1667
1668         if (extract_root_realpath[0] == L'\0' ||
1669             extract_root_realpath[1] != L':' ||
1670             extract_root_realpath[2] != L'\\')
1671         {
1672                 ERROR("Can't understand full path format \"%ls\".  "
1673                       "Try turning reparse point fixups off...",
1674                       extract_root_realpath);
1675                 return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
1676         }
1677
1678         ret = parse_substitute_name(rpdata.substitute_name,
1679                                     rpdata.substitute_name_nbytes,
1680                                     rpdata.rptag);
1681         if (ret < 0)
1682                 return 0;
1683         stripped_nchars = ret;
1684         target = rpdata.substitute_name;
1685         target_nchars = rpdata.substitute_name_nbytes / sizeof(utf16lechar);
1686         stripped_target = target + 6;
1687         stripped_target_nchars = target_nchars - stripped_nchars;
1688
1689         new_target = alloca((6 + extract_root_realpath_nchars +
1690                              stripped_target_nchars) * sizeof(utf16lechar));
1691
1692         p = new_target;
1693         if (stripped_nchars == 6) {
1694                 /* Include \??\ prefix if it was present before */
1695                 wmemcpy(p, L"\\??\\", 4);
1696                 p += 4;
1697         }
1698
1699         /* Print name excludes the \??\ if present. */
1700         new_print_name = p;
1701         if (stripped_nchars != 0) {
1702                 /* Get drive letter from real path to extract root, if a drive
1703                  * letter was present before. */
1704                 *p++ = extract_root_realpath[0];
1705                 *p++ = extract_root_realpath[1];
1706         }
1707         /* Copy the rest of the extract root */
1708         wmemcpy(p, extract_root_realpath + 2, extract_root_realpath_nchars - 2);
1709         p += extract_root_realpath_nchars - 2;
1710
1711         /* Append the stripped target */
1712         wmemcpy(p, stripped_target, stripped_target_nchars);
1713         p += stripped_target_nchars;
1714         new_target_nchars = p - new_target;
1715         new_print_name_nchars = p - new_print_name;
1716
1717         if (new_target_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE ||
1718             new_print_name_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE)
1719         {
1720                 ERROR("Path names too long to do reparse point fixup!");
1721                 return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
1722         }
1723         rpdata.substitute_name = new_target;
1724         rpdata.substitute_name_nbytes = new_target_nchars * sizeof(utf16lechar);
1725         rpdata.print_name = new_print_name;
1726         rpdata.print_name_nbytes = new_print_name_nchars * sizeof(utf16lechar);
1727         return make_reparse_buffer(&rpdata, rpbuf);
1728 }
1729
1730 /* Wrapper around the FSCTL_SET_REPARSE_POINT ioctl to set the reparse data on
1731  * an extracted reparse point. */
1732 static int
1733 win32_set_reparse_data(HANDLE h,
1734                        const struct wim_inode *inode,
1735                        const struct wim_lookup_table_entry *lte,
1736                        const wchar_t *path,
1737                        struct apply_args *args)
1738 {
1739         int ret;
1740         u8 rpbuf[REPARSE_POINT_MAX_SIZE];
1741         DWORD bytesReturned;
1742
1743         DEBUG("Setting reparse data on \"%ls\"", path);
1744
1745         ret = wim_inode_get_reparse_data(inode, rpbuf);
1746         if (ret)
1747                 return ret;
1748
1749         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX &&
1750             (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1751              inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) &&
1752             !inode->i_not_rpfixed)
1753         {
1754                 ret = win32_extract_try_rpfix(rpbuf,
1755                                               args->target_realpath,
1756                                               args->target_realpath_len);
1757                 if (ret)
1758                         return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
1759         }
1760
1761         /* Set the reparse data on the open file using the
1762          * FSCTL_SET_REPARSE_POINT ioctl.
1763          *
1764          * There are contradictions in Microsoft's documentation for this:
1765          *
1766          * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED,
1767          * lpOverlapped is ignored."
1768          *
1769          * --- So setting lpOverlapped to NULL is okay since it's ignored.
1770          *
1771          * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an
1772          * operation returns no output data and lpOutBuffer is NULL,
1773          * DeviceIoControl makes use of lpBytesReturned. After such an
1774          * operation, the value of lpBytesReturned is meaningless."
1775          *
1776          * --- So lpOverlapped not really ignored, as it affects another
1777          *  parameter.  This is the actual behavior: lpBytesReturned must be
1778          *  specified, even though lpBytesReturned is documented as:
1779          *
1780          *  "Not used with this operation; set to NULL."
1781          */
1782         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, rpbuf,
1783                              8 + le16_to_cpu(*(u16*)(rpbuf + 4)),
1784                              NULL, 0,
1785                              &bytesReturned /* lpBytesReturned */,
1786                              NULL /* lpOverlapped */))
1787         {
1788                 DWORD err = GetLastError();
1789                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
1790                 {
1791                         args->num_soft_links_failed++;
1792                         if (args->num_soft_links_failed <= MAX_CREATE_SOFT_LINK_WARNINGS) {
1793                                 WARNING("Can't set reparse data on \"%ls\": Access denied!\n"
1794                                         "          You may be trying to extract a symbolic "
1795                                         "link without the\n"
1796                                         "          SeCreateSymbolicLink privilege, which by "
1797                                         "default non-Administrator\n"
1798                                         "          accounts do not have.", path);
1799                         }
1800                         if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
1801                                 WARNING("Suppressing further warnings regarding failure to extract\n"
1802                                         "          reparse points due to insufficient privileges...");
1803                         }
1804                 } else {
1805                         ERROR("Failed to set reparse data on \"%ls\"", path);
1806                         win32_error(err);
1807                         if (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
1808                             inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)
1809                                 return WIMLIB_ERR_LINK;
1810                         else
1811                                 return WIMLIB_ERR_WRITE;
1812                 }
1813         }
1814         return 0;
1815 }
1816
1817 /* Wrapper around the FSCTL_SET_COMPRESSION ioctl to change the
1818  * FILE_ATTRIBUTE_COMPRESSED flag of a file or directory. */
1819 static int
1820 win32_set_compression_state(HANDLE hFile, USHORT format, const wchar_t *path)
1821 {
1822         DWORD bytesReturned;
1823         if (!DeviceIoControl(hFile, FSCTL_SET_COMPRESSION,
1824                              &format, sizeof(USHORT),
1825                              NULL, 0,
1826                              &bytesReturned, NULL))
1827         {
1828                 /* Could be a warning only, but we only call this if the volume
1829                  * supports compression.  So I'm calling this an error. */
1830                 DWORD err = GetLastError();
1831                 ERROR("Failed to set compression flag on \"%ls\"", path);
1832                 win32_error(err);
1833                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
1834                         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
1835                 else
1836                         return WIMLIB_ERR_WRITE;
1837         }
1838         return 0;
1839 }
1840
1841 /* Wrapper around FSCTL_SET_SPARSE ioctl to set a file as sparse. */
1842 static int
1843 win32_set_sparse(HANDLE hFile, const wchar_t *path)
1844 {
1845         DWORD bytesReturned;
1846         if (!DeviceIoControl(hFile, FSCTL_SET_SPARSE,
1847                              NULL, 0,
1848                              NULL, 0,
1849                              &bytesReturned, NULL))
1850         {
1851                 /* Could be a warning only, but we only call this if the volume
1852                  * supports sparse files.  So I'm calling this an error. */
1853                 DWORD err = GetLastError();
1854                 WARNING("Failed to set sparse flag on \"%ls\"", path);
1855                 win32_error(err);
1856                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
1857                         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
1858                 else
1859                         return WIMLIB_ERR_WRITE;
1860         }
1861         return 0;
1862 }
1863
1864 /*
1865  * Sets the security descriptor on an extracted file.
1866  */
1867 static int
1868 win32_set_security_data(const struct wim_inode *inode,
1869                         HANDLE hFile,
1870                         const wchar_t *path,
1871                         struct apply_args *args)
1872 {
1873         PSECURITY_DESCRIPTOR descriptor;
1874         unsigned long n;
1875         DWORD err;
1876         const struct wim_security_data *sd;
1877
1878         SECURITY_INFORMATION securityInformation = 0;
1879
1880         void *owner = NULL;
1881         void *group = NULL;
1882         ACL *dacl = NULL;
1883         ACL *sacl = NULL;
1884
1885         BOOL owner_defaulted;
1886         BOOL group_defaulted;
1887         BOOL dacl_present;
1888         BOOL dacl_defaulted;
1889         BOOL sacl_present;
1890         BOOL sacl_defaulted;
1891
1892         sd = wim_const_security_data(args->w);
1893         descriptor = sd->descriptors[inode->i_security_id];
1894
1895         GetSecurityDescriptorOwner(descriptor, &owner, &owner_defaulted);
1896         if (owner)
1897                 securityInformation |= OWNER_SECURITY_INFORMATION;
1898
1899         GetSecurityDescriptorGroup(descriptor, &group, &group_defaulted);
1900         if (group)
1901                 securityInformation |= GROUP_SECURITY_INFORMATION;
1902
1903         GetSecurityDescriptorDacl(descriptor, &dacl_present,
1904                                   &dacl, &dacl_defaulted);
1905         if (dacl)
1906                 securityInformation |= DACL_SECURITY_INFORMATION;
1907
1908         GetSecurityDescriptorSacl(descriptor, &sacl_present,
1909                                   &sacl, &sacl_defaulted);
1910         if (sacl)
1911                 securityInformation |= SACL_SECURITY_INFORMATION;
1912
1913 again:
1914         if (securityInformation == 0)
1915                 return 0;
1916         if (SetSecurityInfo(hFile, SE_FILE_OBJECT,
1917                             securityInformation, owner, group, dacl, sacl))
1918                 return 0;
1919         err = GetLastError();
1920         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
1921                 goto fail;
1922         switch (err) {
1923         case ERROR_PRIVILEGE_NOT_HELD:
1924                 if (securityInformation & SACL_SECURITY_INFORMATION) {
1925                         n = args->num_set_sacl_priv_notheld++;
1926                         securityInformation &= ~SACL_SECURITY_INFORMATION;
1927                         sacl = NULL;
1928                         if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
1929                                 WARNING(
1930 "We don't have enough privileges to set the full security\n"
1931 "          descriptor on \"%ls\"!\n", path);
1932                                 if (args->num_set_sd_access_denied +
1933                                     args->num_set_sacl_priv_notheld == 1)
1934                                 {
1935                                         WARNING("%ls", apply_access_denied_msg);
1936                                 }
1937                                 WARNING("Re-trying with SACL omitted.\n", path);
1938                         } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
1939                                 WARNING(
1940 "Suppressing further 'privileges not held' error messages when setting\n"
1941 "          security descriptors.");
1942                         }
1943                         goto again;
1944                 }
1945                 /* Fall through */
1946         case ERROR_INVALID_OWNER:
1947         case ERROR_ACCESS_DENIED:
1948                 n = args->num_set_sd_access_denied++;
1949                 if (n < MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
1950                         WARNING("Failed to set security descriptor on \"%ls\": "
1951                                 "Access denied!\n", path);
1952                         if (args->num_set_sd_access_denied +
1953                             args->num_set_sacl_priv_notheld == 1)
1954                         {
1955                                 WARNING("%ls", apply_access_denied_msg);
1956                         }
1957                 } else if (n == MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
1958                         WARNING(
1959 "Suppressing further access denied error messages when setting\n"
1960 "          security descriptors");
1961                 }
1962                 return 0;
1963         default:
1964 fail:
1965                 ERROR("Failed to set security descriptor on \"%ls\"", path);
1966                 win32_error(err);
1967                 if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
1968                         return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
1969                 else
1970                         return WIMLIB_ERR_WRITE;
1971         }
1972 }
1973
1974
1975 static int
1976 win32_extract_chunk(const void *buf, size_t len, void *arg)
1977 {
1978         HANDLE hStream = arg;
1979
1980         DWORD nbytes_written;
1981         wimlib_assert(len <= 0xffffffff);
1982
1983         if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
1984             nbytes_written != len)
1985         {
1986                 DWORD err = GetLastError();
1987                 ERROR("WriteFile(): write error");
1988                 win32_error(err);
1989                 return WIMLIB_ERR_WRITE;
1990         }
1991         return 0;
1992 }
1993
1994 static int
1995 do_win32_extract_stream(HANDLE hStream, const struct wim_lookup_table_entry *lte)
1996 {
1997         return extract_wim_resource(lte, wim_resource_size(lte),
1998                                     win32_extract_chunk, hStream);
1999 }
2000
2001 struct win32_encrypted_extract_ctx {
2002         const struct wim_lookup_table_entry *lte;
2003         u64 offset;
2004 };
2005
2006 static DWORD WINAPI
2007 win32_encrypted_import_cb(unsigned char *data, void *_ctx,
2008                           unsigned long *len_p)
2009 {
2010         struct win32_encrypted_extract_ctx *ctx = _ctx;
2011         unsigned long len = *len_p;
2012         const struct wim_lookup_table_entry *lte = ctx->lte;
2013
2014         len = min(len, wim_resource_size(lte) - ctx->offset);
2015
2016         if (read_partial_wim_resource_into_buf(lte, len, ctx->offset, data))
2017                 return ERROR_READ_FAULT;
2018
2019         ctx->offset += len;
2020         *len_p = len;
2021         return ERROR_SUCCESS;
2022 }
2023
2024 /* Create an encrypted file and extract the raw encrypted data to it.
2025  *
2026  * @path:  Path to encrypted file to create.
2027  * @lte:   WIM lookup_table entry for the raw encrypted data.
2028  *
2029  * This is separate from do_win32_extract_stream() because the WIM is supposed
2030  * to contain the *raw* encrypted data, which needs to be extracted ("imported")
2031  * using the special APIs OpenEncryptedFileRawW(), WriteEncryptedFileRaw(), and
2032  * CloseEncryptedFileRaw().
2033  *
2034  * Returns 0 on success; nonzero on failure.
2035  */
2036 static int
2037 do_win32_extract_encrypted_stream(const wchar_t *path,
2038                                   const struct wim_lookup_table_entry *lte)
2039 {
2040         void *file_ctx;
2041         int ret;
2042
2043         DEBUG("Opening file \"%ls\" to extract raw encrypted data", path);
2044
2045         ret = OpenEncryptedFileRawW(path, CREATE_FOR_IMPORT, &file_ctx);
2046         if (ret) {
2047                 ERROR("Failed to open \"%ls\" to write raw encrypted data", path);
2048                 win32_error(ret);
2049                 return WIMLIB_ERR_OPEN;
2050         }
2051
2052         if (lte) {
2053                 struct win32_encrypted_extract_ctx ctx;
2054
2055                 ctx.lte = lte;
2056                 ctx.offset = 0;
2057                 ret = WriteEncryptedFileRaw(win32_encrypted_import_cb, &ctx, file_ctx);
2058                 if (ret == ERROR_SUCCESS) {
2059                         ret = 0;
2060                 } else {
2061                         ret = WIMLIB_ERR_WRITE;
2062                         ERROR("Failed to extract encrypted file \"%ls\"", path);
2063                 }
2064         }
2065         CloseEncryptedFileRaw(file_ctx);
2066         return ret;
2067 }
2068
2069 static bool
2070 path_is_root_of_drive(const wchar_t *path)
2071 {
2072         if (!*path)
2073                 return false;
2074
2075         if (*path != L'/' && *path != L'\\') {
2076                 if (*(path + 1) == L':')
2077                         path += 2;
2078                 else
2079                         return false;
2080         }
2081         while (*path == L'/' || *path == L'\\')
2082                 path++;
2083         return (*path == L'\0');
2084 }
2085
2086 static inline DWORD
2087 win32_mask_attributes(DWORD i_attributes)
2088 {
2089         return i_attributes & ~(FILE_ATTRIBUTE_SPARSE_FILE |
2090                                 FILE_ATTRIBUTE_COMPRESSED |
2091                                 FILE_ATTRIBUTE_REPARSE_POINT |
2092                                 FILE_ATTRIBUTE_DIRECTORY |
2093                                 FILE_ATTRIBUTE_ENCRYPTED |
2094                                 FILE_FLAG_DELETE_ON_CLOSE |
2095                                 FILE_FLAG_NO_BUFFERING |
2096                                 FILE_FLAG_OPEN_NO_RECALL |
2097                                 FILE_FLAG_OVERLAPPED |
2098                                 FILE_FLAG_RANDOM_ACCESS |
2099                                 /*FILE_FLAG_SESSION_AWARE |*/
2100                                 FILE_FLAG_SEQUENTIAL_SCAN |
2101                                 FILE_FLAG_WRITE_THROUGH);
2102 }
2103
2104 static inline DWORD
2105 win32_get_create_flags_and_attributes(DWORD i_attributes)
2106 {
2107         /*
2108          * Some attributes cannot be set by passing them to CreateFile().  In
2109          * particular:
2110          *
2111          * FILE_ATTRIBUTE_DIRECTORY:
2112          *   CreateDirectory() must be called instead of CreateFile().
2113          *
2114          * FILE_ATTRIBUTE_SPARSE_FILE:
2115          *   Needs an ioctl.
2116          *   See: win32_set_sparse().
2117          *
2118          * FILE_ATTRIBUTE_COMPRESSED:
2119          *   Not clear from the documentation, but apparently this needs an
2120          *   ioctl as well.
2121          *   See: win32_set_compressed().
2122          *
2123          * FILE_ATTRIBUTE_REPARSE_POINT:
2124          *   Needs an ioctl, with the reparse data specified.
2125          *   See: win32_set_reparse_data().
2126          *
2127          * In addition, clear any file flags in the attributes that we don't
2128          * want, but also specify FILE_FLAG_OPEN_REPARSE_POINT and
2129          * FILE_FLAG_BACKUP_SEMANTICS as we are a backup application.
2130          */
2131         return win32_mask_attributes(i_attributes) |
2132                 FILE_FLAG_OPEN_REPARSE_POINT |
2133                 FILE_FLAG_BACKUP_SEMANTICS;
2134 }
2135
2136 /* Set compression and/or sparse attributes on a stream, if supported by the
2137  * volume. */
2138 static int
2139 win32_set_special_stream_attributes(HANDLE hFile, const struct wim_inode *inode,
2140                                     struct wim_lookup_table_entry *unnamed_stream_lte,
2141                                     const wchar_t *path, unsigned vol_flags)
2142 {
2143         int ret;
2144
2145         if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) {
2146                 if (vol_flags & FILE_FILE_COMPRESSION) {
2147                         ret = win32_set_compression_state(hFile,
2148                                                           COMPRESSION_FORMAT_DEFAULT,
2149                                                           path);
2150                         if (ret)
2151                                 return ret;
2152                 } else {
2153                         DEBUG("Cannot set compression attribute on \"%ls\": "
2154                               "volume does not support transparent compression",
2155                               path);
2156                 }
2157         }
2158
2159         if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) {
2160                 if (vol_flags & FILE_SUPPORTS_SPARSE_FILES) {
2161                         DEBUG("Setting sparse flag on \"%ls\"", path);
2162                         ret = win32_set_sparse(hFile, path);
2163                         if (ret)
2164                                 return ret;
2165                 } else {
2166                         DEBUG("Cannot set sparse attribute on \"%ls\": "
2167                               "volume does not support sparse files",
2168                               path);
2169                 }
2170         }
2171         return 0;
2172 }
2173
2174 /* Pre-create directories; extract encrypted streams */
2175 static int
2176 win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
2177                                    const struct wim_lookup_table_entry *lte,
2178                                    const wchar_t *path,
2179                                    DWORD *creationDisposition_ret,
2180                                    unsigned int vol_flags)
2181 {
2182         DWORD err;
2183         int ret;
2184
2185         /* Directories must be created with CreateDirectoryW().  Then the call
2186          * to CreateFileW() will merely open the directory that was already
2187          * created rather than creating a new file. */
2188         if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY &&
2189             !path_is_root_of_drive(path)) {
2190                 if (!CreateDirectoryW(path, NULL)) {
2191                         err = GetLastError();
2192                         if (err != ERROR_ALREADY_EXISTS) {
2193                                 ERROR("Failed to create directory \"%ls\"",
2194                                       path);
2195                                 win32_error(err);
2196                                 return WIMLIB_ERR_MKDIR;
2197                         }
2198                 }
2199                 DEBUG("Created directory \"%ls\"", path);
2200                 *creationDisposition_ret = OPEN_EXISTING;
2201         }
2202         if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
2203             vol_flags & FILE_SUPPORTS_ENCRYPTION)
2204         {
2205                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
2206                         unsigned remaining_sharing_violations = 100;
2207                         while (!EncryptFile(path)) {
2208                                 if (remaining_sharing_violations &&
2209                                     err == ERROR_SHARING_VIOLATION)
2210                                 {
2211                                         WARNING("Couldn't encrypt directory \"%ls\" "
2212                                                 "due to sharing violation; re-trying "
2213                                                 "after 100 ms", path);
2214                                         Sleep(100);
2215                                         remaining_sharing_violations--;
2216                                 } else {
2217                                         err = GetLastError();
2218                                         ERROR("Failed to encrypt directory \"%ls\"",
2219                                               path);
2220                                         win32_error(err);
2221                                         return WIMLIB_ERR_WRITE;
2222                                 }
2223                         }
2224                 } else {
2225                         ret = do_win32_extract_encrypted_stream(path, lte);
2226                         if (ret)
2227                                 return ret;
2228                         DEBUG("Extracted encrypted file \"%ls\"", path);
2229                 }
2230                 *creationDisposition_ret = OPEN_EXISTING;
2231         }
2232
2233         /* Set file attributes if we created the file.  Otherwise, we haven't
2234          * created the file set and we will set the attributes in the call to
2235          * CreateFileW().
2236          *
2237          * The FAT filesystem does not let you change the attributes of the root
2238          * directory, so treat that as a special case and do not set attributes.
2239          * */
2240         if (*creationDisposition_ret == OPEN_EXISTING &&
2241             !path_is_root_of_drive(path))
2242         {
2243                 if (!SetFileAttributesW(path,
2244                                         win32_mask_attributes(inode->i_attributes)))
2245                 {
2246                         err = GetLastError();
2247                         ERROR("Failed to set attributes on \"%ls\"", path);
2248                         win32_error(err);
2249                         return WIMLIB_ERR_WRITE;
2250                 }
2251         }
2252         return 0;
2253 }
2254
2255 /* Set security descriptor and extract stream data or reparse data (skip the
2256  * unnamed data stream of encrypted files, which was already extracted). */
2257 static int
2258 win32_finish_extract_stream(HANDLE h, const struct wim_dentry *dentry,
2259                             const struct wim_lookup_table_entry *lte,
2260                             const wchar_t *stream_path,
2261                             const wchar_t *stream_name_utf16,
2262                             struct apply_args *args)
2263 {
2264         int ret = 0;
2265         const struct wim_inode *inode = dentry->d_inode;
2266         const wchar_t *short_name;
2267         if (stream_name_utf16 == NULL) {
2268                 /* Unnamed stream. */
2269
2270                 /* Set security descriptor, unless the extract_flags indicate
2271                  * not to or the volume does not supported it.  Note that this
2272                  * is only done when the unnamed stream is being extracted, as
2273                  * security descriptors are per-file and not per-stream. */
2274                 if (inode->i_security_id >= 0 &&
2275                     !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
2276                     && (args->vol_flags & FILE_PERSISTENT_ACLS))
2277                 {
2278                         ret = win32_set_security_data(inode, h, stream_path, args);
2279                         if (ret)
2280                                 return ret;
2281                 }
2282
2283                 /* Handle reparse points.  The data for them needs to be set
2284                  * using a special ioctl.  Note that the reparse point may have
2285                  * been created using CreateFileW() in the case of
2286                  * non-directories or CreateDirectoryW() in the case of
2287                  * directories; but the ioctl works either way.  Also, it is
2288                  * only this step that actually sets the
2289                  * FILE_ATTRIBUTE_REPARSE_POINT, as it is not valid to set it
2290                  * using SetFileAttributesW() or CreateFileW().
2291                  *
2292                  * If the volume does not support reparse points we simply
2293                  * ignore the reparse data.  (N.B. the code currently doesn't
2294                  * actually reach this case because reparse points are skipped
2295                  * entirely on such volumes.) */
2296                 if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2297                         if (args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS) {
2298                                 ret = win32_set_reparse_data(h, inode,
2299                                                              lte, stream_path,
2300                                                              args);
2301                                 if (ret)
2302                                         return ret;
2303                         } else {
2304                                 DEBUG("Cannot set reparse data on \"%ls\": volume "
2305                                       "does not support reparse points", stream_path);
2306                         }
2307                 } else if (lte != NULL &&
2308                            !(args->vol_flags & FILE_SUPPORTS_ENCRYPTION &&
2309                              inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED))
2310                 {
2311                         /* Extract the data of the unnamed stream, unless the
2312                          * lookup table entry is NULL (indicating an empty
2313                          * stream for which no data needs to be extracted), or
2314                          * the stream is encrypted and therefore was already
2315                          * extracted as a special case. */
2316                         ret = do_win32_extract_stream(h, lte);
2317                         if (ret)
2318                                 return ret;
2319                 }
2320
2321                 if (dentry_has_short_name(dentry))
2322                         short_name = dentry->short_name;
2323                 else
2324                         short_name = L"";
2325                 /* Set short name */
2326                 if (!SetFileShortNameW(h, short_name)) {
2327                 #if 0
2328                         DWORD err = GetLastError();
2329                         ERROR("Could not set short name on \"%ls\"", stream_path);
2330                         win32_error(err);
2331                 #endif
2332                 }
2333         } else {
2334                 /* Extract the data for a named data stream. */
2335                 if (lte != NULL) {
2336                         DEBUG("Extracting named data stream \"%ls\" (len = %"PRIu64")",
2337                               stream_path, wim_resource_size(lte));
2338                         ret = do_win32_extract_stream(h, lte);
2339                 }
2340         }
2341         return ret;
2342 }
2343
2344 static int
2345 win32_decrypt_file(HANDLE open_handle, const wchar_t *path)
2346 {
2347         DWORD err;
2348         /* We cannot call DecryptFileW() while there is an open handle to the
2349          * file.  So close it first. */
2350         if (!CloseHandle(open_handle)) {
2351                 err = GetLastError();
2352                 ERROR("Failed to close handle for \"%ls\"", path);
2353                 win32_error(err);
2354                 return WIMLIB_ERR_WRITE;
2355         }
2356         if (!DecryptFileW(path, 0 /* reserved parameter; set to 0 */)) {
2357                 err = GetLastError();
2358                 ERROR("Failed to decrypt file \"%ls\"", path);
2359                 win32_error(err);
2360                 return WIMLIB_ERR_WRITE;
2361         }
2362         return 0;
2363 }
2364
2365 /*
2366  * Create and extract a stream to a file, or create a directory, using the
2367  * Windows API.
2368  *
2369  * This handles reparse points, directories, alternate data streams, encrypted
2370  * files, compressed files, etc.
2371  *
2372  * @dentry: WIM dentry for the file or directory being extracted.
2373  *
2374  * @path:  Path to extract the file to.
2375  *
2376  * @stream_name_utf16:
2377  *         Name of the stream, or NULL if the stream is unnamed.  This will
2378  *         be called with a NULL stream_name_utf16 before any non-NULL
2379  *         stream_name_utf16's.
2380  *
2381  * @lte:   WIM lookup table entry for the stream.  May be NULL to indicate
2382  *         a stream of length 0.
2383  *
2384  * @args:  Additional apply context, including flags indicating supported
2385  *         volume features.
2386  *
2387  * Returns 0 on success; nonzero on failure.
2388  */
2389 static int
2390 win32_extract_stream(const struct wim_dentry *dentry,
2391                      const wchar_t *path,
2392                      const wchar_t *stream_name_utf16,
2393                      struct wim_lookup_table_entry *lte,
2394                      struct apply_args *args)
2395 {
2396         wchar_t *stream_path;
2397         HANDLE h;
2398         int ret;
2399         DWORD err;
2400         DWORD creationDisposition = CREATE_ALWAYS;
2401         DWORD requestedAccess;
2402         BY_HANDLE_FILE_INFORMATION file_info;
2403         unsigned remaining_sharing_violations = 1000;
2404         const struct wim_inode *inode = dentry->d_inode;
2405
2406         if (stream_name_utf16) {
2407                 /* Named stream.  Create a buffer that contains the UTF-16LE
2408                  * string [./]path:stream_name_utf16.  This is needed to
2409                  * create and open the stream using CreateFileW().  I'm not
2410                  * aware of any other APIs to do this.  Note: the '$DATA' suffix
2411                  * seems to be unneeded.  Additional note: a "./" prefix needs
2412                  * to be added when the path is not absolute to avoid ambiguity
2413                  * with drive letters. */
2414                 size_t stream_path_nchars;
2415                 size_t path_nchars;
2416                 size_t stream_name_nchars;
2417                 const wchar_t *prefix;
2418
2419                 path_nchars = wcslen(path);
2420                 stream_name_nchars = wcslen(stream_name_utf16);
2421                 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
2422                 if (path[0] != cpu_to_le16(L'\0') &&
2423                     path[0] != cpu_to_le16(L'/') &&
2424                     path[0] != cpu_to_le16(L'\\') &&
2425                     path[1] != cpu_to_le16(L':'))
2426                 {
2427                         prefix = L"./";
2428                         stream_path_nchars += 2;
2429                 } else {
2430                         prefix = L"";
2431                 }
2432                 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
2433                 swprintf(stream_path, L"%ls%ls:%ls",
2434                          prefix, path, stream_name_utf16);
2435         } else {
2436                 /* Unnamed stream; its path is just the path to the file itself.
2437                  * */
2438                 stream_path = (wchar_t*)path;
2439
2440                 ret = win32_begin_extract_unnamed_stream(inode, lte, path,
2441                                                          &creationDisposition,
2442                                                          args->vol_flags);
2443                 if (ret)
2444                         goto fail;
2445         }
2446
2447         DEBUG("Opening \"%ls\"", stream_path);
2448         /* DELETE access is needed for SetFileShortNameW(), for some reason. */
2449         requestedAccess = GENERIC_READ | GENERIC_WRITE | DELETE |
2450                           ACCESS_SYSTEM_SECURITY;
2451 try_open_again:
2452         /* Open the stream to be extracted.  Depending on what we have set
2453          * creationDisposition to, we may be creating this for the first time,
2454          * or we may be opening on existing stream we already created using
2455          * CreateDirectoryW() or OpenEncryptedFileRawW(). */
2456         h = CreateFileW(stream_path,
2457                         requestedAccess,
2458                         FILE_SHARE_READ,
2459                         NULL,
2460                         creationDisposition,
2461                         win32_get_create_flags_and_attributes(inode->i_attributes),
2462                         NULL);
2463         if (h == INVALID_HANDLE_VALUE) {
2464                 err = GetLastError();
2465                 if (err == ERROR_ACCESS_DENIED &&
2466                     path_is_root_of_drive(stream_path))
2467                 {
2468                         ret = 0;
2469                         goto out;
2470                 }
2471                 if ((err == ERROR_PRIVILEGE_NOT_HELD ||
2472                      err == ERROR_ACCESS_DENIED) &&
2473                     (requestedAccess & ACCESS_SYSTEM_SECURITY))
2474                 {
2475                         /* Try opening the file again without privilege to
2476                          * modify SACL. */
2477                         requestedAccess &= ~ACCESS_SYSTEM_SECURITY;
2478                         goto try_open_again;
2479                 }
2480                 if (err == ERROR_SHARING_VIOLATION) {
2481                         if (remaining_sharing_violations) {
2482                                 --remaining_sharing_violations;
2483                                 /* This can happen when restoring encrypted directories
2484                                  * for some reason.  Probably a bug in EncryptFile(). */
2485                                 WARNING("Couldn't open \"%ls\" due to sharing violation; "
2486                                         "re-trying after 100ms", stream_path);
2487                                 Sleep(100);
2488                                 goto try_open_again;
2489                         } else {
2490                                 ERROR("Too many sharing violations; giving up...");
2491                         }
2492                 } else {
2493                         if (creationDisposition == OPEN_EXISTING)
2494                                 ERROR("Failed to open \"%ls\"", stream_path);
2495                         else
2496                                 ERROR("Failed to create \"%ls\"", stream_path);
2497                         win32_error(err);
2498                 }
2499                 ret = WIMLIB_ERR_OPEN;
2500                 goto fail;
2501         }
2502
2503         /* Check the attributes of the file we just opened, and remove
2504          * encryption or compression if either was set by default but is not
2505          * supposed to be set based on the WIM inode attributes. */
2506         if (!GetFileInformationByHandle(h, &file_info)) {
2507                 err = GetLastError();
2508                 ERROR("Failed to get attributes of \"%ls\"", stream_path);
2509                 win32_error(err);
2510                 ret = WIMLIB_ERR_STAT;
2511                 goto fail_close_handle;
2512         }
2513
2514         /* Remove encryption? */
2515         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED &&
2516             !(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED))
2517         {
2518                 /* File defaulted to encrypted due to being in an encrypted
2519                  * directory, but is not actually supposed to be encrypted.
2520                  *
2521                  * This is a workaround, because I'm not aware of any way to
2522                  * directly (e.g. with CreateFileW()) create an unencrypted file
2523                  * in a directory with FILE_ATTRIBUTE_ENCRYPTED set. */
2524                 ret = win32_decrypt_file(h, stream_path);
2525                 if (ret)
2526                         goto fail; /* win32_decrypt_file() closed the handle. */
2527                 creationDisposition = OPEN_EXISTING;
2528                 goto try_open_again;
2529         }
2530
2531         /* Remove compression? */
2532         if (file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED &&
2533             !(inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED))
2534         {
2535                 /* Similar to the encrypted case, above, if the file defaulted
2536                  * to compressed due to being in an compressed directory, but is
2537                  * not actually supposed to be compressed, explicitly set the
2538                  * compression format to COMPRESSION_FORMAT_NONE. */
2539                 ret = win32_set_compression_state(h, COMPRESSION_FORMAT_NONE,
2540                                                   stream_path);
2541                 if (ret)
2542                         goto fail_close_handle;
2543         }
2544
2545         /* Set compression and/or sparse attributes if needed */
2546         ret = win32_set_special_stream_attributes(h, inode, lte, path,
2547                                                   args->vol_flags);
2548
2549         if (ret)
2550                 goto fail_close_handle;
2551
2552         /* At this point we have at least created the needed stream with the
2553          * appropriate attributes.  We have yet to set the appropriate security
2554          * descriptor and actually extract the stream data (other than for
2555          * extracted files, which were already extracted).
2556          * win32_finish_extract_stream() handles these additional steps. */
2557         ret = win32_finish_extract_stream(h, dentry, lte, stream_path,
2558                                           stream_name_utf16, args);
2559         if (ret)
2560                 goto fail_close_handle;
2561
2562         /* Done extracting the stream.  Close the handle and return. */
2563         DEBUG("Closing \"%ls\"", stream_path);
2564         if (!CloseHandle(h)) {
2565                 err = GetLastError();
2566                 ERROR("Failed to close \"%ls\"", stream_path);
2567                 win32_error(err);
2568                 ret = WIMLIB_ERR_WRITE;
2569                 goto fail;
2570         }
2571         ret = 0;
2572         goto out;
2573 fail_close_handle:
2574         CloseHandle(h);
2575 fail:
2576         ERROR("Error extracting \"%ls\"", stream_path);
2577 out:
2578         return ret;
2579 }
2580
2581 /*
2582  * Creates a file, directory, or reparse point and extracts all streams to it
2583  * (unnamed data stream and/or reparse point stream, plus any alternate data
2584  * streams).  Handles sparse, compressed, and/or encrypted files.
2585  *
2586  * @dentry:     WIM dentry for this file or directory.
2587  * @path:       UTF-16LE external path to extract the inode to.
2588  * @args:       Additional extraction context.
2589  *
2590  * Returns 0 on success; nonzero on failure.
2591  */
2592 static int
2593 win32_extract_streams(const struct wim_dentry *dentry,
2594                       const wchar_t *path, struct apply_args *args)
2595 {
2596         struct wim_lookup_table_entry *unnamed_lte;
2597         int ret;
2598         const struct wim_inode *inode = dentry->d_inode;
2599
2600         /* First extract the unnamed stream. */
2601
2602         unnamed_lte = inode_unnamed_lte_resolved(inode);
2603         ret = win32_extract_stream(dentry, path, NULL, unnamed_lte, args);
2604         if (ret)
2605                 goto out;
2606
2607         /* Extract any named streams, if supported by the volume. */
2608
2609         if (!(args->vol_flags & FILE_NAMED_STREAMS))
2610                 goto out;
2611         for (u16 i = 0; i < inode->i_num_ads; i++) {
2612                 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
2613
2614                 /* Skip the unnamed stream if it's in the ADS entries (we
2615                  * already extracted it...) */
2616                 if (ads_entry->stream_name_nbytes == 0)
2617                         continue;
2618
2619                 /* Skip special UNIX data entries (see documentation for
2620                  * WIMLIB_ADD_FLAG_UNIX_DATA) */
2621                 if (ads_entry->stream_name_nbytes == WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES
2622                     && !memcmp(ads_entry->stream_name,
2623                                WIMLIB_UNIX_DATA_TAG_UTF16LE,
2624                                WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES))
2625                         continue;
2626
2627                 /* Extract the named stream */
2628                 ret = win32_extract_stream(dentry,
2629                                            path,
2630                                            ads_entry->stream_name,
2631                                            ads_entry->lte,
2632                                            args);
2633                 if (ret)
2634                         break;
2635         }
2636 out:
2637         return ret;
2638 }
2639
2640 /* If not done already, load the supported feature flags for the volume onto
2641  * which the image is being extracted, and warn the user about any missing
2642  * features that could be important. */
2643 static int
2644 win32_check_vol_flags(const wchar_t *output_path, struct apply_args *args)
2645 {
2646         if (args->have_vol_flags)
2647                 return 0;
2648
2649         win32_get_vol_flags(output_path, &args->vol_flags);
2650         args->have_vol_flags = true;
2651         /* Warn the user about data that may not be extracted. */
2652         if (!(args->vol_flags & FILE_SUPPORTS_SPARSE_FILES))
2653                 WARNING("Volume does not support sparse files!\n"
2654                         "          Sparse files will be extracted as non-sparse.");
2655         if (!(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
2656                 WARNING("Volume does not support reparse points!\n"
2657                         "          Reparse point data will not be extracted.");
2658         if (!(args->vol_flags & FILE_NAMED_STREAMS)) {
2659                 WARNING("Volume does not support named data streams!\n"
2660                         "          Named data streams will not be extracted.");
2661         }
2662         if (!(args->vol_flags & FILE_SUPPORTS_ENCRYPTION)) {
2663                 WARNING("Volume does not support encryption!\n"
2664                         "          Encrypted files will be extracted as raw data.");
2665         }
2666         if (!(args->vol_flags & FILE_FILE_COMPRESSION)) {
2667                 WARNING("Volume does not support transparent compression!\n"
2668                         "          Compressed files will be extracted as non-compressed.");
2669         }
2670         if (!(args->vol_flags & FILE_PERSISTENT_ACLS)) {
2671                 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
2672                         ERROR("Strict ACLs requested, but the volume does not "
2673                               "support ACLs!");
2674                         return WIMLIB_ERR_VOLUME_LACKS_FEATURES;
2675                 } else {
2676                         WARNING("Volume does not support persistent ACLS!\n"
2677                                 "          File permissions will not be extracted.");
2678                 }
2679         }
2680         return 0;
2681 }
2682
2683 /*
2684  * Try extracting a hard link.
2685  *
2686  * @output_path:  Path to link to be extracted.
2687  *
2688  * @inode:        WIM inode that the link is to; inode->i_extracted_file
2689  *                the path to a name of the file that has already been
2690  *                extracted (we use this to create the hard link).
2691  *
2692  * @args:         Additional apply context, used here to keep track of
2693  *                the number of times creating a hard link failed due to
2694  *                ERROR_INVALID_FUNCTION.  This error should indicate that hard
2695  *                links are not supported by the volume, and we would like to
2696  *                warn the user a few times, but not too many times.
2697  *
2698  * Returns 0 if the hard link was successfully extracted.  Returns
2699  * WIMLIB_ERR_LINK (> 0) if an error occurred, other than hard links possibly
2700  * being unsupported by the volume.  Returns a negative value if creating the
2701  * hard link failed due to ERROR_INVALID_FUNCTION.
2702  */
2703 static int
2704 win32_try_hard_link(const wchar_t *output_path, const struct wim_inode *inode,
2705                     struct apply_args *args)
2706 {
2707         DWORD err;
2708
2709         /* There is a volume flag for this (FILE_SUPPORTS_HARD_LINKS),
2710          * but it's only available on Windows 7 and later.  So no use
2711          * even checking it, really.  Instead, CreateHardLinkW() will
2712          * apparently return ERROR_INVALID_FUNCTION if the volume does
2713          * not support hard links. */
2714         DEBUG("Creating hard link \"%ls => %ls\"",
2715               output_path, inode->i_extracted_file);
2716         if (CreateHardLinkW(output_path, inode->i_extracted_file, NULL))
2717                 return 0;
2718
2719         err = GetLastError();
2720         if (err != ERROR_INVALID_FUNCTION) {
2721                 ERROR("Can't create hard link \"%ls => %ls\"",
2722                       output_path, inode->i_extracted_file);
2723                 win32_error(err);
2724                 return WIMLIB_ERR_LINK;
2725         } else {
2726                 args->num_hard_links_failed++;
2727                 if (args->num_hard_links_failed <= MAX_CREATE_HARD_LINK_WARNINGS) {
2728                         WARNING("Can't create hard link \"%ls => %ls\":\n"
2729                                 "          Volume does not support hard links!\n"
2730                                 "          Falling back to extracting a copy of the file.",
2731                                 output_path, inode->i_extracted_file);
2732                 }
2733                 if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
2734                         WARNING("Suppressing further hard linking warnings...");
2735                 }
2736                 return -1;
2737         }
2738 }
2739
2740 /* Extract a file, directory, reparse point, or hard link to an
2741  * already-extracted file using the Win32 API */
2742 int
2743 win32_do_apply_dentry(const wchar_t *output_path,
2744                       size_t output_path_num_chars,
2745                       struct wim_dentry *dentry,
2746                       struct apply_args *args)
2747 {
2748         int ret;
2749         struct wim_inode *inode = dentry->d_inode;
2750
2751         ret = win32_check_vol_flags(output_path, args);
2752         if (ret)
2753                 return ret;
2754         if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
2755                 /* Linked file, with another name already extracted.  Create a
2756                  * hard link. */
2757                 ret = win32_try_hard_link(output_path, inode, args);
2758                 if (ret >= 0)
2759                         return ret;
2760                 /* Negative return value from win32_try_hard_link() indicates
2761                  * that hard links are probably not supported by the volume.
2762                  * Fall back to extracting a copy of the file. */
2763         }
2764
2765         /* If this is a reparse point and the volume does not support reparse
2766          * points, just skip it completely. */
2767         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
2768             !(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
2769         {
2770                 WARNING("Skipping extraction of reparse point \"%ls\":\n"
2771                         "          Not supported by destination filesystem",
2772                         output_path);
2773         } else {
2774                 /* Create the file, directory, or reparse point, and extract the
2775                  * data streams. */
2776                 ret = win32_extract_streams(dentry, output_path, args);
2777                 if (ret)
2778                         return ret;
2779         }
2780         if (inode->i_extracted_file == NULL) {
2781                 const struct wim_lookup_table_entry *lte;
2782
2783                 /* Tally bytes extracted, including all alternate data streams,
2784                  * unless we extracted a hard link (or, at least extracted a
2785                  * name that was supposed to be a hard link) */
2786                 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
2787                         lte = inode_stream_lte_resolved(inode, i);
2788                         if (lte)
2789                                 args->progress.extract.completed_bytes +=
2790                                                         wim_resource_size(lte);
2791                 }
2792                 if (inode->i_nlink > 1) {
2793                         /* Save extracted path for a later call to
2794                          * CreateHardLinkW() if this inode has multiple links.
2795                          * */
2796                         inode->i_extracted_file = WSTRDUP(output_path);
2797                         if (!inode->i_extracted_file)
2798                                 return WIMLIB_ERR_NOMEM;
2799                 }
2800         }
2801         return 0;
2802 }
2803
2804 /* Set timestamps on an extracted file using the Win32 API */
2805 int
2806 win32_do_apply_dentry_timestamps(const wchar_t *path,
2807                                  size_t path_num_chars,
2808                                  struct wim_dentry *dentry,
2809                                  struct apply_args *args)
2810 {
2811         DWORD err;
2812         HANDLE h;
2813         const struct wim_inode *inode = dentry->d_inode;
2814
2815         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
2816             !(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
2817         {
2818                 /* Skip reparse points not extracted */
2819                 return 0;
2820         }
2821
2822         /* Windows doesn't let you change the timestamps of the root directory
2823          * (at least on FAT, which is dumb but expected since FAT doesn't store
2824          * any metadata about the root directory...) */
2825         if (path_is_root_of_drive(path))
2826                 return 0;
2827
2828         DEBUG("Opening \"%ls\" to set timestamps", path);
2829         h = win32_open_existing_file(path, FILE_WRITE_ATTRIBUTES);
2830         if (h == INVALID_HANDLE_VALUE) {
2831                 err = GetLastError();
2832                 goto fail;
2833         }
2834
2835         FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff,
2836                                  .dwHighDateTime = inode->i_creation_time >> 32};
2837         FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff,
2838                                   .dwHighDateTime = inode->i_last_access_time >> 32};
2839         FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff,
2840                                   .dwHighDateTime = inode->i_last_write_time >> 32};
2841
2842         DEBUG("Calling SetFileTime() on \"%ls\"", path);
2843         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
2844                 err = GetLastError();
2845                 CloseHandle(h);
2846                 goto fail;
2847         }
2848         DEBUG("Closing \"%ls\"", path);
2849         if (!CloseHandle(h)) {
2850                 err = GetLastError();
2851                 goto fail;
2852         }
2853         goto out;
2854 fail:
2855         /* Only warn if setting timestamps failed; still return 0. */
2856         WARNING("Can't set timestamps on \"%ls\"", path);
2857         win32_error(err);
2858 out:
2859         return 0;
2860 }
2861
2862 /* Replacement for POSIX fsync() */
2863 int
2864 fsync(int fd)
2865 {
2866         HANDLE h;
2867
2868         h = (HANDLE)_get_osfhandle(fd);
2869         if (h == INVALID_HANDLE_VALUE)
2870                 goto err;
2871         if (!FlushFileBuffers(h))
2872                 goto err_set_errno;
2873         return 0;
2874 err_set_errno:
2875         set_errno_from_GetLastError();
2876 err:
2877         return -1;
2878 }
2879
2880 /* Use the Win32 API to get the number of processors */
2881 unsigned
2882 win32_get_number_of_processors()
2883 {
2884         SYSTEM_INFO sysinfo;
2885         GetSystemInfo(&sysinfo);
2886         return sysinfo.dwNumberOfProcessors;
2887 }
2888
2889 /* Replacement for POSIX-2008 realpath().  Warning: partial functionality only
2890  * (resolved_path must be NULL).   Also I highly doubt that GetFullPathName
2891  * really does the right thing under all circumstances. */
2892 wchar_t *
2893 realpath(const wchar_t *path, wchar_t *resolved_path)
2894 {
2895         DWORD ret;
2896         DWORD err;
2897         wimlib_assert(resolved_path == NULL);
2898
2899         ret = GetFullPathNameW(path, 0, NULL, NULL);
2900         if (!ret) {
2901                 err = GetLastError();
2902                 goto fail_win32;
2903         }
2904
2905         resolved_path = TMALLOC(ret);
2906         if (!resolved_path)
2907                 goto out;
2908         ret = GetFullPathNameW(path, ret, resolved_path, NULL);
2909         if (!ret) {
2910                 err = GetLastError();
2911                 free(resolved_path);
2912                 resolved_path = NULL;
2913                 goto fail_win32;
2914         }
2915         goto out;
2916 fail_win32:
2917         errno = win32_error_to_errno(err);
2918 out:
2919         return resolved_path;
2920 }
2921
2922 /* rename() on Windows fails if the destination file exists.  And we need to
2923  * make it work on wide characters.  Fix it. */
2924 int
2925 win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath)
2926 {
2927         if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) {
2928                 return 0;
2929         } else {
2930                 set_errno_from_GetLastError();
2931                 return -1;
2932         }
2933 }
2934
2935 /* Replacement for POSIX fnmatch() (partial functionality only) */
2936 int
2937 fnmatch(const wchar_t *pattern, const wchar_t *string, int flags)
2938 {
2939         if (PathMatchSpecW(string, pattern))
2940                 return 0;
2941         else
2942                 return FNM_NOMATCH;
2943 }
2944
2945 /* truncate() replacement */
2946 int
2947 win32_truncate_replacement(const wchar_t *path, off_t size)
2948 {
2949         DWORD err = NO_ERROR;
2950         LARGE_INTEGER liOffset;
2951
2952         HANDLE h = win32_open_existing_file(path, GENERIC_WRITE);
2953         if (h == INVALID_HANDLE_VALUE)
2954                 goto fail;
2955
2956         liOffset.QuadPart = size;
2957         if (!SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
2958                 goto fail_close_handle;
2959
2960         if (!SetEndOfFile(h))
2961                 goto fail_close_handle;
2962         CloseHandle(h);
2963         return 0;
2964
2965 fail_close_handle:
2966         err = GetLastError();
2967         CloseHandle(h);
2968 fail:
2969         if (err == NO_ERROR)
2970                 err = GetLastError();
2971         errno = win32_error_to_errno(err);
2972         return -1;
2973 }
2974
2975
2976 /* This really could be replaced with _wcserror_s, but this doesn't seem to
2977  * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
2978  * linked in by Visual Studio...?). */
2979 extern int
2980 win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
2981 {
2982         static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
2983
2984         pthread_mutex_lock(&strerror_lock);
2985         mbstowcs(buf, strerror(errnum), buflen);
2986         buf[buflen - 1] = '\0';
2987         pthread_mutex_unlock(&strerror_lock);
2988         return 0;
2989 }
2990
2991 static int
2992 do_pread_or_pwrite(int fd, void *buf, size_t count, off_t offset,
2993                    bool is_pwrite)
2994 {
2995         HANDLE h;
2996         LARGE_INTEGER orig_offset;
2997         DWORD bytes_read_or_written;
2998         LARGE_INTEGER relative_offset;
2999         OVERLAPPED overlapped;
3000         BOOL bret;
3001
3002         wimlib_assert(count <= 0xffffffff);
3003
3004         h = (HANDLE)_get_osfhandle(fd);
3005         if (h == INVALID_HANDLE_VALUE)
3006                 goto err;
3007
3008         /* Get original position */
3009         relative_offset.QuadPart = 0;
3010         if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT))
3011                 goto err_set_errno;
3012
3013         memset(&overlapped, 0, sizeof(overlapped));
3014         overlapped.Offset = offset;
3015         overlapped.OffsetHigh = offset >> 32;
3016
3017         /* Do the read or write at the specified offset */
3018         if (is_pwrite)
3019                 bret = WriteFile(h, buf, count, &bytes_read_or_written, &overlapped);
3020         else
3021                 bret = ReadFile(h, buf, count, &bytes_read_or_written, &overlapped);
3022         if (!bret)
3023                 goto err_set_errno;
3024
3025         /* Restore the original position */
3026         if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN))
3027                 goto err_set_errno;
3028
3029         return bytes_read_or_written;
3030 err_set_errno:
3031         set_errno_from_GetLastError();
3032 err:
3033         return -1;
3034 }
3035
3036 /* Dumb Windows implementation of pread().  It temporarily changes the file
3037  * offset, so it is not safe to use with readers/writers on the same file
3038  * descriptor.  */
3039 extern ssize_t
3040 win32_pread(int fd, void *buf, size_t count, off_t offset)
3041 {
3042         return do_pread_or_pwrite(fd, buf, count, offset, false);
3043 }
3044
3045 /* Dumb Windows implementation of pwrite().  It temporarily changes the file
3046  * offset, so it is not safe to use with readers/writers on the same file
3047  * descriptor. */
3048 extern ssize_t
3049 win32_pwrite(int fd, const void *buf, size_t count, off_t offset)
3050 {
3051         return do_pread_or_pwrite(fd, (void*)buf, count, offset, true);
3052 }
3053
3054 /* Dumb Windows implementation of writev().  It writes the vectors one at a
3055  * time. */
3056 extern ssize_t
3057 win32_writev(int fd, const struct iovec *iov, int iovcnt)
3058 {
3059         ssize_t total_bytes_written = 0;
3060
3061         if (iovcnt <= 0) {
3062                 errno = EINVAL;
3063                 return -1;
3064         }
3065         for (int i = 0; i < iovcnt; i++) {
3066                 ssize_t bytes_written;
3067
3068                 bytes_written = write(fd, iov[i].iov_base, iov[i].iov_len);
3069                 if (bytes_written >= 0)
3070                         total_bytes_written += bytes_written;
3071                 if (bytes_written != iov[i].iov_len) {
3072                         if (total_bytes_written == 0)
3073                                 total_bytes_written = -1;
3074                         break;
3075                 }
3076         }
3077         return total_bytes_written;
3078 }
3079
3080 #endif /* __WIN32__ */