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