]> wimlib.net Git - wimlib/blob - src/win32.c
05f457a87df3e28c9e692b2d35f2ae266d5ec5d4
[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 static int
1033 win32_set_compressed(HANDLE hFile, const wchar_t *path)
1034 {
1035         USHORT format = COMPRESSION_FORMAT_DEFAULT;
1036         DWORD bytesReturned = 0;
1037         if (!DeviceIoControl(hFile, FSCTL_SET_COMPRESSION,
1038                              &format, sizeof(USHORT),
1039                              NULL, 0,
1040                              &bytesReturned, NULL))
1041         {
1042                 /* Warning only */
1043                 DWORD err = GetLastError();
1044                 WARNING("Failed to set compression flag on \"%ls\"", path);
1045                 win32_error(err);
1046         }
1047         return 0;
1048 }
1049
1050 static int
1051 win32_set_sparse(HANDLE hFile, const wchar_t *path)
1052 {
1053         DWORD bytesReturned = 0;
1054         if (!DeviceIoControl(hFile, FSCTL_SET_SPARSE,
1055                              NULL, 0,
1056                              NULL, 0,
1057                              &bytesReturned, NULL))
1058         {
1059                 /* Warning only */
1060                 DWORD err = GetLastError();
1061                 WARNING("Failed to set sparse flag on \"%ls\"", path);
1062                 win32_error(err);
1063         }
1064         return 0;
1065 }
1066
1067 /*
1068  * Sets the security descriptor on an extracted file.
1069  */
1070 static int
1071 win32_set_security_data(const struct wim_inode *inode,
1072                         const wchar_t *path,
1073                         struct apply_args *args)
1074 {
1075         PSECURITY_DESCRIPTOR descriptor;
1076         unsigned long n;
1077         DWORD err;
1078
1079         descriptor = wim_const_security_data(args->w)->descriptors[inode->i_security_id];
1080
1081         SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION |
1082                                                    SACL_SECURITY_INFORMATION |
1083                                                    OWNER_SECURITY_INFORMATION |
1084                                                    GROUP_SECURITY_INFORMATION;
1085 again:
1086         if (SetFileSecurityW(path, securityInformation, descriptor))
1087                 return 0;
1088         err = GetLastError();
1089         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
1090                 goto fail;
1091         switch (err) {
1092         case ERROR_PRIVILEGE_NOT_HELD:
1093                 if (securityInformation & SACL_SECURITY_INFORMATION) {
1094                         n = args->num_set_sacl_priv_notheld++;
1095                         securityInformation &= ~SACL_SECURITY_INFORMATION;
1096                         if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
1097                                 WARNING(
1098 "We don't have enough privileges to set the full security\n"
1099 "          descriptor on \"%ls\"!\n", path);
1100                                 if (args->num_set_sd_access_denied +
1101                                     args->num_set_sacl_priv_notheld == 1)
1102                                 {
1103                                         WARNING("%ls", apply_access_denied_msg);
1104                                 }
1105                                 WARNING("Re-trying with SACL omitted.\n", path);
1106                         } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
1107                                 WARNING(
1108 "Suppressing further 'privileges not held' error messages when setting\n"
1109 "          security descriptors.");
1110                         }
1111                         goto again;
1112                 }
1113                 /* Fall through */
1114         case ERROR_INVALID_OWNER:
1115         case ERROR_ACCESS_DENIED:
1116                 n = args->num_set_sd_access_denied++;
1117                 if (n < MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
1118                         WARNING("Failed to set security descriptor on \"%ls\": "
1119                                 "Access denied!\n", path);
1120                         if (args->num_set_sd_access_denied +
1121                             args->num_set_sacl_priv_notheld == 1)
1122                         {
1123                                 WARNING("%ls", apply_access_denied_msg);
1124                         }
1125                 } else if (n == MAX_SET_SD_ACCESS_DENIED_WARNINGS) {
1126                         WARNING(
1127 "Suppressing further access denied error messages when setting\n"
1128 "          security descriptors");
1129                 }
1130                 return 0;
1131         default:
1132 fail:
1133                 ERROR("Failed to set security descriptor on \"%ls\"", path);
1134                 win32_error(err);
1135                 return WIMLIB_ERR_WRITE;
1136         }
1137 }
1138
1139
1140 static int
1141 win32_extract_chunk(const void *buf, size_t len, u64 offset, void *arg)
1142 {
1143         HANDLE hStream = arg;
1144
1145         DWORD nbytes_written;
1146         wimlib_assert(len <= 0xffffffff);
1147
1148         if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
1149             nbytes_written != len)
1150         {
1151                 DWORD err = GetLastError();
1152                 ERROR("WriteFile(): write error");
1153                 win32_error(err);
1154                 return WIMLIB_ERR_WRITE;
1155         }
1156         return 0;
1157 }
1158
1159 static int
1160 do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte)
1161 {
1162         return extract_wim_resource(lte, wim_resource_size(lte),
1163                                     win32_extract_chunk, hStream);
1164 }
1165
1166 static bool
1167 path_is_root_of_drive(const wchar_t *path)
1168 {
1169         if (!*path)
1170                 return false;
1171
1172         if (*path != L'/' && *path != L'\\') {
1173                 if (*(path + 1) == L':')
1174                         path += 2;
1175                 else
1176                         return false;
1177         }
1178         while (*path == L'/' || *path == L'\\')
1179                 path++;
1180         return (*path == L'\0');
1181 }
1182
1183 static DWORD
1184 win32_get_create_flags_and_attributes(DWORD i_attributes)
1185 {
1186         DWORD attributes;
1187
1188         /*
1189          * Some attributes cannot be set by passing them to CreateFile().  In
1190          * particular:
1191          *
1192          * FILE_ATTRIBUTE_DIRECTORY:
1193          *   CreateDirectory() must be called instead of CreateFile().
1194          *
1195          * FILE_ATTRIBUTE_SPARSE_FILE:
1196          *   Needs an ioctl.
1197          *   See: win32_set_sparse().
1198          *
1199          * FILE_ATTRIBUTE_COMPRESSED:
1200          *   Not clear from the documentation, but apparently this needs an
1201          *   ioctl as well.
1202          *   See: win32_set_compressed().
1203          *
1204          * FILE_ATTRIBUTE_REPARSE_POINT:
1205          *   Needs an ioctl, with the reparse data specified.
1206          *   See: win32_set_reparse_data().
1207          *
1208          * In addition, clear any file flags in the attributes that we don't
1209          * want, but also specify FILE_FLAG_OPEN_REPARSE_POINT and
1210          * FILE_FLAG_BACKUP_SEMANTICS as we are a backup application.
1211          */
1212         attributes = i_attributes & ~(FILE_ATTRIBUTE_SPARSE_FILE |
1213                                       FILE_ATTRIBUTE_COMPRESSED |
1214                                       FILE_ATTRIBUTE_REPARSE_POINT |
1215                                       FILE_ATTRIBUTE_DIRECTORY |
1216                                       FILE_FLAG_DELETE_ON_CLOSE |
1217                                       FILE_FLAG_NO_BUFFERING |
1218                                       FILE_FLAG_OPEN_NO_RECALL |
1219                                       FILE_FLAG_OVERLAPPED |
1220                                       FILE_FLAG_RANDOM_ACCESS |
1221                                       /*FILE_FLAG_SESSION_AWARE |*/
1222                                       FILE_FLAG_SEQUENTIAL_SCAN |
1223                                       FILE_FLAG_WRITE_THROUGH);
1224         return attributes |
1225                FILE_FLAG_OPEN_REPARSE_POINT |
1226                FILE_FLAG_BACKUP_SEMANTICS;
1227 }
1228
1229 static bool
1230 inode_has_special_attributes(const struct wim_inode *inode)
1231 {
1232         return (inode->i_attributes & (FILE_ATTRIBUTE_COMPRESSED |
1233                                        FILE_ATTRIBUTE_REPARSE_POINT |
1234                                        FILE_ATTRIBUTE_SPARSE_FILE)) != 0;
1235 }
1236
1237 static int
1238 win32_set_special_attributes(HANDLE hFile, const struct wim_inode *inode,
1239                              struct wim_lookup_table_entry *unnamed_stream_lte,
1240                              const wchar_t *path)
1241 {
1242         int ret;
1243
1244         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1245                 DEBUG("Setting reparse data on \"%ls\"", path);
1246                 ret = win32_set_reparse_data(hFile, inode->i_reparse_tag,
1247                                              unnamed_stream_lte, path);
1248                 if (ret)
1249                         return ret;
1250         }
1251
1252         if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) {
1253                 DEBUG("Setting compression flag on \"%ls\"", path);
1254                 ret = win32_set_compressed(hFile, path);
1255                 if (ret)
1256                         return ret;
1257         }
1258
1259         if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) {
1260                 DEBUG("Setting sparse flag on \"%ls\"", path);
1261                 ret = win32_set_sparse(hFile, path);
1262                 if (ret)
1263                         return ret;
1264         }
1265         return 0;
1266 }
1267
1268 static int
1269 win32_extract_stream(const struct wim_inode *inode,
1270                      const wchar_t *path,
1271                      const wchar_t *stream_name_utf16,
1272                      struct wim_lookup_table_entry *lte)
1273 {
1274         wchar_t *stream_path;
1275         HANDLE h;
1276         int ret;
1277         DWORD err;
1278         DWORD creationDisposition = CREATE_ALWAYS;
1279
1280         if (stream_name_utf16) {
1281                 /* Named stream.  Create a buffer that contains the UTF-16LE
1282                  * string [.\]@path:@stream_name_utf16.  This is needed to
1283                  * create and open the stream using CreateFileW().  I'm not
1284                  * aware of any other APIs to do this.  Note: the '$DATA' suffix
1285                  * seems to be unneeded.  Additional note: a "./" prefix needs
1286                  * to be added when the path is not absolute to avoid ambiguity
1287                  * with drive letters. */
1288                 size_t stream_path_nchars;
1289                 size_t path_nchars;
1290                 size_t stream_name_nchars;
1291                 const wchar_t *prefix;
1292
1293                 path_nchars = wcslen(path);
1294                 stream_name_nchars = wcslen(stream_name_utf16);
1295                 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
1296                 if (path[0] != cpu_to_le16(L'\0') &&
1297                     path[0] != cpu_to_le16(L'/') &&
1298                     path[0] != cpu_to_le16(L'\\') &&
1299                     path[1] != cpu_to_le16(L':'))
1300                 {
1301                         prefix = L"./";
1302                         stream_path_nchars += 2;
1303                 } else {
1304                         prefix = L"";
1305                 }
1306                 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
1307                 swprintf(stream_path, L"%ls%ls:%ls",
1308                          prefix, path, stream_name_utf16);
1309         } else {
1310                 /* Unnamed stream; its path is just the path to the file itself.
1311                  * */
1312                 stream_path = (wchar_t*)path;
1313
1314                 /* Directories must be created with CreateDirectoryW().  Then
1315                  * the call to CreateFileW() will merely open the directory that
1316                  * was already created rather than creating a new file. */
1317                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
1318                         if (!CreateDirectoryW(stream_path, NULL)) {
1319                                 err = GetLastError();
1320                                 switch (err) {
1321                                 case ERROR_ALREADY_EXISTS:
1322                                         break;
1323                                 case ERROR_ACCESS_DENIED:
1324                                         if (path_is_root_of_drive(path))
1325                                                 break;
1326                                         /* Fall through */
1327                                 default:
1328                                         ERROR("Failed to create directory \"%ls\"",
1329                                               stream_path);
1330                                         win32_error(err);
1331                                         ret = WIMLIB_ERR_MKDIR;
1332                                         goto fail;
1333                                 }
1334                         }
1335                         DEBUG("Created directory \"%ls\"", stream_path);
1336                         if (!inode_has_special_attributes(inode)) {
1337                                 ret = 0;
1338                                 goto out;
1339                         }
1340                         creationDisposition = OPEN_EXISTING;
1341                 }
1342         }
1343
1344         DEBUG("Opening \"%ls\"", stream_path);
1345         h = CreateFileW(stream_path,
1346                         GENERIC_READ | GENERIC_WRITE,
1347                         0,
1348                         NULL,
1349                         creationDisposition,
1350                         win32_get_create_flags_and_attributes(inode->i_attributes),
1351                         NULL);
1352         if (h == INVALID_HANDLE_VALUE) {
1353                 err = GetLastError();
1354                 ERROR("Failed to create \"%ls\"", stream_path);
1355                 win32_error(err);
1356                 ret = WIMLIB_ERR_OPEN;
1357                 goto fail;
1358         }
1359
1360         if (stream_name_utf16 == NULL && inode_has_special_attributes(inode)) {
1361                 ret = win32_set_special_attributes(h, inode, lte, path);
1362                 if (ret)
1363                         goto fail_close_handle;
1364         }
1365
1366         if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1367                 if (lte) {
1368                         DEBUG("Extracting \"%ls\" (len = %"PRIu64")",
1369                               stream_path, wim_resource_size(lte));
1370                         ret = do_win32_extract_stream(h, lte);
1371                         if (ret)
1372                                 goto fail_close_handle;
1373                 }
1374         }
1375
1376         DEBUG("Closing \"%ls\"", stream_path);
1377         if (!CloseHandle(h)) {
1378                 err = GetLastError();
1379                 ERROR("Failed to close \"%ls\"", stream_path);
1380                 win32_error(err);
1381                 ret = WIMLIB_ERR_WRITE;
1382                 goto fail;
1383         }
1384         ret = 0;
1385         goto out;
1386 fail_close_handle:
1387         CloseHandle(h);
1388 fail:
1389         ERROR("Error extracting %ls", stream_path);
1390 out:
1391         return ret;
1392 }
1393
1394 /*
1395  * Creates a file, directory, or reparse point and extracts all streams to it
1396  * (unnamed data stream and/or reparse point stream, plus any alternate data
1397  * streams).  This in Win32-specific code.
1398  *
1399  * @inode:      WIM inode for this file or directory.
1400  * @path:       UTF-16LE external path to extract the inode to.
1401  *
1402  * Returns 0 on success; nonzero on failure.
1403  */
1404 static int
1405 win32_extract_streams(const struct wim_inode *inode,
1406                       const wchar_t *path, u64 *completed_bytes_p)
1407 {
1408         struct wim_lookup_table_entry *unnamed_lte;
1409         int ret;
1410
1411         unnamed_lte = inode_unnamed_lte_resolved(inode);
1412         ret = win32_extract_stream(inode, path, NULL, unnamed_lte);
1413         if (ret)
1414                 goto out;
1415         if (unnamed_lte)
1416                 *completed_bytes_p += wim_resource_size(unnamed_lte);
1417         for (u16 i = 0; i < inode->i_num_ads; i++) {
1418                 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
1419                 if (ads_entry->stream_name_nbytes != 0) {
1420                         /* Skip special UNIX data entries (see documentation for
1421                          * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
1422                         if (ads_entry->stream_name_nbytes == WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES
1423                             && !memcmp(ads_entry->stream_name,
1424                                        WIMLIB_UNIX_DATA_TAG_UTF16LE,
1425                                        WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES))
1426                                 continue;
1427                         ret = win32_extract_stream(inode,
1428                                                    path,
1429                                                    ads_entry->stream_name,
1430                                                    ads_entry->lte);
1431                         if (ret)
1432                                 break;
1433                         if (ads_entry->lte)
1434                                 *completed_bytes_p += wim_resource_size(ads_entry->lte);
1435                 }
1436         }
1437 out:
1438         return ret;
1439 }
1440
1441 /* Extract a file, directory, reparse point, or hard link to an
1442  * already-extracted file using the Win32 API */
1443 int
1444 win32_do_apply_dentry(const wchar_t *output_path,
1445                       size_t output_path_num_chars,
1446                       struct wim_dentry *dentry,
1447                       struct apply_args *args)
1448 {
1449         int ret;
1450         struct wim_inode *inode = dentry->d_inode;
1451         DWORD err;
1452
1453         if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
1454                 /* Linked file, with another name already extracted.  Create a
1455                  * hard link. */
1456                 DEBUG("Creating hard link \"%ls => %ls\"",
1457                       output_path, inode->i_extracted_file);
1458                 if (!CreateHardLinkW(output_path, inode->i_extracted_file, NULL)) {
1459                         err = GetLastError();
1460                         ERROR("Can't create hard link \"%ls => %ls\"",
1461                               output_path, inode->i_extracted_file);
1462                         win32_error(err);
1463                         return WIMLIB_ERR_LINK;
1464                 }
1465         } else {
1466                 /* Create the file, directory, or reparse point, and extract the
1467                  * data streams. */
1468                 ret = win32_extract_streams(inode, output_path,
1469                                             &args->progress.extract.completed_bytes);
1470                 if (ret)
1471                         return ret;
1472
1473                 if (inode->i_security_id >= 0 &&
1474                     !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
1475                 {
1476                         ret = win32_set_security_data(inode, output_path, args);
1477                         if (ret)
1478                                 return ret;
1479                 }
1480                 if (inode->i_nlink > 1) {
1481                         /* Save extracted path for a later call to
1482                          * CreateHardLinkW() if this inode has multiple links.
1483                          * */
1484                         inode->i_extracted_file = WSTRDUP(output_path);
1485                         if (!inode->i_extracted_file)
1486                                 ret = WIMLIB_ERR_NOMEM;
1487                 }
1488         }
1489         return 0;
1490 }
1491
1492 /* Set timestamps on an extracted file using the Win32 API */
1493 int
1494 win32_do_apply_dentry_timestamps(const wchar_t *path,
1495                                  size_t path_num_chars,
1496                                  const struct wim_dentry *dentry,
1497                                  const struct apply_args *args)
1498 {
1499         DWORD err;
1500         HANDLE h;
1501         const struct wim_inode *inode = dentry->d_inode;
1502
1503         DEBUG("Opening \"%ls\" to set timestamps", path);
1504         h = win32_open_existing_file(path, FILE_WRITE_ATTRIBUTES);
1505         if (h == INVALID_HANDLE_VALUE) {
1506                 err = GetLastError();
1507                 goto fail;
1508         }
1509
1510         FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff,
1511                                  .dwHighDateTime = inode->i_creation_time >> 32};
1512         FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff,
1513                                   .dwHighDateTime = inode->i_last_access_time >> 32};
1514         FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff,
1515                                   .dwHighDateTime = inode->i_last_write_time >> 32};
1516
1517         DEBUG("Calling SetFileTime() on \"%ls\"", path);
1518         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
1519                 err = GetLastError();
1520                 CloseHandle(h);
1521                 goto fail;
1522         }
1523         DEBUG("Closing \"%ls\"", path);
1524         if (!CloseHandle(h)) {
1525                 err = GetLastError();
1526                 goto fail;
1527         }
1528         goto out;
1529 fail:
1530         /* Only warn if setting timestamps failed; still return 0. */
1531         WARNING("Can't set timestamps on \"%ls\"", path);
1532         win32_error(err);
1533 out:
1534         return 0;
1535 }
1536
1537 /* Replacement for POSIX fsync() */
1538 int
1539 fsync(int fd)
1540 {
1541         DWORD err;
1542         HANDLE h;
1543
1544         h = (HANDLE)_get_osfhandle(fd);
1545         if (h == INVALID_HANDLE_VALUE) {
1546                 err = GetLastError();
1547                 ERROR("Could not get Windows handle for file descriptor");
1548                 win32_error(err);
1549                 errno = EBADF;
1550                 return -1;
1551         }
1552         if (!FlushFileBuffers(h)) {
1553                 err = GetLastError();
1554                 ERROR("Could not flush file buffers to disk");
1555                 win32_error(err);
1556                 errno = EIO;
1557                 return -1;
1558         }
1559         return 0;
1560 }
1561
1562 /* Use the Win32 API to get the number of processors */
1563 unsigned
1564 win32_get_number_of_processors()
1565 {
1566         SYSTEM_INFO sysinfo;
1567         GetSystemInfo(&sysinfo);
1568         return sysinfo.dwNumberOfProcessors;
1569 }
1570
1571 /* Replacement for POSIX-2008 realpath().  Warning: partial functionality only
1572  * (resolved_path must be NULL).   Also I highly doubt that GetFullPathName
1573  * really does the right thing under all circumstances. */
1574 wchar_t *
1575 realpath(const wchar_t *path, wchar_t *resolved_path)
1576 {
1577         DWORD ret;
1578         wimlib_assert(resolved_path == NULL);
1579         DWORD err;
1580
1581         ret = GetFullPathNameW(path, 0, NULL, NULL);
1582         if (!ret) {
1583                 err = GetLastError();
1584                 goto fail_win32;
1585         }
1586
1587         resolved_path = TMALLOC(ret);
1588         if (!resolved_path)
1589                 goto out;
1590         ret = GetFullPathNameW(path, ret, resolved_path, NULL);
1591         if (!ret) {
1592                 err = GetLastError();
1593                 free(resolved_path);
1594                 resolved_path = NULL;
1595                 goto fail_win32;
1596         }
1597         goto out;
1598 fail_win32:
1599         win32_error(err);
1600         errno = -1;
1601 out:
1602         return resolved_path;
1603 }
1604
1605 /* rename() on Windows fails if the destination file exists.  And we need to
1606  * make it work on wide characters.  Fix it. */
1607 int
1608 win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath)
1609 {
1610         if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) {
1611                 return 0;
1612         } else {
1613                 /* As usual, the possible error values are not documented */
1614                 DWORD err = GetLastError();
1615                 ERROR("MoveFileEx(): Can't rename \"%ls\" to \"%ls\"",
1616                       oldpath, newpath);
1617                 win32_error(err);
1618                 errno = -1;
1619                 return -1;
1620         }
1621 }
1622
1623 /* Replacement for POSIX fnmatch() (partial functionality only) */
1624 int
1625 fnmatch(const wchar_t *pattern, const wchar_t *string, int flags)
1626 {
1627         if (PathMatchSpecW(string, pattern))
1628                 return 0;
1629         else
1630                 return FNM_NOMATCH;
1631 }
1632
1633 /* truncate() replacement */
1634 int
1635 win32_truncate_replacement(const wchar_t *path, off_t size)
1636 {
1637         DWORD err = NO_ERROR;
1638         LARGE_INTEGER liOffset;
1639
1640         HANDLE h = win32_open_existing_file(path, GENERIC_WRITE);
1641         if (h == INVALID_HANDLE_VALUE)
1642                 goto fail;
1643
1644         liOffset.QuadPart = size;
1645         if (!SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
1646                 goto fail_close_handle;
1647
1648         if (!SetEndOfFile(h))
1649                 goto fail_close_handle;
1650         CloseHandle(h);
1651         return 0;
1652
1653 fail_close_handle:
1654         err = GetLastError();
1655         CloseHandle(h);
1656 fail:
1657         if (err == NO_ERROR)
1658                 err = GetLastError();
1659         ERROR("Can't truncate \"%ls\" to %"PRIu64" bytes", path, size);
1660         win32_error(err);
1661         errno = -1;
1662         return -1;
1663 }
1664
1665
1666 /* This really could be replaced with _wcserror_s, but this doesn't seem to
1667  * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
1668  * linked in by Visual Studio...?). */
1669 extern int
1670 win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
1671 {
1672         static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
1673
1674         pthread_mutex_lock(&strerror_lock);
1675         mbstowcs(buf, strerror(errnum), buflen);
1676         buf[buflen - 1] = '\0';
1677         pthread_mutex_unlock(&strerror_lock);
1678         return 0;
1679 }
1680
1681 #endif /* __WIN32__ */