]> wimlib.net Git - wimlib/blob - src/win32.c
7636c5fe9d9b05d9b2d293890626cfdc4d801d17
[wimlib] / src / win32.c
1 /*
2  * win32.c
3  *
4  * All 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> /* shlwapi.h for PathMatchSpecW() */
33 #ifdef ERROR /* windows.h defines this */
34 #  undef ERROR
35 #endif
36
37 #include "win32.h"
38 #include "dentry.h"
39 #include "lookup_table.h"
40 #include "security.h"
41 #include "endianness.h"
42 #include <pthread.h>
43
44 #include <errno.h>
45
46 #define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1
47 #define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1
48 struct win32_capture_state {
49         unsigned long num_get_sd_access_denied;
50         unsigned long num_get_sacl_priv_notheld;
51 };
52
53 #define MAX_SET_SD_ACCESS_DENIED_WARNINGS 1
54 #define MAX_SET_SACL_PRIV_NOTHELD_WARNINGS 1
55
56 /* Pointers to functions that are not available on all targetted versions of
57  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
58  * assume it specifies a certain calling convention. */
59
60 /* Vista and later */
61 static HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName,
62                                             STREAM_INFO_LEVELS InfoLevel,
63                                             LPVOID lpFindStreamData,
64                                             DWORD dwFlags) = NULL;
65
66 /* Vista and later */
67 static BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream,
68                                          LPVOID lpFindStreamData) = NULL;
69
70 static HMODULE hKernel32 = NULL;
71
72 /* Try to dynamically load some functions */
73 void
74 win32_global_init()
75 {
76         DWORD err;
77
78         if (hKernel32 == NULL) {
79                 DEBUG("Loading Kernel32.dll");
80                 hKernel32 = LoadLibraryW(L"Kernel32.dll");
81                 if (hKernel32 == NULL) {
82                         err = GetLastError();
83                         WARNING("Can't load Kernel32.dll");
84                         win32_error(err);
85                         return;
86                 }
87         }
88
89         DEBUG("Looking for FindFirstStreamW");
90         win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, "FindFirstStreamW");
91         if (!win32func_FindFirstStreamW) {
92                 WARNING("Could not find function FindFirstStreamW() in Kernel32.dll!");
93                 WARNING("Capturing alternate data streams will not be supported.");
94                 return;
95         }
96
97         DEBUG("Looking for FindNextStreamW");
98         win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32, "FindNextStreamW");
99         if (!win32func_FindNextStreamW) {
100                 WARNING("Could not find function FindNextStreamW() in Kernel32.dll!");
101                 WARNING("Capturing alternate data streams will not be supported.");
102                 win32func_FindFirstStreamW = NULL;
103         }
104 }
105
106 void
107 win32_global_cleanup()
108 {
109         if (hKernel32 != NULL) {
110                 DEBUG("Closing Kernel32.dll");
111                 FreeLibrary(hKernel32);
112                 hKernel32 = NULL;
113         }
114 }
115
116 static const wchar_t *capture_access_denied_msg =
117 L"         If you are not running this program as the administrator, you may\n"
118  "         need to do so, so that all data and metadata can be backed up.\n"
119  "         Otherwise, there may be no way to access the desired data or\n"
120  "         metadata without taking ownership of the file or directory.\n"
121  ;
122
123 static const wchar_t *apply_access_denied_msg =
124 L"If you are not running this program as the administrator, you may\n"
125  "          need to do so, so that all data and metadata can be extracted\n"
126  "          exactly as the origignal copy.  However, if you do not care that\n"
127  "          the security descriptors are extracted correctly, you could run\n"
128  "          `wimlib-imagex apply' with the --no-acls flag instead.\n"
129  ;
130
131 #ifdef ENABLE_ERROR_MESSAGES
132 void
133 win32_error(u32 err_code)
134 {
135         wchar_t *buffer;
136         DWORD nchars;
137         nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
138                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
139                                 NULL, err_code, 0,
140                                 (wchar_t*)&buffer, 0, NULL);
141         if (nchars == 0) {
142                 ERROR("Error printing error message! "
143                       "Computer will self-destruct in 3 seconds.");
144         } else {
145                 ERROR("Win32 error: %ls", buffer);
146                 LocalFree(buffer);
147         }
148 }
149
150 void
151 win32_error_last()
152 {
153         win32_error(GetLastError());
154 }
155 #endif
156
157 static HANDLE
158 win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess)
159 {
160         return CreateFileW(path,
161                            dwDesiredAccess,
162                            FILE_SHARE_READ,
163                            NULL, /* lpSecurityAttributes */
164                            OPEN_EXISTING,
165                            FILE_FLAG_BACKUP_SEMANTICS |
166                                FILE_FLAG_OPEN_REPARSE_POINT,
167                            NULL /* hTemplateFile */);
168 }
169
170 HANDLE
171 win32_open_file_data_only(const wchar_t *path)
172 {
173         return win32_open_existing_file(path, FILE_READ_DATA);
174 }
175
176 int
177 win32_read_file(const wchar_t *filename,
178                 void *handle, u64 offset, size_t size, void *buf)
179 {
180         HANDLE h = handle;
181         DWORD err;
182         DWORD bytesRead;
183         LARGE_INTEGER liOffset = {.QuadPart = offset};
184
185         wimlib_assert(size <= 0xffffffff);
186
187         if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
188                 if (ReadFile(h, buf, size, &bytesRead, NULL) && bytesRead == size)
189                         return 0;
190         err = GetLastError();
191         ERROR("Error reading \"%ls\"", filename);
192         win32_error(err);
193         return WIMLIB_ERR_READ;
194 }
195
196 void
197 win32_close_file(void *handle)
198 {
199         CloseHandle((HANDLE)handle);
200 }
201
202 static u64
203 FILETIME_to_u64(const FILETIME *ft)
204 {
205         return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
206 }
207
208 static int
209 win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path)
210 {
211         WIN32_FIND_DATAW dat;
212         if (FindFirstFileW(path, &dat) && dat.cAlternateFileName[0] != L'\0') {
213                 size_t short_name_nbytes = wcslen(dat.cAlternateFileName) *
214                                            sizeof(wchar_t);
215                 size_t n = short_name_nbytes + sizeof(wchar_t);
216                 dentry->short_name = MALLOC(n);
217                 if (!dentry->short_name)
218                         return WIMLIB_ERR_NOMEM;
219                 memcpy(dentry->short_name, dat.cAlternateFileName, n);
220                 dentry->short_name_nbytes = short_name_nbytes;
221         }
222         /* If we can't read the short filename for some reason, we just ignore
223          * the error and assume the file has no short name.  I don't think this
224          * should be an issue, since the short names are essentially obsolete
225          * anyway. */
226         return 0;
227 }
228
229 static int
230 win32_get_security_descriptor(struct wim_dentry *dentry,
231                               struct sd_set *sd_set,
232                               const wchar_t *path,
233                               struct win32_capture_state *state,
234                               int add_image_flags)
235 {
236         SECURITY_INFORMATION requestedInformation;
237         DWORD lenNeeded = 0;
238         BOOL status;
239         DWORD err;
240         unsigned long n;
241
242         requestedInformation = DACL_SECURITY_INFORMATION |
243                                SACL_SECURITY_INFORMATION |
244                                OWNER_SECURITY_INFORMATION |
245                                GROUP_SECURITY_INFORMATION;
246 again:
247         /* Request length of security descriptor */
248         status = GetFileSecurityW(path, requestedInformation,
249                                   NULL, 0, &lenNeeded);
250         err = GetLastError();
251         if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
252                 DWORD len = lenNeeded;
253                 char buf[len];
254                 if (GetFileSecurityW(path, requestedInformation,
255                                      (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
256                 {
257                         int security_id = sd_set_add_sd(sd_set, buf, len);
258                         if (security_id < 0)
259                                 return WIMLIB_ERR_NOMEM;
260                         else {
261                                 dentry->d_inode->i_security_id = security_id;
262                                 return 0;
263                         }
264                 } else {
265                         err = GetLastError();
266                 }
267         }
268
269         if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS)
270                 goto fail;
271
272         switch (err) {
273         case ERROR_PRIVILEGE_NOT_HELD:
274                 if (requestedInformation & SACL_SECURITY_INFORMATION) {
275                         n = state->num_get_sacl_priv_notheld++;
276                         requestedInformation &= ~SACL_SECURITY_INFORMATION;
277                         if (n < MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
278                                 WARNING(
279 "We don't have enough privileges to read the full security\n"
280 "          descriptor of \"%ls\"!\n"
281 "          Re-trying with SACL omitted.\n", path);
282                         } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
283                                 WARNING(
284 "Suppressing further privileges not held error messages when reading\n"
285 "          security descriptors.");
286                         }
287                         goto again;
288                 }
289                 /* Fall through */
290         case ERROR_ACCESS_DENIED:
291                 n = state->num_get_sd_access_denied++;
292                 if (n < MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
293                         WARNING("Failed to read security descriptor of \"%ls\": "
294                                 "Access denied!\n%ls", path, capture_access_denied_msg);
295                 } else if (n == MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
296                         WARNING("Suppressing further access denied errors messages i"
297                                 "when reading security descriptors");
298                 }
299                 return 0;
300         default:
301 fail:
302                 ERROR("Failed to read security descriptor of \"%ls\"", path);
303                 win32_error(err);
304                 return WIMLIB_ERR_READ;
305         }
306 }
307
308 static int
309 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
310                                   wchar_t *path,
311                                   size_t path_num_chars,
312                                   struct wim_lookup_table *lookup_table,
313                                   struct sd_set *sd_set,
314                                   const struct wimlib_capture_config *config,
315                                   int add_image_flags,
316                                   wimlib_progress_func_t progress_func,
317                                   struct win32_capture_state *state);
318
319 /* Reads the directory entries of directory using a Win32 API and recursively
320  * calls win32_build_dentry_tree() on them. */
321 static int
322 win32_recurse_directory(struct wim_dentry *root,
323                         wchar_t *dir_path,
324                         size_t dir_path_num_chars,
325                         struct wim_lookup_table *lookup_table,
326                         struct sd_set *sd_set,
327                         const struct wimlib_capture_config *config,
328                         int add_image_flags,
329                         wimlib_progress_func_t progress_func,
330                         struct win32_capture_state *state)
331 {
332         WIN32_FIND_DATAW dat;
333         HANDLE hFind;
334         DWORD err;
335         int ret;
336
337         /* Begin reading the directory by calling FindFirstFileW.  Unlike UNIX
338          * opendir(), FindFirstFileW has file globbing built into it.  But this
339          * isn't what we actually want, so just add a dummy glob to get all
340          * entries. */
341         dir_path[dir_path_num_chars] = L'/';
342         dir_path[dir_path_num_chars + 1] = L'*';
343         dir_path[dir_path_num_chars + 2] = L'\0';
344         hFind = FindFirstFileW(dir_path, &dat);
345         dir_path[dir_path_num_chars] = L'\0';
346
347         if (hFind == INVALID_HANDLE_VALUE) {
348                 err = GetLastError();
349                 if (err == ERROR_FILE_NOT_FOUND) {
350                         return 0;
351                 } else {
352                         ERROR("Failed to read directory \"%ls\"", dir_path);
353                         win32_error(err);
354                         return WIMLIB_ERR_READ;
355                 }
356         }
357         ret = 0;
358         do {
359                 /* Skip . and .. entries */
360                 if (dat.cFileName[0] == L'.' &&
361                     (dat.cFileName[1] == L'\0' ||
362                      (dat.cFileName[1] == L'.' &&
363                       dat.cFileName[2] == L'\0')))
364                         continue;
365                 size_t filename_len = wcslen(dat.cFileName);
366
367                 dir_path[dir_path_num_chars] = L'/';
368                 wmemcpy(dir_path + dir_path_num_chars + 1,
369                         dat.cFileName,
370                         filename_len + 1);
371
372                 struct wim_dentry *child;
373                 size_t path_len = dir_path_num_chars + 1 + filename_len;
374                 ret = win32_build_dentry_tree_recursive(&child,
375                                                         dir_path,
376                                                         path_len,
377                                                         lookup_table,
378                                                         sd_set,
379                                                         config,
380                                                         add_image_flags,
381                                                         progress_func,
382                                                         state);
383                 dir_path[dir_path_num_chars] = L'\0';
384                 if (ret)
385                         goto out_find_close;
386                 if (child)
387                         dentry_add_child(root, child);
388         } while (FindNextFileW(hFind, &dat));
389         err = GetLastError();
390         if (err != ERROR_NO_MORE_FILES) {
391                 ERROR("Failed to read directory \"%ls\"", dir_path);
392                 win32_error(err);
393                 if (ret == 0)
394                         ret = WIMLIB_ERR_READ;
395         }
396 out_find_close:
397         FindClose(hFind);
398         return ret;
399 }
400
401 /* Load a reparse point into a WIM inode.  It is just stored in memory.
402  *
403  * @hFile:  Open handle to a reparse point, with permission to read the reparse
404  *          data.
405  *
406  * @inode:  WIM inode for the reparse point.
407  *
408  * @lookup_table:  Stream lookup table for the WIM; an entry will be added to it
409  *                 for the reparse point unless an entry already exists for
410  *                 the exact same data stream.
411  *
412  * @path:  External path to the reparse point.  Used for error messages only.
413  *
414  * Returns 0 on success; nonzero on failure. */
415 static int
416 win32_capture_reparse_point(HANDLE hFile,
417                             struct wim_inode *inode,
418                             struct wim_lookup_table *lookup_table,
419                             const wchar_t *path)
420 {
421         /* "Reparse point data, including the tag and optional GUID,
422          * cannot exceed 16 kilobytes." - MSDN  */
423         char reparse_point_buf[16 * 1024];
424         DWORD bytesReturned;
425
426         if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
427                              NULL, /* "Not used with this operation; set to NULL" */
428                              0, /* "Not used with this operation; set to 0" */
429                              reparse_point_buf, /* "A pointer to a buffer that
430                                                    receives the reparse point data */
431                              sizeof(reparse_point_buf), /* "The size of the output
432                                                            buffer, in bytes */
433                              &bytesReturned,
434                              NULL))
435         {
436                 DWORD err = GetLastError();
437                 ERROR("Failed to get reparse data of \"%ls\"", path);
438                 win32_error(err);
439                 return WIMLIB_ERR_READ;
440         }
441         if (bytesReturned < 8) {
442                 ERROR("Reparse data on \"%ls\" is invalid", path);
443                 return WIMLIB_ERR_READ;
444         }
445         inode->i_reparse_tag = le32_to_cpu(*(u32*)reparse_point_buf);
446         return inode_add_ads_with_data(inode, L"",
447                                        reparse_point_buf + 8,
448                                        bytesReturned - 8, lookup_table);
449 }
450
451 /* Calculate the SHA1 message digest of a Win32 data stream, which may be either
452  * an unnamed or named data stream.
453  *
454  * @path:       Path to the file, with the stream noted at the end for named
455  *              streams.  UTF-16LE encoding.
456  *
457  * @hash:       On success, the SHA1 message digest of the stream is written to
458  *              this location.
459  *
460  * Returns 0 on success; nonzero on failure.
461  */
462 static int
463 win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE])
464 {
465         HANDLE hFile;
466         SHA_CTX ctx;
467         u8 buf[32768];
468         DWORD bytesRead;
469         int ret;
470
471         hFile = win32_open_file_data_only(path);
472         if (hFile == INVALID_HANDLE_VALUE)
473                 return WIMLIB_ERR_OPEN;
474
475         sha1_init(&ctx);
476         for (;;) {
477                 if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) {
478                         ret = WIMLIB_ERR_READ;
479                         goto out_close_handle;
480                 }
481                 if (bytesRead == 0)
482                         break;
483                 sha1_update(&ctx, buf, bytesRead);
484         }
485         ret = 0;
486         sha1_final(hash, &ctx);
487 out_close_handle:
488         CloseHandle(hFile);
489         return ret;
490 }
491
492 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
493  * stream); calculates its SHA1 message digest and either creates a `struct
494  * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
495  * wim_lookup_table_entry' for an identical stream.
496  *
497  * @path:               Path to the file (UTF-16LE).
498  *
499  * @path_num_chars:     Number of 2-byte characters in @path.
500  *
501  * @inode:              WIM inode to save the stream into.
502  *
503  * @lookup_table:       Stream lookup table for the WIM.
504  *
505  * @dat:                A `WIN32_FIND_STREAM_DATA' structure that specifies the
506  *                      stream name.
507  *
508  * Returns 0 on success; nonzero on failure.
509  */
510 static int
511 win32_capture_stream(const wchar_t *path,
512                      size_t path_num_chars,
513                      struct wim_inode *inode,
514                      struct wim_lookup_table *lookup_table,
515                      WIN32_FIND_STREAM_DATA *dat)
516 {
517         struct wim_ads_entry *ads_entry;
518         u8 hash[SHA1_HASH_SIZE];
519         struct wim_lookup_table_entry *lte;
520         int ret;
521         wchar_t *stream_name, *colon;
522         size_t stream_name_nchars;
523         bool is_named_stream;
524         wchar_t *spath;
525         size_t spath_nchars;
526         DWORD err;
527         size_t spath_buf_nbytes;
528         const wchar_t *relpath_prefix;
529         const wchar_t *colonchar;
530
531         /* The stream name should be returned as :NAME:TYPE */
532         stream_name = dat->cStreamName;
533         if (*stream_name != L':')
534                 goto out_invalid_stream_name;
535         stream_name += 1;
536         colon = wcschr(stream_name, L':');
537         if (colon == NULL)
538                 goto out_invalid_stream_name;
539
540         if (wcscmp(colon + 1, L"$DATA")) {
541                 /* Not a DATA stream */
542                 ret = 0;
543                 goto out;
544         }
545
546         *colon = '\0';
547
548         stream_name_nchars = colon - stream_name;
549         is_named_stream = (stream_name_nchars != 0);
550
551         if (is_named_stream) {
552                 /* Allocate an ADS entry for the named stream. */
553                 ads_entry = inode_add_ads_utf16le(inode, stream_name,
554                                                   stream_name_nchars * sizeof(wchar_t));
555                 if (!ads_entry) {
556                         ret = WIMLIB_ERR_NOMEM;
557                         goto out;
558                 }
559         }
560
561         /* Create a UTF-16LE string @spath that gives the filename, then a
562          * colon, then the stream name.  Or, if it's an unnamed stream, just the
563          * filename.  It is MALLOC()'ed so that it can be saved in the
564          * wim_lookup_table_entry if needed.
565          *
566          * As yet another special case, relative paths need to be changed to
567          * begin with an explicit "./" so that, for example, a file t:ads, where
568          * :ads is the part we added, is not interpreted as a file on the t:
569          * drive. */
570         spath_nchars = path_num_chars;
571         relpath_prefix = L"";
572         colonchar = L"";
573         if (is_named_stream) {
574                 spath_nchars += 1 + stream_name_nchars;
575                 colonchar = L":";
576                 if (path_num_chars == 1 &&
577                     path[0] != L'/' &&
578                     path[0] != L'\\')
579                 {
580                         spath_nchars += 2;
581                         relpath_prefix = L"./";
582                 }
583         }
584
585         spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t);
586         spath = MALLOC(spath_buf_nbytes);
587
588         swprintf(spath, L"%ls%ls%ls%ls",
589                  relpath_prefix, path, colonchar, stream_name);
590
591         ret = win32_sha1sum(spath, hash);
592         if (ret) {
593                 err = GetLastError();
594                 ERROR("Failed to read \"%ls\" to calculate SHA1sum", spath);
595                 win32_error(err);
596                 goto out_free_spath;
597         }
598
599         lte = __lookup_resource(lookup_table, hash);
600         if (lte) {
601                 /* Use existing wim_lookup_table_entry that has the same SHA1
602                  * message digest */
603                 lte->refcnt++;
604         } else {
605                 /* Make a new wim_lookup_table_entry */
606                 lte = new_lookup_table_entry();
607                 if (!lte) {
608                         ret = WIMLIB_ERR_NOMEM;
609                         goto out_free_spath;
610                 }
611                 lte->file_on_disk = spath;
612                 lte->win32_file_on_disk_fp = INVALID_HANDLE_VALUE;
613                 spath = NULL;
614                 lte->resource_location = RESOURCE_WIN32;
615                 lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart;
616                 lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart;
617                 copy_hash(lte->hash, hash);
618                 lookup_table_insert(lookup_table, lte);
619         }
620         if (is_named_stream)
621                 ads_entry->lte = lte;
622         else
623                 inode->i_lte = lte;
624 out_free_spath:
625         FREE(spath);
626 out:
627         return ret;
628 out_invalid_stream_name:
629         ERROR("Invalid stream name: \"%ls:%ls\"", path, dat->cStreamName);
630         ret = WIMLIB_ERR_READ;
631         goto out;
632 }
633
634 /* Scans a Win32 file for unnamed and named data streams (not reparse point
635  * streams).
636  *
637  * @path:               Path to the file (UTF-16LE).
638  *
639  * @path_num_chars:     Number of 2-byte characters in @path.
640  *
641  * @inode:              WIM inode to save the stream into.
642  *
643  * @lookup_table:       Stream lookup table for the WIM.
644  *
645  * @file_size:          Size of unnamed data stream.  (Used only if alternate
646  *                      data streams API appears to be unavailable.)
647  *
648  * Returns 0 on success; nonzero on failure.
649  */
650 static int
651 win32_capture_streams(const wchar_t *path,
652                       size_t path_num_chars,
653                       struct wim_inode *inode,
654                       struct wim_lookup_table *lookup_table,
655                       u64 file_size)
656 {
657         WIN32_FIND_STREAM_DATA dat;
658         int ret;
659         HANDLE hFind;
660         DWORD err;
661
662         if (win32func_FindFirstStreamW == NULL)
663                 goto unnamed_only;
664
665         hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0);
666         if (hFind == INVALID_HANDLE_VALUE) {
667                 err = GetLastError();
668
669                 if (err == ERROR_CALL_NOT_IMPLEMENTED)
670                         goto unnamed_only;
671
672                 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
673                  * points and directories */
674                 if ((inode->i_attributes &
675                     (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
676                     && err == ERROR_HANDLE_EOF)
677                 {
678                         return 0;
679                 } else {
680                         if (err == ERROR_ACCESS_DENIED) {
681                                 /* XXX This maybe should be an error. */
682                                 WARNING("Failed to look up data streams "
683                                         "of \"%ls\": Access denied!\n%ls",
684                                         path, capture_access_denied_msg);
685                                 return 0;
686                         } else {
687                                 ERROR("Failed to look up data streams "
688                                       "of \"%ls\"", path);
689                                 win32_error(err);
690                                 return WIMLIB_ERR_READ;
691                         }
692                 }
693         }
694         do {
695                 ret = win32_capture_stream(path,
696                                            path_num_chars,
697                                            inode, lookup_table,
698                                            &dat);
699                 if (ret)
700                         goto out_find_close;
701         } while (win32func_FindNextStreamW(hFind, &dat));
702         err = GetLastError();
703         if (err != ERROR_HANDLE_EOF) {
704                 ERROR("Win32 API: Error reading data streams from \"%ls\"", path);
705                 win32_error(err);
706                 ret = WIMLIB_ERR_READ;
707         }
708 out_find_close:
709         FindClose(hFind);
710         return ret;
711 unnamed_only:
712         /* FindFirstStreamW() API is not available.  Only capture the unnamed
713          * data stream. */
714         if (inode->i_attributes &
715              (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
716         {
717                 ret = 0;
718         } else {
719                 /* Just create our own WIN32_FIND_STREAM_DATA for an unnamed
720                  * stream to reduce the code to a call to the
721                  * already-implemented win32_capture_stream() */
722                 wcscpy(dat.cStreamName, L"::$DATA");
723                 dat.StreamSize.QuadPart = file_size;
724                 ret = win32_capture_stream(path,
725                                            path_num_chars,
726                                            inode, lookup_table,
727                                            &dat);
728         }
729         return ret;
730 }
731
732 static int
733 win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
734                                   wchar_t *path,
735                                   size_t path_num_chars,
736                                   struct wim_lookup_table *lookup_table,
737                                   struct sd_set *sd_set,
738                                   const struct wimlib_capture_config *config,
739                                   int add_image_flags,
740                                   wimlib_progress_func_t progress_func,
741                                   struct win32_capture_state *state)
742 {
743         struct wim_dentry *root = NULL;
744         struct wim_inode *inode;
745         DWORD err;
746         u64 file_size;
747         int ret = 0;
748
749         if (exclude_path(path, path_num_chars, config, true)) {
750                 if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) {
751                         ERROR("Cannot exclude the root directory from capture");
752                         ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
753                         goto out;
754                 }
755                 if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE)
756                     && progress_func)
757                 {
758                         union wimlib_progress_info info;
759                         info.scan.cur_path = path;
760                         info.scan.excluded = true;
761                         progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
762                 }
763                 goto out;
764         }
765
766         if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
767             && progress_func)
768         {
769                 union wimlib_progress_info info;
770                 info.scan.cur_path = path;
771                 info.scan.excluded = false;
772                 progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
773         }
774
775         HANDLE hFile = win32_open_existing_file(path,
776                                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES);
777         if (hFile == INVALID_HANDLE_VALUE) {
778                 err = GetLastError();
779                 ERROR("Win32 API: Failed to open \"%ls\"", path);
780                 win32_error(err);
781                 ret = WIMLIB_ERR_OPEN;
782                 goto out;
783         }
784
785         BY_HANDLE_FILE_INFORMATION file_info;
786         if (!GetFileInformationByHandle(hFile, &file_info)) {
787                 err = GetLastError();
788                 ERROR("Win32 API: Failed to get file information for \"%ls\"",
789                       path);
790                 win32_error(err);
791                 ret = WIMLIB_ERR_STAT;
792                 goto out_close_handle;
793         }
794
795         /* Create a WIM dentry */
796         ret = new_dentry_with_timeless_inode(path_basename_with_len(path, path_num_chars),
797                                              &root);
798         if (ret)
799                 goto out_close_handle;
800
801         /* Start preparing the associated WIM inode */
802         inode = root->d_inode;
803
804         inode->i_attributes = file_info.dwFileAttributes;
805         inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
806         inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
807         inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
808         inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) |
809                         (u64)file_info.nFileIndexLow;
810
811         inode->i_resolved = 1;
812         add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE);
813
814         /* Get DOS name and security descriptor (if any). */
815         ret = win32_get_short_name(root, path);
816         if (ret)
817                 goto out_close_handle;
818
819         if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)) {
820                 ret = win32_get_security_descriptor(root, sd_set, path, state,
821                                                     add_image_flags);
822                 if (ret)
823                         goto out_close_handle;
824         }
825
826         file_size = ((u64)file_info.nFileSizeHigh << 32) |
827                      (u64)file_info.nFileSizeLow;
828
829         if (inode_is_directory(inode)) {
830                 /* Directory (not a reparse point) --- recurse to children */
831
832                 /* But first... directories may have alternate data streams that
833                  * need to be captured. */
834                 ret = win32_capture_streams(path,
835                                             path_num_chars,
836                                             inode,
837                                             lookup_table,
838                                             file_size);
839                 if (ret)
840                         goto out_close_handle;
841                 ret = win32_recurse_directory(root,
842                                               path,
843                                               path_num_chars,
844                                               lookup_table,
845                                               sd_set,
846                                               config,
847                                               add_image_flags,
848                                               progress_func,
849                                               state);
850         } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
851                 /* Reparse point: save the reparse tag and data.  Alternate data
852                  * streams are not captured, if it's even possible for a reparse
853                  * point to have alternate data streams... */
854                 ret = win32_capture_reparse_point(hFile,
855                                                   inode,
856                                                   lookup_table,
857                                                   path);
858         } else {
859                 /* Not a directory, not a reparse point; capture the default
860                  * file contents and any alternate data streams. */
861                 ret = win32_capture_streams(path,
862                                             path_num_chars,
863                                             inode,
864                                             lookup_table,
865                                             file_size);
866         }
867 out_close_handle:
868         CloseHandle(hFile);
869 out:
870         if (ret == 0)
871                 *root_ret = root;
872         else
873                 free_dentry_tree(root, lookup_table);
874         return ret;
875 }
876
877 static void
878 win32_do_capture_warnings(const struct win32_capture_state *state,
879                           int add_image_flags)
880 {
881         if (state->num_get_sacl_priv_notheld == 0 &&
882             state->num_get_sd_access_denied == 0)
883                 return;
884
885         WARNING("");
886         WARNING("Built dentry tree successfully, but with the following problem(s):");
887         if (state->num_get_sacl_priv_notheld != 0) {
888                 WARNING("Could not capture SACL (System Access Control List)\n"
889                         "          on %lu files or directories.",
890                         state->num_get_sacl_priv_notheld);
891         }
892         if (state->num_get_sd_access_denied != 0) {
893                 WARNING("Could not capture security descriptor at all\n"
894                         "          on %lu files or directories.",
895                         state->num_get_sd_access_denied);
896         }
897         WARNING(
898           "Try running the program as the Administrator to make sure all the\n"
899 "          desired metadata has been captured exactly.  However, if you\n"
900 "          do not care about capturing security descriptors correctly, then\n"
901 "          nothing more needs to be done%ls\n",
902         (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS) ? L"." :
903          L", although you might consider\n"
904 "          passing the --no-acls flag to `wimlib-imagex capture' or\n"
905 "          `wimlib-imagex append' to explicitly capture no security\n"
906 "          descriptors.\n");
907 }
908
909 /* Win32 version of capturing a directory tree */
910 int
911 win32_build_dentry_tree(struct wim_dentry **root_ret,
912                         const wchar_t *root_disk_path,
913                         struct wim_lookup_table *lookup_table,
914                         struct sd_set *sd_set,
915                         const struct wimlib_capture_config *config,
916                         int add_image_flags,
917                         wimlib_progress_func_t progress_func,
918                         void *extra_arg)
919 {
920         size_t path_nchars;
921         wchar_t *path;
922         int ret;
923         struct win32_capture_state state;
924
925         path_nchars = wcslen(root_disk_path);
926         if (path_nchars > 32767)
927                 return WIMLIB_ERR_INVALID_PARAM;
928
929         /* There is no check for overflow later when this buffer is being used!
930          * But the max path length on NTFS is 32767 characters, and paths need
931          * to be written specially to even go past 260 characters, so we should
932          * be okay with 32770 characters. */
933         path = MALLOC(32770 * sizeof(wchar_t));
934         if (!path)
935                 return WIMLIB_ERR_NOMEM;
936
937         wmemcpy(path, root_disk_path, path_nchars + 1);
938
939         memset(&state, 0, sizeof(state));
940         ret = win32_build_dentry_tree_recursive(root_ret,
941                                                 path,
942                                                 path_nchars,
943                                                 lookup_table,
944                                                 sd_set,
945                                                 config,
946                                                 add_image_flags,
947                                                 progress_func,
948                                                 &state);
949         FREE(path);
950         if (ret == 0)
951                 win32_do_capture_warnings(&state, add_image_flags);
952         return ret;
953 }
954
955 static int
956 win32_set_reparse_data(HANDLE h,
957                        u32 reparse_tag,
958                        const struct wim_lookup_table_entry *lte,
959                        const wchar_t *path)
960 {
961         int ret;
962         u8 *buf;
963         size_t len;
964
965         if (!lte) {
966                 WARNING("\"%ls\" is marked as a reparse point but had no reparse data",
967                         path);
968                 return 0;
969         }
970         len = wim_resource_size(lte);
971         if (len > 16 * 1024 - 8) {
972                 WARNING("\"%ls\": reparse data too long!", path);
973                 return 0;
974         }
975
976         /* The WIM stream omits the ReparseTag and ReparseDataLength fields, so
977          * leave 8 bytes of space for them at the beginning of the buffer, then
978          * set them manually. */
979         buf = alloca(len + 8);
980         ret = read_full_wim_resource(lte, buf + 8, 0);
981         if (ret)
982                 return ret;
983         *(u32*)(buf + 0) = cpu_to_le32(reparse_tag);
984         *(u16*)(buf + 4) = cpu_to_le16(len);
985         *(u16*)(buf + 6) = 0;
986
987         /* Set the reparse data on the open file using the
988          * FSCTL_SET_REPARSE_POINT ioctl.
989          *
990          * There are contradictions in Microsoft's documentation for this:
991          *
992          * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED,
993          * lpOverlapped is ignored."
994          *
995          * --- So setting lpOverlapped to NULL is okay since it's ignored.
996          *
997          * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an
998          * operation returns no output data and lpOutBuffer is NULL,
999          * DeviceIoControl makes use of lpBytesReturned. After such an
1000          * operation, the value of lpBytesReturned is meaningless."
1001          *
1002          * --- So lpOverlapped not really ignored, as it affects another
1003          *  parameter.  This is the actual behavior: lpBytesReturned must be
1004          *  specified, even though lpBytesReturned is documented as:
1005          *
1006          *  "Not used with this operation; set to NULL."
1007          */
1008         DWORD bytesReturned;
1009         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, buf, len + 8,
1010                              NULL, 0,
1011                              &bytesReturned /* lpBytesReturned */,
1012                              NULL /* lpOverlapped */))
1013         {
1014                 DWORD err = GetLastError();
1015                 ERROR("Failed to set reparse data on \"%ls\"", path);
1016                 win32_error(err);
1017                 return WIMLIB_ERR_WRITE;
1018         }
1019         return 0;
1020 }
1021
1022 /*
1023  * Sets the security descriptor on an extracted file.
1024  */
1025 static int
1026 win32_set_security_data(const struct wim_inode *inode,
1027                         const wchar_t *path,
1028                         struct apply_args *args)
1029 {
1030         PSECURITY_DESCRIPTOR descriptor;
1031         unsigned long n;
1032         DWORD err;
1033
1034         descriptor = wim_const_security_data(args->w)->descriptors[inode->i_security_id];
1035
1036         SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION |
1037                                                    SACL_SECURITY_INFORMATION |
1038                                                    OWNER_SECURITY_INFORMATION |
1039                                                    GROUP_SECURITY_INFORMATION;
1040 again:
1041         if (SetFileSecurityW(path, securityInformation, descriptor))
1042                 return 0;
1043         err = GetLastError();
1044         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
1045                 goto fail;
1046         switch (err) {
1047         case ERROR_PRIVILEGE_NOT_HELD:
1048                 if (securityInformation & SACL_SECURITY_INFORMATION) {
1049                         n = args->num_set_sacl_priv_notheld++;
1050                         securityInformation &= ~SACL_SECURITY_INFORMATION;
1051                         if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
1052                                 WARNING(
1053 "We don't have enough privileges to set the full security\n"
1054 "          descriptor on \"%ls\"!\n", path);
1055                                 if (args->num_set_sd_access_denied +
1056                                     args->num_set_sacl_priv_notheld == 1)
1057                                 {
1058                                         WARNING("%ls", apply_access_denied_msg);
1059                                 }
1060                                 WARNING("Re-trying with SACL omitted.\n", path);
1061                         } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
1062                                 WARNING(
1063 "Suppressing further 'privileges not held' error messages when setting\n"
1064 "          security descriptors.");
1065                         }
1066                         goto again;
1067                 }
1068                 /* Fall through */
1069         case ERROR_INVALID_OWNER:
1070         case ERROR_ACCESS_DENIED:
1071                 n = args->num_set_sd_access_denied++;
1072                 if (n < MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
1073                         WARNING("Failed to set security descriptor on \"%ls\": "
1074                                 "Access denied!\n", path);
1075                         if (args->num_set_sd_access_denied +
1076                             args->num_set_sacl_priv_notheld == 1)
1077                         {
1078                                 WARNING("%ls", apply_access_denied_msg);
1079                         }
1080                 } else if (n == MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
1081                         WARNING(
1082 "Suppressing further access denied error messages when setting\n"
1083 "          security descriptors");
1084                 }
1085                 return 0;
1086         default:
1087 fail:
1088                 ERROR("Failed to set security descriptor on \"%ls\"", path);
1089                 win32_error(err);
1090                 return WIMLIB_ERR_WRITE;
1091         }
1092 }
1093
1094
1095 static int
1096 win32_extract_chunk(const void *buf, size_t len, u64 offset, void *arg)
1097 {
1098         HANDLE hStream = arg;
1099
1100         DWORD nbytes_written;
1101         wimlib_assert(len <= 0xffffffff);
1102
1103         if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
1104             nbytes_written != len)
1105         {
1106                 DWORD err = GetLastError();
1107                 ERROR("WriteFile(): write error");
1108                 win32_error(err);
1109                 return WIMLIB_ERR_WRITE;
1110         }
1111         return 0;
1112 }
1113
1114 static int
1115 do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte)
1116 {
1117         return extract_wim_resource(lte, wim_resource_size(lte),
1118                                     win32_extract_chunk, hStream);
1119 }
1120
1121 static bool
1122 path_is_root_of_drive(const wchar_t *path)
1123 {
1124         if (!*path)
1125                 return false;
1126
1127         if (*path != L'/' && *path != L'\\') {
1128                 if (*(path + 1) == L':')
1129                         path += 2;
1130                 else
1131                         return false;
1132         }
1133         while (*path == L'/' || *path == L'\\')
1134                 path++;
1135         return (*path == L'\0');
1136 }
1137
1138 static int
1139 win32_extract_stream(const struct wim_inode *inode,
1140                      const wchar_t *path,
1141                      const wchar_t *stream_name_utf16,
1142                      struct wim_lookup_table_entry *lte)
1143 {
1144         wchar_t *stream_path;
1145         HANDLE h;
1146         int ret;
1147         DWORD err;
1148         DWORD creationDisposition = CREATE_ALWAYS;
1149
1150         if (stream_name_utf16) {
1151                 /* Named stream.  Create a buffer that contains the UTF-16LE
1152                  * string [.\]@path:@stream_name_utf16.  This is needed to
1153                  * create and open the stream using CreateFileW().  I'm not
1154                  * aware of any other APIs to do this.  Note: the '$DATA' suffix
1155                  * seems to be unneeded.  Additional note: a "./" prefix needs
1156                  * to be added when the path is not absolute to avoid ambiguity
1157                  * with drive letters. */
1158                 size_t stream_path_nchars;
1159                 size_t path_nchars;
1160                 size_t stream_name_nchars;
1161                 const wchar_t *prefix;
1162
1163                 path_nchars = wcslen(path);
1164                 stream_name_nchars = wcslen(stream_name_utf16);
1165                 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
1166                 if (path[0] != cpu_to_le16(L'\0') &&
1167                     path[0] != cpu_to_le16(L'/') &&
1168                     path[0] != cpu_to_le16(L'\\') &&
1169                     path[1] != cpu_to_le16(L':'))
1170                 {
1171                         prefix = L"./";
1172                         stream_path_nchars += 2;
1173                 } else {
1174                         prefix = L"";
1175                 }
1176                 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
1177                 swprintf(stream_path, L"%ls%ls:%ls",
1178                          prefix, path, stream_name_utf16);
1179         } else {
1180                 /* Unnamed stream; its path is just the path to the file itself.
1181                  * */
1182                 stream_path = (wchar_t*)path;
1183
1184                 /* Directories must be created with CreateDirectoryW().  Then
1185                  * the call to CreateFileW() will merely open the directory that
1186                  * was already created rather than creating a new file. */
1187                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
1188                         if (!CreateDirectoryW(stream_path, NULL)) {
1189                                 err = GetLastError();
1190                                 switch (err) {
1191                                 case ERROR_ALREADY_EXISTS:
1192                                         break;
1193                                 case ERROR_ACCESS_DENIED:
1194                                         if (path_is_root_of_drive(path))
1195                                                 break;
1196                                         /* Fall through */
1197                                 default:
1198                                         ERROR("Failed to create directory \"%ls\"",
1199                                               stream_path);
1200                                         win32_error(err);
1201                                         ret = WIMLIB_ERR_MKDIR;
1202                                         goto fail;
1203                                 }
1204                         }
1205                         DEBUG("Created directory \"%ls\"", stream_path);
1206                         if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1207                                 ret = 0;
1208                                 goto out;
1209                         }
1210                         creationDisposition = OPEN_EXISTING;
1211                 }
1212         }
1213
1214         DEBUG("Opening \"%ls\"", stream_path);
1215         h = CreateFileW(stream_path,
1216                         GENERIC_WRITE,
1217                         0,
1218                         NULL,
1219                         creationDisposition,
1220                         FILE_FLAG_OPEN_REPARSE_POINT |
1221                             FILE_FLAG_BACKUP_SEMANTICS |
1222                             inode->i_attributes,
1223                         NULL);
1224         if (h == INVALID_HANDLE_VALUE) {
1225                 err = GetLastError();
1226                 ERROR("Failed to create \"%ls\"", stream_path);
1227                 win32_error(err);
1228                 ret = WIMLIB_ERR_OPEN;
1229                 goto fail;
1230         }
1231
1232         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1233             stream_name_utf16 == NULL)
1234         {
1235                 DEBUG("Setting reparse data on \"%ls\"", path);
1236                 ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path);
1237                 if (ret)
1238                         goto fail_close_handle;
1239         } else {
1240                 if (lte) {
1241                         DEBUG("Extracting \"%ls\" (len = %"PRIu64")",
1242                               stream_path, wim_resource_size(lte));
1243                         ret = do_win32_extract_stream(h, lte);
1244                         if (ret)
1245                                 goto fail_close_handle;
1246                 }
1247         }
1248
1249         DEBUG("Closing \"%ls\"", stream_path);
1250         if (!CloseHandle(h)) {
1251                 err = GetLastError();
1252                 ERROR("Failed to close \"%ls\"", stream_path);
1253                 win32_error(err);
1254                 ret = WIMLIB_ERR_WRITE;
1255                 goto fail;
1256         }
1257         ret = 0;
1258         goto out;
1259 fail_close_handle:
1260         CloseHandle(h);
1261 fail:
1262         ERROR("Error extracting %ls", stream_path);
1263 out:
1264         return ret;
1265 }
1266
1267 /*
1268  * Creates a file, directory, or reparse point and extracts all streams to it
1269  * (unnamed data stream and/or reparse point stream, plus any alternate data
1270  * streams).  This in Win32-specific code.
1271  *
1272  * @inode:      WIM inode for this file or directory.
1273  * @path:       UTF-16LE external path to extract the inode to.
1274  *
1275  * Returns 0 on success; nonzero on failure.
1276  */
1277 static int
1278 win32_extract_streams(const struct wim_inode *inode,
1279                       const wchar_t *path, u64 *completed_bytes_p)
1280 {
1281         struct wim_lookup_table_entry *unnamed_lte;
1282         int ret;
1283
1284         unnamed_lte = inode_unnamed_lte_resolved(inode);
1285         ret = win32_extract_stream(inode, path, NULL, unnamed_lte);
1286         if (ret)
1287                 goto out;
1288         if (unnamed_lte)
1289                 *completed_bytes_p += wim_resource_size(unnamed_lte);
1290         for (u16 i = 0; i < inode->i_num_ads; i++) {
1291                 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
1292                 if (ads_entry->stream_name_nbytes != 0) {
1293                         /* Skip special UNIX data entries (see documentation for
1294                          * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
1295                         if (ads_entry->stream_name_nbytes == WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES
1296                             && !memcmp(ads_entry->stream_name,
1297                                        WIMLIB_UNIX_DATA_TAG_UTF16LE,
1298                                        WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES))
1299                                 continue;
1300                         ret = win32_extract_stream(inode,
1301                                                    path,
1302                                                    ads_entry->stream_name,
1303                                                    ads_entry->lte);
1304                         if (ret)
1305                                 break;
1306                         if (ads_entry->lte)
1307                                 *completed_bytes_p += wim_resource_size(ads_entry->lte);
1308                 }
1309         }
1310 out:
1311         return ret;
1312 }
1313
1314 /* Extract a file, directory, reparse point, or hard link to an
1315  * already-extracted file using the Win32 API */
1316 int
1317 win32_do_apply_dentry(const wchar_t *output_path,
1318                       size_t output_path_num_chars,
1319                       struct wim_dentry *dentry,
1320                       struct apply_args *args)
1321 {
1322         int ret;
1323         struct wim_inode *inode = dentry->d_inode;
1324         DWORD err;
1325
1326         if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
1327                 /* Linked file, with another name already extracted.  Create a
1328                  * hard link. */
1329                 DEBUG("Creating hard link \"%ls => %ls\"",
1330                       output_path, inode->i_extracted_file);
1331                 if (!CreateHardLinkW(output_path, inode->i_extracted_file, NULL)) {
1332                         err = GetLastError();
1333                         ERROR("Can't create hard link \"%ls => %ls\"",
1334                               output_path, inode->i_extracted_file);
1335                         win32_error(err);
1336                         return WIMLIB_ERR_LINK;
1337                 }
1338         } else {
1339                 /* Create the file, directory, or reparse point, and extract the
1340                  * data streams. */
1341                 ret = win32_extract_streams(inode, output_path,
1342                                             &args->progress.extract.completed_bytes);
1343                 if (ret)
1344                         return ret;
1345
1346                 if (inode->i_security_id >= 0 &&
1347                     !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
1348                 {
1349                         ret = win32_set_security_data(inode, output_path, args);
1350                         if (ret)
1351                                 return ret;
1352                 }
1353                 if (inode->i_nlink > 1) {
1354                         /* Save extracted path for a later call to
1355                          * CreateHardLinkW() if this inode has multiple links.
1356                          * */
1357                         inode->i_extracted_file = WSTRDUP(output_path);
1358                         if (!inode->i_extracted_file)
1359                                 ret = WIMLIB_ERR_NOMEM;
1360                 }
1361         }
1362         return 0;
1363 }
1364
1365 /* Set timestamps on an extracted file using the Win32 API */
1366 int
1367 win32_do_apply_dentry_timestamps(const wchar_t *path,
1368                                  size_t path_num_chars,
1369                                  const struct wim_dentry *dentry,
1370                                  const struct apply_args *args)
1371 {
1372         DWORD err;
1373         HANDLE h;
1374         const struct wim_inode *inode = dentry->d_inode;
1375
1376         DEBUG("Opening \"%ls\" to set timestamps", path);
1377         h = win32_open_existing_file(path, FILE_WRITE_ATTRIBUTES);
1378         if (h == INVALID_HANDLE_VALUE) {
1379                 err = GetLastError();
1380                 goto fail;
1381         }
1382
1383         FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff,
1384                                  .dwHighDateTime = inode->i_creation_time >> 32};
1385         FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff,
1386                                   .dwHighDateTime = inode->i_last_access_time >> 32};
1387         FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff,
1388                                   .dwHighDateTime = inode->i_last_write_time >> 32};
1389
1390         DEBUG("Calling SetFileTime() on \"%ls\"", path);
1391         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
1392                 err = GetLastError();
1393                 CloseHandle(h);
1394                 goto fail;
1395         }
1396         DEBUG("Closing \"%ls\"", path);
1397         if (!CloseHandle(h)) {
1398                 err = GetLastError();
1399                 goto fail;
1400         }
1401         goto out;
1402 fail:
1403         /* Only warn if setting timestamps failed; still return 0. */
1404         WARNING("Can't set timestamps on \"%ls\"", path);
1405         win32_error(err);
1406 out:
1407         return 0;
1408 }
1409
1410 /* Replacement for POSIX fsync() */
1411 int
1412 fsync(int fd)
1413 {
1414         DWORD err;
1415         HANDLE h;
1416
1417         h = (HANDLE)_get_osfhandle(fd);
1418         if (h == INVALID_HANDLE_VALUE) {
1419                 err = GetLastError();
1420                 ERROR("Could not get Windows handle for file descriptor");
1421                 win32_error(err);
1422                 errno = EBADF;
1423                 return -1;
1424         }
1425         if (!FlushFileBuffers(h)) {
1426                 err = GetLastError();
1427                 ERROR("Could not flush file buffers to disk");
1428                 win32_error(err);
1429                 errno = EIO;
1430                 return -1;
1431         }
1432         return 0;
1433 }
1434
1435 /* Use the Win32 API to get the number of processors */
1436 unsigned
1437 win32_get_number_of_processors()
1438 {
1439         SYSTEM_INFO sysinfo;
1440         GetSystemInfo(&sysinfo);
1441         return sysinfo.dwNumberOfProcessors;
1442 }
1443
1444 /* Replacement for POSIX-2008 realpath().  Warning: partial functionality only
1445  * (resolved_path must be NULL).   Also I highly doubt that GetFullPathName
1446  * really does the right thing under all circumstances. */
1447 wchar_t *
1448 realpath(const wchar_t *path, wchar_t *resolved_path)
1449 {
1450         DWORD ret;
1451         wimlib_assert(resolved_path == NULL);
1452         DWORD err;
1453
1454         ret = GetFullPathNameW(path, 0, NULL, NULL);
1455         if (!ret) {
1456                 err = GetLastError();
1457                 goto fail_win32;
1458         }
1459
1460         resolved_path = TMALLOC(ret);
1461         if (!resolved_path)
1462                 goto out;
1463         ret = GetFullPathNameW(path, ret, resolved_path, NULL);
1464         if (!ret) {
1465                 err = GetLastError();
1466                 free(resolved_path);
1467                 resolved_path = NULL;
1468                 goto fail_win32;
1469         }
1470         goto out;
1471 fail_win32:
1472         win32_error(err);
1473         errno = -1;
1474 out:
1475         return resolved_path;
1476 }
1477
1478 /* rename() on Windows fails if the destination file exists.  And we need to
1479  * make it work on wide characters.  Fix it. */
1480 int
1481 win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath)
1482 {
1483         if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) {
1484                 return 0;
1485         } else {
1486                 /* As usual, the possible error values are not documented */
1487                 DWORD err = GetLastError();
1488                 ERROR("MoveFileEx(): Can't rename \"%ls\" to \"%ls\"",
1489                       oldpath, newpath);
1490                 win32_error(err);
1491                 errno = -1;
1492                 return -1;
1493         }
1494 }
1495
1496 /* Replacement for POSIX fnmatch() (partial functionality only) */
1497 int
1498 fnmatch(const wchar_t *pattern, const wchar_t *string, int flags)
1499 {
1500         if (PathMatchSpecW(string, pattern))
1501                 return 0;
1502         else
1503                 return FNM_NOMATCH;
1504 }
1505
1506 /* truncate() replacement */
1507 int
1508 win32_truncate_replacement(const wchar_t *path, off_t size)
1509 {
1510         DWORD err = NO_ERROR;
1511         LARGE_INTEGER liOffset;
1512
1513         HANDLE h = win32_open_existing_file(path, GENERIC_WRITE);
1514         if (h == INVALID_HANDLE_VALUE)
1515                 goto fail;
1516
1517         liOffset.QuadPart = size;
1518         if (!SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
1519                 goto fail_close_handle;
1520
1521         if (!SetEndOfFile(h))
1522                 goto fail_close_handle;
1523         CloseHandle(h);
1524         return 0;
1525
1526 fail_close_handle:
1527         err = GetLastError();
1528         CloseHandle(h);
1529 fail:
1530         if (err == NO_ERROR)
1531                 err = GetLastError();
1532         ERROR("Can't truncate \"%ls\" to %"PRIu64" bytes", path, size);
1533         win32_error(err);
1534         errno = -1;
1535         return -1;
1536 }
1537
1538
1539 /* This really could be replaced with _wcserror_s, but this doesn't seem to
1540  * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
1541  * linked in by Visual Studio...?). */
1542 extern int
1543 win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
1544 {
1545         static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
1546
1547         pthread_mutex_lock(&strerror_lock);
1548         mbstowcs(buf, strerror(errnum), buflen);
1549         buf[buflen - 1] = '\0';
1550         pthread_mutex_unlock(&strerror_lock);
1551         return 0;
1552 }
1553
1554 #endif /* __WIN32__ */