Update Win32 support; inc version to 1.3.0
authorEric Biggers <ebiggers3@gmail.com>
Sun, 10 Mar 2013 19:36:10 +0000 (14:36 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 10 Mar 2013 19:36:10 +0000 (14:36 -0500)
13 files changed:
Makefile.am
NEWS
README
README.WINDOWS [new file with mode: 0644]
configure.ac
doc/imagex-apply.1.in
doc/imagex-capture.1.in
doc/imagex-mount.1.in
doc/imagex.1.in
src/extract_image.c
src/mount_image.c
src/wimlib.h
src/win32.c

index 9f45562..bfff423 100644 (file)
@@ -53,6 +53,7 @@ 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               \
diff --git a/NEWS b/NEWS
index 25d1a31..31a63bc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 Only the most important changes more recent than version 0.6 are noted here.
 
-Version 1.2.7:
+Version 1.3.0:
+       Experimental support for Windows builds of wimlib has been added.
+
        --source-list option added to `imagex capture' and `imagex append'.
 
 Version 1.2.6:
diff --git a/README b/README
index 0cc804c..3eea193 100644 (file)
--- a/README
+++ b/README
@@ -1,10 +1,13 @@
                                   WIMLIB
 
-This is wimlib version 1.2.7 (March 2013).  wimlib can be used to read,
+This is wimlib version 1.3.0 (March 2013).  wimlib can be used to read,
 write, and mount files in the Windows Imaging Format (WIM files).  These files
 are normally created by using the `imagex.exe' utility on Windows, but this
 library provides a free implementation of imagex for UNIX-based systems.
 
+wimlib 1.3.0 has added experimental support for Windows.  See the file
+"README.WINDOWS" for more details.
+
                                   WIM FILES
 
 A Windows Imaging (WIM) file is an archive.  Like some other archive formats
diff --git a/README.WINDOWS b/README.WINDOWS
new file mode 100644 (file)
index 0000000..a0521f0
--- /dev/null
@@ -0,0 +1,41 @@
+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.
+
+The Windows builds use native Win32 calls when appropriate to handle alternate
+data streams, security descriptors, and reparse points.
+
+Windows support currently has the following limitations:
+
+-  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 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 there is no equivalent of
+   FUSE, which I used to get mounting working on Linux and BSD, so I would have
+   to program a "Filesystem Filter" driver with Microsoft's eccentric API.
+
+-  wimlib's API is not compatible with Microsoft's WIMGAPI, although they offer
+   some of the same functionality.
+
+So to be clear:
+
+"imagex capture", "imagex append", and "imagex apply" will work on Windows and
+have the added advantage of saving and restoring alternate data streams,
+security descriptors, and reparse points.
+
+"imagex delete", "imagex dir", "imagex export", "imagex info", "imagex join",
+"imagex optimize", and "imagex split" are all portable and should work the same
+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 is free software, so you can modify and/or audit the source code.
index c0c0f13..5f962f3 100644 (file)
@@ -1,4 +1,4 @@
-AC_INIT([wimlib], [1.2.7], [ebiggers3@gmail.com])
+AC_INIT([wimlib], [1.3.0], [ebiggers3@gmail.com])
 AC_CONFIG_SRCDIR([src/wim.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
@@ -171,6 +171,29 @@ else
 fi
 AC_SUBST([PTHREAD_LDADD], [$PTHREAD_LDADD])
 
+case "$host" in
+       *-*-cygwin*)
+               dnl -no-undefined is needed to build a DLL in a Cygwin environment.
+               CYGWIN_EXTRA_LDFLAGS="-no-undefined"
+
+               dnl -fvisibility=hidden should not be used in a Cygwin
+               dnl  environment
+               VISIBILITY_CFLAGS=""
+
+               WITH_NTFS_3G_DEFAULT="no"
+               WITH_FUSE_DEFAULT="no"
+               ;;
+       *)
+               CYGWIN_EXTRA_LDFLAGS=""
+               VISIBILITY_CFLAGS="-fvisibility=hidden"
+               WITH_NTFS_3G_DEFAULT="yes"
+               WITH_FUSE_DEFAULT="yes"
+               ;;
+esac
+
+AC_SUBST([CYGWIN_EXTRA_LDFLAGS], [$CYGWIN_EXTRA_LDFLAGS])
+AC_SUBST([VISIBILITY_CFLAGS], [$VISIBILITY_CFLAGS])
+
 AC_MSG_CHECKING([whether to include support for ntfs-3g])
 AC_ARG_WITH([ntfs-3g],
              AS_HELP_STRING([--without-ntfs-3g], [build without NTFS-3g.
@@ -178,7 +201,7 @@ AC_ARG_WITH([ntfs-3g],
                              information when capturing or applying WIMs to a
                              NTFS filesystem.]),
        [WITH_NTFS_3G=$withval],
-       [WITH_NTFS_3G=yes]
+       [WITH_NTFS_3G=$WITH_NTFS_3G_DEFAULT]
        )
 AC_MSG_RESULT([$WITH_NTFS_3G])
 if test "x$WITH_NTFS_3G" = "xyes"; then
@@ -220,7 +243,7 @@ AC_ARG_WITH([fuse],
                                        This will disable the ability to mount
                                        WIM files.]),
        [WITH_FUSE=$withval],
-       [WITH_FUSE=yes]
+       [WITH_FUSE=$WITH_FUSE_DEFAULT]
        )
 AC_MSG_RESULT([$WITH_FUSE])
 if test "x$WITH_FUSE" = "xyes"; then
@@ -323,22 +346,4 @@ AC_SUBST([LIBCRYPTO_LDADD], [$LIBCRYPTO_LDADD])
 AC_SUBST([LIBCRYPTO_CFLAGS], [$LIBCRYPTO_CFLAGS])
 AC_SUBST([SHA1_SOURCES], [$SHA1_SOURCES])
 
-case "$host" in
-       *-*-cygwin*)
-               dnl -no-undefined is needed to build a DLL in a Cygwin environment.
-               CYGWIN_EXTRA_LDFLAGS="-no-undefined"
-
-               dnl -fvisibility=hidden should not be used in a Cygwin
-               dnl  environment
-               VISIBILITY_CFLAGS=""
-               ;;
-       *)
-               CYGWIN_EXTRA_LDFLAGS=""
-               VISIBILITY_CFLAGS="-fvisibility=hidden"
-               ;;
-esac
-
-AC_SUBST([CYGWIN_EXTRA_LDFLAGS], [$CYGWIN_EXTRA_LDFLAGS])
-AC_SUBST([VISIBILITY_CFLAGS], [$VISIBILITY_CFLAGS])
-
 AC_OUTPUT
index a525caf..5197f78 100644 (file)
@@ -11,6 +11,9 @@ imagex-apply \- Extract one image, or all images, from a WIM archive
 \fBimagex apply\fR extracts an image, or all images, from the Windows Imaging
 (WIM) file \fIWIMFILE\fR.
 
+Note: this man page primarily documents the UNIX behavior.  See \fBWINDOWS
+VERSION\fR for information specific to the Windows build of wimlib.
+
 \fIIMAGE\fR specifies the WIM image to extract.  It may be a 1-based index of an
 image in the WIM, the name of an image in the WIM, or the keyword "all" to
 indicate that all images are to be extracted.  Use the \fBimagex info\fR (1)
@@ -190,6 +193,28 @@ imagex apply mywim.swm 1 dir --ref="mywim*.swm"
 .RE
 .PP
 
+.SH WINDOWS VERSION
+
+This section documents the differences between \fBimagex apply\fR in the Windows
+builds of wimlib versus the rest of this man page, which is written to document
+UNIX version.
+
+\fBimagex apply\fR does not have separate "normal" and "NTFS" modes on Windows.
+There is simply one mode, and it uses the Windows API to apply NTFS-specific
+information, including alternate data streams, reparse points, hard link, and
+symbolic links.  So, you essentially get the advantages of the "NTFS mode"
+documented above, but you can apply the WIM image to any directory, not just an
+entire NTFS volume.  This is mostly the same behavior as Microsoft's ImageX.
+
+\fB--hardlink\fR, \fB--symlink\fR, and \fB--unix-data\fR are not supported on
+Windows.
+
+Other than the differences documented in this section, the Windows version
+should be essentially equivalent to the UNIX version.  However, one additional
+thing to note is that wimlib's Windows version of ImageX is NOT written to be
+command-line compatible with Microsoft's version of ImageX, although they are
+very similar.
+
 .SH OPTIONS
 .TP 6
 \fB--check\fR
index 677ddcf..a35179c 100644 (file)
@@ -17,6 +17,9 @@ Imaging (WIM) image from a directory tree.  The \fBimagex capture\fR command
 creates a new WIM file containing the captured image, while the \fBimagex
 append\fR command appends the captured image to an existing WIM file.
 
+Note: this man page primarily documents the UNIX behavior.  See \fBWINDOWS
+VERSION\fR for information specific to the Windows build of wimlib.
+
 A WIM image is an independent directory tree in the WIM file.  A WIM file may
 contain any number of separate images.  However, files are stored only one time
 in the entire WIM, regardless of how many images the file appears in.
@@ -106,6 +109,29 @@ multiple files or directories to be incorporated into a WIM image using a single
 \fBimagex capture\fR or \fBimagex append\fR command.  See the documentation for
 \fB--source-list\fR below.
 
+.SH WINDOWS VERSION
+
+This section documents the differences between \fBimagex capture\fR and
+\fBimagex append\fR in the Windows builds of wimlib versus the rest of this man
+page, which is written to document UNIX version.
+
+\fBimagex capture\fR and \fBimagex append\fR do not have separate "normal" and
+"NTFS" modes on Windows.  There is simply one mode, and it uses the Windows API
+to capture NTFS-specific information, including alternate data streams, reparse
+points, hard link, and symbolic links.  So, you essentially get the advantages
+of the "NTFS mode" documented above, but you can capture a WIM image from any
+directory, not just an entire NTFS volume.  This is mostly the same behavior as
+Microsoft's ImageX.
+
+The \fB--source-list\fR option is supported on Windows, but the
+\fB--dereference\fR option is not.
+
+Other than the differences documented in this section, the Windows version
+should be essentially equivalent to the UNIX version.  However, one additional
+thing to note is that wimlib's Windows version of ImageX is NOT written to be
+command-line compatible with Microsoft's version of ImageX, although they are
+very similar.
+
 .SH OPTIONS
 .TP 6
 \fB--boot\fR
index 8a22d89..0844e4a 100644 (file)
@@ -63,6 +63,8 @@ imagex mount mywim.swm 1 dir --ref="mywim*.swm"
 
 If wimlib was configured using the \fB--without-fuse\fR flag, then the \fBimagex
 mount\fR, \fBimagex mountrw\fR, and \fBimagex unmount\fR commands will not work.
+Also, these commands are not available in the experimental Windows builds of
+wimlib.
 
 You can mount multiple images from a WIM file read-only at the same time, but
 you can only mount one image at a time from a WIM read-write.
index bff5517..7afbbd4 100644 (file)
@@ -93,11 +93,15 @@ application modes (although the \fBimagex\fR subcommands for the modes are the
 same): one for general image capture and application, and one for the capture or
 application of an image specifically from/to an NTFS volume.
 
+Note: the above applies to UNIX builds.  On the experimental Windows builds of
+wimlib, there is only one image capture and application mode, similar to
+Microsoft's ImageX.
+
 .IP \[bu]
 Microsoft's version has some weird limitations, like it won't let you extract a
 WIM on a shared folder, and it requires some commands to be run only from
 Windows PE and not from regular Windows.  This version does not have these
-unusual limitations, although it won't actually run on Windows anyway.
+unusual limitations.
 
 .IP \[bu]
 There are bugs in Microsoft's WIM library and I obviously have not included the
@@ -116,7 +120,8 @@ easily join the parts of a split WIM.
 .IP \[bu]
 wimlib's \fBimagex apply\fR supports keeping files hard-linked or symlinked
 across WIM images when extracted from a WIM.  So you can, for example, extract
-different versions of Windows from an install.wim without using much extra space.
+different versions of Windows from an install.wim without using much extra
+space.  (Note: this functionality is only available in UNIX builds of wimlib.)
 
 .IP \[bu]
 wimlib's \fBimagex capture\fR supports combining multiple separate directories
@@ -136,11 +141,13 @@ owners, and groups are stored.
 .IP \[bu]
 wimlib's \fBimagex mount\fR and \fBimagex mountrw\fR are much faster than
 Microsoft's versions for some reason.  I don't know what they have their program
-do that takes so long to simply set up a mountpoint.
+do that takes so long to simply set up a mountpoint.  (Note: this functionality
+is only available on UNIX builds.)
 
 .IP \[bu]
 wimlib's \fBimagex mount\fR supports mounting an image from a split WIM, but
-Microsoft's software does not.
+Microsoft's software does not.  (Note: this functionality is only available on
+UNIX builds.)
 
 .SH WARNING
 
index 8d21eda..6735602 100644 (file)
@@ -32,9 +32,9 @@
 
 #if defined(__CYGWIN__) || defined(__WIN32__)
 #include <windows.h>
-#      ifdef ERROR
-#              undef ERROR
-#      endif
+#ifdef ERROR
+#undef ERROR
+#endif
 #include <wchar.h>
 #endif
 
 static int win32_set_reparse_data(HANDLE h,
                                  u32 reparse_tag,
                                  const struct wim_lookup_table_entry *lte,
-                                 const wchar_t *path,
-                                 const char *path_utf8)
+                                 const wchar_t *path)
 {
        int ret;
        u8 *buf;
        size_t len;
-       
+
        if (!lte) {
-               WARNING("\"%s\" is marked as a reparse point but had no reparse data",
-                       path_utf8);
+               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("\"%s\": reparse data too long!", path_utf8);
+               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, NULL, NULL))
+                            NULL, 0,
+                            &bytesReturned /* lpBytesReturned */,
+                            NULL /* lpOverlapped */))
        {
                DWORD err = GetLastError();
-               ERROR("Failed to set reparse data on \"%s\"",
-                     path_utf8);
+               ERROR("Failed to set reparse data on \"%ls\"", path);
                win32_error(err);
-               ret = WIMLIB_ERR_WRITE;
-       } else
-               ret = 0;
-       return ret;
+               return WIMLIB_ERR_WRITE;
+       }
+       return 0;
 }
 
 
@@ -137,8 +164,7 @@ static int do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry
 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,
-                               const char *path_utf8)
+                               struct wim_lookup_table_entry *lte)
 {
        wchar_t *stream_path;
        HANDLE h;
@@ -147,42 +173,59 @@ static int win32_extract_stream(const struct wim_inode *inode,
        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: note that 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 = wcslen(path);
-               size_t stream_name_nchars = wcslen(stream_name_utf16);
+               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[1] != L'\\') {
+                       prefix = L"./";
+                       stream_path_nchars += 2;
+               } else {
+                       prefix = L"";
+               }
                stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
-
-               memcpy(stream_path, path, path_nchars * sizeof(wchar_t));
-               stream_path[path_nchars] = L':';
-               memcpy(&stream_path[path_nchars + 1], stream_name_utf16,
-                      stream_name_nchars * sizeof(wchar_t));
-               stream_path[stream_path_nchars] = L'\0';
-
-               /*wsprintf(stream_path, stream_path_nchars, L"%ls:%ls",*/
-                        /*path, stream_name_utf16);*/
+               swprintf(stream_path, stream_path_nchars + 1, L"%ls%ls:%ls",
+                        prefix, path, stream_name_utf16);
        } else {
+               /* Unnamed stream; it's 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_FILE_EXISTS &&
-                                   dentry_is_root(inode_first_dentry(inode)))
-                               {
-                                       /* Already exists */
-                                       return 0;
+                               if (err != ERROR_ALREADY_EXISTS) {
+                                       ERROR("Failed to create directory \"%ls\"",
+                                             path);
+                                       win32_error(err);
+                                       ret = WIMLIB_ERR_MKDIR;
+                                       goto fail;
                                }
-                               ERROR("Failed to create directory \"%s\"", path_utf8);
-                               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,
                        0,
@@ -194,37 +237,33 @@ static int win32_extract_stream(const struct wim_inode *inode,
                        NULL);
        if (h == INVALID_HANDLE_VALUE) {
                err = GetLastError();
-               if (err == ERROR_FILE_EXISTS &&
-                   dentry_is_root(inode_first_dentry(inode)))
-               {
-                       /* Already exists */
-                       return 0;
-               }
-               ERROR("Failed to create \"%s\"", path_utf8);
+               ERROR("Failed to create \"%ls\"", stream_path);
                win32_error(err);
                ret = WIMLIB_ERR_OPEN;
                goto fail;
        }
 
-       if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               DEBUG("Setting reparse data on \"%s\"", path_utf8);
-               ret = win32_set_reparse_data(h, inode->i_reparse_tag,
-                                            lte, path, path_utf8);
+       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 stream for \"%s\" (len = %zu)",
-                             path_utf8, wim_resource_size(lte));
+                       DEBUG("Extracting \"%ls\" (len = %zu)",
+                             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 \"%s\"", path_utf8);
+               ERROR("Failed to close \"%ls\"", stream_path);
                win32_error(err);
                ret = WIMLIB_ERR_WRITE;
                goto fail;
@@ -234,43 +273,67 @@ static int win32_extract_stream(const struct wim_inode *inode,
 fail_close_handle:
        CloseHandle(h);
 fail:
-       ERROR("Error extracting %s", path_utf8);
+       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,
-                                const char *path_utf8)
+                                const wchar_t *path)
 {
        struct wim_lookup_table_entry *unnamed_lte;
        int ret;
 
-       ret = win32_extract_stream(inode, path, NULL, unnamed_lte, path_utf8);
+       unnamed_lte = inode_unnamed_lte_resolved(inode);
+       ret = win32_extract_stream(inode, path, NULL, unnamed_lte);
        if (ret)
                goto out;
-
        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,
-                                                  path_utf8);
+                                                  ads_entry->lte);
                        if (ret)
-                               goto out;
+                               break;
                }
        }
-       ret = 0;
 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.
+ * @path_utf8:  @path in UTF-8 for error messages only.
+ *
+ * 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,
-                                  const char *path_utf8)
+                                  const struct wim_security_data *sd)
 {
        SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION |
                                                   SACL_SECURITY_INFORMATION |
@@ -280,7 +343,7 @@ static int win32_set_security_data(const struct wim_inode *inode,
                              (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id]))
        {
                DWORD err = GetLastError();
-               ERROR("Can't set security descriptor on \"%s\"", path_utf8);
+               ERROR("Can't set security descriptor on \"%ls\"", path);
                win32_error(err);
                return WIMLIB_ERR_WRITE;
        }
@@ -632,49 +695,50 @@ static int apply_dentry_normal(struct wim_dentry *dentry, void *arg)
                return ret;
 
        if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
-               /* Linked file, with another name already extracted */
-               if (!CreateHardLinkW((const wchar_t*)inode->i_extracted_file,
-                                    (const wchar_t*)utf16_path,
+               /* 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 \"%s\"", output_path);
+                       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);
-                       goto out_free_utf16_path;
                }
-               ret = 0;
-               goto out_free_utf16_path;
-       }
-       ret = win32_extract_streams(inode, (const wchar_t*)utf16_path,
-                                   output_path);
-       if (ret)
-               goto out_free_utf16_path;
-
-       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),
-                                             output_path);
+       } else {
+               /* Create the file, directory, or reparse point, and extract the
+                * data streams. */
+               ret = win32_extract_streams(inode, (const wchar_t*)utf16_path);
                if (ret)
                        goto out_free_utf16_path;
-       }
-
-       /*if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {*/
-               /*ret = win32_set_reparse_data(inode, path, output_path);*/
-               /*if (ret)*/
-                       /*goto out_free_utf16_path;*/
-       /*}*/
 
-       if (inode->i_nlink > 1) {
-               inode->i_extracted_file = utf16_path;
-               utf16_path = NULL;
+               /* 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;
+               }
        }
-       ret = 0;
 out_free_utf16_path:
        FREE(utf16_path);
+out:
        return ret;
 #else
        if (inode_is_symlink(inode))
@@ -692,14 +756,21 @@ out_free_utf16_path:
 static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
 {
        struct apply_args *args = arg;
-       size_t len = strlen(args->target);
-       char output_path[len + dentry->full_path_utf8_len + 1];
-       const struct wim_inode *inode = dentry->d_inode;
+       size_t len;
+       char *output_path;
        int ret;
+       const struct wim_inode *inode = dentry->d_inode;
 
-       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 = 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 */
@@ -709,13 +780,15 @@ static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
        HANDLE h;
        BOOL bret1, bret2;
 
-       ret = utf8_to_utf16(output_path, len + dentry->full_path_utf8_len,
-                           &utf16_path, &utf16_path_len);
+       ret = utf8_to_utf16(output_path, len, &utf16_path, &utf16_path_len);
        if (ret)
                return ret;
-       h = CreateFile(utf16_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
-                      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
-                      NULL);
+
+       DEBUG("Opening \"%ls\" to set timestamps", utf16_path);
+       h = CreateFileW(utf16_path, GENERIC_WRITE, FILE_SHARE_READ,
+                       NULL, OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+                       NULL);
 
        if (h == INVALID_HANDLE_VALUE)
                err = GetLastError();
@@ -730,18 +803,19 @@ static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
        FILETIME lastWriteTime = {.dwLowDateTime = dentry->d_inode->i_last_write_time & 0xffffffff,
                                  .dwHighDateTime = dentry->d_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;
        }
        return 0;
 fail:
-       err = GetLastError();
        ERROR("Can't set timestamps on \"%s\"", output_path);
        win32_error(err);
        return WIMLIB_ERR_WRITE;
index 417d987..839acbc 100644 (file)
@@ -2625,8 +2625,12 @@ out:
 
 static inline int mount_unsupported_error()
 {
+#if defined(__CYGWIN__) || defined (__WIN32__)
+       ERROR("Sorry-- Mounting WIM images is not supported on Windows!");
+#else
        ERROR("wimlib was compiled with --without-fuse, which disables support "
              "for mounting WIMs.");
+#endif
        return WIMLIB_ERR_UNSUPPORTED;
 }
 
index 0b46a8c..6abb6e5 100644 (file)
@@ -31,7 +31,7 @@
  *
  * \section intro Introduction
  *
- * This is the documentation for the library interface of wimlib 1.2.7.  If you
+ * This is the documentation for the library interface of wimlib 1.3.0.  If you
  * have installed wimlib and want to know how to use the @c imagex program,
  * please see the man pages instead.  Also: the actual project page where you
  * can download the source code for the library is at <a
 #define WIMLIB_MAJOR_VERSION 1
 
 /** Minor version of the library (for example, the 2 in 1.2.5). */
-#define WIMLIB_MINOR_VERSION 2
+#define WIMLIB_MINOR_VERSION 3
 
 /** Patch version of the library (for example, the 5 in 1.2.5). */
-#define WIMLIB_PATCH_VERSION 7
+#define WIMLIB_PATCH_VERSION 0
 
 /**
  * Opaque structure that represents a WIM file.  This is an in-memory structure
index b3468a9..3b75481 100644 (file)
@@ -40,7 +40,7 @@ void *win32_open_file(const void *path)
                                   FILE_FLAG_OPEN_REPARSE_POINT,
                           NULL /* hTemplateFile */);
 }
-               
+
 int win32_read_file(const char *filename,
                    void *handle, u64 offset, size_t size, u8 *buf)
 {
@@ -48,7 +48,7 @@ int win32_read_file(const char *filename,
        DWORD err;
        DWORD bytesRead;
        LARGE_INTEGER liOffset = {.QuadPart = offset};
-       
+
        wimlib_assert(size <= 0xffffffff);
 
        if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))