From 8c26ca707e56d9848e52076ad3f7c26ea7fa338d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 17 Mar 2013 16:31:53 -0500 Subject: [PATCH] re-organize win32 code --- Makefile.am | 15 +- NEWS | 2 +- README.WINDOWS | 52 +- configure.ac | 33 +- src/add_image.c | 657 ++---------------------- src/extract_image.c | 542 +++----------------- src/lookup_table.c | 4 +- src/mount_image.c | 2 +- src/resource.c | 14 +- src/security.c | 6 +- src/security.h | 2 +- src/wimlib_internal.h | 21 +- src/win32.c | 1110 ++++++++++++++++++++++++++++++++++++++++- src/win32.h | 53 ++ src/write.c | 36 +- 15 files changed, 1366 insertions(+), 1183 deletions(-) create mode 100644 src/win32.h diff --git a/Makefile.am b/Makefile.am index 5dc694b7..7700f46a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,13 @@ ACLOCAL_AMFLAGS = -I m4 -AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CPPFLAGS = -I$(top_srcdir)/src $(WINDOWS_CPPFLAGS) AM_CFLAGS = -std=gnu99 -D_LARGEFILE_SOURCE \ -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE lib_LTLIBRARIES = libwim.la -libwim_la_LDFLAGS = -version-info 4:0:2 $(WINDOWS_EXTRA_LDFLAGS) +libwim_la_LDFLAGS = -version-info 4:0:2 $(WINDOWS_LDFLAGS) libwim_la_SOURCES = \ src/add_image.c \ @@ -53,7 +53,6 @@ libwim_la_SOURCES = \ src/wim.c \ src/wimlib.h \ src/wimlib_internal.h \ - src/win32.c \ src/write.c \ src/xml.c \ src/xml.h \ @@ -99,8 +98,9 @@ bin_PROGRAMS = imagex imagex_SOURCES = programs/imagex.c imagex_LDADD = $(top_builddir)/libwim.la -if WINDOWS_BUILD +if WINDOWS_NATIVE_BUILD imagex_SOURCES += programs/imagex-win32.c programs/imagex-win32.h +libwim_la_SOURCES += src/win32.c endif dist_bin_SCRIPTS = programs/mkwinpeimg @@ -160,13 +160,10 @@ if WITH_NTFS_3G dist_check_SCRIPTS += tests/test-imagex-ntfs endif -if WINDOWS_BUILD +if WINDOWS_NATIVE_BUILD +# TODO: The tests need to be re-written for Windows builds. TESTS = else -# TODO: The tests need to be re-written for Windows builds. One issue (that -# applies to both test-imagex and test-imagex-capture_and_apply) is that -# Cygwin's 'ln -s' will create some sort of regular file with special contents -# rather than a reparse point. TESTS = $(dist_check_SCRIPTS) endif diff --git a/NEWS b/NEWS index 31a63bc1..5c657558 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ Only the most important changes more recent than version 0.6 are noted here. Version 1.3.0: - Experimental support for Windows builds of wimlib has been added. + Added experimental support for native Windows builds. --source-list option added to `imagex capture' and `imagex append'. diff --git a/README.WINDOWS b/README.WINDOWS index 952cbd85..45b7ad56 100644 --- a/README.WINDOWS +++ b/README.WINDOWS @@ -1,23 +1,18 @@ -wimlib 1.3.0 has added experimental support for Windows builds. These builds -include both the "wimlib" library (built as a DLL) and the "imagex" executable. + INTRODUCTION -The Windows builds use native Win32 calls when appropriate to handle alternate -data streams, security descriptors, and reparse points. +wimlib 1.3.0 has added experimental support for Windows builds. The Windows +build consists of both the "wimlib" library (which can be built as a DLL) and +the "imagex" executable. -Windows support currently has the following limitations: +The Windows build of wimlib uses native Win32 calls when appropriate to handle +alternate data streams, security descriptors, and reparse points. -- It relies on the Cygwin UNIX-compatibility layer. You do not, however, need - to have the Cygwin distribution installed to run it, as I have posted a ZIP - file on SourceForge that contains the build of wimlib and "imagex" along with - the DLLs needed for it to run. Please note that these DLLs are free and open - source software; see http://www.cygwin.com/ for more details. +Mounting WIM files is not supported on Windows. Also please note that wimlib's +"imagex" is NOT intended to be command-line compatible with Microsoft's +"imagex", and wimlib is NOT intended to be API compatible with Microsoft's +WIMGAPI. They are similar, though. -- Mounting WIM files is not supported. - -- wimlib's API is not compatible with Microsoft's WIMGAPI, although they offer - some of the same functionality. - -So, in terms of the "imagex" program: + NOTES ABOUT IMAGEX "imagex capture", "imagex append", and "imagex apply" will work on Windows and have the added advantage of saving and restoring alternate data streams, @@ -29,11 +24,32 @@ way on Windows as on UNIX. "imagex mount", "imagex mountrw", and "imagex unmount" will NOT work on Windows. + So on Windows, why would you want to use wimlib's ImageX instead of Microsoft's? Well, here are a few reasons: -- wimlib can be freely distributed; there is no need to download a 1.8 gigabyte - "Windows Automated Installation Kit". - wimlib offers fast multithreaded compression, so making WIM images can be much faster. + +- wimlib can correctly save and restore alternate data streams, which + Microsoft's ImageX sometimes captures incorrectly due to a bug. + - wimlib is free software, so you can modify and/or audit the source code. + +See the man page for 'imagex' for more information. + + BUILDING ON WINDOWS + +Actually doing the Windows build is a bit tricky, and I'd recommend you download +precompiled binaries from http://sourceforge.net/projects/wimlib/files/ instead. +I did it using MinGW-w64 on a Linux host, with the following configuration +command: + +$ ./configure --host=i686-w64-mingw32 \ + CPPFLAGS=-I/opt/libxml2-min/i686-w64-mingw32/include/libxml2 \ + LDFLAGS=-L/opt/libxml2-min/i686-w64-mingw32/lib + +Building wimlib using Cygwin is not supported. I was trying this for a while, +but I ran into some issues with mixing native Win32 functions and +Cygwin-provided functions, so I just made it possible to do a native Win32 build +instead. diff --git a/configure.ac b/configure.ac index ad04b478..1326258c 100644 --- a/configure.ac +++ b/configure.ac @@ -163,40 +163,41 @@ else fi AC_SUBST([PTHREAD_LDADD], [$PTHREAD_LDADD]) +WITH_NTFS_3G_DEFAULT="yes" +WITH_FUSE_DEFAULT="yes" +WINDOWS_NATIVE_BUILD="no" +VISIBILITY_CFLAGS="-fvisibility=hidden" +WINDOWS_CPPFLAGS="" +WINDOWS_LDFLAGS="" +WINDOWS_LDADD="" + case "$host" in *-*-mingw*) # Native Windows - WINDOWS_EXTRA_LDFLAGS="-no-undefined" - VISIBILITY_CFLAGS="" WITH_NTFS_3G_DEFAULT="no" WITH_FUSE_DEFAULT="no" - WINDOWS_BUILD="yes" + WINDOWS_NATIVE_BUILD="yes" + VISIBILITY_CFLAGS="" + WINDOWS_CPPFLAGS="-D_POSIX" + WINDOWS_LDFLAGS="-no-undefined" WINDOWS_LDADD="-lshlwapi" ;; *-*-cygwin*) # Cygwin (WARNING: not well supported) - WINDOWS_EXTRA_LDFLAGS="-no-undefined" - VISIBILITY_CFLAGS="" WITH_NTFS_3G_DEFAULT="no" WITH_FUSE_DEFAULT="no" - WINDOWS_BUILD="yes" - WINDOWS_LDADD="" + VISIBILITY_CFLAGS="" ;; *) # UNIX / other - WINDOWS_EXTRA_LDFLAGS="" - VISIBILITY_CFLAGS="-fvisibility=hidden" - WITH_NTFS_3G_DEFAULT="yes" - WITH_FUSE_DEFAULT="yes" - WINDOWS_BUILD="no" - WINDOWS_LDADD="" ;; esac -AC_SUBST([WINDOWS_EXTRA_LDFLAGS], [$WINDOWS_EXTRA_LDFLAGS]) AC_SUBST([VISIBILITY_CFLAGS], [$VISIBILITY_CFLAGS]) +AC_SUBST([WINDOWS_LDFLAGS], [$WINDOWS_LDFLAGS]) AC_SUBST([WINDOWS_LDADD], [$WINDOWS_LDADD]) -AM_CONDITIONAL([WINDOWS_BUILD], [test "x$WINDOWS_BUILD" = "xyes"]) +AC_SUBST([WINDOWS_CPPFLAGS], [$WINDOWS_CPPFLAGS]) +AM_CONDITIONAL([WINDOWS_NATIVE_BUILD], [test "x$WINDOWS_NATIVE_BUILD" = "xyes"]) AC_MSG_CHECKING([whether to include support for ntfs-3g]) AC_ARG_WITH([ntfs-3g], @@ -236,7 +237,7 @@ else LIBNTFS_3G_LDADD= LIBNTFS_3G_CFLAGS= - if test "x$WINDOWS_BUILD" != "xyes"; then + if test "x$WINDOWS_NATIVE_BUILD" != "xyes"; then AM_ICONV if test "x$am_cv_func_iconv" != "xyes"; then AC_MSG_ERROR([Cannot find the iconv() function. diff --git a/src/add_image.c b/src/add_image.c index a57541f9..8af2b072 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -23,35 +23,22 @@ #include "config.h" -#if defined(__CYGWIN__) || defined(__WIN32__) -# include -# include -# include -# ifdef ERROR -# undef ERROR -# endif -# include "security.h" -#else -# include -# include -# include "timestamp.h" -#endif - #ifdef __WIN32__ -#include +# include "win32.h" +#else +# include +# include +# include +# include "timestamp.h" #endif #include "wimlib_internal.h" #include "dentry.h" #include "lookup_table.h" #include "xml.h" + #include #include - -#ifndef __WIN32__ -#include -#endif - #include #include #include @@ -60,9 +47,6 @@ #include #endif -#define WIMLIB_ADD_IMAGE_FLAG_ROOT 0x80000000 -#define WIMLIB_ADD_IMAGE_FLAG_SOURCE 0x40000000 - /* * Adds the dentry tree and security data for a new image to the image metadata * array of the WIMStruct. @@ -114,439 +98,7 @@ err: } -#if defined(__CYGWIN__) || defined(__WIN32__) - -static u64 FILETIME_to_u64(const FILETIME *ft) -{ - return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime; -} - - -static int build_dentry_tree(struct wim_dentry **root_ret, - const char *root_disk_path, - struct wim_lookup_table *lookup_table, - struct wim_security_data *sd, - const struct capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func, - void *extra_arg); - -static int win32_get_short_name(struct wim_dentry *dentry, - const wchar_t *path_utf16) -{ - WIN32_FIND_DATAW dat; - if (FindFirstFileW(path_utf16, &dat) && - dat.cAlternateFileName[0] != L'\0') - { - size_t short_name_len = wcslen(dat.cAlternateFileName) * 2; - size_t n = short_name_len + sizeof(wchar_t); - dentry->short_name = MALLOC(n); - if (!dentry->short_name) - return WIMLIB_ERR_NOMEM; - memcpy(dentry->short_name, dat.cAlternateFileName, n); - dentry->short_name_len = short_name_len; - } - return 0; -} - -static int win32_get_security_descriptor(struct wim_dentry *dentry, - struct sd_set *sd_set, - const wchar_t *path_utf16) -{ - SECURITY_INFORMATION requestedInformation; - DWORD lenNeeded = 0; - BOOL status; - DWORD err; - - requestedInformation = DACL_SECURITY_INFORMATION | - SACL_SECURITY_INFORMATION | - OWNER_SECURITY_INFORMATION | - GROUP_SECURITY_INFORMATION; - /* Request length of security descriptor */ - status = GetFileSecurityW(path_utf16, requestedInformation, - NULL, 0, &lenNeeded); - err = GetLastError(); - if (!status && err == ERROR_INSUFFICIENT_BUFFER) { - DWORD len = lenNeeded; - char buf[len]; - if (GetFileSecurityW(path_utf16, requestedInformation, - (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded)) - { - int security_id = sd_set_add_sd(sd_set, buf, len); - if (security_id < 0) - return WIMLIB_ERR_NOMEM; - else { - dentry->d_inode->i_security_id = security_id; - return 0; - } - } else { - err = GetLastError(); - } - } - ERROR("Win32 API: Failed to read security descriptor of \"%ls\"", - path_utf16); - win32_error(err); - return WIMLIB_ERR_READ; -} - -/* Reads the directory entries of directory using a Win32 API and recursively - * calls build_dentry_tree() on them. */ -static int win32_recurse_directory(struct wim_dentry *root, - const char *root_disk_path, - struct wim_lookup_table *lookup_table, - struct wim_security_data *sd, - const struct capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func, - struct sd_set *sd_set, - const wchar_t *path_utf16, - size_t path_utf16_nchars) -{ - WIN32_FIND_DATAW dat; - HANDLE hFind; - DWORD err; - int ret; - - { - /* Begin reading the directory by calling FindFirstFileW. - * Unlike UNIX opendir(), FindFirstFileW has file globbing built - * into it. But this isn't what we actually want, so just add a - * dummy glob to get all entries. */ - wchar_t pattern_buf[path_utf16_nchars + 3]; - memcpy(pattern_buf, path_utf16, - path_utf16_nchars * sizeof(wchar_t)); - pattern_buf[path_utf16_nchars] = L'/'; - pattern_buf[path_utf16_nchars + 1] = L'*'; - pattern_buf[path_utf16_nchars + 2] = L'\0'; - hFind = FindFirstFileW(pattern_buf, &dat); - } - if (hFind == INVALID_HANDLE_VALUE) { - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) { - return 0; - } else { - ERROR("Win32 API: Failed to read directory \"%s\"", - root_disk_path); - win32_error(err); - return WIMLIB_ERR_READ; - } - } - ret = 0; - do { - /* Skip . and .. entries */ - if (!(dat.cFileName[0] == L'.' && - (dat.cFileName[1] == L'\0' || - (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0')))) - { - struct wim_dentry *child; - - char *utf8_name; - size_t utf8_name_nbytes; - ret = utf16_to_utf8((const char*)dat.cFileName, - wcslen(dat.cFileName) * sizeof(wchar_t), - &utf8_name, - &utf8_name_nbytes); - if (ret) - goto out_find_close; - - char name[strlen(root_disk_path) + 1 + utf8_name_nbytes + 1]; - sprintf(name, "%s/%s", root_disk_path, utf8_name); - FREE(utf8_name); - ret = build_dentry_tree(&child, name, lookup_table, - sd, config, add_image_flags, - progress_func, sd_set); - if (ret) - goto out_find_close; - if (child) - dentry_add_child(root, child); - } - } while (FindNextFileW(hFind, &dat)); - err = GetLastError(); - if (err != ERROR_NO_MORE_FILES) { - ERROR("Win32 API: Failed to read directory \"%s\"", root_disk_path); - win32_error(err); - if (ret == 0) - ret = WIMLIB_ERR_READ; - } -out_find_close: - FindClose(hFind); - return ret; -} - -/* Load a reparse point into a WIM inode. It is just stored in memory. - * - * @hFile: Open handle to a reparse point, with permission to read the reparse - * data. - * - * @inode: WIM inode for the reparse point. - * - * @lookup_table: Stream lookup table for the WIM; an entry will be added to it - * for the reparse point unless an entry already exists for - * the exact same data stream. - * - * @path: External path to the parse point (UTF-8). Used for error messages - * only. - * - * Returns 0 on success; nonzero on failure. */ -static int win32_capture_reparse_point(HANDLE hFile, - struct wim_inode *inode, - struct wim_lookup_table *lookup_table, - const char *path) -{ - /* "Reparse point data, including the tag and optional GUID, - * cannot exceed 16 kilobytes." - MSDN */ - char reparse_point_buf[16 * 1024]; - DWORD bytesReturned; - - if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, - NULL, 0, reparse_point_buf, - sizeof(reparse_point_buf), &bytesReturned, NULL)) - { - DWORD err = GetLastError(); - ERROR("Win32 API: Failed to get reparse data of \"%s\"", path); - win32_error(err); - return WIMLIB_ERR_READ; - } - if (bytesReturned < 8) { - ERROR("Reparse data on \"%s\" is invalid", path); - return WIMLIB_ERR_READ; - } - inode->i_reparse_tag = *(u32*)reparse_point_buf; - return inode_add_ads_with_data(inode, "", - (const u8*)reparse_point_buf + 8, - bytesReturned - 8, lookup_table); -} - -/* Calculate the SHA1 message digest of a Win32 data stream, which may be either - * an unnamed or named data stream. - * - * @path: Path to the file, with the stream noted at the end for named - * streams. UTF-16LE encoding. - * - * @hash: On success, the SHA1 message digest of the stream is written to - * this location. - * - * Returns 0 on success; nonzero on failure. - */ -static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE]) -{ - HANDLE hFile; - SHA_CTX ctx; - u8 buf[32768]; - DWORD bytesRead; - int ret; - - hFile = win32_open_file_readonly(path); - if (hFile == INVALID_HANDLE_VALUE) - return WIMLIB_ERR_OPEN; - - sha1_init(&ctx); - for (;;) { - if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) { - ret = WIMLIB_ERR_READ; - goto out_close_handle; - } - if (bytesRead == 0) - break; - sha1_update(&ctx, buf, bytesRead); - } - ret = 0; - sha1_final(hash, &ctx); -out_close_handle: - CloseHandle(hFile); - return ret; -} - -/* Scans an unnamed or named stream of a Win32 file (not a reparse point - * stream); calculates its SHA1 message digest and either creates a `struct - * wim_lookup_table_entry' in memory for it, or uses an existing 'struct - * wim_lookup_table_entry' for an identical stream. - * - * @path_utf16: Path to the file (UTF-16LE). - * - * @path_utf16_nchars: Number of 2-byte characters in @path_utf16. - * - * @inode: WIM inode to save the stream into. - * - * @lookup_table: Stream lookup table for the WIM. - * - * @dat: A `WIN32_FIND_STREAM_DATA' structure that specifies the - * stream name. - * - * Returns 0 on success; nonzero on failure. - */ -static int win32_capture_stream(const wchar_t *path_utf16, - size_t path_utf16_nchars, - struct wim_inode *inode, - struct wim_lookup_table *lookup_table, - WIN32_FIND_STREAM_DATA *dat) -{ - struct wim_ads_entry *ads_entry; - u8 hash[SHA1_HASH_SIZE]; - struct wim_lookup_table_entry *lte; - int ret; - wchar_t *p, *colon; - bool is_named_stream; - wchar_t *spath; - size_t spath_nchars; - DWORD err; - - /* The stream name should be returned as :NAME:TYPE */ - p = dat->cStreamName; - if (*p != L':') - goto out_invalid_stream_name; - p += 1; - colon = wcschr(p, L':'); - if (colon == NULL) - goto out_invalid_stream_name; - - if (wcscmp(colon + 1, L"$DATA")) { - /* Not a DATA stream */ - ret = 0; - goto out; - } - - is_named_stream = (p != colon); - if (is_named_stream) { - /* Allocate an ADS entry for the named stream. */ - char *utf8_stream_name; - size_t utf8_stream_name_len; - ret = utf16_to_utf8((const char *)p, - (colon - p) * sizeof(wchar_t), - &utf8_stream_name, - &utf8_stream_name_len); - if (ret) - goto out; - ads_entry = inode_add_ads(inode, utf8_stream_name); - FREE(utf8_stream_name); - if (!ads_entry) { - ret = WIMLIB_ERR_NOMEM; - goto out; - } - } - - /* Create a UTF-16 string @spath that gives the filename, then a colon, - * then the stream name. Or, if it's an unnamed stream, just the - * filename. It is MALLOC()'ed so that it can be saved in the - * wim_lookup_table_entry if needed. */ - *colon = '\0'; - spath_nchars = path_utf16_nchars; - if (is_named_stream) - spath_nchars += colon - p + 1; - - spath = MALLOC((spath_nchars + 1) * sizeof(wchar_t)); - memcpy(spath, path_utf16, path_utf16_nchars * sizeof(wchar_t)); - if (is_named_stream) { - spath[path_utf16_nchars] = L':'; - memcpy(&spath[path_utf16_nchars + 1], p, (colon - p) * sizeof(wchar_t)); - } - spath[spath_nchars] = L'\0'; - - ret = win32_sha1sum(spath, hash); - if (ret) { - err = GetLastError(); - ERROR("Win32 API: Failed to read \"%ls\" to calculate SHA1sum", - path_utf16); - win32_error(err); - goto out_free_spath; - } - - lte = __lookup_resource(lookup_table, hash); - if (lte) { - /* Use existing wim_lookup_table_entry that has the same SHA1 - * message digest */ - lte->refcnt++; - } else { - /* Make a new wim_lookup_table_entry */ - lte = new_lookup_table_entry(); - if (!lte) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_spath; - } - lte->file_on_disk = (char*)spath; - spath = NULL; - lte->resource_location = RESOURCE_WIN32; - lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart; - lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart; - copy_hash(lte->hash, hash); - lookup_table_insert(lookup_table, lte); - } - if (is_named_stream) - ads_entry->lte = lte; - else - inode->i_lte = lte; -out_free_spath: - FREE(spath); -out: - return ret; -out_invalid_stream_name: - ERROR("Invalid stream name: \"%ls:%ls\"", path_utf16, dat->cStreamName); - ret = WIMLIB_ERR_READ; - goto out; -} - -/* Scans a Win32 file for unnamed and named data streams (not reparse point - * streams). - * - * @path_utf16: Path to the file (UTF-16LE). - * - * @path_utf16_nchars: Number of 2-byte characters in @path_utf16. - * - * @inode: WIM inode to save the stream into. - * - * @lookup_table: Stream lookup table for the WIM. - * - * Returns 0 on success; nonzero on failure. - */ -static int win32_capture_streams(const wchar_t *path_utf16, - size_t path_utf16_nchars, - struct wim_inode *inode, - struct wim_lookup_table *lookup_table) -{ - WIN32_FIND_STREAM_DATA dat; - int ret; - HANDLE hFind; - DWORD err; - - hFind = FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0); - if (hFind == INVALID_HANDLE_VALUE) { - err = GetLastError(); - - /* Seems legal for this to return ERROR_HANDLE_EOF on reparse - * points and directories */ - if ((inode->i_attributes & - (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) - && err == ERROR_HANDLE_EOF) - { - return 0; - } else { - ERROR("Win32 API: Failed to look up data streams of \"%ls\"", - path_utf16); - win32_error(err); - return WIMLIB_ERR_READ; - } - } - do { - ret = win32_capture_stream(path_utf16, - path_utf16_nchars, - inode, lookup_table, - &dat); - if (ret) - goto out_find_close; - } while (FindNextStreamW(hFind, &dat)); - err = GetLastError(); - if (err != ERROR_HANDLE_EOF) { - ERROR("Win32 API: Error reading data streams from \"%ls\"", path_utf16); - win32_error(err); - ret = WIMLIB_ERR_READ; - } -out_find_close: - FindClose(hFind); - return ret; -} - -#endif - +#ifndef __WIN32__ /* * build_dentry_tree(): * Recursively builds a tree of WIM dentries from an on-disk directory @@ -581,14 +133,14 @@ out_find_close: * the on-disk files during a call to wimlib_write() or * wimlib_overwrite(). */ -static int build_dentry_tree(struct wim_dentry **root_ret, - const char *root_disk_path, - struct wim_lookup_table *lookup_table, - struct wim_security_data *sd, - const struct capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func, - void *extra_arg) +static int unix_build_dentry_tree(struct wim_dentry **root_ret, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + void *extra_arg) { struct wim_dentry *root = NULL; int ret = 0; @@ -620,7 +172,6 @@ static int build_dentry_tree(struct wim_dentry **root_ret, progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); } -#if !defined(__CYGWIN__) && !defined(__WIN32__) /* UNIX version of capturing a directory tree */ struct stat root_stbuf; int (*stat_fn)(const char *restrict, struct stat *restrict); @@ -789,9 +340,11 @@ static int build_dentry_tree(struct wim_dentry **root_ret, || (result->d_name[1] == '.' && result->d_name[2] == '\0'))) continue; strcpy(name + len + 1, result->d_name); - ret = build_dentry_tree(&child, name, lookup_table, - NULL, config, add_image_flags, - progress_func, NULL); + ret = unix_build_dentry_tree(&child, name, + lookup_table, + NULL, config, + add_image_flags, + progress_func, NULL); if (ret != 0) break; if (child) @@ -841,126 +394,6 @@ static int build_dentry_tree(struct wim_dentry **root_ret, ret = WIMLIB_ERR_READLINK; } } -#else - /* Win32 version of capturing a directory tree */ - - wchar_t *path_utf16; - size_t path_utf16_nchars; - struct sd_set *sd_set; - DWORD err; - - if (extra_arg == NULL) { - sd_set = alloca(sizeof(struct sd_set)); - sd_set->rb_root.rb_node = NULL, - sd_set->sd = sd; - } else { - sd_set = extra_arg; - } - - ret = utf8_to_utf16(root_disk_path, strlen(root_disk_path), - (char**)&path_utf16, &path_utf16_nchars); - if (ret) - goto out_destroy_sd_set; - path_utf16_nchars /= sizeof(wchar_t); - - HANDLE hFile = win32_open_file_readonly(path_utf16); - if (hFile == INVALID_HANDLE_VALUE) { - err = GetLastError(); - ERROR("Win32 API: Failed to open \"%s\"", root_disk_path); - win32_error(err); - ret = WIMLIB_ERR_OPEN; - goto out_free_path_utf16; - } - - BY_HANDLE_FILE_INFORMATION file_info; - if (!GetFileInformationByHandle(hFile, &file_info)) { - err = GetLastError(); - ERROR("Win32 API: Failed to get file information for \"%s\"", - root_disk_path); - win32_error(err); - ret = WIMLIB_ERR_STAT; - goto out_close_handle; - } - - /* Create a WIM dentry */ - root = new_dentry_with_timeless_inode(path_basename(root_disk_path)); - if (!root) { - if (errno == EILSEQ) - ret = WIMLIB_ERR_INVALID_UTF8_STRING; - else if (errno == ENOMEM) - ret = WIMLIB_ERR_NOMEM; - else - ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE; - goto out_close_handle; - } - - /* Start preparing the associated WIM inode */ - inode = root->d_inode; - - inode->i_attributes = file_info.dwFileAttributes; - inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime); - inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime); - inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime); - inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) | - (u64)file_info.nFileIndexLow; - - inode->i_resolved = 1; - add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); - - /* Get DOS name and security descriptor (if any). */ - ret = win32_get_short_name(root, path_utf16); - if (ret) - goto out_close_handle; - ret = win32_get_security_descriptor(root, sd_set, path_utf16); - if (ret) - goto out_close_handle; - - if (inode_is_directory(inode)) { - /* Directory (not a reparse point) --- recurse to children */ - - /* But first... directories may have alternate data streams that - * need to be captured. */ - ret = win32_capture_streams(path_utf16, - path_utf16_nchars, - inode, - lookup_table); - if (ret) - goto out_close_handle; - ret = win32_recurse_directory(root, - root_disk_path, - lookup_table, - sd, - config, - add_image_flags, - progress_func, - sd_set, - path_utf16, - path_utf16_nchars); - } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - /* Reparse point: save the reparse tag and data */ - ret = win32_capture_reparse_point(hFile, - inode, - lookup_table, - root_disk_path); - } else { - /* Not a directory, not a reparse point; capture the default - * file contents and any alternate data streams. */ - ret = win32_capture_streams(path_utf16, - path_utf16_nchars, - inode, - lookup_table); - } -out_close_handle: - CloseHandle(hFile); -out_free_path_utf16: - FREE(path_utf16); -out_destroy_sd_set: - if (extra_arg == NULL) - destroy_sd_set(sd_set); -#endif - /* The below lines of code are common to both UNIX and Win32 builds. It - * simply returns the captured directory tree if the capture was - * successful, or frees it if the capture was unsuccessful. */ out: if (ret == 0) *root_ret = root; @@ -968,6 +401,7 @@ out: free_dentry_tree(root, lookup_table); return ret; } +#endif /* !__WIN32__ */ enum pattern_type { NONE = 0, @@ -1146,19 +580,6 @@ static int capture_config_set_prefix(struct capture_config *config, return 0; } -static bool path_matches_pattern(const char *path, const char *pattern) -{ -#ifdef __WIN32__ - return PathMatchSpecA(path, pattern); -#else - return fnmatch(pattern, path, FNM_PATHNAME - #ifdef FNM_CASEFOLD - | FNM_CASEFOLD - #endif - ) == 0; -#endif -} - static bool match_pattern(const char *path, const char *path_basename, const struct pattern_list *list) { @@ -1177,7 +598,12 @@ static bool match_pattern(const char *path, const char *path_basename, string = path_basename; } - if (path_matches_pattern(string, pat)) { + if (fnmatch(pat, string, FNM_PATHNAME + #ifdef FNM_CASEFOLD + | FNM_CASEFOLD + #endif + ) == 0) + { DEBUG("`%s' matches the pattern \"%s\"", string, pat); return true; @@ -1232,7 +658,7 @@ static const char *canonicalize_target_path(char *target_path) return target_path; } -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ static void zap_backslashes(char *s) { while (*s) { @@ -1251,7 +677,7 @@ static void canonicalize_targets(struct wimlib_capture_source *sources, DEBUG("Canonicalizing { source: \"%s\", target=\"%s\"}", sources->fs_source_path, sources->wim_target_path); -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ /* The Windows API can handle forward slashes. Just get rid of * backslashes to avoid confusing other parts of the library * code. */ @@ -1504,11 +930,15 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, return WIMLIB_ERR_UNSUPPORTED; #endif } else { - capture_tree = build_dentry_tree; + #ifdef __WIN32__ + capture_tree = win32_build_dentry_tree; + #else + capture_tree = unix_build_dentry_tree; + #endif extra_arg = NULL; } -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) { ERROR("Capturing UNIX-specific data is not supported on Windows"); return WIMLIB_ERR_INVALID_PARAM; @@ -1572,11 +1002,10 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, } else { size_t i; -#if defined(__CYGWIN__) || defined(__WIN32__) - win32_acquire_privilege(SE_BACKUP_NAME); - win32_acquire_privilege(SE_SECURITY_NAME); - win32_acquire_privilege(SE_TAKE_OWNERSHIP_NAME); -#endif + #ifdef __WIN32__ + win32_acquire_capture_privileges(); + #endif + root_dentry = NULL; i = 0; do { @@ -1670,10 +1099,8 @@ out_free_security_data: out_destroy_capture_config: destroy_capture_config(&config); out: -#if defined(__CYGWIN__) || defined(__WIN32__) - win32_release_privilege(SE_BACKUP_NAME); - win32_release_privilege(SE_SECURITY_NAME); - win32_release_privilege(SE_TAKE_OWNERSHIP_NAME); +#ifdef __WIN32__ + win32_release_capture_privileges(); #endif return ret; } diff --git a/src/extract_image.c b/src/extract_image.c index 86063819..7955417f 100644 --- a/src/extract_image.c +++ b/src/extract_image.c @@ -25,19 +25,16 @@ #include "config.h" -#if defined(__CYGWIN__) || defined(__WIN32__) -# include -# ifdef ERROR -# undef ERROR -# endif -# include +#include + +#ifdef __WIN32__ +# include "win32.h" #else -# include -# ifdef HAVE_UTIME_H -# include -# endif -# include "timestamp.h" -# include +# ifdef HAVE_UTIME_H +# include +# endif +# include "timestamp.h" +# include #endif #include @@ -54,304 +51,14 @@ #include "xml.h" #ifdef WITH_NTFS_3G -#include +# include #endif #ifdef HAVE_ALLOCA_H -#include -#endif - -#if defined(__WIN32__) -# define swprintf _snwprintf -# define mkdir(path, mode) (!CreateDirectoryA(path, NULL)) +# include #endif -#if defined(__CYGWIN__) || defined(__WIN32__) - -static int win32_set_reparse_data(HANDLE h, - u32 reparse_tag, - const struct wim_lookup_table_entry *lte, - const wchar_t *path) -{ - int ret; - u8 *buf; - size_t len; - - if (!lte) { - WARNING("\"%ls\" is marked as a reparse point but had no reparse data", - path); - return 0; - } - len = wim_resource_size(lte); - if (len > 16 * 1024 - 8) { - WARNING("\"%ls\": reparse data too long!", path); - return 0; - } - - /* The WIM stream omits the ReparseTag and ReparseDataLength fields, so - * leave 8 bytes of space for them at the beginning of the buffer, then - * set them manually. */ - buf = alloca(len + 8); - ret = read_full_wim_resource(lte, buf + 8, 0); - if (ret) - return ret; - *(u32*)(buf + 0) = reparse_tag; - *(u16*)(buf + 4) = len; - *(u16*)(buf + 6) = 0; - - /* Set the reparse data on the open file using the - * FSCTL_SET_REPARSE_POINT ioctl. - * - * There are contradictions in Microsoft's documentation for this: - * - * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED, - * lpOverlapped is ignored." - * - * --- So setting lpOverlapped to NULL is okay since it's ignored. - * - * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an - * operation returns no output data and lpOutBuffer is NULL, - * DeviceIoControl makes use of lpBytesReturned. After such an - * operation, the value of lpBytesReturned is meaningless." - * - * --- So lpOverlapped not really ignored, as it affects another - * parameter. This is the actual behavior: lpBytesReturned must be - * specified, even though lpBytesReturned is documented as: - * - * "Not used with this operation; set to NULL." - */ - DWORD bytesReturned; - if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, buf, len + 8, - NULL, 0, - &bytesReturned /* lpBytesReturned */, - NULL /* lpOverlapped */)) - { - DWORD err = GetLastError(); - ERROR("Failed to set reparse data on \"%ls\"", path); - win32_error(err); - return WIMLIB_ERR_WRITE; - } - return 0; -} - - -static int win32_extract_chunk(const u8 *buf, size_t len, u64 offset, void *arg) -{ - HANDLE hStream = arg; - - DWORD nbytes_written; - wimlib_assert(len <= 0xffffffff); - - if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) || - nbytes_written != len) - { - DWORD err = GetLastError(); - ERROR("WriteFile(): write error"); - win32_error(err); - return WIMLIB_ERR_WRITE; - } - return 0; -} - -static int do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte) -{ - return extract_wim_resource(lte, wim_resource_size(lte), - win32_extract_chunk, hStream); -} - -static int win32_extract_stream(const struct wim_inode *inode, - const wchar_t *path, - const wchar_t *stream_name_utf16, - struct wim_lookup_table_entry *lte) -{ - wchar_t *stream_path; - HANDLE h; - int ret; - DWORD err; - DWORD creationDisposition = CREATE_ALWAYS; - - if (stream_name_utf16) { - /* Named stream. Create a buffer that contains the UTF-16LE - * string [./]@path:@stream_name_utf16. This is needed to - * create and open the stream using CreateFileW(). I'm not - * aware of any other APIs to do this. Note: the '$DATA' suffix - * seems to be unneeded. Additional note: a "./" prefix needs - * to be added when the path is not absolute to avoid ambiguity - * with drive letters. */ - size_t stream_path_nchars; - size_t path_nchars; - size_t stream_name_nchars; - const wchar_t *prefix; - - path_nchars = wcslen(path); - stream_name_nchars = wcslen(stream_name_utf16); - stream_path_nchars = path_nchars + 1 + stream_name_nchars; - if (path[0] != L'/' && path[0] != L'\\') { - prefix = L"./"; - stream_path_nchars += 2; - } else { - prefix = L""; - } - stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t)); - swprintf(stream_path, stream_path_nchars + 1, L"%ls%ls:%ls", - prefix, path, stream_name_utf16); - } else { - /* Unnamed stream; its path is just the path to the file itself. - * */ - stream_path = (wchar_t*)path; - - /* Directories must be created with CreateDirectoryW(). Then - * the call to CreateFileW() will merely open the directory that - * was already created rather than creating a new file. */ - if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { - if (!CreateDirectoryW(stream_path, NULL)) { - err = GetLastError(); - if (err != ERROR_ALREADY_EXISTS) { - ERROR("Failed to create directory \"%ls\"", - stream_path); - win32_error(err); - ret = WIMLIB_ERR_MKDIR; - goto fail; - } - } - DEBUG("Created directory \"%ls\"", stream_path); - if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - ret = 0; - goto out; - } - creationDisposition = OPEN_EXISTING; - } - } - - DEBUG("Opening \"%ls\"", stream_path); - h = CreateFileW(stream_path, - GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, - 0, - NULL, - creationDisposition, - FILE_FLAG_OPEN_REPARSE_POINT | - FILE_FLAG_BACKUP_SEMANTICS | - inode->i_attributes, - NULL); - if (h == INVALID_HANDLE_VALUE) { - err = GetLastError(); - ERROR("Failed to create \"%ls\"", stream_path); - win32_error(err); - ret = WIMLIB_ERR_OPEN; - goto fail; - } - - if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT && - stream_name_utf16 == NULL) - { - DEBUG("Setting reparse data on \"%ls\"", path); - ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path); - if (ret) - goto fail_close_handle; - } else { - if (lte) { - DEBUG("Extracting \"%ls\" (len = %"PRIu64")", - stream_path, wim_resource_size(lte)); - ret = do_win32_extract_stream(h, lte); - if (ret) - goto fail_close_handle; - } - } - - DEBUG("Closing \"%ls\"", stream_path); - if (!CloseHandle(h)) { - err = GetLastError(); - ERROR("Failed to close \"%ls\"", stream_path); - win32_error(err); - ret = WIMLIB_ERR_WRITE; - goto fail; - } - ret = 0; - goto out; -fail_close_handle: - CloseHandle(h); -fail: - ERROR("Error extracting %ls", stream_path); -out: - return ret; -} - -/* - * Creates a file, directory, or reparse point and extracts all streams to it - * (unnamed data stream and/or reparse point stream, plus any alternate data - * streams). This in Win32-specific code. - * - * @inode: WIM inode for this file or directory. - * @path: UTF-16LE external path to extract the inode to. - * - * Returns 0 on success; nonzero on failure. - */ -static int win32_extract_streams(struct wim_inode *inode, - const wchar_t *path, u64 *completed_bytes_p) -{ - struct wim_lookup_table_entry *unnamed_lte; - int ret; - - unnamed_lte = inode_unnamed_lte_resolved(inode); - ret = win32_extract_stream(inode, path, NULL, unnamed_lte); - if (ret) - goto out; - if (unnamed_lte) - *completed_bytes_p += wim_resource_size(unnamed_lte); - for (u16 i = 0; i < inode->i_num_ads; i++) { - const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i]; - if (ads_entry->stream_name_len != 0) { - /* Skip special UNIX data entries (see documentation for - * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */ - if (ads_entry->stream_name_len == WIMLIB_UNIX_DATA_TAG_LEN - && !memcmp(ads_entry->stream_name_utf8, - WIMLIB_UNIX_DATA_TAG, - WIMLIB_UNIX_DATA_TAG_LEN)) - continue; - ret = win32_extract_stream(inode, - path, - (const wchar_t*)ads_entry->stream_name, - ads_entry->lte); - if (ret) - break; - if (ads_entry->lte) - *completed_bytes_p += wim_resource_size(ads_entry->lte); - } - } -out: - return ret; -} - -/* - * Sets the security descriptor on an extracted file. This is Win32-specific - * code. - * - * @inode: The WIM inode that was extracted and has a security descriptor. - * @path: UTF-16LE external path that the inode was extracted to. - * @sd: Security data for the WIM image. - * - * Returns 0 on success; nonzero on failure. - */ -static int win32_set_security_data(const struct wim_inode *inode, - const wchar_t *path, - const struct wim_security_data *sd) -{ - SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION | - SACL_SECURITY_INFORMATION | - OWNER_SECURITY_INFORMATION | - GROUP_SECURITY_INFORMATION; - if (!SetFileSecurityW(path, securityInformation, - (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id])) - { - DWORD err = GetLastError(); - ERROR("Can't set security descriptor on \"%ls\"", path); - win32_error(err); - return WIMLIB_ERR_WRITE; - } - return 0; -} - -#else /* __CYGWIN__ || __WIN32__ */ +#ifndef __WIN32__ static int extract_regular_file_linked(struct wim_dentry *dentry, const char *output_path, struct apply_args *args, @@ -620,7 +327,7 @@ static int extract_symlink(struct wim_dentry *dentry, return 0; } -#endif /* !(__CYGWIN__ || __WIN32__) */ +#endif /* !__WIN32__ */ static int extract_directory(struct wim_dentry *dentry, const char *output_path, bool is_root) @@ -652,7 +359,7 @@ static int extract_directory(struct wim_dentry *dentry, } dir_exists: ret = 0; -#if !defined(__CYGWIN__) && !defined(__WIN32__) +#ifndef __WIN32__ if (dentry) { struct wimlib_unix_data unix_data; ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL); @@ -667,82 +374,14 @@ dir_exists: return ret; } -/* Extracts a file, directory, or symbolic link from the WIM archive. */ -static int apply_dentry_normal(struct wim_dentry *dentry, void *arg) +#ifndef __WIN32__ +static int unix_apply_dentry(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + const struct apply_args *args) { - struct apply_args *args = arg; - struct wim_inode *inode = dentry->d_inode; - size_t len; - char *output_path; - - len = strlen(args->target); - if (dentry_is_root(dentry)) { - output_path = (char*)args->target; - } else { - output_path = alloca(len + dentry->full_path_utf8_len + 1); - memcpy(output_path, args->target, len); - memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); - output_path[len + dentry->full_path_utf8_len] = '\0'; - len += dentry->full_path_utf8_len; - } - -#if defined(__CYGWIN__) || defined(__WIN32__) - char *utf16_path; - size_t utf16_path_len; - DWORD err; - int ret; - ret = utf8_to_utf16(output_path, len, &utf16_path, &utf16_path_len); - if (ret) - return ret; + const struct wim_inode *inode = dentry->d_inode; - if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) { - /* Linked file, with another name already extracted. Create a - * hard link. */ - DEBUG("Creating hard link \"%ls => %ls\"", - (const wchar_t*)utf16_path, - (const wchar_t*)inode->i_extracted_file); - if (!CreateHardLinkW((const wchar_t*)utf16_path, - (const wchar_t*)inode->i_extracted_file, - NULL)) - { - err = GetLastError(); - ERROR("Can't create hard link \"%ls => %ls\"", - (const wchar_t*)utf16_path, - (const wchar_t*)inode->i_extracted_file); - ret = WIMLIB_ERR_LINK; - win32_error(err); - } - } else { - /* Create the file, directory, or reparse point, and extract the - * data streams. */ - ret = win32_extract_streams(inode, (const wchar_t*)utf16_path, - &args->progress.extract.completed_bytes); - if (ret) - goto out_free_utf16_path; - - /* Set security descriptor if present */ - if (inode->i_security_id != -1) { - DEBUG("Setting security descriptor %d on %s", - inode->i_security_id, output_path); - ret = win32_set_security_data(inode, - (const wchar_t*)utf16_path, - wim_const_security_data(args->w)); - if (ret) - goto out_free_utf16_path; - } - if (inode->i_nlink > 1) { - /* Save extracted path for a later call to - * CreateHardLinkW() if this inode has multiple links. - * */ - inode->i_extracted_file = utf16_path; - goto out; - } - } -out_free_utf16_path: - FREE(utf16_path); -out: - return ret; -#else if (inode_is_symlink(inode)) return extract_symlink(dentry, args, output_path); else if (inode_is_directory(inode)) @@ -751,83 +390,14 @@ out: output_path, false); else return extract_regular_file(dentry, args, output_path); -#endif } -/* Apply timestamps to an extracted file or directory */ -static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg) +static int unix_apply_dentry_timestamps(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + struct apply_args *args) { - struct apply_args *args = arg; - size_t len; - char *output_path; int ret; - const struct wim_inode *inode = dentry->d_inode; - - len = strlen(args->target); - if (dentry_is_root(dentry)) { - output_path = (char*)args->target; - } else { - output_path = alloca(len + dentry->full_path_utf8_len + 1); - memcpy(output_path, args->target, len); - memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); - output_path[len + dentry->full_path_utf8_len] = '\0'; - len += dentry->full_path_utf8_len; - } - -#if defined(__CYGWIN__) || defined(__WIN32__) - /* Win32 */ - char *utf16_path; - size_t utf16_path_len; - DWORD err; - HANDLE h; - - ret = utf8_to_utf16(output_path, len, &utf16_path, &utf16_path_len); - if (ret) - return ret; - - DEBUG("Opening \"%s\" to set timestamps", output_path); - h = CreateFileW((const wchar_t*)utf16_path, - GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - NULL); - - if (h == INVALID_HANDLE_VALUE) - err = GetLastError(); - FREE(utf16_path); - if (h == INVALID_HANDLE_VALUE) - goto fail; - - FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff, - .dwHighDateTime = inode->i_creation_time >> 32}; - FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff, - .dwHighDateTime = inode->i_last_access_time >> 32}; - FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff, - .dwHighDateTime = inode->i_last_write_time >> 32}; - - DEBUG("Calling SetFileTime() on \"%s\"", output_path); - if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) { - err = GetLastError(); - CloseHandle(h); - goto fail; - } - DEBUG("Closing \"%s\"", output_path); - if (!CloseHandle(h)) { - err = GetLastError(); - goto fail; - } - goto out; -fail: - /* Only warn if setting timestamps failed. */ - WARNING("Can't set timestamps on \"%s\"", output_path); - win32_error(err); -out: - return 0; -#else - /* UNIX */ - /* Convert the WIM timestamps, which are accurate to 100 nanoseconds, * into struct timeval's. */ struct timeval tv[2]; @@ -856,6 +426,56 @@ out: } } return 0; +} +#endif /* !__WIN32__ */ + +/* Extracts a file, directory, or symbolic link from the WIM archive. */ +static int apply_dentry_normal(struct wim_dentry *dentry, void *arg) +{ + struct apply_args *args = arg; + struct wim_inode *inode = dentry->d_inode; + size_t len; + char *output_path; + + len = strlen(args->target); + if (dentry_is_root(dentry)) { + output_path = (char*)args->target; + } else { + output_path = alloca(len + dentry->full_path_utf8_len + 1); + memcpy(output_path, args->target, len); + memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); + output_path[len + dentry->full_path_utf8_len] = '\0'; + len += dentry->full_path_utf8_len; + } +#ifdef __WIN32__ + return win32_apply_dentry(output_path, len, dentry, args); +#else + return unix_apply_dentry(output_path, len, dentry, args); +#endif +} + + +/* Apply timestamps to an extracted file or directory */ +static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg) +{ + struct apply_args *args = arg; + size_t len; + char *output_path; + + len = strlen(args->target); + if (dentry_is_root(dentry)) { + output_path = (char*)args->target; + } else { + output_path = alloca(len + dentry->full_path_utf8_len + 1); + memcpy(output_path, args->target, len); + memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); + output_path[len + dentry->full_path_utf8_len] = '\0'; + len += dentry->full_path_utf8_len; + } +#ifdef __WIN32__ + return win32_apply_dentry_timestamps(output_path, len, dentry, args); +#else + return unix_apply_dentry_timestamps(output_path, len, dentry, args); #endif } @@ -1276,7 +896,7 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, == (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) return WIMLIB_ERR_INVALID_PARAM; -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { ERROR("Extracting UNIX data is not supported on Windows"); return WIMLIB_ERR_INVALID_PARAM; @@ -1323,10 +943,8 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, w->lookup_table = joined_tab; } -#if defined(__CYGWIN__) || defined(__WIN32__) - win32_acquire_privilege(SE_RESTORE_NAME); - win32_acquire_privilege(SE_SECURITY_NAME); - win32_acquire_privilege(SE_TAKE_OWNERSHIP_NAME); +#ifdef __WIN32__ + win32_acquire_restore_privileges(); #endif if (image == WIMLIB_ALL_IMAGES) { extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; @@ -1337,10 +955,8 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, ret = extract_single_image(w, image, target, extract_flags, progress_func); } -#if defined(__CYGWIN__) || defined(__WIN32__) - win32_release_privilege(SE_RESTORE_NAME); - win32_release_privilege(SE_SECURITY_NAME); - win32_release_privilege(SE_TAKE_OWNERSHIP_NAME); +#ifdef __WIN32__ + win32_release_restore_privileges(); #endif if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | diff --git a/src/lookup_table.c b/src/lookup_table.c index fee69cbb..ab13e5e1 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -86,7 +86,7 @@ clone_lookup_table_entry(const struct wim_lookup_table_entry *old) switch (new->resource_location) { case RESOURCE_IN_STAGING_FILE: case RESOURCE_IN_FILE_ON_DISK: -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ case RESOURCE_WIN32: #endif BUILD_BUG_ON((void*)&old->file_on_disk != @@ -141,7 +141,7 @@ void free_lookup_table_entry(struct wim_lookup_table_entry *lte) case RESOURCE_IN_STAGING_FILE: case RESOURCE_IN_ATTACHED_BUFFER: case RESOURCE_IN_FILE_ON_DISK: -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ case RESOURCE_WIN32: #endif BUILD_BUG_ON((void*)<e->file_on_disk != diff --git a/src/mount_image.c b/src/mount_image.c index 839acbc8..a17bf502 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -2625,7 +2625,7 @@ out: static inline int mount_unsupported_error() { -#if defined(__CYGWIN__) || defined (__WIN32__) +#if defined(__WIN32__) ERROR("Sorry-- Mounting WIM images is not supported on Windows!"); #else ERROR("wimlib was compiled with --without-fuse, which disables support " diff --git a/src/resource.c b/src/resource.c index 8d617614..db9c635b 100644 --- a/src/resource.c +++ b/src/resource.c @@ -30,16 +30,20 @@ #include "xpress.h" #include "sha1.h" +#ifdef __WIN32__ +# include "win32.h" +#endif + #include #include #include #include #ifdef WITH_NTFS_3G -#include -#include -#include -#include +# include +# include +# include +# include #endif /* @@ -558,7 +562,7 @@ int read_wim_resource(const struct wim_lookup_table_entry *lte, u8 buf[], if (fp != lte->file_on_disk_fp) fclose(fp); break; -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ case RESOURCE_WIN32: wimlib_assert(lte->file_on_disk_fp != NULL); ret = win32_read_file(lte->file_on_disk, lte->file_on_disk_fp, diff --git a/src/security.c b/src/security.c index 9c55ef2d..2e60aa53 100644 --- a/src/security.c +++ b/src/security.c @@ -560,7 +560,9 @@ void free_security_data(struct wim_security_data *sd) } } -#if defined(WITH_NTFS_3G) || defined(__CYGWIN__) || defined(__WIN32__) +/* The security tree stuff is only needed when NTFS capture is supported, either + * through NTFS-3G or through a native Windows build. */ +#if defined(WITH_NTFS_3G) || defined(__WIN32__) struct sd_node { int security_id; u8 hash[SHA1_HASH_SIZE]; @@ -686,4 +688,4 @@ out_free_node: out: return -1; } -#endif /* WITH_NTFS_3G || __CYGWIN__ || __WIN32__ */ +#endif /* WITH_NTFS_3G || __WIN32__ */ diff --git a/src/security.h b/src/security.h index d6d7d60a..4d701d0c 100644 --- a/src/security.h +++ b/src/security.h @@ -12,7 +12,7 @@ #ifndef _WIMLIB_SECURITY_H #define _WIMLIB_SECURITY_H -#if defined(WITH_NTFS_3G) || defined(__CYGWIN__) || defined(__WIN32__) +#if defined(WITH_NTFS_3G) || defined(__WIN32__) /* Red-black tree that maps SHA1 message digests of security descriptors to * security IDs, which are themselves indices into the table of security * descriptors in the 'struct wim_security_data'. */ diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 5a076a5c..3f05d882 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -528,23 +528,6 @@ extern int for_image(WIMStruct *w, int image, int (*visitor)(WIMStruct *)); extern void destroy_image_metadata(struct wim_image_metadata *imd, struct wim_lookup_table *lt); -/* win32.c */ - -#if defined(__CYGWIN__) || defined(__WIN32__) -extern int win32_read_file(const char *filename, void *handle, u64 offset, - size_t size, u8 *buf); -extern void *win32_open_file_readonly(const void *path_utf16); -extern void win32_close_file(void *handle); -extern bool win32_acquire_privilege(const char *privilege); -extern bool win32_release_privilege(const char *privilege); -#ifdef ENABLE_ERROR_MESSAGES -extern void win32_error(u32 err); -#else -#define win32_error(err) -#endif -#endif - - /* write.c */ /* Internal use only */ @@ -553,6 +536,10 @@ extern void win32_error(u32 err); #define WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML 0x20000000 #define WIMLIB_WRITE_MASK_PUBLIC 0x1fffffff +#define WIMLIB_ADD_IMAGE_FLAG_ROOT 0x80000000 +#define WIMLIB_ADD_IMAGE_FLAG_SOURCE 0x40000000 + + extern int begin_write(WIMStruct *w, const char *path, int write_flags); extern void close_wim_writable(WIMStruct *w); diff --git a/src/win32.c b/src/win32.c index 3ac8deca..e2dc6a1b 100644 --- a/src/win32.c +++ b/src/win32.c @@ -1,13 +1,52 @@ -#include "config.h" +/* + * win32.c + * + * All the code specific to native Windows builds is in here. + */ + +/* + * Copyright (C) 2013 Eric Biggers + * + * This file is part of wimlib, a library for working with WIM files. + * + * wimlib is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with wimlib; if not, see http://www.gnu.org/licenses/. + */ + +#ifndef __WIN32__ +# error "This file contains Windows code" +#endif -#if defined(__CYGWIN__) || defined(__WIN32__) +#include "config.h" #include -# ifdef ERROR -# undef ERROR -# endif +#include +#include +#include +#ifdef ERROR +# undef ERROR +#endif + + +/* Microsoft's swprintf() violates the C standard and they require programmers + * to do this weird define to get the correct function. */ +#define swprintf _snwprintf -#include "wimlib_internal.h" +#include "win32.h" +#include "dentry.h" +#include "lookup_table.h" +#include "security.h" +#include #ifdef ENABLE_ERROR_MESSAGES void win32_error(u32 err_code) @@ -110,15 +149,1068 @@ out: return ret; } -bool win32_acquire_privilege(const char *privilege) +static bool win32_acquire_privilege(const char *privilege) { return win32_modify_privilege(privilege, true); } -bool win32_release_privilege(const char *privilege) +static bool win32_release_privilege(const char *privilege) { return win32_modify_privilege(privilege, false); } -#endif /* __CYGWIN__ || __WIN32__ */ +void win32_acquire_capture_privileges() +{ + win32_acquire_privilege(SE_BACKUP_NAME); + win32_acquire_privilege(SE_SECURITY_NAME); +} + +void win32_release_capture_privileges() +{ + win32_release_privilege(SE_BACKUP_NAME); + win32_release_privilege(SE_SECURITY_NAME); +} + +void win32_acquire_restore_privileges() +{ + win32_acquire_privilege(SE_RESTORE_NAME); + win32_acquire_privilege(SE_SECURITY_NAME); + win32_acquire_privilege(SE_TAKE_OWNERSHIP_NAME); +} + +void win32_release_restore_privileges() +{ + win32_release_privilege(SE_RESTORE_NAME); + win32_release_privilege(SE_SECURITY_NAME); + win32_release_privilege(SE_TAKE_OWNERSHIP_NAME); +} + +static u64 FILETIME_to_u64(const FILETIME *ft) +{ + return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime; +} + + +int win32_build_dentry_tree(struct wim_dentry **root_ret, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + void *extra_arg); + +static int win32_get_short_name(struct wim_dentry *dentry, + const wchar_t *path_utf16) +{ + WIN32_FIND_DATAW dat; + if (FindFirstFileW(path_utf16, &dat) && + dat.cAlternateFileName[0] != L'\0') + { + size_t short_name_len = wcslen(dat.cAlternateFileName) * 2; + size_t n = short_name_len + sizeof(wchar_t); + dentry->short_name = MALLOC(n); + if (!dentry->short_name) + return WIMLIB_ERR_NOMEM; + memcpy(dentry->short_name, dat.cAlternateFileName, n); + dentry->short_name_len = short_name_len; + } + return 0; +} + +static int win32_get_security_descriptor(struct wim_dentry *dentry, + struct sd_set *sd_set, + const wchar_t *path_utf16) +{ + SECURITY_INFORMATION requestedInformation; + DWORD lenNeeded = 0; + BOOL status; + DWORD err; + + requestedInformation = DACL_SECURITY_INFORMATION | + SACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION; + /* Request length of security descriptor */ + status = GetFileSecurityW(path_utf16, requestedInformation, + NULL, 0, &lenNeeded); + err = GetLastError(); + if (!status && err == ERROR_INSUFFICIENT_BUFFER) { + DWORD len = lenNeeded; + char buf[len]; + if (GetFileSecurityW(path_utf16, requestedInformation, + (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded)) + { + int security_id = sd_set_add_sd(sd_set, buf, len); + if (security_id < 0) + return WIMLIB_ERR_NOMEM; + else { + dentry->d_inode->i_security_id = security_id; + return 0; + } + } else { + err = GetLastError(); + } + } + ERROR("Win32 API: Failed to read security descriptor of \"%ls\"", + path_utf16); + win32_error(err); + return WIMLIB_ERR_READ; +} + +/* Reads the directory entries of directory using a Win32 API and recursively + * calls build_dentry_tree() on them. */ +static int win32_recurse_directory(struct wim_dentry *root, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + struct sd_set *sd_set, + const wchar_t *path_utf16, + size_t path_utf16_nchars) +{ + WIN32_FIND_DATAW dat; + HANDLE hFind; + DWORD err; + int ret; + + { + /* Begin reading the directory by calling FindFirstFileW. + * Unlike UNIX opendir(), FindFirstFileW has file globbing built + * into it. But this isn't what we actually want, so just add a + * dummy glob to get all entries. */ + wchar_t pattern_buf[path_utf16_nchars + 3]; + memcpy(pattern_buf, path_utf16, + path_utf16_nchars * sizeof(wchar_t)); + pattern_buf[path_utf16_nchars] = L'/'; + pattern_buf[path_utf16_nchars + 1] = L'*'; + pattern_buf[path_utf16_nchars + 2] = L'\0'; + hFind = FindFirstFileW(pattern_buf, &dat); + } + if (hFind == INVALID_HANDLE_VALUE) { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) { + return 0; + } else { + ERROR("Win32 API: Failed to read directory \"%s\"", + root_disk_path); + win32_error(err); + return WIMLIB_ERR_READ; + } + } + ret = 0; + do { + /* Skip . and .. entries */ + if (!(dat.cFileName[0] == L'.' && + (dat.cFileName[1] == L'\0' || + (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0')))) + { + struct wim_dentry *child; + + char *utf8_name; + size_t utf8_name_nbytes; + ret = utf16_to_utf8((const char*)dat.cFileName, + wcslen(dat.cFileName) * sizeof(wchar_t), + &utf8_name, + &utf8_name_nbytes); + if (ret) + goto out_find_close; + + char name[strlen(root_disk_path) + 1 + utf8_name_nbytes + 1]; + sprintf(name, "%s/%s", root_disk_path, utf8_name); + FREE(utf8_name); + ret = win32_build_dentry_tree(&child, name, lookup_table, + sd, config, add_image_flags, + progress_func, sd_set); + if (ret) + goto out_find_close; + if (child) + dentry_add_child(root, child); + } + } while (FindNextFileW(hFind, &dat)); + err = GetLastError(); + if (err != ERROR_NO_MORE_FILES) { + ERROR("Win32 API: Failed to read directory \"%s\"", root_disk_path); + win32_error(err); + if (ret == 0) + ret = WIMLIB_ERR_READ; + } +out_find_close: + FindClose(hFind); + return ret; +} + +/* Load a reparse point into a WIM inode. It is just stored in memory. + * + * @hFile: Open handle to a reparse point, with permission to read the reparse + * data. + * + * @inode: WIM inode for the reparse point. + * + * @lookup_table: Stream lookup table for the WIM; an entry will be added to it + * for the reparse point unless an entry already exists for + * the exact same data stream. + * + * @path: External path to the parse point (UTF-8). Used for error messages + * only. + * + * Returns 0 on success; nonzero on failure. */ +static int win32_capture_reparse_point(HANDLE hFile, + struct wim_inode *inode, + struct wim_lookup_table *lookup_table, + const char *path) +{ + /* "Reparse point data, including the tag and optional GUID, + * cannot exceed 16 kilobytes." - MSDN */ + char reparse_point_buf[16 * 1024]; + DWORD bytesReturned; + + if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, + NULL, 0, reparse_point_buf, + sizeof(reparse_point_buf), &bytesReturned, NULL)) + { + DWORD err = GetLastError(); + ERROR("Win32 API: Failed to get reparse data of \"%s\"", path); + win32_error(err); + return WIMLIB_ERR_READ; + } + if (bytesReturned < 8) { + ERROR("Reparse data on \"%s\" is invalid", path); + return WIMLIB_ERR_READ; + } + inode->i_reparse_tag = *(u32*)reparse_point_buf; + return inode_add_ads_with_data(inode, "", + (const u8*)reparse_point_buf + 8, + bytesReturned - 8, lookup_table); +} + +/* Calculate the SHA1 message digest of a Win32 data stream, which may be either + * an unnamed or named data stream. + * + * @path: Path to the file, with the stream noted at the end for named + * streams. UTF-16LE encoding. + * + * @hash: On success, the SHA1 message digest of the stream is written to + * this location. + * + * Returns 0 on success; nonzero on failure. + */ +static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE]) +{ + HANDLE hFile; + SHA_CTX ctx; + u8 buf[32768]; + DWORD bytesRead; + int ret; + + hFile = win32_open_file_readonly(path); + if (hFile == INVALID_HANDLE_VALUE) + return WIMLIB_ERR_OPEN; + + sha1_init(&ctx); + for (;;) { + if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) { + ret = WIMLIB_ERR_READ; + goto out_close_handle; + } + if (bytesRead == 0) + break; + sha1_update(&ctx, buf, bytesRead); + } + ret = 0; + sha1_final(hash, &ctx); +out_close_handle: + CloseHandle(hFile); + return ret; +} + +/* Scans an unnamed or named stream of a Win32 file (not a reparse point + * stream); calculates its SHA1 message digest and either creates a `struct + * wim_lookup_table_entry' in memory for it, or uses an existing 'struct + * wim_lookup_table_entry' for an identical stream. + * + * @path_utf16: Path to the file (UTF-16LE). + * + * @path_utf16_nchars: Number of 2-byte characters in @path_utf16. + * + * @inode: WIM inode to save the stream into. + * + * @lookup_table: Stream lookup table for the WIM. + * + * @dat: A `WIN32_FIND_STREAM_DATA' structure that specifies the + * stream name. + * + * Returns 0 on success; nonzero on failure. + */ +static int win32_capture_stream(const wchar_t *path_utf16, + size_t path_utf16_nchars, + struct wim_inode *inode, + struct wim_lookup_table *lookup_table, + WIN32_FIND_STREAM_DATA *dat) +{ + struct wim_ads_entry *ads_entry; + u8 hash[SHA1_HASH_SIZE]; + struct wim_lookup_table_entry *lte; + int ret; + wchar_t *p, *colon; + bool is_named_stream; + wchar_t *spath; + size_t spath_nchars; + DWORD err; + + /* The stream name should be returned as :NAME:TYPE */ + p = dat->cStreamName; + if (*p != L':') + goto out_invalid_stream_name; + p += 1; + colon = wcschr(p, L':'); + if (colon == NULL) + goto out_invalid_stream_name; + + if (wcscmp(colon + 1, L"$DATA")) { + /* Not a DATA stream */ + ret = 0; + goto out; + } + + is_named_stream = (p != colon); + if (is_named_stream) { + /* Allocate an ADS entry for the named stream. */ + char *utf8_stream_name; + size_t utf8_stream_name_len; + ret = utf16_to_utf8((const char *)p, + (colon - p) * sizeof(wchar_t), + &utf8_stream_name, + &utf8_stream_name_len); + if (ret) + goto out; + ads_entry = inode_add_ads(inode, utf8_stream_name); + FREE(utf8_stream_name); + if (!ads_entry) { + ret = WIMLIB_ERR_NOMEM; + goto out; + } + } + + /* Create a UTF-16 string @spath that gives the filename, then a colon, + * then the stream name. Or, if it's an unnamed stream, just the + * filename. It is MALLOC()'ed so that it can be saved in the + * wim_lookup_table_entry if needed. */ + *colon = '\0'; + spath_nchars = path_utf16_nchars; + if (is_named_stream) + spath_nchars += colon - p + 1; + + spath = MALLOC((spath_nchars + 1) * sizeof(wchar_t)); + memcpy(spath, path_utf16, path_utf16_nchars * sizeof(wchar_t)); + if (is_named_stream) { + spath[path_utf16_nchars] = L':'; + memcpy(&spath[path_utf16_nchars + 1], p, (colon - p) * sizeof(wchar_t)); + } + spath[spath_nchars] = L'\0'; + + ret = win32_sha1sum(spath, hash); + if (ret) { + err = GetLastError(); + ERROR("Win32 API: Failed to read \"%ls\" to calculate SHA1sum", + path_utf16); + win32_error(err); + goto out_free_spath; + } + + lte = __lookup_resource(lookup_table, hash); + if (lte) { + /* Use existing wim_lookup_table_entry that has the same SHA1 + * message digest */ + lte->refcnt++; + } else { + /* Make a new wim_lookup_table_entry */ + lte = new_lookup_table_entry(); + if (!lte) { + ret = WIMLIB_ERR_NOMEM; + goto out_free_spath; + } + lte->file_on_disk = (char*)spath; + spath = NULL; + lte->resource_location = RESOURCE_WIN32; + lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart; + lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart; + copy_hash(lte->hash, hash); + lookup_table_insert(lookup_table, lte); + } + if (is_named_stream) + ads_entry->lte = lte; + else + inode->i_lte = lte; +out_free_spath: + FREE(spath); +out: + return ret; +out_invalid_stream_name: + ERROR("Invalid stream name: \"%ls:%ls\"", path_utf16, dat->cStreamName); + ret = WIMLIB_ERR_READ; + goto out; +} + +/* Scans a Win32 file for unnamed and named data streams (not reparse point + * streams). + * + * @path_utf16: Path to the file (UTF-16LE). + * + * @path_utf16_nchars: Number of 2-byte characters in @path_utf16. + * + * @inode: WIM inode to save the stream into. + * + * @lookup_table: Stream lookup table for the WIM. + * + * Returns 0 on success; nonzero on failure. + */ +static int win32_capture_streams(const wchar_t *path_utf16, + size_t path_utf16_nchars, + struct wim_inode *inode, + struct wim_lookup_table *lookup_table) +{ + WIN32_FIND_STREAM_DATA dat; + int ret; + HANDLE hFind; + DWORD err; + + hFind = FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0); + if (hFind == INVALID_HANDLE_VALUE) { + err = GetLastError(); + + /* Seems legal for this to return ERROR_HANDLE_EOF on reparse + * points and directories */ + if ((inode->i_attributes & + (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) + && err == ERROR_HANDLE_EOF) + { + return 0; + } else { + ERROR("Win32 API: Failed to look up data streams of \"%ls\"", + path_utf16); + win32_error(err); + return WIMLIB_ERR_READ; + } + } + do { + ret = win32_capture_stream(path_utf16, + path_utf16_nchars, + inode, lookup_table, + &dat); + if (ret) + goto out_find_close; + } while (FindNextStreamW(hFind, &dat)); + err = GetLastError(); + if (err != ERROR_HANDLE_EOF) { + ERROR("Win32 API: Error reading data streams from \"%ls\"", path_utf16); + win32_error(err); + ret = WIMLIB_ERR_READ; + } +out_find_close: + FindClose(hFind); + return ret; +} + +/* Win32 version of capturing a directory tree */ +int win32_build_dentry_tree(struct wim_dentry **root_ret, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + void *extra_arg) +{ + struct wim_dentry *root = NULL; + int ret = 0; + struct wim_inode *inode; + + wchar_t *path_utf16; + size_t path_utf16_nchars; + struct sd_set *sd_set; + DWORD err; + + if (exclude_path(root_disk_path, config, true)) { + if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) { + ERROR("Cannot exclude the root directory from capture"); + ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; + goto out; + } + if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) + && progress_func) + { + union wimlib_progress_info info; + info.scan.cur_path = root_disk_path; + info.scan.excluded = true; + progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); + } + goto out; + } + + if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) + && progress_func) + { + union wimlib_progress_info info; + info.scan.cur_path = root_disk_path; + info.scan.excluded = false; + progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); + } + + if (extra_arg == NULL) { + sd_set = alloca(sizeof(struct sd_set)); + sd_set->rb_root.rb_node = NULL, + sd_set->sd = sd; + } else { + sd_set = extra_arg; + } + + ret = utf8_to_utf16(root_disk_path, strlen(root_disk_path), + (char**)&path_utf16, &path_utf16_nchars); + if (ret) + goto out_destroy_sd_set; + path_utf16_nchars /= sizeof(wchar_t); + + HANDLE hFile = win32_open_file_readonly(path_utf16); + if (hFile == INVALID_HANDLE_VALUE) { + err = GetLastError(); + ERROR("Win32 API: Failed to open \"%s\"", root_disk_path); + win32_error(err); + ret = WIMLIB_ERR_OPEN; + goto out_free_path_utf16; + } + + BY_HANDLE_FILE_INFORMATION file_info; + if (!GetFileInformationByHandle(hFile, &file_info)) { + err = GetLastError(); + ERROR("Win32 API: Failed to get file information for \"%s\"", + root_disk_path); + win32_error(err); + ret = WIMLIB_ERR_STAT; + goto out_close_handle; + } + + /* Create a WIM dentry */ + root = new_dentry_with_timeless_inode(path_basename(root_disk_path)); + if (!root) { + if (errno == EILSEQ) + ret = WIMLIB_ERR_INVALID_UTF8_STRING; + else if (errno == ENOMEM) + ret = WIMLIB_ERR_NOMEM; + else + ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE; + goto out_close_handle; + } + + /* Start preparing the associated WIM inode */ + inode = root->d_inode; + + inode->i_attributes = file_info.dwFileAttributes; + inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime); + inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime); + inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime); + inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) | + (u64)file_info.nFileIndexLow; + + inode->i_resolved = 1; + add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); + + /* Get DOS name and security descriptor (if any). */ + ret = win32_get_short_name(root, path_utf16); + if (ret) + goto out_close_handle; + ret = win32_get_security_descriptor(root, sd_set, path_utf16); + if (ret) + goto out_close_handle; + + if (inode_is_directory(inode)) { + /* Directory (not a reparse point) --- recurse to children */ + + /* But first... directories may have alternate data streams that + * need to be captured. */ + ret = win32_capture_streams(path_utf16, + path_utf16_nchars, + inode, + lookup_table); + if (ret) + goto out_close_handle; + ret = win32_recurse_directory(root, + root_disk_path, + lookup_table, + sd, + config, + add_image_flags, + progress_func, + sd_set, + path_utf16, + path_utf16_nchars); + } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* Reparse point: save the reparse tag and data */ + ret = win32_capture_reparse_point(hFile, + inode, + lookup_table, + root_disk_path); + } else { + /* Not a directory, not a reparse point; capture the default + * file contents and any alternate data streams. */ + ret = win32_capture_streams(path_utf16, + path_utf16_nchars, + inode, + lookup_table); + } +out_close_handle: + CloseHandle(hFile); +out_free_path_utf16: + FREE(path_utf16); +out_destroy_sd_set: + if (extra_arg == NULL) + destroy_sd_set(sd_set); +out: + if (ret == 0) + *root_ret = root; + else + free_dentry_tree(root, lookup_table); + return ret; +} + +/* Replacement for POSIX fnmatch() (partial functionality only) */ +extern int fnmatch(const char *pattern, const char *string, int flags) +{ + if (PathMatchSpecA(string, pattern)) + return 0; + else + return FNM_NOMATCH; +} + +static int win32_set_reparse_data(HANDLE h, + u32 reparse_tag, + const struct wim_lookup_table_entry *lte, + const wchar_t *path) +{ + int ret; + u8 *buf; + size_t len; + + if (!lte) { + WARNING("\"%ls\" is marked as a reparse point but had no reparse data", + path); + return 0; + } + len = wim_resource_size(lte); + if (len > 16 * 1024 - 8) { + WARNING("\"%ls\": reparse data too long!", path); + return 0; + } + + /* The WIM stream omits the ReparseTag and ReparseDataLength fields, so + * leave 8 bytes of space for them at the beginning of the buffer, then + * set them manually. */ + buf = alloca(len + 8); + ret = read_full_wim_resource(lte, buf + 8, 0); + if (ret) + return ret; + *(u32*)(buf + 0) = reparse_tag; + *(u16*)(buf + 4) = len; + *(u16*)(buf + 6) = 0; + + /* Set the reparse data on the open file using the + * FSCTL_SET_REPARSE_POINT ioctl. + * + * There are contradictions in Microsoft's documentation for this: + * + * "If hDevice was opened without specifying FILE_FLAG_OVERLAPPED, + * lpOverlapped is ignored." + * + * --- So setting lpOverlapped to NULL is okay since it's ignored. + * + * "If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an + * operation returns no output data and lpOutBuffer is NULL, + * DeviceIoControl makes use of lpBytesReturned. After such an + * operation, the value of lpBytesReturned is meaningless." + * + * --- So lpOverlapped not really ignored, as it affects another + * parameter. This is the actual behavior: lpBytesReturned must be + * specified, even though lpBytesReturned is documented as: + * + * "Not used with this operation; set to NULL." + */ + DWORD bytesReturned; + if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, buf, len + 8, + NULL, 0, + &bytesReturned /* lpBytesReturned */, + NULL /* lpOverlapped */)) + { + DWORD err = GetLastError(); + ERROR("Failed to set reparse data on \"%ls\"", path); + win32_error(err); + return WIMLIB_ERR_WRITE; + } + return 0; +} + + +static int win32_extract_chunk(const u8 *buf, size_t len, u64 offset, void *arg) +{ + HANDLE hStream = arg; + + DWORD nbytes_written; + wimlib_assert(len <= 0xffffffff); + + if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) || + nbytes_written != len) + { + DWORD err = GetLastError(); + ERROR("WriteFile(): write error"); + win32_error(err); + return WIMLIB_ERR_WRITE; + } + return 0; +} + +static int do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte) +{ + return extract_wim_resource(lte, wim_resource_size(lte), + win32_extract_chunk, hStream); +} + +static int win32_extract_stream(const struct wim_inode *inode, + const wchar_t *path, + const wchar_t *stream_name_utf16, + struct wim_lookup_table_entry *lte) +{ + wchar_t *stream_path; + HANDLE h; + int ret; + DWORD err; + DWORD creationDisposition = CREATE_ALWAYS; + + if (stream_name_utf16) { + /* Named stream. Create a buffer that contains the UTF-16LE + * string [./]@path:@stream_name_utf16. This is needed to + * create and open the stream using CreateFileW(). I'm not + * aware of any other APIs to do this. Note: the '$DATA' suffix + * seems to be unneeded. Additional note: a "./" prefix needs + * to be added when the path is not absolute to avoid ambiguity + * with drive letters. */ + size_t stream_path_nchars; + size_t path_nchars; + size_t stream_name_nchars; + const wchar_t *prefix; + + path_nchars = wcslen(path); + stream_name_nchars = wcslen(stream_name_utf16); + stream_path_nchars = path_nchars + 1 + stream_name_nchars; + if (path[0] != L'/' && path[0] != L'\\') { + prefix = L"./"; + stream_path_nchars += 2; + } else { + prefix = L""; + } + stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t)); + swprintf(stream_path, stream_path_nchars + 1, L"%ls%ls:%ls", + prefix, path, stream_name_utf16); + } else { + /* Unnamed stream; its path is just the path to the file itself. + * */ + stream_path = (wchar_t*)path; + + /* Directories must be created with CreateDirectoryW(). Then + * the call to CreateFileW() will merely open the directory that + * was already created rather than creating a new file. */ + if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { + if (!CreateDirectoryW(stream_path, NULL)) { + err = GetLastError(); + if (err != ERROR_ALREADY_EXISTS) { + ERROR("Failed to create directory \"%ls\"", + stream_path); + win32_error(err); + ret = WIMLIB_ERR_MKDIR; + goto fail; + } + } + DEBUG("Created directory \"%ls\"", stream_path); + if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + ret = 0; + goto out; + } + creationDisposition = OPEN_EXISTING; + } + } + + DEBUG("Opening \"%ls\"", stream_path); + h = CreateFileW(stream_path, + GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, + 0, + NULL, + creationDisposition, + FILE_FLAG_OPEN_REPARSE_POINT | + FILE_FLAG_BACKUP_SEMANTICS | + inode->i_attributes, + NULL); + if (h == INVALID_HANDLE_VALUE) { + err = GetLastError(); + ERROR("Failed to create \"%ls\"", stream_path); + win32_error(err); + ret = WIMLIB_ERR_OPEN; + goto fail; + } + + if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT && + stream_name_utf16 == NULL) + { + DEBUG("Setting reparse data on \"%ls\"", path); + ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path); + if (ret) + goto fail_close_handle; + } else { + if (lte) { + DEBUG("Extracting \"%ls\" (len = %"PRIu64")", + stream_path, wim_resource_size(lte)); + ret = do_win32_extract_stream(h, lte); + if (ret) + goto fail_close_handle; + } + } + + DEBUG("Closing \"%ls\"", stream_path); + if (!CloseHandle(h)) { + err = GetLastError(); + ERROR("Failed to close \"%ls\"", stream_path); + win32_error(err); + ret = WIMLIB_ERR_WRITE; + goto fail; + } + ret = 0; + goto out; +fail_close_handle: + CloseHandle(h); +fail: + ERROR("Error extracting %ls", stream_path); +out: + return ret; +} + +/* + * Creates a file, directory, or reparse point and extracts all streams to it + * (unnamed data stream and/or reparse point stream, plus any alternate data + * streams). This in Win32-specific code. + * + * @inode: WIM inode for this file or directory. + * @path: UTF-16LE external path to extract the inode to. + * + * Returns 0 on success; nonzero on failure. + */ +static int win32_extract_streams(const struct wim_inode *inode, + const wchar_t *path, u64 *completed_bytes_p) +{ + struct wim_lookup_table_entry *unnamed_lte; + int ret; + + unnamed_lte = inode_unnamed_lte_resolved(inode); + ret = win32_extract_stream(inode, path, NULL, unnamed_lte); + if (ret) + goto out; + if (unnamed_lte) + *completed_bytes_p += wim_resource_size(unnamed_lte); + for (u16 i = 0; i < inode->i_num_ads; i++) { + const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i]; + if (ads_entry->stream_name_len != 0) { + /* Skip special UNIX data entries (see documentation for + * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */ + if (ads_entry->stream_name_len == WIMLIB_UNIX_DATA_TAG_LEN + && !memcmp(ads_entry->stream_name_utf8, + WIMLIB_UNIX_DATA_TAG, + WIMLIB_UNIX_DATA_TAG_LEN)) + continue; + ret = win32_extract_stream(inode, + path, + (const wchar_t*)ads_entry->stream_name, + ads_entry->lte); + if (ret) + break; + if (ads_entry->lte) + *completed_bytes_p += wim_resource_size(ads_entry->lte); + } + } +out: + return ret; +} + +/* + * Sets the security descriptor on an extracted file. This is Win32-specific + * code. + * + * @inode: The WIM inode that was extracted and has a security descriptor. + * @path: UTF-16LE external path that the inode was extracted to. + * @sd: Security data for the WIM image. + * + * Returns 0 on success; nonzero on failure. + */ +static int win32_set_security_data(const struct wim_inode *inode, + const wchar_t *path, + const struct wim_security_data *sd) +{ + SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION | + SACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION; + if (!SetFileSecurityW(path, securityInformation, + (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id])) + { + DWORD err = GetLastError(); + ERROR("Can't set security descriptor on \"%ls\"", path); + win32_error(err); + return WIMLIB_ERR_WRITE; + } + return 0; +} + +int win32_apply_dentry(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + struct apply_args *args) +{ + char *utf16_path; + size_t utf16_path_len; + DWORD err; + int ret; + struct wim_inode *inode = dentry->d_inode; + + ret = utf8_to_utf16(output_path, output_path_len, + &utf16_path, &utf16_path_len); + if (ret) + return ret; + + if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) { + /* Linked file, with another name already extracted. Create a + * hard link. */ + DEBUG("Creating hard link \"%ls => %ls\"", + (const wchar_t*)utf16_path, + (const wchar_t*)inode->i_extracted_file); + if (!CreateHardLinkW((const wchar_t*)utf16_path, + (const wchar_t*)inode->i_extracted_file, + NULL)) + { + err = GetLastError(); + ERROR("Can't create hard link \"%ls => %ls\"", + (const wchar_t*)utf16_path, + (const wchar_t*)inode->i_extracted_file); + ret = WIMLIB_ERR_LINK; + win32_error(err); + } + } else { + /* Create the file, directory, or reparse point, and extract the + * data streams. */ + ret = win32_extract_streams(inode, (const wchar_t*)utf16_path, + &args->progress.extract.completed_bytes); + if (ret) + goto out_free_utf16_path; + + /* Set security descriptor if present */ + if (inode->i_security_id != -1) { + DEBUG("Setting security descriptor %d on %s", + inode->i_security_id, output_path); + ret = win32_set_security_data(inode, + (const wchar_t*)utf16_path, + wim_const_security_data(args->w)); + if (ret) + goto out_free_utf16_path; + } + if (inode->i_nlink > 1) { + /* Save extracted path for a later call to + * CreateHardLinkW() if this inode has multiple links. + * */ + inode->i_extracted_file = utf16_path; + goto out; + } + } +out_free_utf16_path: + FREE(utf16_path); +out: + return ret; +} + +int win32_apply_dentry_timestamps(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + const struct apply_args *args) +{ + /* Win32 */ + char *utf16_path; + size_t utf16_path_len; + DWORD err; + HANDLE h; + int ret; + const struct wim_inode *inode = dentry->d_inode; + + ret = utf8_to_utf16(output_path, output_path_len, + &utf16_path, &utf16_path_len); + if (ret) + return ret; + + DEBUG("Opening \"%s\" to set timestamps", output_path); + h = CreateFileW((const wchar_t*)utf16_path, + GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + + if (h == INVALID_HANDLE_VALUE) + err = GetLastError(); + FREE(utf16_path); + if (h == INVALID_HANDLE_VALUE) + goto fail; + + FILETIME creationTime = {.dwLowDateTime = inode->i_creation_time & 0xffffffff, + .dwHighDateTime = inode->i_creation_time >> 32}; + FILETIME lastAccessTime = {.dwLowDateTime = inode->i_last_access_time & 0xffffffff, + .dwHighDateTime = inode->i_last_access_time >> 32}; + FILETIME lastWriteTime = {.dwLowDateTime = inode->i_last_write_time & 0xffffffff, + .dwHighDateTime = inode->i_last_write_time >> 32}; + + DEBUG("Calling SetFileTime() on \"%s\"", output_path); + if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) { + err = GetLastError(); + CloseHandle(h); + goto fail; + } + DEBUG("Closing \"%s\"", output_path); + if (!CloseHandle(h)) { + err = GetLastError(); + goto fail; + } + goto out; +fail: + /* Only warn if setting timestamps failed. */ + WARNING("Can't set timestamps on \"%s\"", output_path); + win32_error(err); +out: + return 0; +} + +/* Replacement for POSIX fsync() */ +int fsync(int fd) +{ + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + if (!FlushFileBuffers(h)) { + errno = EIO; + return -1; + } + return 0; +} + +unsigned win32_get_number_of_processors() +{ + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +} diff --git a/src/win32.h b/src/win32.h new file mode 100644 index 00000000..e702d69a --- /dev/null +++ b/src/win32.h @@ -0,0 +1,53 @@ +#ifndef _WIMLIB_WIN32_H +#define _WIMLIB_WIN32_H + +#include "wimlib_internal.h" +#include + +extern void win32_release_capture_privileges(); +extern void win32_acquire_capture_privileges(); + +extern void win32_release_restore_privileges(); +extern void win32_acquire_restore_privileges(); + +extern int win32_build_dentry_tree(struct wim_dentry **root_ret, + const char *root_disk_path, + struct wim_lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int add_image_flags, + wimlib_progress_func_t progress_func, + void *extra_arg); + +extern int win32_read_file(const char *filename, void *handle, u64 offset, + size_t size, u8 *buf); +extern void *win32_open_file_readonly(const void *path_utf16); +extern void win32_close_file(void *handle); + +#ifdef ENABLE_ERROR_MESSAGES +extern void win32_error(u32 err); +#else +# define win32_error(err) +#endif + +#define FNM_PATHNAME 0x1 +#define FNM_NOMATCH 1 +extern int fnmatch(const char *pattern, const char *string, int flags); + +#define mkdir(name, mode) _mkdir(name) + +extern int win32_apply_dentry(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + struct apply_args *args); + +extern int win32_apply_dentry_timestamps(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + const struct apply_args *args); + +extern int fsync(int fd); + +extern unsigned win32_get_number_of_processors(); + +#endif /* _WIMLIB_WIN32_H */ diff --git a/src/write.c b/src/write.c index ea3f583c..77257767 100644 --- a/src/write.c +++ b/src/write.c @@ -29,14 +29,11 @@ #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK) /* On BSD, this should be included before "list.h" so that "list.h" can * overwrite the LIST_HEAD macro. */ -#include +# include #endif #ifdef __WIN32__ -# include -# ifdef ERROR -# undef ERROR -# endif +# include #endif #include "list.h" @@ -49,30 +46,23 @@ #include "xpress.h" #ifdef ENABLE_MULTITHREADED_COMPRESSION -#include +# include #endif #include #include #ifdef WITH_NTFS_3G -#include -#include -#include -#include +# include +# include +# include +# include #endif #ifdef HAVE_ALLOCA_H -#include +# include #else -#include -#endif - -#ifdef __WIN32__ -# ifdef fsync -# undef fsync -# endif -# define fsync(fd) 0 +# include #endif static int fflush_and_ftruncate(FILE *fp, off_t size) @@ -308,7 +298,7 @@ static int prepare_resource_for_read(struct wim_lookup_table_entry *lte } break; #endif -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ case RESOURCE_WIN32: if (!lte->file_on_disk_fp) { lte->file_on_disk_fp = win32_open_file_readonly(lte->file_on_disk); @@ -347,7 +337,7 @@ static void end_wim_resource_read(struct wim_lookup_table_entry *lte ntfs_inode_close(ni); } #endif -#if defined(__CYGWIN__) || defined(__WIN32__) +#ifdef __WIN32__ else if (lte->resource_location == RESOURCE_WIN32 && lte->file_on_disk_fp) { @@ -1222,9 +1212,7 @@ out: static long get_default_num_threads() { #ifdef __WIN32__ - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return sysinfo.dwNumberOfProcessors; + return win32_get_number_of_processors(); #else return sysconf(_SC_NPROCESSORS_ONLN); #endif -- 2.43.0