4 * All the library 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"
34 #include <shlwapi.h> /* shlwapi.h for PathMatchSpecA() */
35 #ifdef ERROR /* windows.h defines this */
41 #include "lookup_table.h"
46 #ifdef ENABLE_ERROR_MESSAGES
47 void win32_error(u32 err_code)
51 nchars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
53 (char*)&buffer, 0, NULL);
55 ERROR("Error printing error message! "
56 "Computer will self-destruct in 3 seconds.");
58 ERROR("Win32 error: %s", buffer);
64 void *win32_open_file_readonly(const void *path)
66 return CreateFileW((const wchar_t*)path,
68 FILE_READ_ATTRIBUTES |
70 ACCESS_SYSTEM_SECURITY,
72 NULL, /* lpSecurityAttributes */
74 FILE_FLAG_BACKUP_SEMANTICS |
75 FILE_FLAG_OPEN_REPARSE_POINT,
76 NULL /* hTemplateFile */);
79 int win32_read_file(const char *filename,
80 void *handle, u64 offset, size_t size, void *buf)
85 LARGE_INTEGER liOffset = {.QuadPart = offset};
87 wimlib_assert(size <= 0xffffffff);
89 if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
90 if (ReadFile(h, buf, size, &bytesRead, NULL) && bytesRead == size)
93 ERROR("Error reading \"%s\"", filename);
95 return WIMLIB_ERR_READ;
98 void win32_close_file(void *handle)
100 CloseHandle((HANDLE)handle);
103 static bool win32_modify_privilege(const char *privilege, bool enable)
107 TOKEN_PRIVILEGES newState;
110 DEBUG("%s privilege %s",
111 enable ? "Enabling" : "Disabling", privilege);
113 if (!OpenProcessToken(GetCurrentProcess(),
114 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
117 DEBUG("OpenProcessToken() failed");
121 if (!LookupPrivilegeValue(NULL, privilege, &luid)) {
122 DEBUG("LookupPrivilegeValue() failed");
126 newState.PrivilegeCount = 1;
127 newState.Privileges[0].Luid = luid;
128 newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
129 ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
131 DEBUG("AdjustTokenPrivileges() failed");
135 DWORD err = GetLastError();
137 WARNING("Failed to %s privilege %s",
138 enable ? "enable" : "disable", privilege);
139 WARNING("The program will continue, but if permission issues are "
140 "encountered, you may need to run this program as the administrator");
145 static bool win32_acquire_privilege(const char *privilege)
147 return win32_modify_privilege(privilege, true);
150 static bool win32_release_privilege(const char *privilege)
152 return win32_modify_privilege(privilege, false);
156 void win32_acquire_capture_privileges()
158 win32_acquire_privilege(SE_BACKUP_NAME);
159 win32_acquire_privilege(SE_SECURITY_NAME);
162 void win32_release_capture_privileges()
164 win32_release_privilege(SE_BACKUP_NAME);
165 win32_release_privilege(SE_SECURITY_NAME);
168 void win32_acquire_restore_privileges()
170 win32_acquire_privilege(SE_RESTORE_NAME);
171 win32_acquire_privilege(SE_SECURITY_NAME);
172 win32_acquire_privilege(SE_TAKE_OWNERSHIP_NAME);
175 void win32_release_restore_privileges()
177 win32_release_privilege(SE_RESTORE_NAME);
178 win32_release_privilege(SE_SECURITY_NAME);
179 win32_release_privilege(SE_TAKE_OWNERSHIP_NAME);
182 static u64 FILETIME_to_u64(const FILETIME *ft)
184 return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
187 static int win32_get_short_name(struct wim_dentry *dentry,
188 const wchar_t *path_utf16)
190 WIN32_FIND_DATAW dat;
191 if (FindFirstFileW(path_utf16, &dat) &&
192 dat.cAlternateFileName[0] != L'\0')
194 size_t short_name_len = wcslen(dat.cAlternateFileName) * 2;
195 size_t n = short_name_len + sizeof(wchar_t);
196 dentry->short_name = MALLOC(n);
197 if (!dentry->short_name)
198 return WIMLIB_ERR_NOMEM;
199 memcpy(dentry->short_name, dat.cAlternateFileName, n);
200 dentry->short_name_len = short_name_len;
205 static int win32_get_security_descriptor(struct wim_dentry *dentry,
206 struct sd_set *sd_set,
207 const wchar_t *path_utf16)
209 SECURITY_INFORMATION requestedInformation;
214 requestedInformation = DACL_SECURITY_INFORMATION |
215 SACL_SECURITY_INFORMATION |
216 OWNER_SECURITY_INFORMATION |
217 GROUP_SECURITY_INFORMATION;
218 /* Request length of security descriptor */
219 status = GetFileSecurityW(path_utf16, requestedInformation,
220 NULL, 0, &lenNeeded);
221 err = GetLastError();
222 if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
223 DWORD len = lenNeeded;
225 if (GetFileSecurityW(path_utf16, requestedInformation,
226 (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
228 int security_id = sd_set_add_sd(sd_set, buf, len);
230 return WIMLIB_ERR_NOMEM;
232 dentry->d_inode->i_security_id = security_id;
236 err = GetLastError();
239 ERROR("Win32 API: Failed to read security descriptor of \"%ls\"",
242 return WIMLIB_ERR_READ;
245 /* Reads the directory entries of directory using a Win32 API and recursively
246 * calls win32_build_dentry_tree() on them. */
247 static int win32_recurse_directory(struct wim_dentry *root,
248 const char *root_disk_path,
249 struct wim_lookup_table *lookup_table,
250 struct wim_security_data *sd,
251 const struct capture_config *config,
253 wimlib_progress_func_t progress_func,
254 struct sd_set *sd_set,
255 const wchar_t *path_utf16,
256 size_t path_utf16_nchars)
258 WIN32_FIND_DATAW dat;
264 /* Begin reading the directory by calling FindFirstFileW.
265 * Unlike UNIX opendir(), FindFirstFileW has file globbing built
266 * into it. But this isn't what we actually want, so just add a
267 * dummy glob to get all entries. */
268 wchar_t pattern_buf[path_utf16_nchars + 3];
269 memcpy(pattern_buf, path_utf16,
270 path_utf16_nchars * sizeof(wchar_t));
271 pattern_buf[path_utf16_nchars] = L'/';
272 pattern_buf[path_utf16_nchars + 1] = L'*';
273 pattern_buf[path_utf16_nchars + 2] = L'\0';
274 hFind = FindFirstFileW(pattern_buf, &dat);
276 if (hFind == INVALID_HANDLE_VALUE) {
277 err = GetLastError();
278 if (err == ERROR_FILE_NOT_FOUND) {
281 ERROR("Win32 API: Failed to read directory \"%s\"",
284 return WIMLIB_ERR_READ;
289 /* Skip . and .. entries */
290 if (!(dat.cFileName[0] == L'.' &&
291 (dat.cFileName[1] == L'\0' ||
292 (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0'))))
294 struct wim_dentry *child;
297 size_t utf8_name_nbytes;
298 ret = utf16_to_utf8((const char*)dat.cFileName,
299 wcslen(dat.cFileName) * sizeof(wchar_t),
305 char name[strlen(root_disk_path) + 1 + utf8_name_nbytes + 1];
306 sprintf(name, "%s/%s", root_disk_path, utf8_name);
308 ret = win32_build_dentry_tree(&child, name, lookup_table,
309 sd, config, add_image_flags,
310 progress_func, sd_set);
314 dentry_add_child(root, child);
316 } while (FindNextFileW(hFind, &dat));
317 err = GetLastError();
318 if (err != ERROR_NO_MORE_FILES) {
319 ERROR("Win32 API: Failed to read directory \"%s\"", root_disk_path);
322 ret = WIMLIB_ERR_READ;
329 /* Load a reparse point into a WIM inode. It is just stored in memory.
331 * @hFile: Open handle to a reparse point, with permission to read the reparse
334 * @inode: WIM inode for the reparse point.
336 * @lookup_table: Stream lookup table for the WIM; an entry will be added to it
337 * for the reparse point unless an entry already exists for
338 * the exact same data stream.
340 * @path: External path to the parse point (UTF-8). Used for error messages
343 * Returns 0 on success; nonzero on failure. */
344 static int win32_capture_reparse_point(HANDLE hFile,
345 struct wim_inode *inode,
346 struct wim_lookup_table *lookup_table,
349 /* "Reparse point data, including the tag and optional GUID,
350 * cannot exceed 16 kilobytes." - MSDN */
351 char reparse_point_buf[16 * 1024];
354 if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
355 NULL, 0, reparse_point_buf,
356 sizeof(reparse_point_buf), &bytesReturned, NULL))
358 DWORD err = GetLastError();
359 ERROR("Win32 API: Failed to get reparse data of \"%s\"", path);
361 return WIMLIB_ERR_READ;
363 if (bytesReturned < 8) {
364 ERROR("Reparse data on \"%s\" is invalid", path);
365 return WIMLIB_ERR_READ;
367 inode->i_reparse_tag = *(u32*)reparse_point_buf;
368 return inode_add_ads_with_data(inode, "",
369 (const u8*)reparse_point_buf + 8,
370 bytesReturned - 8, lookup_table);
373 /* Calculate the SHA1 message digest of a Win32 data stream, which may be either
374 * an unnamed or named data stream.
376 * @path: Path to the file, with the stream noted at the end for named
377 * streams. UTF-16LE encoding.
379 * @hash: On success, the SHA1 message digest of the stream is written to
382 * Returns 0 on success; nonzero on failure.
384 static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE])
392 hFile = win32_open_file_readonly(path);
393 if (hFile == INVALID_HANDLE_VALUE)
394 return WIMLIB_ERR_OPEN;
398 if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) {
399 ret = WIMLIB_ERR_READ;
400 goto out_close_handle;
404 sha1_update(&ctx, buf, bytesRead);
407 sha1_final(hash, &ctx);
413 /* Scans an unnamed or named stream of a Win32 file (not a reparse point
414 * stream); calculates its SHA1 message digest and either creates a `struct
415 * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
416 * wim_lookup_table_entry' for an identical stream.
418 * @path_utf16: Path to the file (UTF-16LE).
420 * @path_utf16_nchars: Number of 2-byte characters in @path_utf16.
422 * @inode: WIM inode to save the stream into.
424 * @lookup_table: Stream lookup table for the WIM.
426 * @dat: A `WIN32_FIND_STREAM_DATA' structure that specifies the
429 * Returns 0 on success; nonzero on failure.
431 static int win32_capture_stream(const wchar_t *path_utf16,
432 size_t path_utf16_nchars,
433 struct wim_inode *inode,
434 struct wim_lookup_table *lookup_table,
435 WIN32_FIND_STREAM_DATA *dat)
437 struct wim_ads_entry *ads_entry;
438 u8 hash[SHA1_HASH_SIZE];
439 struct wim_lookup_table_entry *lte;
442 bool is_named_stream;
447 /* The stream name should be returned as :NAME:TYPE */
448 p = dat->cStreamName;
450 goto out_invalid_stream_name;
452 colon = wcschr(p, L':');
454 goto out_invalid_stream_name;
456 if (wcscmp(colon + 1, L"$DATA")) {
457 /* Not a DATA stream */
462 is_named_stream = (p != colon);
463 if (is_named_stream) {
464 /* Allocate an ADS entry for the named stream. */
465 char *utf8_stream_name;
466 size_t utf8_stream_name_len;
467 ret = utf16_to_utf8((const char *)p,
468 (colon - p) * sizeof(wchar_t),
470 &utf8_stream_name_len);
473 ads_entry = inode_add_ads(inode, utf8_stream_name);
474 FREE(utf8_stream_name);
476 ret = WIMLIB_ERR_NOMEM;
481 /* Create a UTF-16 string @spath that gives the filename, then a colon,
482 * then the stream name. Or, if it's an unnamed stream, just the
483 * filename. It is MALLOC()'ed so that it can be saved in the
484 * wim_lookup_table_entry if needed. */
486 spath_nchars = path_utf16_nchars;
488 spath_nchars += colon - p + 1;
490 spath = MALLOC((spath_nchars + 1) * sizeof(wchar_t));
491 memcpy(spath, path_utf16, path_utf16_nchars * sizeof(wchar_t));
492 if (is_named_stream) {
493 spath[path_utf16_nchars] = L':';
494 memcpy(&spath[path_utf16_nchars + 1], p, (colon - p) * sizeof(wchar_t));
496 spath[spath_nchars] = L'\0';
498 ret = win32_sha1sum(spath, hash);
500 err = GetLastError();
501 ERROR("Win32 API: Failed to read \"%ls\" to calculate SHA1sum",
507 lte = __lookup_resource(lookup_table, hash);
509 /* Use existing wim_lookup_table_entry that has the same SHA1
513 /* Make a new wim_lookup_table_entry */
514 lte = new_lookup_table_entry();
516 ret = WIMLIB_ERR_NOMEM;
519 lte->file_on_disk = (char*)spath;
521 lte->resource_location = RESOURCE_WIN32;
522 lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart;
523 lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart;
524 copy_hash(lte->hash, hash);
525 lookup_table_insert(lookup_table, lte);
528 ads_entry->lte = lte;
535 out_invalid_stream_name:
536 ERROR("Invalid stream name: \"%ls:%ls\"", path_utf16, dat->cStreamName);
537 ret = WIMLIB_ERR_READ;
541 /* Scans a Win32 file for unnamed and named data streams (not reparse point
544 * @path_utf16: Path to the file (UTF-16LE).
546 * @path_utf16_nchars: Number of 2-byte characters in @path_utf16.
548 * @inode: WIM inode to save the stream into.
550 * @lookup_table: Stream lookup table for the WIM.
552 * Returns 0 on success; nonzero on failure.
554 static int win32_capture_streams(const wchar_t *path_utf16,
555 size_t path_utf16_nchars,
556 struct wim_inode *inode,
557 struct wim_lookup_table *lookup_table)
559 WIN32_FIND_STREAM_DATA dat;
564 hFind = FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0);
565 if (hFind == INVALID_HANDLE_VALUE) {
566 err = GetLastError();
568 /* Seems legal for this to return ERROR_HANDLE_EOF on reparse
569 * points and directories */
570 if ((inode->i_attributes &
571 (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
572 && err == ERROR_HANDLE_EOF)
576 ERROR("Win32 API: Failed to look up data streams of \"%ls\"",
579 return WIMLIB_ERR_READ;
583 ret = win32_capture_stream(path_utf16,
589 } while (FindNextStreamW(hFind, &dat));
590 err = GetLastError();
591 if (err != ERROR_HANDLE_EOF) {
592 ERROR("Win32 API: Error reading data streams from \"%ls\"", path_utf16);
594 ret = WIMLIB_ERR_READ;
601 /* Win32 version of capturing a directory tree */
602 int win32_build_dentry_tree(struct wim_dentry **root_ret,
603 const char *root_disk_path,
604 struct wim_lookup_table *lookup_table,
605 struct wim_security_data *sd,
606 const struct capture_config *config,
608 wimlib_progress_func_t progress_func,
611 struct wim_dentry *root = NULL;
613 struct wim_inode *inode;
616 size_t path_utf16_nchars;
617 struct sd_set *sd_set;
620 if (exclude_path(root_disk_path, config, true)) {
621 if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) {
622 ERROR("Cannot exclude the root directory from capture");
623 ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
626 if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
629 union wimlib_progress_info info;
630 info.scan.cur_path = root_disk_path;
631 info.scan.excluded = true;
632 progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
637 if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
640 union wimlib_progress_info info;
641 info.scan.cur_path = root_disk_path;
642 info.scan.excluded = false;
643 progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
646 if (extra_arg == NULL) {
647 sd_set = alloca(sizeof(struct sd_set));
648 sd_set->rb_root.rb_node = NULL,
654 ret = utf8_to_utf16(root_disk_path, strlen(root_disk_path),
655 (char**)&path_utf16, &path_utf16_nchars);
657 goto out_destroy_sd_set;
658 path_utf16_nchars /= sizeof(wchar_t);
660 HANDLE hFile = win32_open_file_readonly(path_utf16);
661 if (hFile == INVALID_HANDLE_VALUE) {
662 err = GetLastError();
663 ERROR("Win32 API: Failed to open \"%s\"", root_disk_path);
665 ret = WIMLIB_ERR_OPEN;
666 goto out_free_path_utf16;
669 BY_HANDLE_FILE_INFORMATION file_info;
670 if (!GetFileInformationByHandle(hFile, &file_info)) {
671 err = GetLastError();
672 ERROR("Win32 API: Failed to get file information for \"%s\"",
675 ret = WIMLIB_ERR_STAT;
676 goto out_close_handle;
679 /* Create a WIM dentry */
680 root = new_dentry_with_timeless_inode(path_basename(root_disk_path));
683 ret = WIMLIB_ERR_INVALID_UTF8_STRING;
684 else if (errno == ENOMEM)
685 ret = WIMLIB_ERR_NOMEM;
687 ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE;
688 goto out_close_handle;
691 /* Start preparing the associated WIM inode */
692 inode = root->d_inode;
694 inode->i_attributes = file_info.dwFileAttributes;
695 inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime);
696 inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime);
697 inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
698 inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) |
699 (u64)file_info.nFileIndexLow;
701 inode->i_resolved = 1;
702 add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE);
704 /* Get DOS name and security descriptor (if any). */
705 ret = win32_get_short_name(root, path_utf16);
707 goto out_close_handle;
708 ret = win32_get_security_descriptor(root, sd_set, path_utf16);
710 goto out_close_handle;
712 if (inode_is_directory(inode)) {
713 /* Directory (not a reparse point) --- recurse to children */
715 /* But first... directories may have alternate data streams that
716 * need to be captured. */
717 ret = win32_capture_streams(path_utf16,
722 goto out_close_handle;
723 ret = win32_recurse_directory(root,
733 } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
734 /* Reparse point: save the reparse tag and data */
735 ret = win32_capture_reparse_point(hFile,
740 /* Not a directory, not a reparse point; capture the default
741 * file contents and any alternate data streams. */
742 ret = win32_capture_streams(path_utf16,
752 if (extra_arg == NULL)
753 destroy_sd_set(sd_set);
758 free_dentry_tree(root, lookup_table);
762 /* Replacement for POSIX fnmatch() (partial functionality only) */
763 int fnmatch(const char *pattern, const char *string, int flags)
765 if (PathMatchSpecA(string, pattern))
771 static int win32_set_reparse_data(HANDLE h,
773 const struct wim_lookup_table_entry *lte,
781 WARNING("\"%ls\" is marked as a reparse point but had no reparse data",
785 len = wim_resource_size(lte);
786 if (len > 16 * 1024 - 8) {
787 WARNING("\"%ls\": reparse data too long!", path);
791 /* The WIM stream omits the ReparseTag and ReparseDataLength fields, so
792 * leave 8 bytes of space for them at the beginning of the buffer, then
793 * set them manually. */
794 buf = alloca(len + 8);
795 ret = read_full_wim_resource(lte, buf + 8, 0);
798 *(u32*)(buf + 0) = reparse_tag;
799 *(u16*)(buf + 4) = len;
800 *(u16*)(buf + 6) = 0;
802 /* Set the reparse data on the open file using the
803 * FSCTL_SET_REPARSE_POINT ioctl.
805 * There are contradictions in Microsoft's documentation for this:
807 * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED,
808 * lpOverlapped is ignored."
810 * --- So setting lpOverlapped to NULL is okay since it's ignored.
812 * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an
813 * operation returns no output data and lpOutBuffer is NULL,
814 * DeviceIoControl makes use of lpBytesReturned. After such an
815 * operation, the value of lpBytesReturned is meaningless."
817 * --- So lpOverlapped not really ignored, as it affects another
818 * parameter. This is the actual behavior: lpBytesReturned must be
819 * specified, even though lpBytesReturned is documented as:
821 * "Not used with this operation; set to NULL."
824 if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, buf, len + 8,
826 &bytesReturned /* lpBytesReturned */,
827 NULL /* lpOverlapped */))
829 DWORD err = GetLastError();
830 ERROR("Failed to set reparse data on \"%ls\"", path);
832 return WIMLIB_ERR_WRITE;
838 static int win32_extract_chunk(const u8 *buf, size_t len, u64 offset, void *arg)
840 HANDLE hStream = arg;
842 DWORD nbytes_written;
843 wimlib_assert(len <= 0xffffffff);
845 if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
846 nbytes_written != len)
848 DWORD err = GetLastError();
849 ERROR("WriteFile(): write error");
851 return WIMLIB_ERR_WRITE;
856 static int do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte)
858 return extract_wim_resource(lte, wim_resource_size(lte),
859 win32_extract_chunk, hStream);
862 static int win32_extract_stream(const struct wim_inode *inode,
864 const wchar_t *stream_name_utf16,
865 struct wim_lookup_table_entry *lte)
867 wchar_t *stream_path;
871 DWORD creationDisposition = CREATE_ALWAYS;
873 if (stream_name_utf16) {
874 /* Named stream. Create a buffer that contains the UTF-16LE
875 * string [./]@path:@stream_name_utf16. This is needed to
876 * create and open the stream using CreateFileW(). I'm not
877 * aware of any other APIs to do this. Note: the '$DATA' suffix
878 * seems to be unneeded. Additional note: a "./" prefix needs
879 * to be added when the path is not absolute to avoid ambiguity
880 * with drive letters. */
881 size_t stream_path_nchars;
883 size_t stream_name_nchars;
884 const wchar_t *prefix;
886 path_nchars = wcslen(path);
887 stream_name_nchars = wcslen(stream_name_utf16);
888 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
889 if (path[0] != L'/' && path[0] != L'\\') {
891 stream_path_nchars += 2;
895 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
896 swprintf(stream_path, stream_path_nchars + 1, L"%ls%ls:%ls",
897 prefix, path, stream_name_utf16);
899 /* Unnamed stream; its path is just the path to the file itself.
901 stream_path = (wchar_t*)path;
903 /* Directories must be created with CreateDirectoryW(). Then
904 * the call to CreateFileW() will merely open the directory that
905 * was already created rather than creating a new file. */
906 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
907 if (!CreateDirectoryW(stream_path, NULL)) {
908 err = GetLastError();
909 if (err != ERROR_ALREADY_EXISTS) {
910 ERROR("Failed to create directory \"%ls\"",
913 ret = WIMLIB_ERR_MKDIR;
917 DEBUG("Created directory \"%ls\"", stream_path);
918 if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
922 creationDisposition = OPEN_EXISTING;
926 DEBUG("Opening \"%ls\"", stream_path);
927 h = CreateFileW(stream_path,
928 GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY,
932 FILE_FLAG_OPEN_REPARSE_POINT |
933 FILE_FLAG_BACKUP_SEMANTICS |
936 if (h == INVALID_HANDLE_VALUE) {
937 err = GetLastError();
938 ERROR("Failed to create \"%ls\"", stream_path);
940 ret = WIMLIB_ERR_OPEN;
944 if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
945 stream_name_utf16 == NULL)
947 DEBUG("Setting reparse data on \"%ls\"", path);
948 ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path);
950 goto fail_close_handle;
953 DEBUG("Extracting \"%ls\" (len = %"PRIu64")",
954 stream_path, wim_resource_size(lte));
955 ret = do_win32_extract_stream(h, lte);
957 goto fail_close_handle;
961 DEBUG("Closing \"%ls\"", stream_path);
962 if (!CloseHandle(h)) {
963 err = GetLastError();
964 ERROR("Failed to close \"%ls\"", stream_path);
966 ret = WIMLIB_ERR_WRITE;
974 ERROR("Error extracting %ls", stream_path);
980 * Creates a file, directory, or reparse point and extracts all streams to it
981 * (unnamed data stream and/or reparse point stream, plus any alternate data
982 * streams). This in Win32-specific code.
984 * @inode: WIM inode for this file or directory.
985 * @path: UTF-16LE external path to extract the inode to.
987 * Returns 0 on success; nonzero on failure.
989 static int win32_extract_streams(const struct wim_inode *inode,
990 const wchar_t *path, u64 *completed_bytes_p)
992 struct wim_lookup_table_entry *unnamed_lte;
995 unnamed_lte = inode_unnamed_lte_resolved(inode);
996 ret = win32_extract_stream(inode, path, NULL, unnamed_lte);
1000 *completed_bytes_p += wim_resource_size(unnamed_lte);
1001 for (u16 i = 0; i < inode->i_num_ads; i++) {
1002 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
1003 if (ads_entry->stream_name_len != 0) {
1004 /* Skip special UNIX data entries (see documentation for
1005 * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
1006 if (ads_entry->stream_name_len == WIMLIB_UNIX_DATA_TAG_LEN
1007 && !memcmp(ads_entry->stream_name_utf8,
1008 WIMLIB_UNIX_DATA_TAG,
1009 WIMLIB_UNIX_DATA_TAG_LEN))
1011 ret = win32_extract_stream(inode,
1013 (const wchar_t*)ads_entry->stream_name,
1018 *completed_bytes_p += wim_resource_size(ads_entry->lte);
1026 * Sets the security descriptor on an extracted file. This is Win32-specific
1029 * @inode: The WIM inode that was extracted and has a security descriptor.
1030 * @path: UTF-16LE external path that the inode was extracted to.
1031 * @sd: Security data for the WIM image.
1033 * Returns 0 on success; nonzero on failure.
1035 static int win32_set_security_data(const struct wim_inode *inode,
1036 const wchar_t *path,
1037 const struct wim_security_data *sd)
1039 SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION |
1040 SACL_SECURITY_INFORMATION |
1041 OWNER_SECURITY_INFORMATION |
1042 GROUP_SECURITY_INFORMATION;
1043 if (!SetFileSecurityW(path, securityInformation,
1044 (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id]))
1046 DWORD err = GetLastError();
1047 ERROR("Can't set security descriptor on \"%ls\"", path);
1049 return WIMLIB_ERR_WRITE;
1054 /* Extract a file, directory, reparse point, or hard link to an
1055 * already-extracted file using the Win32 API */
1056 int win32_do_apply_dentry(const char *output_path,
1057 size_t output_path_len,
1058 struct wim_dentry *dentry,
1059 struct apply_args *args)
1062 size_t utf16_path_len;
1065 struct wim_inode *inode = dentry->d_inode;
1067 ret = utf8_to_utf16(output_path, output_path_len,
1068 &utf16_path, &utf16_path_len);
1072 if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
1073 /* Linked file, with another name already extracted. Create a
1075 DEBUG("Creating hard link \"%ls => %ls\"",
1076 (const wchar_t*)utf16_path,
1077 (const wchar_t*)inode->i_extracted_file);
1078 if (!CreateHardLinkW((const wchar_t*)utf16_path,
1079 (const wchar_t*)inode->i_extracted_file,
1082 err = GetLastError();
1083 ERROR("Can't create hard link \"%ls => %ls\"",
1084 (const wchar_t*)utf16_path,
1085 (const wchar_t*)inode->i_extracted_file);
1086 ret = WIMLIB_ERR_LINK;
1090 /* Create the file, directory, or reparse point, and extract the
1092 ret = win32_extract_streams(inode, (const wchar_t*)utf16_path,
1093 &args->progress.extract.completed_bytes);
1095 goto out_free_utf16_path;
1097 /* Set security descriptor if present */
1098 if (inode->i_security_id != -1) {
1099 DEBUG("Setting security descriptor %d on %s",
1100 inode->i_security_id, output_path);
1101 ret = win32_set_security_data(inode,
1102 (const wchar_t*)utf16_path,
1103 wim_const_security_data(args->w));
1105 goto out_free_utf16_path;
1107 if (inode->i_nlink > 1) {
1108 /* Save extracted path for a later call to
1109 * CreateHardLinkW() if this inode has multiple links.
1111 inode->i_extracted_file = utf16_path;
1115 out_free_utf16_path:
1121 /* Set timestamps on an extracted file using the Win32 API */
1122 int win32_do_apply_dentry_timestamps(const char *output_path,
1123 size_t output_path_len,
1124 const struct wim_dentry *dentry,
1125 const struct apply_args *args)
1129 size_t utf16_path_len;
1133 const struct wim_inode *inode = dentry->d_inode;
1135 ret = utf8_to_utf16(output_path, output_path_len,
1136 &utf16_path, &utf16_path_len);
1140 DEBUG("Opening \"%s\" to set timestamps", output_path);
1141 h = CreateFileW((const wchar_t*)utf16_path,
1142 GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY,
1146 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1149 if (h == INVALID_HANDLE_VALUE)
1150 err = GetLastError();
1152 if (h == INVALID_HANDLE_VALUE)
1155 FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff,
1156 .dwHighDateTime = inode->i_creation_time >> 32};
1157 FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff,
1158 .dwHighDateTime = inode->i_last_access_time >> 32};
1159 FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff,
1160 .dwHighDateTime = inode->i_last_write_time >> 32};
1162 DEBUG("Calling SetFileTime() on \"%s\"", output_path);
1163 if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
1164 err = GetLastError();
1168 DEBUG("Closing \"%s\"", output_path);
1169 if (!CloseHandle(h)) {
1170 err = GetLastError();
1175 /* Only warn if setting timestamps failed. */
1176 WARNING("Can't set timestamps on \"%s\"", output_path);
1182 /* Replacement for POSIX fsync() */
1185 HANDLE h = (HANDLE)_get_osfhandle(fd);
1186 if (h == INVALID_HANDLE_VALUE) {
1190 if (!FlushFileBuffers(h)) {
1197 /* Use the Win32 API to get the number of processors */
1198 unsigned win32_get_number_of_processors()
1200 SYSTEM_INFO sysinfo;
1201 GetSystemInfo(&sysinfo);
1202 return sysinfo.dwNumberOfProcessors;
1205 /* Replacement for POSIX-2008 realpath(). Warning: partial functionality only
1206 * (resolved_path must be NULL). Also I highly doubt that GetFullPathName
1207 * really does the right thing under all circumstances. */
1209 realpath(const mbchar *path, mbchar *resolved_path)
1212 wimlib_assert(resolved_path == NULL);
1214 ret = GetFullPathNameA(path, 0, NULL, NULL);
1218 resolved_path = MALLOC(ret + 1);
1221 ret = GetFullPathNameA(path, ret, resolved_path, NULL);
1223 free(resolved_path);
1226 return resolved_path;
1228 win32_error(GetLastError());