4 * All the code specific to native Windows builds is in here.
8 * Copyright (C) 2013 Eric Biggers
10 * This file is part of wimlib, a library for working with WIM files.
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)
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
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/.
27 # error "This file contains Windows code"
40 /* Microsoft's swprintf() violates the C standard and they require programmers
41 * to do this weird define to get the correct function. */
42 #define swprintf _snwprintf
46 #include "lookup_table.h"
51 #ifdef ENABLE_ERROR_MESSAGES
52 void win32_error(u32 err_code)
56 nchars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
58 (char*)&buffer, 0, NULL);
60 ERROR("Error printing error message! "
61 "Computer will self-destruct in 3 seconds.");
63 ERROR("Win32 error: %s", buffer);
68 #define win32_error(err_code)
71 void *win32_open_file_readonly(const void *path)
73 return CreateFileW((const wchar_t*)path,
75 FILE_READ_ATTRIBUTES |
77 ACCESS_SYSTEM_SECURITY,
79 NULL, /* lpSecurityAttributes */
81 FILE_FLAG_BACKUP_SEMANTICS |
82 FILE_FLAG_OPEN_REPARSE_POINT,
83 NULL /* hTemplateFile */);
86 int win32_read_file(const char *filename,
87 void *handle, u64 offset, size_t size, u8 *buf)
92 LARGE_INTEGER liOffset = {.QuadPart = offset};
94 wimlib_assert(size <= 0xffffffff);
96 if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
97 if (ReadFile(h, buf, size, &bytesRead, NULL) && bytesRead == size)
100 ERROR("Error reading \"%s\"", filename);
102 return WIMLIB_ERR_READ;
105 void win32_close_file(void *handle)
107 CloseHandle((HANDLE)handle);
110 static bool win32_modify_privilege(const char *privilege, bool enable)
114 TOKEN_PRIVILEGES newState;
117 DEBUG("%s privilege %s",
118 enable ? "Enabling" : "Disabling", privilege);
120 if (!OpenProcessToken(GetCurrentProcess(),
121 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
124 DEBUG("OpenProcessToken() failed");
128 if (!LookupPrivilegeValue(NULL, privilege, &luid)) {
129 DEBUG("LookupPrivilegeValue() failed");
133 newState.PrivilegeCount = 1;
134 newState.Privileges[0].Luid = luid;
135 newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
136 ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
138 DEBUG("AdjustTokenPrivileges() failed");
142 DWORD err = GetLastError();
144 WARNING("Failed to %s privilege %s",
145 enable ? "enable" : "disable", privilege);
146 WARNING("The program will continue, but if permission issues are "
147 "encountered, you may need to run this program as the administrator");
152 static bool win32_acquire_privilege(const char *privilege)
154 return win32_modify_privilege(privilege, true);
157 static bool win32_release_privilege(const char *privilege)
159 return win32_modify_privilege(privilege, false);
163 void win32_acquire_capture_privileges()
165 win32_acquire_privilege(SE_BACKUP_NAME);
166 win32_acquire_privilege(SE_SECURITY_NAME);
169 void win32_release_capture_privileges()
171 win32_release_privilege(SE_BACKUP_NAME);
172 win32_release_privilege(SE_SECURITY_NAME);
175 void win32_acquire_restore_privileges()
177 win32_acquire_privilege(SE_RESTORE_NAME);
178 win32_acquire_privilege(SE_SECURITY_NAME);
179 win32_acquire_privilege(SE_TAKE_OWNERSHIP_NAME);
182 void win32_release_restore_privileges()
184 win32_release_privilege(SE_RESTORE_NAME);
185 win32_release_privilege(SE_SECURITY_NAME);
186 win32_release_privilege(SE_TAKE_OWNERSHIP_NAME);
189 static u64 FILETIME_to_u64(const FILETIME *ft)
191 return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
195 int win32_build_dentry_tree(struct wim_dentry **root_ret,
196 const char *root_disk_path,
197 struct wim_lookup_table *lookup_table,
198 struct wim_security_data *sd,
199 const struct capture_config *config,
201 wimlib_progress_func_t progress_func,
204 static int win32_get_short_name(struct wim_dentry *dentry,
205 const wchar_t *path_utf16)
207 WIN32_FIND_DATAW dat;
208 if (FindFirstFileW(path_utf16, &dat) &&
209 dat.cAlternateFileName[0] != L'\0')
211 size_t short_name_len = wcslen(dat.cAlternateFileName) * 2;
212 size_t n = short_name_len + sizeof(wchar_t);
213 dentry->short_name = MALLOC(n);
214 if (!dentry->short_name)
215 return WIMLIB_ERR_NOMEM;
216 memcpy(dentry->short_name, dat.cAlternateFileName, n);
217 dentry->short_name_len = short_name_len;
222 static int win32_get_security_descriptor(struct wim_dentry *dentry,
223 struct sd_set *sd_set,
224 const wchar_t *path_utf16)
226 SECURITY_INFORMATION requestedInformation;
231 requestedInformation = DACL_SECURITY_INFORMATION |
232 SACL_SECURITY_INFORMATION |
233 OWNER_SECURITY_INFORMATION |
234 GROUP_SECURITY_INFORMATION;
235 /* Request length of security descriptor */
236 status = GetFileSecurityW(path_utf16, requestedInformation,
237 NULL, 0, &lenNeeded);
238 err = GetLastError();
239 if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
240 DWORD len = lenNeeded;
242 if (GetFileSecurityW(path_utf16, requestedInformation,
243 (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
245 int security_id = sd_set_add_sd(sd_set, buf, len);
247 return WIMLIB_ERR_NOMEM;
249 dentry->d_inode->i_security_id = security_id;
253 err = GetLastError();
256 ERROR("Win32 API: Failed to read security descriptor of \"%ls\"",
259 return WIMLIB_ERR_READ;
262 /* Reads the directory entries of directory using a Win32 API and recursively
263 * calls build_dentry_tree() on them. */
264 static int win32_recurse_directory(struct wim_dentry *root,
265 const char *root_disk_path,
266 struct wim_lookup_table *lookup_table,
267 struct wim_security_data *sd,
268 const struct capture_config *config,
270 wimlib_progress_func_t progress_func,
271 struct sd_set *sd_set,
272 const wchar_t *path_utf16,
273 size_t path_utf16_nchars)
275 WIN32_FIND_DATAW dat;
281 /* Begin reading the directory by calling FindFirstFileW.
282 * Unlike UNIX opendir(), FindFirstFileW has file globbing built
283 * into it. But this isn't what we actually want, so just add a
284 * dummy glob to get all entries. */
285 wchar_t pattern_buf[path_utf16_nchars + 3];
286 memcpy(pattern_buf, path_utf16,
287 path_utf16_nchars * sizeof(wchar_t));
288 pattern_buf[path_utf16_nchars] = L'/';
289 pattern_buf[path_utf16_nchars + 1] = L'*';
290 pattern_buf[path_utf16_nchars + 2] = L'\0';
291 hFind = FindFirstFileW(pattern_buf, &dat);
293 if (hFind == INVALID_HANDLE_VALUE) {
294 err = GetLastError();
295 if (err == ERROR_FILE_NOT_FOUND) {
298 ERROR("Win32 API: Failed to read directory \"%s\"",
301 return WIMLIB_ERR_READ;
306 /* Skip . and .. entries */
307 if (!(dat.cFileName[0] == L'.' &&
308 (dat.cFileName[1] == L'\0' ||
309 (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0'))))
311 struct wim_dentry *child;
314 size_t utf8_name_nbytes;
315 ret = utf16_to_utf8((const char*)dat.cFileName,
316 wcslen(dat.cFileName) * sizeof(wchar_t),
322 char name[strlen(root_disk_path) + 1 + utf8_name_nbytes + 1];
323 sprintf(name, "%s/%s", root_disk_path, utf8_name);
325 ret = win32_build_dentry_tree(&child, name, lookup_table,
326 sd, config, add_image_flags,
327 progress_func, sd_set);
331 dentry_add_child(root, child);
333 } while (FindNextFileW(hFind, &dat));
334 err = GetLastError();
335 if (err != ERROR_NO_MORE_FILES) {
336 ERROR("Win32 API: Failed to read directory \"%s\"", root_disk_path);
339 ret = WIMLIB_ERR_READ;
346 /* Load a reparse point into a WIM inode. It is just stored in memory.
348 * @hFile: Open handle to a reparse point, with permission to read the reparse
351 * @inode: WIM inode for the reparse point.
353 * @lookup_table: Stream lookup table for the WIM; an entry will be added to it
354 * for the reparse point unless an entry already exists for
355 * the exact same data stream.
357 * @path: External path to the parse point (UTF-8). Used for error messages
360 * Returns 0 on success; nonzero on failure. */
361 static int win32_capture_reparse_point(HANDLE hFile,
362 struct wim_inode *inode,
363 struct wim_lookup_table *lookup_table,
366 /* "Reparse point data, including the tag and optional GUID,
367 * cannot exceed 16 kilobytes." - MSDN */
368 char reparse_point_buf[16 * 1024];
371 if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
372 NULL, 0, reparse_point_buf,
373 sizeof(reparse_point_buf), &bytesReturned, NULL))
375 DWORD err = GetLastError();
376 ERROR("Win32 API: Failed to get reparse data of \"%s\"", path);
378 return WIMLIB_ERR_READ;
380 if (bytesReturned < 8) {
381 ERROR("Reparse data on \"%s\" is invalid", path);
382 return WIMLIB_ERR_READ;
384 inode->i_reparse_tag = *(u32*)reparse_point_buf;
385 return inode_add_ads_with_data(inode, "",
386 (const u8*)reparse_point_buf + 8,
387 bytesReturned - 8, lookup_table);
390 /* Calculate the SHA1 message digest of a Win32 data stream, which may be either
391 * an unnamed or named data stream.
393 * @path: Path to the file, with the stream noted at the end for named
394 * streams. UTF-16LE encoding.
396 * @hash: On success, the SHA1 message digest of the stream is written to
399 * Returns 0 on success; nonzero on failure.
401 static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE])
409 hFile = win32_open_file_readonly(path);
410 if (hFile == INVALID_HANDLE_VALUE)
411 return WIMLIB_ERR_OPEN;
415 if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) {
416 ret = WIMLIB_ERR_READ;
417 goto out_close_handle;
421 sha1_update(&ctx, buf, bytesRead);
424 sha1_final(hash, &ctx);
430 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
431 * stream); calculates its SHA1 message digest and either creates a `struct
432 * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
433 * wim_lookup_table_entry' for an identical stream.
435 * @path_utf16: Path to the file (UTF-16LE).
437 * @path_utf16_nchars: Number of 2-byte characters in @path_utf16.
439 * @inode: WIM inode to save the stream into.
441 * @lookup_table: Stream lookup table for the WIM.
443 * @dat: A `WIN32_FIND_STREAM_DATA' structure that specifies the
446 * Returns 0 on success; nonzero on failure.
448 static int win32_capture_stream(const wchar_t *path_utf16,
449 size_t path_utf16_nchars,
450 struct wim_inode *inode,
451 struct wim_lookup_table *lookup_table,
452 WIN32_FIND_STREAM_DATA *dat)
454 struct wim_ads_entry *ads_entry;
455 u8 hash[SHA1_HASH_SIZE];
456 struct wim_lookup_table_entry *lte;
459 bool is_named_stream;
464 /* The stream name should be returned as :NAME:TYPE */
465 p = dat->cStreamName;
467 goto out_invalid_stream_name;
469 colon = wcschr(p, L':');
471 goto out_invalid_stream_name;
473 if (wcscmp(colon + 1, L"$DATA")) {
474 /* Not a DATA stream */
479 is_named_stream = (p != colon);
480 if (is_named_stream) {
481 /* Allocate an ADS entry for the named stream. */
482 char *utf8_stream_name;
483 size_t utf8_stream_name_len;
484 ret = utf16_to_utf8((const char *)p,
485 (colon - p) * sizeof(wchar_t),
487 &utf8_stream_name_len);
490 ads_entry = inode_add_ads(inode, utf8_stream_name);
491 FREE(utf8_stream_name);
493 ret = WIMLIB_ERR_NOMEM;
498 /* Create a UTF-16 string @spath that gives the filename, then a colon,
499 * then the stream name. Or, if it's an unnamed stream, just the
500 * filename. It is MALLOC()'ed so that it can be saved in the
501 * wim_lookup_table_entry if needed. */
503 spath_nchars = path_utf16_nchars;
505 spath_nchars += colon - p + 1;
507 spath = MALLOC((spath_nchars + 1) * sizeof(wchar_t));
508 memcpy(spath, path_utf16, path_utf16_nchars * sizeof(wchar_t));
509 if (is_named_stream) {
510 spath[path_utf16_nchars] = L':';
511 memcpy(&spath[path_utf16_nchars + 1], p, (colon - p) * sizeof(wchar_t));
513 spath[spath_nchars] = L'\0';
515 ret = win32_sha1sum(spath, hash);
517 err = GetLastError();
518 ERROR("Win32 API: Failed to read \"%ls\" to calculate SHA1sum",
524 lte = __lookup_resource(lookup_table, hash);
526 /* Use existing wim_lookup_table_entry that has the same SHA1
530 /* Make a new wim_lookup_table_entry */
531 lte = new_lookup_table_entry();
533 ret = WIMLIB_ERR_NOMEM;
536 lte->file_on_disk = (char*)spath;
538 lte->resource_location = RESOURCE_WIN32;
539 lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart;
540 lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart;
541 copy_hash(lte->hash, hash);
542 lookup_table_insert(lookup_table, lte);
545 ads_entry->lte = lte;
552 out_invalid_stream_name:
553 ERROR("Invalid stream name: \"%ls:%ls\"", path_utf16, dat->cStreamName);
554 ret = WIMLIB_ERR_READ;
558 /* Scans a Win32 file for unnamed and named data streams (not reparse point
561 * @path_utf16: Path to the file (UTF-16LE).
563 * @path_utf16_nchars: Number of 2-byte characters in @path_utf16.
565 * @inode: WIM inode to save the stream into.
567 * @lookup_table: Stream lookup table for the WIM.
569 * Returns 0 on success; nonzero on failure.
571 static int win32_capture_streams(const wchar_t *path_utf16,
572 size_t path_utf16_nchars,
573 struct wim_inode *inode,
574 struct wim_lookup_table *lookup_table)
576 WIN32_FIND_STREAM_DATA dat;
581 hFind = FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0);
582 if (hFind == INVALID_HANDLE_VALUE) {
583 err = GetLastError();
585 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
586 * points and directories */
587 if ((inode->i_attributes &
588 (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
589 && err == ERROR_HANDLE_EOF)
593 ERROR("Win32 API: Failed to look up data streams of \"%ls\"",
596 return WIMLIB_ERR_READ;
600 ret = win32_capture_stream(path_utf16,
606 } while (FindNextStreamW(hFind, &dat));
607 err = GetLastError();
608 if (err != ERROR_HANDLE_EOF) {
609 ERROR("Win32 API: Error reading data streams from \"%ls\"", path_utf16);
611 ret = WIMLIB_ERR_READ;
618 /* Win32 version of capturing a directory tree */
619 int win32_build_dentry_tree(struct wim_dentry **root_ret,
620 const char *root_disk_path,
621 struct wim_lookup_table *lookup_table,
622 struct wim_security_data *sd,
623 const struct capture_config *config,
625 wimlib_progress_func_t progress_func,
628 struct wim_dentry *root = NULL;
630 struct wim_inode *inode;
633 size_t path_utf16_nchars;
634 struct sd_set *sd_set;
637 if (exclude_path(root_disk_path, config, true)) {
638 if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) {
639 ERROR("Cannot exclude the root directory from capture");
640 ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
643 if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
646 union wimlib_progress_info info;
647 info.scan.cur_path = root_disk_path;
648 info.scan.excluded = true;
649 progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
654 if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
657 union wimlib_progress_info info;
658 info.scan.cur_path = root_disk_path;
659 info.scan.excluded = false;
660 progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
663 if (extra_arg == NULL) {
664 sd_set = alloca(sizeof(struct sd_set));
665 sd_set->rb_root.rb_node = NULL,
671 ret = utf8_to_utf16(root_disk_path, strlen(root_disk_path),
672 (char**)&path_utf16, &path_utf16_nchars);
674 goto out_destroy_sd_set;
675 path_utf16_nchars /= sizeof(wchar_t);
677 HANDLE hFile = win32_open_file_readonly(path_utf16);
678 if (hFile == INVALID_HANDLE_VALUE) {
679 err = GetLastError();
680 ERROR("Win32 API: Failed to open \"%s\"", root_disk_path);
682 ret = WIMLIB_ERR_OPEN;
683 goto out_free_path_utf16;
686 BY_HANDLE_FILE_INFORMATION file_info;
687 if (!GetFileInformationByHandle(hFile, &file_info)) {
688 err = GetLastError();
689 ERROR("Win32 API: Failed to get file information for \"%s\"",
692 ret = WIMLIB_ERR_STAT;
693 goto out_close_handle;
696 /* Create a WIM dentry */
697 root = new_dentry_with_timeless_inode(path_basename(root_disk_path));
700 ret = WIMLIB_ERR_INVALID_UTF8_STRING;
701 else if (errno == ENOMEM)
702 ret = WIMLIB_ERR_NOMEM;
704 ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE;
705 goto out_close_handle;
708 /* Start preparing the associated WIM inode */
709 inode = root->d_inode;
711 inode->i_attributes = file_info.dwFileAttributes;
712 inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
713 inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
714 inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
715 inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) |
716 (u64)file_info.nFileIndexLow;
718 inode->i_resolved = 1;
719 add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE);
721 /* Get DOS name and security descriptor (if any). */
722 ret = win32_get_short_name(root, path_utf16);
724 goto out_close_handle;
725 ret = win32_get_security_descriptor(root, sd_set, path_utf16);
727 goto out_close_handle;
729 if (inode_is_directory(inode)) {
730 /* Directory (not a reparse point) --- recurse to children */
732 /* But first... directories may have alternate data streams that
733 * need to be captured. */
734 ret = win32_capture_streams(path_utf16,
739 goto out_close_handle;
740 ret = win32_recurse_directory(root,
750 } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
751 /* Reparse point: save the reparse tag and data */
752 ret = win32_capture_reparse_point(hFile,
757 /* Not a directory, not a reparse point; capture the default
758 * file contents and any alternate data streams. */
759 ret = win32_capture_streams(path_utf16,
769 if (extra_arg == NULL)
770 destroy_sd_set(sd_set);
775 free_dentry_tree(root, lookup_table);
779 /* Replacement for POSIX fnmatch() (partial functionality only) */
780 extern int fnmatch(const char *pattern, const char *string, int flags)
782 if (PathMatchSpecA(string, pattern))
788 static int win32_set_reparse_data(HANDLE h,
790 const struct wim_lookup_table_entry *lte,
798 WARNING("\"%ls\" is marked as a reparse point but had no reparse data",
802 len = wim_resource_size(lte);
803 if (len > 16 * 1024 - 8) {
804 WARNING("\"%ls\": reparse data too long!", path);
808 /* The WIM stream omits the ReparseTag and ReparseDataLength fields, so
809 * leave 8 bytes of space for them at the beginning of the buffer, then
810 * set them manually. */
811 buf = alloca(len + 8);
812 ret = read_full_wim_resource(lte, buf + 8, 0);
815 *(u32*)(buf + 0) = reparse_tag;
816 *(u16*)(buf + 4) = len;
817 *(u16*)(buf + 6) = 0;
819 /* Set the reparse data on the open file using the
820 * FSCTL_SET_REPARSE_POINT ioctl.
822 * There are contradictions in Microsoft's documentation for this:
824 * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED,
825 * lpOverlapped is ignored."
827 * --- So setting lpOverlapped to NULL is okay since it's ignored.
829 * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an
830 * operation returns no output data and lpOutBuffer is NULL,
831 * DeviceIoControl makes use of lpBytesReturned. After such an
832 * operation, the value of lpBytesReturned is meaningless."
834 * --- So lpOverlapped not really ignored, as it affects another
835 * parameter. This is the actual behavior: lpBytesReturned must be
836 * specified, even though lpBytesReturned is documented as:
838 * "Not used with this operation; set to NULL."
841 if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, buf, len + 8,
843 &bytesReturned /* lpBytesReturned */,
844 NULL /* lpOverlapped */))
846 DWORD err = GetLastError();
847 ERROR("Failed to set reparse data on \"%ls\"", path);
849 return WIMLIB_ERR_WRITE;
855 static int win32_extract_chunk(const u8 *buf, size_t len, u64 offset, void *arg)
857 HANDLE hStream = arg;
859 DWORD nbytes_written;
860 wimlib_assert(len <= 0xffffffff);
862 if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
863 nbytes_written != len)
865 DWORD err = GetLastError();
866 ERROR("WriteFile(): write error");
868 return WIMLIB_ERR_WRITE;
873 static int do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte)
875 return extract_wim_resource(lte, wim_resource_size(lte),
876 win32_extract_chunk, hStream);
879 static int win32_extract_stream(const struct wim_inode *inode,
881 const wchar_t *stream_name_utf16,
882 struct wim_lookup_table_entry *lte)
884 wchar_t *stream_path;
888 DWORD creationDisposition = CREATE_ALWAYS;
890 if (stream_name_utf16) {
891 /* Named stream. Create a buffer that contains the UTF-16LE
892 * string [./]@path:@stream_name_utf16. This is needed to
893 * create and open the stream using CreateFileW(). I'm not
894 * aware of any other APIs to do this. Note: the '$DATA' suffix
895 * seems to be unneeded. Additional note: a "./" prefix needs
896 * to be added when the path is not absolute to avoid ambiguity
897 * with drive letters. */
898 size_t stream_path_nchars;
900 size_t stream_name_nchars;
901 const wchar_t *prefix;
903 path_nchars = wcslen(path);
904 stream_name_nchars = wcslen(stream_name_utf16);
905 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
906 if (path[0] != L'/' && path[0] != L'\\') {
908 stream_path_nchars += 2;
912 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
913 swprintf(stream_path, stream_path_nchars + 1, L"%ls%ls:%ls",
914 prefix, path, stream_name_utf16);
916 /* Unnamed stream; its path is just the path to the file itself.
918 stream_path = (wchar_t*)path;
920 /* Directories must be created with CreateDirectoryW(). Then
921 * the call to CreateFileW() will merely open the directory that
922 * was already created rather than creating a new file. */
923 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
924 if (!CreateDirectoryW(stream_path, NULL)) {
925 err = GetLastError();
926 if (err != ERROR_ALREADY_EXISTS) {
927 ERROR("Failed to create directory \"%ls\"",
930 ret = WIMLIB_ERR_MKDIR;
934 DEBUG("Created directory \"%ls\"", stream_path);
935 if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
939 creationDisposition = OPEN_EXISTING;
943 DEBUG("Opening \"%ls\"", stream_path);
944 h = CreateFileW(stream_path,
945 GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY,
949 FILE_FLAG_OPEN_REPARSE_POINT |
950 FILE_FLAG_BACKUP_SEMANTICS |
953 if (h == INVALID_HANDLE_VALUE) {
954 err = GetLastError();
955 ERROR("Failed to create \"%ls\"", stream_path);
957 ret = WIMLIB_ERR_OPEN;
961 if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
962 stream_name_utf16 == NULL)
964 DEBUG("Setting reparse data on \"%ls\"", path);
965 ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path);
967 goto fail_close_handle;
970 DEBUG("Extracting \"%ls\" (len = %"PRIu64")",
971 stream_path, wim_resource_size(lte));
972 ret = do_win32_extract_stream(h, lte);
974 goto fail_close_handle;
978 DEBUG("Closing \"%ls\"", stream_path);
979 if (!CloseHandle(h)) {
980 err = GetLastError();
981 ERROR("Failed to close \"%ls\"", stream_path);
983 ret = WIMLIB_ERR_WRITE;
991 ERROR("Error extracting %ls", stream_path);
997 * Creates a file, directory, or reparse point and extracts all streams to it
998 * (unnamed data stream and/or reparse point stream, plus any alternate data
999 * streams). This in Win32-specific code.
1001 * @inode: WIM inode for this file or directory.
1002 * @path: UTF-16LE external path to extract the inode to.
1004 * Returns 0 on success; nonzero on failure.
1006 static int win32_extract_streams(const struct wim_inode *inode,
1007 const wchar_t *path, u64 *completed_bytes_p)
1009 struct wim_lookup_table_entry *unnamed_lte;
1012 unnamed_lte = inode_unnamed_lte_resolved(inode);
1013 ret = win32_extract_stream(inode, path, NULL, unnamed_lte);
1017 *completed_bytes_p += wim_resource_size(unnamed_lte);
1018 for (u16 i = 0; i < inode->i_num_ads; i++) {
1019 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
1020 if (ads_entry->stream_name_len != 0) {
1021 /* Skip special UNIX data entries (see documentation for
1022 * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
1023 if (ads_entry->stream_name_len == WIMLIB_UNIX_DATA_TAG_LEN
1024 && !memcmp(ads_entry->stream_name_utf8,
1025 WIMLIB_UNIX_DATA_TAG,
1026 WIMLIB_UNIX_DATA_TAG_LEN))
1028 ret = win32_extract_stream(inode,
1030 (const wchar_t*)ads_entry->stream_name,
1035 *completed_bytes_p += wim_resource_size(ads_entry->lte);
1043 * Sets the security descriptor on an extracted file. This is Win32-specific
1046 * @inode: The WIM inode that was extracted and has a security descriptor.
1047 * @path: UTF-16LE external path that the inode was extracted to.
1048 * @sd: Security data for the WIM image.
1050 * Returns 0 on success; nonzero on failure.
1052 static int win32_set_security_data(const struct wim_inode *inode,
1053 const wchar_t *path,
1054 const struct wim_security_data *sd)
1056 SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION |
1057 SACL_SECURITY_INFORMATION |
1058 OWNER_SECURITY_INFORMATION |
1059 GROUP_SECURITY_INFORMATION;
1060 if (!SetFileSecurityW(path, securityInformation,
1061 (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id]))
1063 DWORD err = GetLastError();
1064 ERROR("Can't set security descriptor on \"%ls\"", path);
1066 return WIMLIB_ERR_WRITE;
1071 int win32_apply_dentry(const char *output_path,
1072 size_t output_path_len,
1073 const struct wim_dentry *dentry,
1074 struct apply_args *args)
1077 size_t utf16_path_len;
1080 struct wim_inode *inode = dentry->d_inode;
1082 ret = utf8_to_utf16(output_path, output_path_len,
1083 &utf16_path, &utf16_path_len);
1087 if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
1088 /* Linked file, with another name already extracted. Create a
1090 DEBUG("Creating hard link \"%ls => %ls\"",
1091 (const wchar_t*)utf16_path,
1092 (const wchar_t*)inode->i_extracted_file);
1093 if (!CreateHardLinkW((const wchar_t*)utf16_path,
1094 (const wchar_t*)inode->i_extracted_file,
1097 err = GetLastError();
1098 ERROR("Can't create hard link \"%ls => %ls\"",
1099 (const wchar_t*)utf16_path,
1100 (const wchar_t*)inode->i_extracted_file);
1101 ret = WIMLIB_ERR_LINK;
1105 /* Create the file, directory, or reparse point, and extract the
1107 ret = win32_extract_streams(inode, (const wchar_t*)utf16_path,
1108 &args->progress.extract.completed_bytes);
1110 goto out_free_utf16_path;
1112 /* Set security descriptor if present */
1113 if (inode->i_security_id != -1) {
1114 DEBUG("Setting security descriptor %d on %s",
1115 inode->i_security_id, output_path);
1116 ret = win32_set_security_data(inode,
1117 (const wchar_t*)utf16_path,
1118 wim_const_security_data(args->w));
1120 goto out_free_utf16_path;
1122 if (inode->i_nlink > 1) {
1123 /* Save extracted path for a later call to
1124 * CreateHardLinkW() if this inode has multiple links.
1126 inode->i_extracted_file = utf16_path;
1130 out_free_utf16_path:
1136 int win32_apply_dentry_timestamps(const char *output_path,
1137 size_t output_path_len,
1138 const struct wim_dentry *dentry,
1139 const struct apply_args *args)
1143 size_t utf16_path_len;
1147 const struct wim_inode *inode = dentry->d_inode;
1149 ret = utf8_to_utf16(output_path, output_path_len,
1150 &utf16_path, &utf16_path_len);
1154 DEBUG("Opening \"%s\" to set timestamps", output_path);
1155 h = CreateFileW((const wchar_t*)utf16_path,
1156 GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY,
1160 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1163 if (h == INVALID_HANDLE_VALUE)
1164 err = GetLastError();
1166 if (h == INVALID_HANDLE_VALUE)
1169 FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff,
1170 .dwHighDateTime = inode->i_creation_time >> 32};
1171 FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff,
1172 .dwHighDateTime = inode->i_last_access_time >> 32};
1173 FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff,
1174 .dwHighDateTime = inode->i_last_write_time >> 32};
1176 DEBUG("Calling SetFileTime() on \"%s\"", output_path);
1177 if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
1178 err = GetLastError();
1182 DEBUG("Closing \"%s\"", output_path);
1183 if (!CloseHandle(h)) {
1184 err = GetLastError();
1189 /* Only warn if setting timestamps failed. */
1190 WARNING("Can't set timestamps on \"%s\"", output_path);
1196 /* Replacement for POSIX fsync() */
1199 HANDLE h = (HANDLE)_get_osfhandle(fd);
1200 if (h == INVALID_HANDLE_VALUE) {
1204 if (!FlushFileBuffers(h)) {
1211 unsigned win32_get_number_of_processors()
1213 SYSTEM_INFO sysinfo;
1214 GetSystemInfo(&sysinfo);
1215 return sysinfo.dwNumberOfProcessors;