re-organize win32 code
authorEric Biggers <ebiggers3@gmail.com>
Sun, 17 Mar 2013 21:31:53 +0000 (16:31 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 17 Mar 2013 21:31:53 +0000 (16:31 -0500)
15 files changed:
Makefile.am
NEWS
README.WINDOWS
configure.ac
src/add_image.c
src/extract_image.c
src/lookup_table.c
src/mount_image.c
src/resource.c
src/security.c
src/security.h
src/wimlib_internal.h
src/win32.c
src/win32.h [new file with mode: 0644]
src/write.c

index 5dc694b..7700f46 100644 (file)
@@ -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 31a63bc..5c65755 100644 (file)
--- 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'.
 
index 952cbd8..45b7ad5 100644 (file)
@@ -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.
index ad04b47..1326258 100644 (file)
@@ -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.
index a57541f..8af2b07 100644 (file)
 
 #include "config.h"
 
-#if defined(__CYGWIN__) || defined(__WIN32__)
-#      include <windows.h>
-#      include <ntdef.h>
-#      include <wchar.h>
-#      ifdef ERROR
-#              undef ERROR
-#      endif
-#      include "security.h"
-#else
-#      include <dirent.h>
-#      include <sys/stat.h>
-#      include "timestamp.h"
-#endif
-
 #ifdef __WIN32__
-#include <shlwapi.h>
+#  include "win32.h"
+#else
+#  include <dirent.h>
+#  include <sys/stat.h>
+#  include <fnmatch.h>
+#  include "timestamp.h"
 #endif
 
 #include "wimlib_internal.h"
 #include "dentry.h"
 #include "lookup_table.h"
 #include "xml.h"
+
 #include <ctype.h>
 #include <errno.h>
-
-#ifndef __WIN32__
-#include <fnmatch.h>
-#endif
-
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -60,9 +47,6 @@
 #include <alloca.h>
 #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;
 }
index 8606381..7955417 100644 (file)
 
 #include "config.h"
 
-#if defined(__CYGWIN__) || defined(__WIN32__)
-#      include <windows.h>
-#      ifdef ERROR
-#              undef ERROR
-#      endif
-#      include <wchar.h>
+#include <dirent.h>
+
+#ifdef __WIN32__
+#  include "win32.h"
 #else
-#      include <dirent.h>
-#      ifdef HAVE_UTIME_H
-#              include <utime.h>
-#      endif
-#      include "timestamp.h"
-#      include <sys/time.h>
+#  ifdef HAVE_UTIME_H
+#    include <utime.h>
+#  endif
+#  include "timestamp.h"
+#  include <sys/time.h>
 #endif
 
 #include <errno.h>
 #include "xml.h"
 
 #ifdef WITH_NTFS_3G
-#include <ntfs-3g/volume.h>
+#  include <ntfs-3g/volume.h>
 #endif
 
 #ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
-
-#if defined(__WIN32__)
-#      define swprintf _snwprintf
-#      define mkdir(path, mode) (!CreateDirectoryA(path, NULL))
+#  include <alloca.h>
 #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 |
index fee69cb..ab13e5e 100644 (file)
@@ -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*)&lte->file_on_disk !=
index 839acbc..a17bf50 100644 (file)
@@ -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 "
index 8d61761..db9c635 100644 (file)
 #include "xpress.h"
 #include "sha1.h"
 
+#ifdef __WIN32__
+#  include "win32.h"
+#endif
+
 #include <errno.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <unistd.h>
 
 #ifdef WITH_NTFS_3G
-#include <time.h>
-#include <ntfs-3g/attrib.h>
-#include <ntfs-3g/inode.h>
-#include <ntfs-3g/dir.h>
+#  include <time.h>
+#  include <ntfs-3g/attrib.h>
+#  include <ntfs-3g/inode.h>
+#  include <ntfs-3g/dir.h>
 #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,
index 9c55ef2..2e60aa5 100644 (file)
@@ -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__ */
index d6d7d60..4d701d0 100644 (file)
@@ -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'. */
index 5a076a5..3f05d88 100644 (file)
@@ -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);
 
index 3ac8dec..e2dc6a1 100644 (file)
@@ -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 <windows.h>
-#      ifdef ERROR
-#              undef ERROR
-#      endif
+#include <ntdef.h>
+#include <wchar.h>
+#include <shlwapi.h>
+#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 <errno.h>
 
 #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 (file)
index 0000000..e702d69
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _WIMLIB_WIN32_H
+#define _WIMLIB_WIN32_H
+
+#include "wimlib_internal.h"
+#include <direct.h>
+
+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 */
index ea3f583..7725776 100644 (file)
 #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 <sys/file.h>
+#  include <sys/file.h>
 #endif
 
 #ifdef __WIN32__
-#      include <windows.h>
-#      ifdef ERROR
-#              undef ERROR
-#      endif
+#  include <win32.h>
 #endif
 
 #include "list.h"
 #include "xpress.h"
 
 #ifdef ENABLE_MULTITHREADED_COMPRESSION
-#include <pthread.h>
+#  include <pthread.h>
 #endif
 
 #include <unistd.h>
 #include <errno.h>
 
 #ifdef WITH_NTFS_3G
-#include <time.h>
-#include <ntfs-3g/attrib.h>
-#include <ntfs-3g/inode.h>
-#include <ntfs-3g/dir.h>
+#  include <time.h>
+#  include <ntfs-3g/attrib.h>
+#  include <ntfs-3g/inode.h>
+#  include <ntfs-3g/dir.h>
 #endif
 
 #ifdef HAVE_ALLOCA_H
-#include <alloca.h>
+#  include <alloca.h>
 #else
-#include <stdlib.h>
-#endif
-
-#ifdef __WIN32__
-#      ifdef fsync
-#              undef fsync
-#      endif
-#      define fsync(fd) 0
+#  include <stdlib.h>
 #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