]> wimlib.net Git - wimlib/commitdiff
Update mount implementation
authorEric Biggers <ebiggers3@gmail.com>
Sat, 24 May 2014 02:43:19 +0000 (21:43 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 25 May 2014 19:48:15 +0000 (14:48 -0500)
- Improve comments and documentation
- Speed up fd allocation by tracking lower bound on next available slot
- Open staging files by directory-relative names
- Simplify, and hopefully improve, how unmounting images works.

15 files changed:
doc/man1/imagex-mount.1.in
include/wimlib.h
include/wimlib/inode.h
include/wimlib/lookup_table.h
include/wimlib/wim.h
include/wimlib/write.h
programs/imagex.c
src/dentry.c
src/inode.c
src/lookup_table.c
src/mount_image.c
src/resource.c
src/util.c
src/wim.c
src/write.c

index f475527c3d79c312b9d079fc9562e73d30062bea..2f3e061855ad6a7536e6e23a0fb76e4fa2d39617 100644 (file)
@@ -6,7 +6,7 @@
 .br
 \fB@IMAGEX_PROGNAME@ mountrw\fR \fIWIMFILE\fR [\fIIMAGE\fR] \fIDIRECTORY\fR [\fIOPTION\fR...]
 .br
 .br
 \fB@IMAGEX_PROGNAME@ mountrw\fR \fIWIMFILE\fR [\fIIMAGE\fR] \fIDIRECTORY\fR [\fIOPTION\fR...]
 .br
-\fB@IMAGEX_PROGNAME@ unmount\fR \fIDIRECTORY\fR [--commit] [--check] [--rebuild]
+\fB@IMAGEX_PROGNAME@ unmount\fR \fIDIRECTORY\fR [\fIOPTION\fR...]
 .SH DESCRIPTION
 The \fB@IMAGEX_PROGNAME@ mount\fR and \fB@IMAGEX_PROGNAME@ mountrw\fR commands
 will mount the image in the Windows Imaging (WIM) file \fIWIMFILE\fR specified
 .SH DESCRIPTION
 The \fB@IMAGEX_PROGNAME@ mount\fR and \fB@IMAGEX_PROGNAME@ mountrw\fR commands
 will mount the image in the Windows Imaging (WIM) file \fIWIMFILE\fR specified
@@ -21,9 +21,9 @@ be the name of an image in the WIM.  Use the \fB@IMAGEX_PROGNAME@ info\fR (1)
 command to see the available images in the WIM.  \fIIMAGE\fR may be omitted if
 \fIWIMFILE\fR contains only one image.
 .PP
 command to see the available images in the WIM.  \fIIMAGE\fR may be omitted if
 \fIWIMFILE\fR contains only one image.
 .PP
-The WIM image can be unmounted using the \fB@IMAGEX_PROGNAME@ unmount\fR command.  Changes
-made to a WIM mounted read-write will be discarded unless the \fB--commit\fR
-flag is provided to \fB@IMAGEX_PROGNAME@ unmount\fR.
+The WIM image can be unmounted using the \fB@IMAGEX_PROGNAME@ unmount\fR
+command.  Changes made to a WIM mounted read-write will be discarded unless the
+\fB--commit\fR flag is provided to \fB@IMAGEX_PROGNAME@ unmount\fR.
 .SH SPLIT WIMS
 You may use \fB@IMAGEX_PROGNAME@ mount\fR to mount an image from a split WIM
 read-only.  However, you may not mount an image from a split WIM read-write.
 .SH SPLIT WIMS
 You may use \fB@IMAGEX_PROGNAME@ mount\fR to mount an image from a split WIM
 read-only.  However, you may not mount an image from a split WIM read-write.
@@ -62,16 +62,16 @@ All files in the mounted WIM will be accessible regardless of whether there is a
 security descriptor in the WIM associated with the file or not.  New files or
 directories created in a read-write mounted WIM will be created with no security
 descriptor.  Although there is support for accessing named data streams (see the
 security descriptor in the WIM associated with the file or not.  New files or
 directories created in a read-write mounted WIM will be created with no security
 descriptor.  Although there is support for accessing named data streams (see the
-\fB--streams-interface\fR option), it is currently not possible
-to set or get DOS names, file attributes, or security
-descriptors in a mounted WIM.
+\fB--streams-interface\fR option), it is currently not possible to set or get
+DOS names, file attributes, or security descriptors in a mounted WIM.
 .PP
 By default, changes to a read-write WIM are made in-place by appending to the
 WIM.  This is nice for big WIM files, since the entire file doesn't have to be
 rebuilt to make a small change.  But, if you are making many changes to a
 read-write mounted WIM, especially deleting large files, it is suggested to
 .PP
 By default, changes to a read-write WIM are made in-place by appending to the
 WIM.  This is nice for big WIM files, since the entire file doesn't have to be
 rebuilt to make a small change.  But, if you are making many changes to a
 read-write mounted WIM, especially deleting large files, it is suggested to
-provide the \fB--rebuild\fR option to \fB@IMAGEX_PROGNAME@ unmount\fR to force the WIM to
-be rebuilt, or else run \fB@IMAGEX_PROGNAME@ optimize\fR on the WIM afterwards.
+provide the \fB--rebuild\fR option to \fB@IMAGEX_PROGNAME@ unmount\fR to force
+the WIM to be rebuilt, or else run \fB@IMAGEX_PROGNAME@ optimize\fR on the WIM
+afterwards.
 .PP
 wimlib v1.6.0 and later can mount version 3584 WIMs, which usually use packed,
 LZMS-compressed streams and may carry the \fI.esd\fR file extension rather than
 .PP
 wimlib v1.6.0 and later can mount version 3584 WIMs, which usually use packed,
 LZMS-compressed streams and may carry the \fI.esd\fR file extension rather than
@@ -148,6 +148,15 @@ specified in /etc/fuse.conf (with the FUSE implementation on Linux, at least).
 Update the WIM file with the changes that have been made.  Has no effect if the
 mount is read-only.
 .TP
 Update the WIM file with the changes that have been made.  Has no effect if the
 mount is read-only.
 .TP
+\fB--force\fR
+In combination with \fB--commit\fR, force the WIM image to be committed even if
+there are open file descriptors to the WIM image.  Any such file descriptors
+will be immediately closed, and the WIM image will be committed and unmounted.
+.IP
+\fB--lazy\fR is a deprecated alias for \fB--force\fR.  (Unmounts are now "lazy"
+by default with regards to the kernel-level mountpoint, except in the case with
+\fB--commit\fR described above.)
+.TP
 \fB--check\fR
 When writing \fIWIMFILE\fR, include an integrity table.  Has no effect if the
 mount is read-only or if \fB--commit\fR was not specified.  The default behavior
 \fB--check\fR
 When writing \fIWIMFILE\fR, include an integrity table.  Has no effect if the
 mount is read-only or if \fB--commit\fR was not specified.  The default behavior
@@ -159,28 +168,23 @@ Rebuilding the WIM is slower, but will save a little bit of space that would
 otherwise be left as a hole in the WIM.  Even more space will be saved if the
 read-write mount resulted in streams being deleted from the WIM.  Also see
 .TP
 otherwise be left as a hole in the WIM.  Even more space will be saved if the
 read-write mount resulted in streams being deleted from the WIM.  Also see
 .TP
-\fB--lazy\fR
-Pass the \fB-z\fR option to \fBfusermount\fR, which performs a "lazy" unmount
-where the filesystem is detached immediately even if it is still busy.  However,
-even with this option, \fB@IMAGEX_PROGNAME@ unmount\fR still waits for the
-filesystem to become unbusy; \fB--lazy\fR will only stop the unmount from
-immediately failing.
-.TP
 \fB--new-image\fR
 In combination with \fB--commit\fR for a read-write mounted image, causes the
 modified image to be committed as a new, unnamed image appended to the WIM
 archive.  The original image will be unmodified.
 .SH IMPLEMENTATION DETAILS
 \fB--new-image\fR
 In combination with \fB--commit\fR for a read-write mounted image, causes the
 modified image to be committed as a new, unnamed image appended to the WIM
 archive.  The original image will be unmodified.
 .SH IMPLEMENTATION DETAILS
-Since a WIM is an archive and not a filesystem, \fB@IMAGEX_PROGNAME@ mountrw\fR creates a
-temporary staging directory to contain files that are created or modified.  This
-directory is located in the same directory as \fIWIMFILE\fR by default, but the
-location can be set using the \fB--staging-dir\fR option.  When the filesystem
-is unmounted with \fB--commit\fR, the WIM is modified in-place (or rebuild
-completely with \fB--rebuild\fR), merging in the staging files as needed.  Then,
-the temporary staging directory is deleted.
+Since a WIM is an archive and not a filesystem, \fB@IMAGEX_PROGNAME@ mountrw\fR
+creates a temporary staging directory to contain files that are created or
+modified.  This directory is located in the same directory as \fIWIMFILE\fR by
+default, but the location can be set using the \fB--staging-dir\fR option.  When
+the filesystem is unmounted with \fB--commit\fR, the WIM is modified in-place
+(or rebuilt completely with \fB--rebuild\fR), merging in the staging files as
+needed.  Then, the temporary staging directory is deleted.
 .PP
 .PP
-\fB@IMAGEX_PROGNAME@ unmount\fR runs in a separate process from the process that previously
-ran \fB@IMAGEX_PROGNAME@ mount\fR, and these two processes communicate using POSIX message
-queues.  See \fIsrc/mount_image.c\fR in the sources for details.
+\fB@IMAGEX_PROGNAME@ unmount\fR runs in a separate process from the process that
+previously ran \fB@IMAGEX_PROGNAME@ mount\fR.  When unmounting a read-write
+mounted WIM image with \fB--commit\fR, these two processes communicate using a
+POSIX message queue so that the unmount process can track the progress of the
+mount process.  See \fIsrc/mount_image.c\fR in the sources for details.
 .SH SEE ALSO
 .BR @IMAGEX_PROGNAME@ (1)
 .SH SEE ALSO
 .BR @IMAGEX_PROGNAME@ (1)
index ea15d8e7e6a51cc54efabda478aa52c17dac468e..ea48aaefb7cfdf050fdd75ef5419c179d5381765 100644 (file)
@@ -572,9 +572,13 @@ enum wimlib_progress_msg {
         * a file is being extracted normally (not as a WIMBoot "pointer file")
         * due to it matching a pattern in the [PrepopulateList] section of the
         * configuration file \Windows\System32\WimBootCompress.ini in the WIM
         * a file is being extracted normally (not as a WIMBoot "pointer file")
         * due to it matching a pattern in the [PrepopulateList] section of the
         * configuration file \Windows\System32\WimBootCompress.ini in the WIM
-        * image.  @info will point to ::wimlib_progress_info.wimboot_exclude.
+        * image.  @info will point to ::wimlib_progress_info.wimboot_exclude.
         */
        WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE = 24,
         */
        WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE = 24,
+
+       /** Starting to unmount a WIM image.  @p info will point to
+        * ::wimlib_progress_info.unmount.  */
+       WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN = 25,
 };
 
 /** Valid return values from user-provided progress functions
 };
 
 /** Valid return values from user-provided progress functions
@@ -961,6 +965,25 @@ union wimlib_progress_info {
                /** Path to which the file is being extracted  */
                const wimlib_tchar *extraction_path;
        } wimboot_exclude;
                /** Path to which the file is being extracted  */
                const wimlib_tchar *extraction_path;
        } wimboot_exclude;
+
+       /** Valid on messages ::WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN.  */
+       struct wimlib_progress_info_unmount {
+               /** Path to directory being unmounted  */
+               const wimlib_tchar *mountpoint;
+
+               /** Path to WIM file being unmounted  */
+               const wimlib_tchar *mounted_wim;
+
+               /** 1-based index of image being unmounted.  */
+               uint32_t mounted_image;
+
+               /** Flags that were passed to wimlib_mount_image() when the
+                * mountpoint was set up.  */
+               uint32_t mount_flags;
+
+               /** Flags passed to wimlib_unmount_image().  */
+               uint32_t unmount_flags;
+       } unmount;
 };
 
 /**
 };
 
 /**
@@ -1604,23 +1627,24 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
 /** Enable FUSE debugging by passing the @c -d flag to @c fuse_main().*/
 #define WIMLIB_MOUNT_FLAG_DEBUG                                0x00000002
 
 /** Enable FUSE debugging by passing the @c -d flag to @c fuse_main().*/
 #define WIMLIB_MOUNT_FLAG_DEBUG                                0x00000002
 
-/** Do not allow accessing alternate data streams in the mounted WIM image. */
+/** Do not allow accessing named data streams in the mounted WIM image.  */
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE                0x00000004
 
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE                0x00000004
 
-/** Access alternate data streams in the mounted WIM image through extended file
- * attributes.  This is the default mode. */
+/** Access named data streams in the mounted WIM image through extended file
+ * attributes named "user.X", where X is the name of a data stream.  This is the
+ * default mode.  */
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR       0x00000008
 
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR       0x00000008
 
-/** Access alternate data streams in the mounted WIM image by specifying the
- * file name, a colon, then the alternate file stream name. */
+/** Access named data streams in the mounted WIM image by specifying the file
+ * name, a colon, then the name of the data stream.  */
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS     0x00000010
 
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS     0x00000010
 
-/** Use UNIX file owners, groups, and modes if available in the WIM (see
- * ::WIMLIB_ADD_FLAG_UNIX_DATA). */
+/** Use UNIX metadata if available in the WIM image.  See
+ * ::WIMLIB_ADD_FLAG_UNIX_DATA */
 #define WIMLIB_MOUNT_FLAG_UNIX_DATA                    0x00000020
 
 #define WIMLIB_MOUNT_FLAG_UNIX_DATA                    0x00000020
 
-/** Allow other users to see the mounted filesystem.  (this passes the @c
- * allow_other option to FUSE mount) */
+/** Allow other users to see the mounted filesystem.  This passes the @c
+ * allow_other option to the FUSE mount.  */
 #define WIMLIB_MOUNT_FLAG_ALLOW_OTHER                  0x00000040
 
 /** @} */
 #define WIMLIB_MOUNT_FLAG_ALLOW_OTHER                  0x00000040
 
 /** @} */
@@ -1652,25 +1676,36 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
 /** @ingroup G_mounting_wim_images
  * @{ */
 
 /** @ingroup G_mounting_wim_images
  * @{ */
 
-/** See ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY.  */
+/** Provide ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY when committing the WIM image.
+ * Ignored if ::WIMLIB_UNMOUNT_FLAG_COMMIT not also specified.  */
 #define WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY            0x00000001
 
 #define WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY            0x00000001
 
-/** Unless this flag is given, changes to a read-write mounted WIM are
- * discarded.  Ignored for read-only mounts.  */
+/** Commit changes to the read-write mounted WIM image.
+ * If this flag is not specified, changes will be discarded.  */
 #define WIMLIB_UNMOUNT_FLAG_COMMIT                     0x00000002
 
 #define WIMLIB_UNMOUNT_FLAG_COMMIT                     0x00000002
 
-/** See ::WIMLIB_WRITE_FLAG_REBUILD.  */
+/** Provide ::WIMLIB_WRITE_FLAG_REBUILD when committing the WIM image.
+ * Ignored if ::WIMLIB_UNMOUNT_FLAG_COMMIT not also specified.  */
 #define WIMLIB_UNMOUNT_FLAG_REBUILD                    0x00000004
 
 #define WIMLIB_UNMOUNT_FLAG_REBUILD                    0x00000004
 
-/** See ::WIMLIB_WRITE_FLAG_RECOMPRESS */
+/** Provide ::WIMLIB_WRITE_FLAG_RECOMPRESS when committing the WIM image.
+ * Ignored if ::WIMLIB_UNMOUNT_FLAG_COMMIT not also specified.  */
 #define WIMLIB_UNMOUNT_FLAG_RECOMPRESS                 0x00000008
 
 #define WIMLIB_UNMOUNT_FLAG_RECOMPRESS                 0x00000008
 
-/** Do a "lazy" unmount (detach filesystem immediately, even if busy).  */
-#define WIMLIB_UNMOUNT_FLAG_LAZY                       0x00000010
+/**
+ * In combination with ::WIMLIB_UNMOUNT_FLAG_COMMIT for a read-write mounted WIM
+ * image, forces all file descriptors to the open WIM image to be closed before
+ * committing it.
+ *
+ * Without ::WIMLIB_UNMOUNT_FLAG_COMMIT or with a read-only mounted WIM image,
+ * this flag has no effect.
+ */
+#define WIMLIB_UNMOUNT_FLAG_FORCE                      0x00000010
 
 /** In combination with ::WIMLIB_UNMOUNT_FLAG_COMMIT for a read-write mounted
 
 /** In combination with ::WIMLIB_UNMOUNT_FLAG_COMMIT for a read-write mounted
- * image, causes the modified image to be committed as a new, unnamed image
- * appended to the archive.  The original image will be unmodified.  */
+ * WIM image, causes the modified image to be committed to the WIM file as a
+ * new, unnamed image appended to the archive.  The original image in the WIM
+ * file will be unmodified.  */
 #define WIMLIB_UNMOUNT_FLAG_NEW_IMAGE                  0x00000020
 
 /** @} */
 #define WIMLIB_UNMOUNT_FLAG_NEW_IMAGE                  0x00000020
 
 /** @} */
@@ -2076,6 +2111,9 @@ enum wimlib_error_code {
        WIMLIB_ERR_ABORTED_BY_PROGRESS,
        WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS,
        WIMLIB_ERR_MKNOD,
        WIMLIB_ERR_ABORTED_BY_PROGRESS,
        WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS,
        WIMLIB_ERR_MKNOD,
+       WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY,
+       WIMLIB_ERR_NOT_A_MOUNTPOINT,
+       WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT,
 };
 
 
 };
 
 
@@ -3062,82 +3100,75 @@ wimlib_join_with_progress(const wimlib_tchar * const *swms,
 /**
  * @ingroup G_mounting_wim_images
  *
 /**
  * @ingroup G_mounting_wim_images
  *
- * Mounts an image in a WIM file on a directory read-only or read-write.
- *
- * As this is implemented using FUSE (Filesystme in UserSpacE), this is not
- * supported if wimlib was configured with @c --without-fuse.  This includes
- * Windows builds of wimlib; ::WIMLIB_ERR_UNSUPPORTED will be returned in such
- * cases.
- *
- * Calling this function daemonizes the process, unless
- * ::WIMLIB_MOUNT_FLAG_DEBUG was specified or an early occur occurs.  If the
- * mount is read-write (::WIMLIB_MOUNT_FLAG_READWRITE specified), modifications
- * to the WIM are staged in a temporary directory.
- *
- * It is safe to mount multiple images from the same underlying WIM file
- * read-only at the same time, but only if different ::WIMStruct's are used.  It
- * is @b not safe to mount multiple images from the same WIM file read-write at
- * the same time.
- *
- * wimlib_mount_image() cannot be used on an image that was exported with
- * wimlib_export_image() while the dentry trees for both images are still in
- * memory.  In addition, wimlib_mount_image() may not be used to mount an image
- * that already has modifications pending (e.g. an image added with
- * wimlib_add_image()).
+ * Mounts an image from a WIM file on a directory read-only or read-write.
  *
  * @param wim
  *     Pointer to the ::WIMStruct containing the image to be mounted.
  * @param image
  *
  * @param wim
  *     Pointer to the ::WIMStruct containing the image to be mounted.
  * @param image
- *     The number of the image to mount, indexed starting from it.  It must be
- *     an existing, single image.
+ *     The 1-based index of the image to mount.
  * @param dir
  * @param dir
- *     The path to an existing empty directory to mount the image on.
+ *     The path to an existing empty directory on which to mount the WIM image.
  * @param mount_flags
  * @param mount_flags
- *     Bitwise OR of flags prefixed with WIMLIB_MOUNT_FLAG.
+ *     Bitwise OR of flags prefixed with WIMLIB_MOUNT_FLAG.  Use
+ *     ::WIMLIB_MOUNT_FLAG_READWRITE to request a read-write mount instead of a
+ *     read-only mount.
  * @param staging_dir
  * @param staging_dir
- *     If non-NULL, the name of a directory in which the staging directory will
- *     be created.  Ignored if ::WIMLIB_MOUNT_FLAG_READWRITE is not specified
- *     in @p mount_flags.  If left @c NULL, the staging directory is created in
- *     the same directory as the WIM file that @p wim was originally read from.
+ *     If non-NULL, the name of a directory in which a temporary directory for
+ *     storing modified or added files will be created.  Ignored if
+ *     ::WIMLIB_MOUNT_FLAG_READWRITE is not specified in @p mount_flags.  If
+ *     left @c NULL, the staging directory is created in the same directory as
+ *     the WIM file that @p wim was originally read from.  The staging
+ *     directory is deleted when the image is unmounted.
  *
  *
- * @return 0 on success; nonzero on error.
+ * @return 0 on success; nonzero on error.  The possible error codes include:
  *
  * @retval ::WIMLIB_ERR_ALREADY_LOCKED
  *
  * @retval ::WIMLIB_ERR_ALREADY_LOCKED
- *     A read-write mount was requested, but an exclusive advisory lock on
- *     the on-disk WIM file could not be acquired because another thread or
- *     process has mounted an image from the WIM read-write or is currently
- *     modifying the WIM in-place.
+ *     An image from the WIM file is already mounted read-write, or another
+ *     process is currently appending data to the WIM file.
  * @retval ::WIMLIB_ERR_FUSE
  * @retval ::WIMLIB_ERR_FUSE
- *     A non-zero status was returned by @c fuse_main().
+ *     A non-zero status code was returned by @c fuse_main().
  * @retval ::WIMLIB_ERR_INVALID_IMAGE
  *     @p image does not specify an existing, single image in @p wim.
  * @retval ::WIMLIB_ERR_INVALID_PARAM
  * @retval ::WIMLIB_ERR_INVALID_IMAGE
  *     @p image does not specify an existing, single image in @p wim.
  * @retval ::WIMLIB_ERR_INVALID_PARAM
- *     @p image is shared among multiple ::WIMStruct's as a result of a call to
- *     wimlib_export_image(), or @p image has been added with
- *     wimlib_add_image().
+ *     @p wim was @c NULL; or @p dir was NULL or the empty string; or an
+ *     unrecognized flag was specified in @p mount_flags; or the WIM image has
+ *     already been modified in memory (e.g. by wimlib_update_image()).
  * @retval ::WIMLIB_ERR_MKDIR
  *     ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but the
  *     staging directory could not be created.
  * @retval ::WIMLIB_ERR_MKDIR
  *     ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but the
  *     staging directory could not be created.
- * @retval ::WIMLIB_ERR_NOTDIR
- *     Could not determine the current working directory.
- * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND
- *     One of the dentries in the image referenced a stream not present in the
- *     WIM's lookup table (or in any of the lookup tables of the split WIM
- *     parts).
  * @retval ::WIMLIB_ERR_WIM_IS_READONLY
  * @retval ::WIMLIB_ERR_WIM_IS_READONLY
- *     ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but @p
- *     wim is considered read-only because of any of the reasons mentioned in
- *     the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
+ *     ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but the
+ *     WIM file is considered read-only because of any of the reasons mentioned
+ *     in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  * @retval ::WIMLIB_ERR_UNSUPPORTED
  *     Mounting is not supported, either because the platform is Windows, or
  * @retval ::WIMLIB_ERR_UNSUPPORTED
  *     Mounting is not supported, either because the platform is Windows, or
- *     because the platform is UNIX-like and wimlib was compiled with @c
- *     --without-fuse.
+ *     because the platform is UNIX-like and wimlib was compiled using
+ *     <code>--without-fuse</code>.
  *
  * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION,
  * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND,
  * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or
  * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for
  * different reasons) to read the metadata resource for the image to mount.
  *
  * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION,
  * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND,
  * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or
  * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for
  * different reasons) to read the metadata resource for the image to mount.
+ *
+ * The ability to mount WIM image is implemented using FUSE (Filesystem in
+ * UserSpacE).  Depending on how FUSE is set up on your system, this function
+ * may work as normal users in addition to the root user.
+ *
+ * Mounting WIM images is not supported if wimlib was configured
+ * <code>--without-fuse</code>.  This includes Windows builds of wimlib;
+ * ::WIMLIB_ERR_UNSUPPORTED will be returned in such cases.
+ *
+ * Calling this function daemonizes the process, unless
+ * ::WIMLIB_MOUNT_FLAG_DEBUG was specified or an early error occurs.
+ *
+ * It is safe to mount multiple images from the same underlying WIM file
+ * read-only at the same time, but only if different ::WIMStruct's are used.  It
+ * is @b not safe to mount multiple images from the same WIM file read-write at
+ * the same time.
+ *
+ * To unmount the image, call wimlib_unmount_image().  This may be done in a
+ * different process.
  */
 extern int
 wimlib_mount_image(WIMStruct *wim,
  */
 extern int
 wimlib_mount_image(WIMStruct *wim,
@@ -3861,64 +3892,38 @@ wimlib_split(WIMStruct *wim,
  *
  * Unmounts a WIM image that was mounted using wimlib_mount_image().
  *
  *
  * Unmounts a WIM image that was mounted using wimlib_mount_image().
  *
- * The image to unmount is specified by the path to the mountpoint, not the
- * original ::WIMStruct passed to wimlib_mount_image(), which should not be
- * touched and also may have been allocated in a different process.
- *
- * To unmount the image, the process calling this function communicates with the
- * process that is managing the mounted WIM image.  This function blocks until it
- * is known whether the unmount succeeded or failed.  In the case of a
- * read-write mounted WIM, the unmount is not considered to have succeeded until
- * all changes have been saved to the underlying WIM file.
+ * When unmounting a read-write mounted image, the default behavior is to
+ * discard changes to the image.  Use ::WIMLIB_UNMOUNT_FLAG_COMMIT to cause the
+ * WIM image to be committed.
  *
  * @param dir
  *
  * @param dir
- *     The directory that the WIM image was mounted on.
+ *     The directory the WIM image was mounted on.
  * @param unmount_flags
  * @param unmount_flags
- *     Bitwise OR of the flags ::WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY,
- *     ::WIMLIB_UNMOUNT_FLAG_COMMIT, ::WIMLIB_UNMOUNT_FLAG_REBUILD, and/or
- *     ::WIMLIB_UNMOUNT_FLAG_RECOMPRESS.  None of these flags affect read-only
- *     mounts.
+ *     Bitwise OR of flags prefixed with @p WIMLIB_UNMOUNT_FLAG.
  *
  *
- * @return 0 on success; nonzero on error.
+ * @return 0 on success; nonzero on error.  The possible error codes include:
  *
  *
- * @retval ::WIMLIB_ERR_DELETE_STAGING_DIR
- *     The filesystem daemon was unable to remove the staging directory and the
- *     temporary files that it contains.
- * @retval ::WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED
- *     The filesystem daemon appears to have terminated before sending an exit
- *     status.
- * @retval ::WIMLIB_ERR_FORK
- *     Could not @c fork() the process.
- * @retval ::WIMLIB_ERR_FUSERMOUNT
- *     The @b fusermount program could not be executed or exited with a failure
- *     status.
+ * @retval ::WIMLIB_ERR_NOT_A_MOUNTPOINT
+ *     There is no WIM image mounted on the specified directory.
+ * @retval ::WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY
+ *     The read-write mounted WIM image cannot be committed because there are
+ *     file descriptors open to it, and ::WIMLIB_UNMOUNT_FLAG_FORCE was not
+ *     specified.
  * @retval ::WIMLIB_ERR_MQUEUE
  * @retval ::WIMLIB_ERR_MQUEUE
- *     Could not open a POSIX message queue to communicate with the filesystem
- *     daemon servicing the mounted filesystem, could not send a message
- *     through the queue, or could not receive a message through the queue.
- * @retval ::WIMLIB_ERR_NOMEM
- *     Failed to allocate needed memory.
- * @retval ::WIMLIB_ERR_OPEN
- *     The filesystem daemon could not open a temporary file for writing the
- *     new WIM.
- * @retval ::WIMLIB_ERR_READ
- *     A read error occurred when the filesystem daemon tried to a file from
- *     the staging directory
- * @retval ::WIMLIB_ERR_RENAME
- *     The filesystem daemon failed to rename the newly written WIM file to the
- *     original WIM file.
+ *     Could not create a POSIX message queue.
+ * @retval ::WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT
+ *     The WIM image was mounted by a different user.
  * @retval ::WIMLIB_ERR_UNSUPPORTED
  *     Mounting is not supported, either because the platform is Windows, or
  * @retval ::WIMLIB_ERR_UNSUPPORTED
  *     Mounting is not supported, either because the platform is Windows, or
- *     because the platform is UNIX-like and wimlib was compiled with @c
+ *     because the platform is UNIX-like and wimlib was compiled using @c
  *     --without-fuse.
  *     --without-fuse.
- * @retval ::WIMLIB_ERR_WRITE
- *     A write error occurred when the filesystem daemon was writing to the new
- *     WIM file, or the filesystem daemon was unable to flush changes that had
- *     been made to files in the staging directory.
+ *
+ * Note: you can also unmount the image by using the @c umount() system call, or
+ * by using the @c umount or @c fusermount programs.  However, you need to call
+ * this function if you want changes to be committed.
  */
 extern int
  */
 extern int
-wimlib_unmount_image(const wimlib_tchar *dir,
-                    int unmount_flags);
+wimlib_unmount_image(const wimlib_tchar *dir, int unmount_flags);
 
 /**
  * @ingroup G_mounting_wim_images
 
 /**
  * @ingroup G_mounting_wim_images
index 8c6aa95fef8cdccb05c16dd759e804000b26b392..84b49590ef73620e63e9ed5654a5d222b115cc56 100644 (file)
@@ -177,10 +177,17 @@ struct wim_inode {
                };
 
 #ifdef WITH_FUSE
                };
 
 #ifdef WITH_FUSE
-               /* Used only during image mount:  Table of file descriptors that
-                * have been opened to this inode.  The table is automatically
-                * freed when the last file descriptor is closed.  */
-               struct wimfs_fd **i_fds;
+               struct {
+                       /* Used only during image mount:  Table of file
+                        * descriptors that have been opened to this inode.
+                        * This table is freed when the last file descriptor is
+                        * closed.  */
+                       struct wimfs_fd **i_fds;
+
+                       /* Lower bound on the index of the next available entry
+                        * in 'i_fds'.  */
+                       u16 i_next_fd;
+               };
 #endif
        };
 
 #endif
        };
 
@@ -316,8 +323,7 @@ free_inode(struct wim_inode *inode);
                dentry_full_path(inode_first_dentry(inode))
 
 extern struct wim_ads_entry *
                dentry_full_path(inode_first_dentry(inode))
 
 extern struct wim_ads_entry *
-inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
-                   u16 *idx_ret);
+inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name);
 
 extern struct wim_ads_entry *
 inode_add_ads_utf16le(struct wim_inode *inode,
 
 extern struct wim_ads_entry *
 inode_add_ads_utf16le(struct wim_inode *inode,
@@ -327,7 +333,7 @@ inode_add_ads_utf16le(struct wim_inode *inode,
 extern struct wim_ads_entry *
 inode_add_ads(struct wim_inode *dentry, const tchar *stream_name);
 
 extern struct wim_ads_entry *
 inode_add_ads(struct wim_inode *dentry, const tchar *stream_name);
 
-extern int
+extern struct wim_ads_entry *
 inode_add_ads_with_data(struct wim_inode *inode, const tchar *name,
                        const void *value, size_t size,
                        struct wim_lookup_table *lookup_table);
 inode_add_ads_with_data(struct wim_inode *inode, const tchar *name,
                        const void *value, size_t size,
                        struct wim_lookup_table *lookup_table);
@@ -340,7 +346,7 @@ inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len,
                         struct wim_lookup_table *lookup_table);
 
 extern void
                         struct wim_lookup_table *lookup_table);
 
 extern void
-inode_remove_ads(struct wim_inode *inode, u16 idx,
+inode_remove_ads(struct wim_inode *inode, struct wim_ads_entry *entry,
                 struct wim_lookup_table *lookup_table);
 
 static inline bool
                 struct wim_lookup_table *lookup_table);
 
 static inline bool
@@ -481,6 +487,15 @@ inode_stream_name_nbytes(const struct wim_inode *inode, unsigned stream_idx)
                return inode->i_ads_entries[stream_idx - 1].stream_name_nbytes;
 }
 
                return inode->i_ads_entries[stream_idx - 1].stream_name_nbytes;
 }
 
+static inline u32
+inode_stream_idx_to_id(const struct wim_inode *inode, unsigned stream_idx)
+{
+       if (stream_idx == 0)
+               return 0;
+       else
+               return inode->i_ads_entries[stream_idx - 1].stream_id;
+}
+
 extern struct wim_lookup_table_entry *
 inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *stream_idx_ret);
 
 extern struct wim_lookup_table_entry *
 inode_unnamed_stream_resolved(const struct wim_inode *inode, u16 *stream_idx_ret);
 
index 216565e1939b334f2e6c9ac576199683b9137045..f26a6573cbeabb2bb0976f16e57ffe44583f7ce0 100644 (file)
@@ -169,7 +169,10 @@ struct wim_lookup_table_entry {
                tchar *file_on_disk;
                void *attached_buffer;
        #ifdef WITH_FUSE
                tchar *file_on_disk;
                void *attached_buffer;
        #ifdef WITH_FUSE
-               tchar *staging_file_name;
+               struct {
+                       char *staging_file_name;
+                       int staging_dir_fd;
+               };
        #endif
        #ifdef WITH_NTFS_3G
                struct ntfs_location *ntfs_loc;
        #endif
        #ifdef WITH_NTFS_3G
                struct ntfs_location *ntfs_loc;
index 86926d8983232817d3f663814790f9477dfbdbd4..c78fe90524c4acd999f0742d2e70ff95bfa94a74 100644 (file)
@@ -66,7 +66,8 @@ struct WIMStruct {
         * generates WIMs with invalid reference counts.)  */
        u8 refcnts_ok : 1;
 
         * generates WIMs with invalid reference counts.)  */
        u8 refcnts_ok : 1;
 
-       u8 wim_locked : 1;
+       /* Has the underlying WIM file been locked for appending?  */
+       u8 locked_for_append : 1;
 
        /* One of WIMLIB_COMPRESSION_TYPE_*, cached from the header flags. */
        u8 compression_type;
 
        /* One of WIMLIB_COMPRESSION_TYPE_*, cached from the header flags. */
        u8 compression_type;
index 68ada29832e1efa6e0f49972bb7cf32c9162b070..c15e6ced5ebd75c2de401e24a467bb1bd6578c85 100644 (file)
 
 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
 extern int
 
 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
 extern int
-lock_wim(WIMStruct *wim, int fd);
+lock_wim_for_append(WIMStruct *wim, int fd);
+extern void
+unlock_wim_for_append(WIMStruct *wim, int fd);
 #else
 static inline int
 #else
 static inline int
-lock_wim(WIMStruct *wim, int fd)
+lock_wim_for_append(WIMStruct *wim, int fd)
+{
+       return 0;
+}
+static inline void
+unlock_wim_for_append(WIMStruct *wim, int fd)
 {
        return 0;
 }
 {
        return 0;
 }
index 6351475f5178494656bd5f653d2432d8c918c588..233e1f826125bb940e598df5b4d88fe01524a338 100644 (file)
@@ -365,6 +365,7 @@ static const struct option unmount_options[] = {
        {T("check"),   no_argument, NULL, IMAGEX_CHECK_OPTION},
        {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
        {T("lazy"),    no_argument, NULL, IMAGEX_LAZY_OPTION},
        {T("check"),   no_argument, NULL, IMAGEX_CHECK_OPTION},
        {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
        {T("lazy"),    no_argument, NULL, IMAGEX_LAZY_OPTION},
+       {T("force"),    no_argument, NULL, IMAGEX_FORCE_OPTION},
        {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
        {NULL, 0, NULL, 0},
 };
        {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
        {NULL, 0, NULL, 0},
 };
@@ -1216,6 +1217,20 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
                              info->wimboot_exclude.path_in_wim);
                break;
                imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
                              info->wimboot_exclude.path_in_wim);
                break;
+       case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
+               if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+                       if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
+                               imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
+                                             info->unmount.mounted_wim,
+                                             info->unmount.mounted_image);
+                       } else {
+                               imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
+                                             info->unmount.mounted_wim,
+                                             info->unmount.mounted_image);
+                               imagex_printf(T("\t(Use --commit to keep changes.)\n"));
+                       }
+               }
+               break;
        default:
                break;
        }
        default:
                break;
        }
@@ -3673,7 +3688,14 @@ imagex_unmount(int argc, tchar **argv, int cmd)
                        unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
                        break;
                case IMAGEX_LAZY_OPTION:
                        unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
                        break;
                case IMAGEX_LAZY_OPTION:
-                       unmount_flags |= WIMLIB_UNMOUNT_FLAG_LAZY;
+               case IMAGEX_FORCE_OPTION:
+                       /* Now, unmount is lazy by default.  However, committing
+                        * the image will fail with
+                        * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
+                        * file descriptors on the WIM image.  The
+                        * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
+                        * descriptors to be closed.  */
+                       unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
                        break;
                case IMAGEX_NEW_IMAGE_OPTION:
                        unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
                        break;
                case IMAGEX_NEW_IMAGE_OPTION:
                        unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
@@ -3693,13 +3715,19 @@ imagex_unmount(int argc, tchar **argv, int cmd)
                                       "without --commit also specified!"));
                        goto out_err;
                }
                                       "without --commit also specified!"));
                        goto out_err;
                }
-               imagex_printf(T("Committing changes as new image...\n"));
        }
 
        ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
                                                 imagex_progress_func, NULL);
        }
 
        ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
                                                 imagex_progress_func, NULL);
-       if (ret)
+       if (ret) {
                imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
                imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
+               if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
+                       imagex_printf(T(
+                               "\tNote: Use --commit --force to force changes "
+                                       "to be committed, regardless\n"
+                               "\t      of open files.\n"));
+               }
+       }
 out:
        return ret;
 
 out:
        return ret;
 
@@ -4049,8 +4077,8 @@ T(
 #if WIM_MOUNTING_SUPPORTED
 [CMD_UNMOUNT] =
 T(
 #if WIM_MOUNTING_SUPPORTED
 [CMD_UNMOUNT] =
 T(
-"    %"TS" DIRECTORY [--commit] [--check] [--rebuild] [--lazy]\n"
-"                    [--new-image]\n"
+"    %"TS" DIRECTORY [--commit] [--force] [--new-image]\n"
+"                         [--check] [--rebuild]\n"
 ),
 #endif
 [CMD_UPDATE] =
 ),
 #endif
 [CMD_UPDATE] =
index 2dcc6167164d7d063adaade8c72163f62a4ec093..763198d13844131da118f343178d1b68670cb1b9 100644 (file)
@@ -840,15 +840,13 @@ wim_pathname_to_stream(WIMStruct *wim,
 
        if (stream_name) {
                struct wim_ads_entry *ads_entry;
 
        if (stream_name) {
                struct wim_ads_entry *ads_entry;
-               u16 ads_idx;
-               ads_entry = inode_get_ads_entry(inode, stream_name,
-                                               &ads_idx);
-               if (ads_entry) {
-                       stream_idx = ads_idx + 1;
-                       lte = ads_entry->lte;
-               } else {
-                       return -ENOENT;
-               }
+
+               ads_entry = inode_get_ads_entry(inode, stream_name);
+               if (!ads_entry)
+                       return -errno;
+
+               stream_idx = ads_entry - inode->i_ads_entries + 1;
+               lte = ads_entry->lte;
        } else {
                lte = inode_unnamed_stream_resolved(inode, &stream_idx);
        }
        } else {
                lte = inode_unnamed_stream_resolved(inode, &stream_idx);
        }
index 57d92fb265b0cbbb2429636f7cdc8d5a95073ba3..ae971d8343da5bc239aec20cbf9401546b27efc2 100644 (file)
@@ -39,6 +39,8 @@
 #include "wimlib/security.h"
 #include "wimlib/timestamp.h"
 
 #include "wimlib/security.h"
 #include "wimlib/timestamp.h"
 
+#include <errno.h>
+
 /* Allocate a new inode.  Set the timestamps to the current time.  */
 struct wim_inode *
 new_inode(void)
 /* Allocate a new inode.  Set the timestamps to the current time.  */
 struct wim_inode *
 new_inode(void)
@@ -138,10 +140,11 @@ ads_entry_has_name(const struct wim_ads_entry *entry,
  *
  * If @p stream_name is the empty string, NULL is returned --- that is, this
  * function will not return "unnamed" alternate data stream entries.
  *
  * If @p stream_name is the empty string, NULL is returned --- that is, this
  * function will not return "unnamed" alternate data stream entries.
+ *
+ * If NULL is returned, errno is set.
  */
 struct wim_ads_entry *
  */
 struct wim_ads_entry *
-inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
-                   u16 *idx_ret)
+inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name)
 {
        int ret;
        const utf16lechar *stream_name_utf16le;
 {
        int ret;
        const utf16lechar *stream_name_utf16le;
@@ -149,11 +152,15 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
        u16 i;
        struct wim_ads_entry *result;
 
        u16 i;
        struct wim_ads_entry *result;
 
-       if (inode->i_num_ads == 0)
+       if (inode->i_num_ads == 0) {
+               errno = ENOENT;
                return NULL;
                return NULL;
+       }
 
 
-       if (stream_name[0] == T('\0'))
+       if (stream_name[0] == T('\0')) {
+               errno = ENOENT;
                return NULL;
                return NULL;
+       }
 
        ret = tstr_get_utf16le_and_len(stream_name, &stream_name_utf16le,
                                       &stream_name_utf16le_nbytes);
 
        ret = tstr_get_utf16le_and_len(stream_name, &stream_name_utf16le,
                                       &stream_name_utf16le_nbytes);
@@ -168,8 +175,6 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
                                       stream_name_utf16le_nbytes,
                                       default_ignore_case))
                {
                                       stream_name_utf16le_nbytes,
                                       default_ignore_case))
                {
-                       if (idx_ret)
-                               *idx_ret = i;
                        result = &inode->i_ads_entries[i];
                        break;
                }
                        result = &inode->i_ads_entries[i];
                        break;
                }
@@ -177,6 +182,8 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name,
 
        tstr_put_utf16le(stream_name_utf16le);
 
 
        tstr_put_utf16le(stream_name_utf16le);
 
+       if (!result)
+               errno = ENOENT;
        return result;
 }
 
        return result;
 }
 
@@ -190,6 +197,7 @@ do_inode_add_ads(struct wim_inode *inode,
 
        if (inode->i_num_ads >= 0xfffe) {
                ERROR("Too many alternate data streams in one inode!");
 
        if (inode->i_num_ads >= 0xfffe) {
                ERROR("Too many alternate data streams in one inode!");
+               errno = EFBIG;
                return NULL;
        }
        num_ads = inode->i_num_ads + 1;
                return NULL;
        }
        num_ads = inode->i_num_ads + 1;
@@ -232,24 +240,21 @@ inode_add_ads_utf16le(struct wim_inode *inode,
 
 /*
  * Add an alternate stream entry to a WIM inode.  On success, returns a pointer
 
 /*
  * Add an alternate stream entry to a WIM inode.  On success, returns a pointer
- * to the new entry; on failure, returns NULL.
+ * to the new entry; on failure, returns NULL and sets errno.
  */
 struct wim_ads_entry *
 inode_add_ads(struct wim_inode *inode, const tchar *stream_name)
 {
        utf16lechar *stream_name_utf16le = NULL;
        size_t stream_name_utf16le_nbytes = 0;
  */
 struct wim_ads_entry *
 inode_add_ads(struct wim_inode *inode, const tchar *stream_name)
 {
        utf16lechar *stream_name_utf16le = NULL;
        size_t stream_name_utf16le_nbytes = 0;
-       int ret;
        struct wim_ads_entry *result;
 
        struct wim_ads_entry *result;
 
-       if (stream_name && *stream_name) {
-               ret = tstr_to_utf16le(stream_name,
-                                     tstrlen(stream_name) * sizeof(tchar),
-                                     &stream_name_utf16le,
-                                     &stream_name_utf16le_nbytes);
-               if (ret)
+       if (stream_name && *stream_name)
+               if (tstr_to_utf16le(stream_name,
+                                   tstrlen(stream_name) * sizeof(tchar),
+                                   &stream_name_utf16le,
+                                   &stream_name_utf16le_nbytes))
                        return NULL;
                        return NULL;
-       }
 
        result = do_inode_add_ads(inode, stream_name_utf16le,
                                  stream_name_utf16le_nbytes);
 
        result = do_inode_add_ads(inode, stream_name_utf16le,
                                  stream_name_utf16le_nbytes);
@@ -258,27 +263,25 @@ inode_add_ads(struct wim_inode *inode, const tchar *stream_name)
        return result;
 }
 
        return result;
 }
 
-int
+struct wim_ads_entry *
 inode_add_ads_with_data(struct wim_inode *inode, const tchar *name,
                        const void *value, size_t size,
                        struct wim_lookup_table *lookup_table)
 {
 inode_add_ads_with_data(struct wim_inode *inode, const tchar *name,
                        const void *value, size_t size,
                        struct wim_lookup_table *lookup_table)
 {
-       struct wim_ads_entry *new_ads_entry;
+       struct wim_ads_entry *new_entry;
 
        wimlib_assert(inode->i_resolved);
 
 
        wimlib_assert(inode->i_resolved);
 
-       new_ads_entry = inode_add_ads(inode, name);
-       if (new_ads_entry == NULL)
-               return WIMLIB_ERR_NOMEM;
+       new_entry = inode_add_ads(inode, name);
+       if (!new_entry)
+               return NULL;
 
 
-       new_ads_entry->lte = new_stream_from_data_buffer(value, size,
-                                                        lookup_table);
-       if (new_ads_entry->lte == NULL) {
-               inode_remove_ads(inode, new_ads_entry - inode->i_ads_entries,
-                                lookup_table);
-               return WIMLIB_ERR_NOMEM;
+       new_entry->lte = new_stream_from_data_buffer(value, size, lookup_table);
+       if (!new_entry->lte) {
+               inode_remove_ads(inode, new_entry, lookup_table);
+               return NULL;
        }
        }
-       return 0;
+       return new_entry;
 }
 
 bool
 }
 
 bool
@@ -305,22 +308,20 @@ inode_set_unnamed_stream(struct wim_inode *inode, const void *data, size_t len,
 
 /* Remove an alternate data stream from a WIM inode  */
 void
 
 /* Remove an alternate data stream from a WIM inode  */
 void
-inode_remove_ads(struct wim_inode *inode, u16 idx,
+inode_remove_ads(struct wim_inode *inode, struct wim_ads_entry *entry,
                 struct wim_lookup_table *lookup_table)
 {
                 struct wim_lookup_table *lookup_table)
 {
-       struct wim_ads_entry *ads_entry;
        struct wim_lookup_table_entry *lte;
        struct wim_lookup_table_entry *lte;
+       unsigned idx = entry - inode->i_ads_entries;
 
        wimlib_assert(idx < inode->i_num_ads);
        wimlib_assert(inode->i_resolved);
 
 
        wimlib_assert(idx < inode->i_num_ads);
        wimlib_assert(inode->i_resolved);
 
-       ads_entry = &inode->i_ads_entries[idx];
-
-       lte = ads_entry->lte;
+       lte = entry->lte;
        if (lte)
                lte_decrement_refcnt(lte, lookup_table);
 
        if (lte)
                lte_decrement_refcnt(lte, lookup_table);
 
-       destroy_ads_entry(ads_entry);
+       destroy_ads_entry(entry);
 
        memmove(&inode->i_ads_entries[idx],
                &inode->i_ads_entries[idx + 1],
 
        memmove(&inode->i_ads_entries[idx],
                &inode->i_ads_entries[idx + 1],
index 1950ab21a89bbbefb75fc56473ce64936d12753c..0e4f96a3244a0c917097830afeab1972e8a72dd8 100644 (file)
@@ -269,7 +269,8 @@ lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
                         * that there still may be open file descriptors to it.)
                         * */
                        if (lte->resource_location == RESOURCE_IN_STAGING_FILE)
                         * that there still may be open file descriptors to it.)
                         * */
                        if (lte->resource_location == RESOURCE_IN_STAGING_FILE)
-                               unlink(lte->staging_file_name);
+                               unlinkat(lte->staging_dir_fd,
+                                        lte->staging_file_name, 0);
                #endif
                } else {
                        if (!should_retain_lte(lte))
                #endif
                } else {
                        if (!should_retain_lte(lte))
index 168ee8523bc9bc038a9c678bdbf9c2f50fce62e9..86cc8153e65d5bc37882487b0033bc957e6de6c2 100644 (file)
 #endif
 
 #include "wimlib.h"
 #endif
 
 #include "wimlib.h"
-#include "wimlib/error.h"
 
 #ifdef WITH_FUSE
 
 #ifdef __WIN32__
 
 #ifdef WITH_FUSE
 
 #ifdef __WIN32__
-#  error "FUSE mount not supported on Win32!  Please configure --without-fuse"
+#  error "FUSE mount not supported on Windows!  Please configure --without-fuse"
 #endif
 
 #endif
 
-#include "wimlib/encoding.h"
-#include "wimlib/file_io.h"
 #include "wimlib/dentry.h"
 #include "wimlib/dentry.h"
-#include "wimlib/inode.h"
-#include "wimlib/lookup_table.h"
+#include "wimlib/encoding.h"
 #include "wimlib/metadata.h"
 #include "wimlib/paths.h"
 #include "wimlib/progress.h"
 #include "wimlib/reparse.h"
 #include "wimlib/metadata.h"
 #include "wimlib/paths.h"
 #include "wimlib/progress.h"
 #include "wimlib/reparse.h"
-#include "wimlib/resource.h"
 #include "wimlib/timestamp.h"
 #include "wimlib/unix_data.h"
 #include "wimlib/timestamp.h"
 #include "wimlib/unix_data.h"
-#include "wimlib/version.h"
 #include "wimlib/write.h"
 #include "wimlib/xml.h"
 
 #include "wimlib/write.h"
 #include "wimlib/xml.h"
 
+#include <dirent.h>
 #include <errno.h>
 #include <errno.h>
-#include <ftw.h>
 #include <limits.h>
 #include <mqueue.h>
 #include <limits.h>
 #include <mqueue.h>
-#include <signal.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 #include <utime.h>
 
 #define FUSE_USE_VERSION 26
 #include <fuse.h>
 #include <unistd.h>
 #include <utime.h>
 
 #define FUSE_USE_VERSION 26
 #include <fuse.h>
-
-#ifdef ENABLE_XATTR
 #include <attr/xattr.h>
 #include <attr/xattr.h>
+
+#ifndef O_NOFOLLOW
+#  define O_NOFOLLOW 0  /* Security only...  */
 #endif
 
 #endif
 
-#define MSG_VERSION_TOO_HIGH   -1
-#define MSG_BREAK_LOOP         -2
+#define WIMFS_MQUEUE_NAME_LEN 32
+
+#define WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS 0x80000000
+
+struct wimfs_unmount_info {
+       unsigned unmount_flags;
+       char mq_name[WIMFS_MQUEUE_NAME_LEN + 1];
+};
+
+struct commit_progress_report {
+       enum wimlib_progress_msg msg;
+       union wimlib_progress_info info;
+};
 
 
-/* File descriptor to a file open on the WIM filesystem. */
+/* Description of an open file on a mounted WIM image.  Actually, this
+ * represents the open state of a particular data stream of an inode, rather
+ * than the inode itself.  (An inode might have multiple named data streams in
+ * addition to the default, unnamed data stream.)  At a given time, an inode in
+ * the WIM image might have multiple file descriptors open to it, each to any
+ * one of its data streams.  */
 struct wimfs_fd {
 struct wimfs_fd {
+
+       /* Pointer to the inode of this open file.
+        * 'i_num_opened_fds' of the inode tracks the number of file descriptors
+        * that reference it.  */
        struct wim_inode *f_inode;
        struct wim_inode *f_inode;
+
+       /* Pointer to the lookup table entry for the data stream that has been
+        * opened.  'num_opened_fds' of the lookup table entry tracks the number
+        * of file descriptors that reference it.  Or, this value may be NULL,
+        * which indicates that the opened stream is empty and consequently does
+        * not have a lookup table entry.  */
        struct wim_lookup_table_entry *f_lte;
        struct wim_lookup_table_entry *f_lte;
-       struct filedes staging_fd;
-       u16 idx;
-       u32 stream_id;
+
+       /* If valid (filedes_valid(&f_staging_fd)), this contains the
+        * corresponding native file descriptor for the staging file that has
+        * been created for reading from and/or writing to this open stream.  A
+        * single staging file might have multiple file descriptors open to it
+        * simultaneously, each used by a different 'struct wimfs_fd'.
+        *
+        * Or, if invalid (!filedes_valid(&f_staging_fd)), this 'struct
+        * wimfs_fd' is not associated with a staging file.  This is permissible
+        * only if this 'struct wimfs_fd' was opened read-only and the stream
+        * has not yet been extracted to a staging file.  */
+       struct filedes f_staging_fd;
+
+       /* 0-based index of this file descriptor in the file descriptor table of
+        * its inode.  */
+       u16 f_idx;
+
+       /* Unique ID of the opened stream in the inode.  This will stay the same
+        * even if the indices of the inode's alternate data streams are changed
+        * by a deletion.  */
+       u32 f_stream_id;
 };
 
 };
 
+#define WIMFS_FD(fi) ((struct wimfs_fd *)(uintptr_t)((fi)->fh))
+
+/* Context structure for a mounted WIM image.  */
 struct wimfs_context {
 struct wimfs_context {
-       /* The WIMStruct for the mounted WIM. */
+       /* The WIMStruct containing the mounted image.  The mounted image is the
+        * currently selected image (wim->current_image).  */
        WIMStruct *wim;
 
        WIMStruct *wim;
 
-       /* Name of the staging directory for a read-write mount.  Whenever a new file is
-        * created, it is done so in the staging directory.  Furthermore, whenever a
-        * file in the WIM is modified, it is extracted to the staging directory.  If
-        * changes are commited when the WIM is unmounted, the file resources are merged
-        * in from the staging directory when writing the new WIM. */
-       char *staging_dir_name;
-       size_t staging_dir_name_len;
-
-       /* Flags passed to wimlib_mount(). */
+       /* Flags passed to wimlib_mount_image() (WIMLIB_MOUNT_FLAG_*).  */
        int mount_flags;
 
        int mount_flags;
 
-       /* Default flags to use when looking up a WIM dentry (depends on whether
-        * the Windows interface to alternate data streams is being used or
-        * not). */
+       /* Default flags for path lookup in the WIM image.  */
        int default_lookup_flags;
 
        int default_lookup_flags;
 
-       /* Next inode number to be assigned.  Note: I didn't bother with a
-        * bitmap of free inode numbers since this isn't even a "real"
-        * filesystem anyway. */
-       u64 next_ino;
-
-       /* List of inodes in the mounted image */
-       struct list_head *image_inode_list;
+       /* Information about the user who has mounted the WIM image  */
+       uid_t owner_uid;
+       gid_t owner_gid;
 
 
-       /* Original list of streams in the mounted image, linked by
-        * mount_orig_stream_list.  */
-       struct list_head orig_stream_list;
+       /* Information about the staging directory for a read-write mount.  */
+       int parent_dir_fd;
+       int staging_dir_fd;
+       char *staging_dir_name;
 
 
-       /* Name and message queue descriptors for message queues between the
-        * filesystem daemon process and the unmount process.  These are used
-        * when the filesystem is unmounted and the process running
-        * wimlib_unmount_image() needs to communicate with the filesystem
-        * daemon running fuse_main() (i.e. the process created by a call to
-        * wimlib_mount_image().  */
-       char *unmount_to_daemon_mq_name;
-       char *daemon_to_unmount_mq_name;
-       mqd_t unmount_to_daemon_mq;
-       mqd_t daemon_to_unmount_mq;
+       /* For read-write mounts, the inode number to be assigned to the next
+        * created file.  Note: since this isn't a persistent filesystem and we
+        * can re-assign the inode numbers just before mounting the image, it's
+        * good enough to just generate inode numbers sequentially.  */
+       u64 next_ino;
 
 
-       uid_t default_uid;
-       gid_t default_gid;
+       /* Number of file descriptors open to the mounted WIM image.  */
+       unsigned long num_open_fds;
 
 
-       int status;
-       bool have_status;
+       /* Original list of single-instance streams in the mounted image, linked
+        * by 'struct wim_lookup_table_entry'.orig_stream_list.  */
+       struct list_head orig_stream_list;
 };
 
 };
 
-static void
-init_wimfs_context(struct wimfs_context *ctx)
-{
-       memset(ctx, 0, sizeof(*ctx));
-       ctx->unmount_to_daemon_mq = (mqd_t)-1;
-       ctx->daemon_to_unmount_mq = (mqd_t)-1;
-}
-
 #define WIMFS_CTX(fuse_ctx) ((struct wimfs_context*)(fuse_ctx)->private_data)
 
 #define WIMFS_CTX(fuse_ctx) ((struct wimfs_context*)(fuse_ctx)->private_data)
 
+/* Retrieve the context structure for the currently mounted WIM image.
+ *
+ * Note: this is a per-thread variable.  It is possible for different threads to
+ * mount different images at the same time in the same process, although they
+ * must use different WIMStructs!  */
 static inline struct wimfs_context *
 wimfs_get_context(void)
 {
        return WIMFS_CTX(fuse_get_context());
 }
 
 static inline struct wimfs_context *
 wimfs_get_context(void)
 {
        return WIMFS_CTX(fuse_get_context());
 }
 
+static void
+wimfs_inc_num_open_fds(void)
+{
+       wimfs_get_context()->num_open_fds++;
+}
+
+static void
+wimfs_dec_num_open_fds(void)
+{
+       wimfs_get_context()->num_open_fds--;
+}
+
+/* Retrieve the WIMStruct for the currently mounted WIM image.  */
 static inline WIMStruct *
 wimfs_get_WIMStruct(void)
 {
 static inline WIMStruct *
 wimfs_get_WIMStruct(void)
 {
@@ -166,23 +201,37 @@ get_lookup_flags(const struct wimfs_context *ctx)
        return ctx->default_lookup_flags;
 }
 
        return ctx->default_lookup_flags;
 }
 
-/* Returns nonzero if write permission is requested on the file open flags */
-static inline int
+/* Is write permission requested on the file?  */
+static inline bool
 flags_writable(int open_flags)
 {
        int accmode = (open_flags & O_ACCMODE);
        return (accmode == O_RDWR || accmode == O_WRONLY);
 }
 
 flags_writable(int open_flags)
 {
        int accmode = (open_flags & O_ACCMODE);
        return (accmode == O_RDWR || accmode == O_WRONLY);
 }
 
+static mode_t
+fuse_mask_mode(mode_t mode, const struct fuse_context *fuse_ctx)
+{
+#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
+       mode &= ~fuse_ctx->umask;
+#endif
+       return mode;
+}
+
 /*
 /*
- * Allocate a file descriptor for a stream.
+ * Allocate a file descriptor to a data stream in the mounted WIM image.
  *
  *
- * @inode:     inode containing the stream we're opening
- * @stream_id: ID of the stream we're opening
- * @lte:       Lookup table entry for the stream (may be NULL)
- * @fd_ret:    Return the allocated file descriptor if successful.
+ * @inode
+ *     A pointer to the inode containing the stream being opened.
+ * @stream_id
+ *     The ID of the data stream being opened within the inode.
+ * @lte
+ *     A pointer to the lookup table entry for the stream data.  Or, for a
+ *     0-byte stream, this may be NULL.
+ * @fd_ret
+ *     On success, a pointer to the new file descriptor will be stored here.
  *
  *
- * Return 0 iff successful or negative error code if unsuccessful.
+ * Returns 0 or a -errno code.
  */
 static int
 alloc_wimfs_fd(struct wim_inode *inode,
  */
 static int
 alloc_wimfs_fd(struct wim_inode *inode,
@@ -190,27 +239,29 @@ alloc_wimfs_fd(struct wim_inode *inode,
               struct wim_lookup_table_entry *lte,
               struct wimfs_fd **fd_ret)
 {
               struct wim_lookup_table_entry *lte,
               struct wimfs_fd **fd_ret)
 {
-       static const u16 fds_per_alloc = 8;
+       static const u16 min_fds_per_alloc = 8;
        static const u16 max_fds = 0xffff;
        static const u16 max_fds = 0xffff;
-
-       DEBUG("Allocating fd for stream ID %u from inode %#"PRIx64" "
-             "(open = %u, allocated = %u)",
-             stream_id, inode->i_ino, inode->i_num_opened_fds,
-             inode->i_num_allocated_fds);
+       u16 i;
+       struct wimfs_fd *fd;
 
        if (inode->i_num_opened_fds == inode->i_num_allocated_fds) {
 
        if (inode->i_num_opened_fds == inode->i_num_allocated_fds) {
-               struct wimfs_fd **fds;
                u16 num_new_fds;
                u16 num_new_fds;
+               struct wimfs_fd **fds;
 
 
-               if (inode->i_num_allocated_fds == max_fds)
-                       return -EMFILE;
+               /* Expand this inode's file descriptor table.  */
+
+               num_new_fds = max(min_fds_per_alloc,
+                                 inode->i_num_allocated_fds / 4);
 
 
-               num_new_fds = min(fds_per_alloc,
+               num_new_fds = min(num_new_fds,
                                  max_fds - inode->i_num_allocated_fds);
 
                                  max_fds - inode->i_num_allocated_fds);
 
+               if (num_new_fds == 0)
+                       return -EMFILE;
+
                fds = REALLOC(inode->i_fds,
                              (inode->i_num_allocated_fds + num_new_fds) *
                fds = REALLOC(inode->i_fds,
                              (inode->i_num_allocated_fds + num_new_fds) *
-                               sizeof(inode->i_fds[0]));
+                               sizeof(fds[0]));
                if (!fds)
                        return -ENOMEM;
 
                if (!fds)
                        return -ENOMEM;
 
@@ -218,111 +269,127 @@ alloc_wimfs_fd(struct wim_inode *inode,
                       num_new_fds * sizeof(fds[0]));
                inode->i_fds = fds;
                inode->i_num_allocated_fds += num_new_fds;
                       num_new_fds * sizeof(fds[0]));
                inode->i_fds = fds;
                inode->i_num_allocated_fds += num_new_fds;
+               inode->i_next_fd = inode->i_num_opened_fds;
        }
        }
-       for (u16 i = 0; ; i++) {
-               if (!inode->i_fds[i]) {
-                       struct wimfs_fd *fd = CALLOC(1, sizeof(*fd));
-                       if (!fd)
-                               return -ENOMEM;
-
-                       fd->f_inode     = inode;
-                       fd->f_lte       = lte;
-                       filedes_invalidate(&fd->staging_fd);
-                       fd->idx         = i;
-                       fd->stream_id   = stream_id;
-                       *fd_ret         = fd;
-                       inode->i_fds[i] = fd;
-                       inode->i_num_opened_fds++;
-                       if (lte)
-                               lte->num_opened_fds++;
-                       DEBUG("Allocated fd (idx = %u)", fd->idx);
-                       return 0;
-               }
-       }
+
+       /* Allocate the file descriptor in the first available space in the
+        * inode's file descriptor table.
+        *
+        * i_next_fd is the lower bound on the next open slot.  */
+       for (i = inode->i_next_fd; inode->i_fds[i]; i++)
+               ;
+
+       fd = MALLOC(sizeof(*fd));
+       if (!fd)
+               return -ENOMEM;
+
+       fd->f_inode     = inode;
+       fd->f_lte       = lte;
+       filedes_invalidate(&fd->f_staging_fd);
+       fd->f_idx       = i;
+       fd->f_stream_id = stream_id;
+       *fd_ret         = fd;
+       inode->i_fds[i] = fd;
+       inode->i_num_opened_fds++;
+       if (lte)
+               lte->num_opened_fds++;
+       wimfs_inc_num_open_fds();
+       inode->i_next_fd = i + 1;
+       return 0;
 }
 
 }
 
-static void
-inode_put_fd(struct wim_inode *inode, struct wimfs_fd *fd)
+/*
+ * Close a file descriptor to a data stream in the mounted WIM image.
+ *
+ * Returns 0 or a -errno code.  The file descriptor is always closed.
+ */
+static int
+close_wimfs_fd(struct wimfs_fd *fd)
 {
 {
-       wimlib_assert(inode != NULL);
-       wimlib_assert(fd->f_inode == inode);
-       wimlib_assert(inode->i_num_opened_fds != 0);
-       wimlib_assert(fd->idx < inode->i_num_allocated_fds);
-       wimlib_assert(inode->i_fds[fd->idx] == fd);
+       int ret = 0;
+       struct wim_inode *inode;
+
+       /* Close the staging file if open.  */
+       if (filedes_valid(&fd->f_staging_fd))
+                if (filedes_close(&fd->f_staging_fd))
+                        ret = -errno;
+
+       /* Release this file descriptor from its lookup table entry.  */
+       if (fd->f_lte)
+               lte_decrement_num_opened_fds(fd->f_lte);
+
+       wimfs_dec_num_open_fds();
 
 
-       inode->i_fds[fd->idx] = NULL;
+       /* Release this file descriptor from its inode.  */
+       inode = fd->f_inode;
+       inode->i_fds[fd->f_idx] = NULL;
+       if (fd->f_idx < inode->i_next_fd)
+               inode->i_next_fd = fd->f_idx;
        FREE(fd);
        if (--inode->i_num_opened_fds == 0) {
        FREE(fd);
        if (--inode->i_num_opened_fds == 0) {
+               /* The last file descriptor to this inode was closed.  */
                FREE(inode->i_fds);
                inode->i_fds = NULL;
                inode->i_num_allocated_fds = 0;
                if (inode->i_nlink == 0)
                FREE(inode->i_fds);
                inode->i_fds = NULL;
                inode->i_num_allocated_fds = 0;
                if (inode->i_nlink == 0)
+                       /* No links to this inode remain.  Get rid of it.  */
                        free_inode(inode);
        }
                        free_inode(inode);
        }
+       return ret;
 }
 
 }
 
-static int
-lte_put_fd(struct wim_lookup_table_entry *lte, struct wimfs_fd *fd)
-{
-       wimlib_assert(fd->f_lte == lte);
-
-       if (!lte) /* Empty stream with no lookup table entry */
-               return 0;
-
-       /* Close staging file descriptor if needed. */
-
-       if (lte->resource_location == RESOURCE_IN_STAGING_FILE
-            && filedes_valid(&fd->staging_fd))
-       {
-               if (filedes_close(&fd->staging_fd)) {
-                       ERROR_WITH_ERRNO("Failed to close staging file");
-                       return -errno;
-               }
-       }
-       lte_decrement_num_opened_fds(lte);
-       return 0;
-}
-
-/* Close a file descriptor. */
-static int
-close_wimfs_fd(struct wimfs_fd *fd)
+/*
+ * Translate a path into the corresponding inode in the mounted WIM image.
+ *
+ * See get_dentry() for more information.
+ *
+ * Returns a pointer to the resulting inode, or NULL with errno set.
+ */
+static struct wim_inode *
+wim_pathname_to_inode(WIMStruct *wim, const char *path)
 {
 {
-       int ret;
-       DEBUG("Closing fd (ino = %#"PRIx64", opened = %u, allocated = %u)",
-             fd->f_inode->i_ino, fd->f_inode->i_num_opened_fds,
-             fd->f_inode->i_num_allocated_fds);
-       ret = lte_put_fd(fd->f_lte, fd);
-       if (ret)
-               return ret;
-
-       inode_put_fd(fd->f_inode, fd);
-       return 0;
-}
+       struct wim_dentry *dentry;
 
 
-static mode_t
-fuse_mask_mode(mode_t mode, struct fuse_context *fuse_ctx)
-{
-#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
-       mode &= ~fuse_ctx->umask;
-#endif
-       return mode;
+       dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
+       if (!dentry)
+               return NULL;
+       return dentry->d_inode;
 }
 
 /*
 }
 
 /*
- * Add a new dentry with a new inode to a WIM image.
+ * Create a new file in the mounted WIM image.
  *
  *
- * Returns 0 on success, or negative error number on failure.
+ * @fuse_ctx
+ *     The FUSE context for the mounted image.
+ * @path
+ *     The path at which to create the first link to the new file.  If a file
+ *     already exists at this path, -EEXIST is returned.
+ * @mode
+ *     The UNIX mode for the new file.  This is only honored if
+ *     WIMLIB_MOUNT_FLAG_UNIX_DATA was passed to wimlib_mount_image().
+ * @rdev
+ *     The device ID for the new file, encoding the major and minor device
+ *     numbers.  This is only honored if WIMLIB_MOUNT_FLAG_UNIX_DATA was passed
+ *     to wimlib_mount_image().
+ * @attributes
+ *     Windows file attributes to use for the new file.
+ * @dentry_ret
+ *     On success, a pointer to the new dentry is returned here.  Its d_inode
+ *     member will point to the new inode that was created for it and added to
+ *     the mounted WIM image.
+ *
+ * Returns 0 or a -errno code.
  */
 static int
 create_dentry(struct fuse_context *fuse_ctx, const char *path,
  */
 static int
 create_dentry(struct fuse_context *fuse_ctx, const char *path,
-             mode_t mode, dev_t rdev, int attributes,
+             mode_t mode, dev_t rdev, u32 attributes,
              struct wim_dentry **dentry_ret)
 {
              struct wim_dentry **dentry_ret)
 {
+       struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
        struct wim_dentry *parent;
        struct wim_dentry *parent;
-       struct wim_dentry *new;
        const char *basename;
        const char *basename;
-       struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
-       int ret;
+       struct wim_dentry *new_dentry;
+       struct wim_inode *new_inode;
 
        parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE);
        if (!parent)
 
        parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE);
        if (!parent)
@@ -332,16 +399,18 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path,
                return -ENOTDIR;
 
        basename = path_basename(path);
                return -ENOTDIR;
 
        basename = path_basename(path);
+
        if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE))
                return -EEXIST;
 
        if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE))
                return -EEXIST;
 
-       ret = new_dentry_with_inode(basename, &new);
-       if (ret)
+       if (new_dentry_with_inode(basename, &new_dentry))
                return -ENOMEM;
 
                return -ENOMEM;
 
-       new->d_inode->i_resolved = 1;
-       new->d_inode->i_ino = wimfs_ctx->next_ino++;
-       new->d_inode->i_attributes = attributes;
+       new_inode = new_dentry->d_inode;
+
+       new_inode->i_resolved = 1;
+       new_inode->i_ino = wimfs_ctx->next_ino++;
+       new_inode->i_attributes = attributes;
 
        if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
                struct wimlib_unix_data unix_data;
 
        if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
                struct wimlib_unix_data unix_data;
@@ -350,50 +419,44 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path,
                unix_data.gid = fuse_ctx->gid;
                unix_data.mode = fuse_mask_mode(mode, fuse_ctx);
                unix_data.rdev = rdev;
                unix_data.gid = fuse_ctx->gid;
                unix_data.mode = fuse_mask_mode(mode, fuse_ctx);
                unix_data.rdev = rdev;
-
-               if (!inode_set_unix_data(new->d_inode, &unix_data,
-                                        UNIX_DATA_ALL))
+               if (!inode_set_unix_data(new_inode, &unix_data, UNIX_DATA_ALL))
                {
                {
-                       free_dentry(new);
+                       free_dentry(new_dentry);
                        return -ENOMEM;
                }
        }
                        return -ENOMEM;
                }
        }
-       dentry_add_child(parent, new);
-       list_add_tail(&new->d_inode->i_list, wimfs_ctx->image_inode_list);
+
+       list_add_tail(&new_inode->i_list,
+                     &wim_get_current_image_metadata(wimfs_ctx->wim)->inode_list);
+
+       dentry_add_child(parent, new_dentry);
+
        if (dentry_ret)
        if (dentry_ret)
-               *dentry_ret = new;
+               *dentry_ret = new_dentry;
        return 0;
 }
 
        return 0;
 }
 
-static struct wim_inode *
-wim_pathname_to_inode(WIMStruct *wim, const tchar *path)
-{
-       struct wim_dentry *dentry;
-       dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
-       if (dentry)
-               return dentry->d_inode;
-       else
-               return NULL;
-}
-
 /*
 /*
- * Remove a dentry from a mounted WIM image; i.e. remove an alias for an inode.
+ * Remove a dentry from the mounted WIM image; i.e. remove an alias for an
+ * inode.
  */
 static void
 remove_dentry(struct wim_dentry *dentry,
              struct wim_lookup_table *lookup_table)
 {
  */
 static void
 remove_dentry(struct wim_dentry *dentry,
              struct wim_lookup_table *lookup_table)
 {
-       /* Put a reference to each stream the inode contains.  */
+       /* Drop the reference to each stream the inode contains.  */
        inode_unref_streams(dentry->d_inode, lookup_table);
 
        /* Unlink the dentry from the image's dentry tree.  */
        unlink_dentry(dentry);
 
        /* Delete the dentry.  This will also decrement the link count of the
        inode_unref_streams(dentry->d_inode, lookup_table);
 
        /* Unlink the dentry from the image's dentry tree.  */
        unlink_dentry(dentry);
 
        /* Delete the dentry.  This will also decrement the link count of the
-        * corresponding inode.  */
+        * corresponding inode, and possibly cause it to be deleted as well.  */
        free_dentry(dentry);
 }
 
        free_dentry(dentry);
 }
 
+/* Generate UNIX filetype mode bits for the specified WIM inode, based on its
+ * Windows file attributes.  */
 static mode_t
 inode_unix_file_type(const struct wim_inode *inode)
 {
 static mode_t
 inode_unix_file_type(const struct wim_inode *inode)
 {
@@ -405,17 +468,22 @@ inode_unix_file_type(const struct wim_inode *inode)
                return S_IFREG;
 }
 
                return S_IFREG;
 }
 
+/* Generate a default UNIX mode for the specified WIM inode.  */
 static mode_t
 inode_default_unix_mode(const struct wim_inode *inode)
 {
        return inode_unix_file_type(inode) | 0777;
 }
 
 static mode_t
 inode_default_unix_mode(const struct wim_inode *inode)
 {
        return inode_unix_file_type(inode) | 0777;
 }
 
-/* Transfers file attributes from a struct wim_inode to a `stat' buffer.
+/*
+ * Retrieve standard UNIX metadata ('struct stat') for a WIM inode.
  *
  *
- * The lookup table entry tells us which stream in the inode we are statting.
- * For a named data stream, everything returned is the same as the unnamed data
- * stream except possibly the size and block count. */
+ * @lte specifies the stream of the inode that is being queried.  We mostly
+ * return the same information for all streams, but st_size and st_blocks may be
+ * different for different streams.
+ *
+ * This always returns 0.
+ */
 static int
 inode_to_stbuf(const struct wim_inode *inode,
               const struct wim_lookup_table_entry *lte,
 static int
 inode_to_stbuf(const struct wim_inode *inode,
               const struct wim_lookup_table_entry *lte,
@@ -428,22 +496,25 @@ inode_to_stbuf(const struct wim_inode *inode,
        if ((ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) &&
            inode_get_unix_data(inode, &unix_data))
        {
        if ((ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) &&
            inode_get_unix_data(inode, &unix_data))
        {
+               /* Use the user ID, group ID, mode, and device ID from the
+                * inode's extra UNIX metadata information.  */
                stbuf->st_uid = unix_data.uid;
                stbuf->st_gid = unix_data.gid;
                stbuf->st_mode = unix_data.mode;
                stbuf->st_rdev = unix_data.rdev;
        } else {
                stbuf->st_uid = unix_data.uid;
                stbuf->st_gid = unix_data.gid;
                stbuf->st_mode = unix_data.mode;
                stbuf->st_rdev = unix_data.rdev;
        } else {
-               stbuf->st_uid = ctx->default_uid;
-               stbuf->st_gid = ctx->default_gid;
+               /* Generate default values for the user ID, group ID, and mode.
+                *
+                * Note: in the case of an allow_other mount, fuse_context.uid
+                * may not be the same as wimfs_context.owner_uid!  */
+               stbuf->st_uid = ctx->owner_uid;
+               stbuf->st_gid = ctx->owner_gid;
                stbuf->st_mode = inode_default_unix_mode(inode);
                stbuf->st_mode = inode_default_unix_mode(inode);
-               stbuf->st_rdev = 0;
        }
        }
-       stbuf->st_ino = (ino_t)inode->i_ino;
+       stbuf->st_ino = inode->i_ino;
        stbuf->st_nlink = inode->i_nlink;
        if (lte)
                stbuf->st_size = lte->size;
        stbuf->st_nlink = inode->i_nlink;
        if (lte)
                stbuf->st_size = lte->size;
-       else
-               stbuf->st_size = 0;
 #ifdef HAVE_STAT_NANOSECOND_PRECISION
        stbuf->st_atim = wim_timestamp_to_timespec(inode->i_last_access_time);
        stbuf->st_mtim = wim_timestamp_to_timespec(inode->i_last_write_time);
 #ifdef HAVE_STAT_NANOSECOND_PRECISION
        stbuf->st_atim = wim_timestamp_to_timespec(inode->i_last_access_time);
        stbuf->st_mtim = wim_timestamp_to_timespec(inode->i_last_write_time);
@@ -457,6 +528,7 @@ inode_to_stbuf(const struct wim_inode *inode,
        return 0;
 }
 
        return 0;
 }
 
+/* Update the last access and last write timestamps of a WIM inode.  */
 static void
 touch_inode(struct wim_inode *inode)
 {
 static void
 touch_inode(struct wim_inode *inode)
 {
@@ -465,61 +537,37 @@ touch_inode(struct wim_inode *inode)
        inode->i_last_write_time = now;
 }
 
        inode->i_last_write_time = now;
 }
 
-/* Creates a new staging file and returns its file descriptor opened for
- * writing.
- *
- * @name_ret: A location into which the a pointer to the newly allocated name of
- *           the staging file is stored.
+/*
+ * Create a new file in the staging directory for a read-write mounted image.
  *
  *
- * @ctx:      Context for the WIM filesystem; this provides the name of the
- *           staging directory.
+ * On success, returns the file descriptor for the new staging file, opened for
+ * writing.  In addition, stores the allocated name of the staging file in
+ * @name_ret.
  *
  *
- * On success, returns the file descriptor for the staging file, opened for
- * writing.  On failure, returns -1 and sets errno.
+ * On failure, returns -1 and sets errno.
  */
 static int
  */
 static int
-create_staging_file(char **name_ret, struct wimfs_context *ctx)
+create_staging_file(const struct wimfs_context *ctx, char **name_ret)
 {
 {
-       size_t name_len;
-       char *name;
-       struct stat stbuf;
-       int fd;
-       int errno_save;
 
        static const size_t STAGING_FILE_NAME_LEN = 20;
 
        static const size_t STAGING_FILE_NAME_LEN = 20;
+       char *name;
+       int fd;
 
 
-       name_len = ctx->staging_dir_name_len + 1 + STAGING_FILE_NAME_LEN;
-       name = MALLOC(name_len + 1);
-       if (!name) {
-               errno = ENOMEM;
-               return -1;
-       }
-
-       do {
-
-               memcpy(name, ctx->staging_dir_name, ctx->staging_dir_name_len);
-               name[ctx->staging_dir_name_len] = '/';
-               randomize_char_array_with_alnum(name + ctx->staging_dir_name_len + 1,
-                                               STAGING_FILE_NAME_LEN);
-               name[name_len] = '\0';
-
-
-       /* Just in case, verify that the randomly generated name doesn't name an
-        * existing file, and try again if so  */
-       } while (stat(name, &stbuf) == 0);
-
-       if (errno != ENOENT) /* other error?! */
+       name = MALLOC(STAGING_FILE_NAME_LEN + 1);
+       if (!name)
                return -1;
                return -1;
-
-       /* doesn't exist--- ok */
-
-       DEBUG("Creating staging file `%s'", name);
-
-       fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0600);
-       if (fd == -1) {
-               errno_save = errno;
+       name[STAGING_FILE_NAME_LEN] = '\0';
+
+retry:
+       randomize_char_array_with_alnum(name, STAGING_FILE_NAME_LEN);
+       fd = openat(ctx->staging_dir_fd, name,
+                   O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
+       if (unlikely(fd < 0)) {
+               if (unlikely(errno == EEXIST))
+                       /* Try again with another name.  */
+                       goto retry;
                FREE(name);
                FREE(name);
-               errno = errno_save;
        } else {
                *name_ret = name;
        }
        } else {
                *name_ret = name;
        }
@@ -528,104 +576,105 @@ create_staging_file(char **name_ret, struct wimfs_context *ctx)
 
 /*
  * Extract a WIM resource to the staging directory.
 
 /*
  * Extract a WIM resource to the staging directory.
+ * This is necessary if a stream using the resource is being opened for writing.
  *
  *
- * @inode:  Inode that contains the stream we are extracting
+ * @inode
+ *     The inode containing the stream being opened for writing.
  *
  *
- * @stream_id: Identifier for the stream (it stays constant even if the indices
- * of the stream entries are changed)
+ * @stream_idx
+ *     The index of the stream in @inode being opened for writing.
  *
  *
- * @lte: Pointer to pointer to the lookup table entry for the stream we need to
- * extract, or NULL if there was no lookup table entry present for the stream
+ * @lte_ptr
+ *     *lte_ptr is the lookup table entry for the stream being extracted, or
+ *     NULL if the stream does not have a lookup table entry (which is possible
+ *     if the stream is empty).  On success, *lte_ptr will be set to point to a
+ *     lookup table entry that represents the resource in its new location in a
+ *     staging file.  This may be the same as the old entry in the case that it
+ *     was reused, or it may be a new entry.
  *
  *
- * @size:  Number of bytes of the stream we want to extract (this supports the
- * wimfs_truncate() function).  It may be more than the actual stream length, in
- * which case the extra space is filled with zeroes.
+ * @size
+ *     Number of bytes of the stream to extract and include in the staging file
+ *     resource.  It may be less than the actual stream length, in which case
+ *     only a prefix of the resource will be extracted.  It may also be more
+ *     than the actual stream length, in which case the extra space will be
+ *     zero-filled.
  *
  *
- * @ctx:  Context for the WIM filesystem.
- *
- * Returns 0 on success or a negative error code on failure.
+ * Returns 0 or a -errno code.
  */
 static int
 extract_resource_to_staging_dir(struct wim_inode *inode,
  */
 static int
 extract_resource_to_staging_dir(struct wim_inode *inode,
-                               u32 stream_id,
-                               struct wim_lookup_table_entry **lte,
+                               u16 stream_idx,
+                               struct wim_lookup_table_entry **lte_ptr,
                                off_t size,
                                off_t size,
-                               struct wimfs_context *ctx)
+                               const struct wimfs_context *ctx)
 {
 {
+       struct wim_lookup_table_entry *old_lte;
+       struct wim_lookup_table_entry *new_lte;
        char *staging_file_name;
        char *staging_file_name;
-       int ret;
-       int fd;
-       struct wim_lookup_table_entry *old_lte, *new_lte;
+       int staging_fd;
        off_t extract_size;
        off_t extract_size;
+       int result;
+       u32 stream_id;
+       int ret;
 
 
-       DEBUG("Extracting resource to staging dir: inode %"PRIu64", "
-             "stream id %"PRIu32, inode->i_ino, stream_id);
-
-       old_lte = *lte;
-
-       wimlib_assert(old_lte == NULL ||
-                     old_lte->resource_location != RESOURCE_IN_STAGING_FILE);
+       old_lte = *lte_ptr;
 
 
-       /* Create the staging file */
-       fd = create_staging_file(&staging_file_name, ctx);
-       if (fd == -1)
+       /* Create the staging file */
+       staging_fd = create_staging_file(ctx, &staging_file_name);
+       if (unlikely(staging_fd < 0))
                return -errno;
 
                return -errno;
 
-       /* Extract the stream to the staging file (possibly truncated) */
+       /* Extract the stream to the staging file (possibly truncated) */
        if (old_lte) {
        if (old_lte) {
-               struct filedes wimlib_fd;
-               filedes_init(&wimlib_fd, fd);
+               struct filedes fd;
+
+               filedes_init(&fd, staging_fd);
+               errno = 0;
                extract_size = min(old_lte->size, size);
                extract_size = min(old_lte->size, size);
-               ret = extract_stream_to_fd(old_lte, &wimlib_fd, extract_size);
+               result = extract_stream_to_fd(old_lte, &fd, extract_size);
        } else {
        } else {
-               ret = 0;
                extract_size = 0;
                extract_size = 0;
+               result = 0;
        }
 
        /* In the case of truncate() to more than the file length, extend the
        }
 
        /* In the case of truncate() to more than the file length, extend the
-        * file with zeroes by calling ftruncate() on the underlying staging
-        * file */
-       if (ret == 0 && size > extract_size)
-               ret = ftruncate(fd, size);
-
-       /* Close the staging file descriptor and check for errors.  If there's
-        * an error, unlink the staging file. */
-       if (ret != 0 || close(fd) != 0) {
-               if (errno != 0)
-                       ret = -errno;
-               else
-                       ret = -EIO;
-               close(fd);
+        * staging file with zeroes by calling ftruncate().  */
+       if (!result && size > extract_size)
+               result = ftruncate(staging_fd, size);
+
+       /* Close the staging file.  */
+       if (close(staging_fd))
+               result = -1;
+
+       /* If an error occurred, unlink the staging file.  */
+       if (unlikely(result)) {
+               /* extract_stream_to_fd() should set errno, but if it didn't,
+                * set a default value.  */
+               ret = errno ? -errno : -EIO;
                goto out_delete_staging_file;
        }
 
        /* Now deal with the lookup table entries.  We may be able to re-use the
                goto out_delete_staging_file;
        }
 
        /* Now deal with the lookup table entries.  We may be able to re-use the
-        * existing entry, but we may have to create a new one instead. */
+        * existing entry, but we may have to create a new one instead.  */
+
+       stream_id = inode_stream_idx_to_id(inode, stream_idx);
 
        if (old_lte && inode->i_nlink == old_lte->refcnt) {
                /* The reference count of the existing lookup table entry is the
                 * same as the link count of the inode that contains the stream
 
        if (old_lte && inode->i_nlink == old_lte->refcnt) {
                /* The reference count of the existing lookup table entry is the
                 * same as the link count of the inode that contains the stream
-                * we're opening.  Therefore, ALL the references to the lookup
+                * we're opening.  Therefore, all the references to the lookup
                 * table entry correspond to the stream we're trying to extract,
                 * so the lookup table entry can be re-used.  */
                 * table entry correspond to the stream we're trying to extract,
                 * so the lookup table entry can be re-used.  */
-               DEBUG("Re-using lookup table entry");
                lookup_table_unlink(ctx->wim->lookup_table, old_lte);
                lookup_table_unlink(ctx->wim->lookup_table, old_lte);
+               lte_put_resource(old_lte);
                new_lte = old_lte;
        } else {
                new_lte = old_lte;
        } else {
-               if (old_lte) {
-                       /* There's an existing lookup table entry, but its
-                        * reference count is greater than the link count for
-                        * the inode containing a stream we're opening.
-                        * Therefore, we need to split the lookup table entry.
-                        */
-                       wimlib_assert(old_lte->refcnt > inode->i_nlink);
-                       DEBUG("Splitting lookup table entry "
-                             "(inode->i_nlink = %u, old_lte->refcnt = %u)",
-                             inode->i_nlink, old_lte->refcnt);
-               }
+               /* We need to split the old lookup table entry because it also
+                * has other references.  Or, there was no old lookup table
+                * entry, so we need to create a new one anyway.  */
 
                new_lte = new_lookup_table_entry();
 
                new_lte = new_lookup_table_entry();
-               if (!new_lte) {
+               if (unlikely(!new_lte)) {
                        ret = -ENOMEM;
                        goto out_delete_staging_file;
                }
                        ret = -ENOMEM;
                        goto out_delete_staging_file;
                }
@@ -640,568 +689,268 @@ extract_resource_to_staging_dir(struct wim_inode *inode,
                 * At the same time, we need to count the number of these opened
                 * file descriptors to the new lookup table entry.  If there's
                 * an old lookup table entry, this number needs to be subtracted
                 * At the same time, we need to count the number of these opened
                 * file descriptors to the new lookup table entry.  If there's
                 * an old lookup table entry, this number needs to be subtracted
-                * from the fd's opened to the old entry. */
+                * from the fd's opened to the old entry.  */
                for (u16 i = 0, j = 0; j < inode->i_num_opened_fds; i++) {
                for (u16 i = 0, j = 0; j < inode->i_num_opened_fds; i++) {
-                       struct wimfs_fd *fd = inode->i_fds[i];
-                       if (fd) {
-                               if (fd->stream_id == stream_id) {
-                                       int raw_fd;
-
-                                       wimlib_assert(fd->f_lte == old_lte);
-                                       wimlib_assert(!filedes_valid(&fd->staging_fd));
-                                       fd->f_lte = new_lte;
-                                       new_lte->num_opened_fds++;
-                                       raw_fd = open(staging_file_name, O_RDONLY);
-                                       if (raw_fd < 0) {
-                                               ret = -errno;
-                                               goto out_revert_fd_changes;
-                                       }
-                                       filedes_init(&fd->staging_fd, raw_fd);
-                               }
-                               j++;
+                       struct wimfs_fd *fd;
+                       int raw_fd;
+
+                       fd = inode->i_fds[i];
+                       if (!fd)
+                               continue;
+
+                       j++;
+
+                       if (fd->f_stream_id != stream_id)
+                               continue;
+
+                       /* This is a readonly fd for the same stream.  */
+                       fd->f_lte = new_lte;
+                       new_lte->num_opened_fds++;
+                       raw_fd = openat(ctx->staging_dir_fd, staging_file_name,
+                                       O_RDONLY | O_NOFOLLOW);
+                       if (unlikely(raw_fd < 0)) {
+                               ret = -errno;
+                               goto out_revert_fd_changes;
                        }
                        }
+                       filedes_init(&fd->f_staging_fd, raw_fd);
                }
                }
-               DEBUG("%hu fd's were already opened to the file we extracted",
-                     new_lte->num_opened_fds);
                if (old_lte) {
                        old_lte->num_opened_fds -= new_lte->num_opened_fds;
                        old_lte->refcnt -= inode->i_nlink;
                }
        }
 
                if (old_lte) {
                        old_lte->num_opened_fds -= new_lte->num_opened_fds;
                        old_lte->refcnt -= inode->i_nlink;
                }
        }
 
-       lte_put_resource(new_lte);
-       new_lte->refcnt              = inode->i_nlink;
-       new_lte->resource_location   = RESOURCE_IN_STAGING_FILE;
-       new_lte->staging_file_name   = staging_file_name;
-       new_lte->size                = size;
+       new_lte->refcnt            = inode->i_nlink;
+       new_lte->resource_location = RESOURCE_IN_STAGING_FILE;
+       new_lte->staging_file_name = staging_file_name;
+       new_lte->staging_dir_fd    = ctx->staging_dir_fd;
+       new_lte->size              = size;
 
        add_unhashed_stream(new_lte, inode, stream_id,
                            &wim_get_current_image_metadata(ctx->wim)->unhashed_streams);
 
        add_unhashed_stream(new_lte, inode, stream_id,
                            &wim_get_current_image_metadata(ctx->wim)->unhashed_streams);
-       *retrieve_lte_pointer(new_lte) = new_lte;
-       *lte = new_lte;
+       if (stream_idx == 0)
+               inode->i_lte = new_lte;
+       else
+               inode->i_ads_entries[stream_idx - 1].lte = new_lte;
+       *lte_ptr = new_lte;
        return 0;
        return 0;
+
 out_revert_fd_changes:
 out_revert_fd_changes:
-       for (u16 i = 0, j = 0; j < new_lte->num_opened_fds; i++) {
+       for (u16 i = 0; new_lte->num_opened_fds; i++) {
                struct wimfs_fd *fd = inode->i_fds[i];
                struct wimfs_fd *fd = inode->i_fds[i];
-               if (fd && fd->stream_id == stream_id && fd->f_lte == new_lte) {
+               if (fd && fd->f_stream_id == stream_id) {
                        fd->f_lte = old_lte;
                        fd->f_lte = old_lte;
-                       if (filedes_valid(&fd->staging_fd)) {
-                               filedes_close(&fd->staging_fd);
-                               filedes_invalidate(&fd->staging_fd);
+                       if (filedes_valid(&fd->f_staging_fd)) {
+                               filedes_close(&fd->f_staging_fd);
+                               filedes_invalidate(&fd->f_staging_fd);
                        }
                        }
-                       j++;
+                       new_lte->num_opened_fds--;
                }
        }
        free_lookup_table_entry(new_lte);
 out_delete_staging_file:
                }
        }
        free_lookup_table_entry(new_lte);
 out_delete_staging_file:
-       unlink(staging_file_name);
+       unlinkat(ctx->staging_dir_fd, staging_file_name, 0);
        FREE(staging_file_name);
        return ret;
 }
 
 /*
        FREE(staging_file_name);
        return ret;
 }
 
 /*
- * Creates a randomly named staging directory and saves its name in the
- * filesystem context structure.
+ * Create the staging directory for the WIM file.
+ *
+ * The staging directory will be created in the directory specified by the open
+ * file descriptor @parent_dir_fd.  It will be given a randomly generated name
+ * based on @wim_basename, the name of the WIM file.
+ *
+ * On success, returns a file descriptor to the open staging directory with
+ * O_RDONLY access.  In addition, stores the allocated name of the staging
+ * directory (relative to @parent_dir_fd) in @staging_dir_name_ret.
+ * On failure, returns -1 and sets errno.
  */
 static int
  */
 static int
-make_staging_dir(struct wimfs_context *ctx, const char *user_prefix)
+make_staging_dir_at(int parent_dir_fd, const char *wim_basename,
+                   char **staging_dir_name_ret)
 {
 {
+       static const char common_suffix[8] = ".staging";
        static const size_t random_suffix_len = 10;
        static const size_t random_suffix_len = 10;
-       static const char *common_suffix = ".staging";
-       static const size_t common_suffix_len = 8;
-
-       char *staging_dir_name = NULL;
+       size_t wim_basename_len;
        size_t staging_dir_name_len;
        size_t staging_dir_name_len;
-       size_t prefix_len;
-       const char *wim_basename;
-       char *real_user_prefix = NULL;
-       int ret;
-
-       if (user_prefix) {
-               real_user_prefix = realpath(user_prefix, NULL);
-               if (!real_user_prefix) {
-                       ERROR_WITH_ERRNO("Could not resolve `%s'",
-                                        real_user_prefix);
-                       ret = WIMLIB_ERR_NOTDIR;
-                       goto out;
-               }
-               wim_basename = path_basename(ctx->wim->filename);
-               prefix_len = strlen(real_user_prefix) + 1 + strlen(wim_basename);
-       } else {
-               prefix_len = strlen(ctx->wim->filename);
-       }
-
-       staging_dir_name_len = prefix_len + common_suffix_len + random_suffix_len;
+       char *staging_dir_name;
+       char *p;
+       int fd;
 
 
+       wim_basename_len = strlen(wim_basename);
+       staging_dir_name_len = wim_basename_len + sizeof(common_suffix) +
+                              random_suffix_len;
        staging_dir_name = MALLOC(staging_dir_name_len + 1);
        staging_dir_name = MALLOC(staging_dir_name_len + 1);
-       if (!staging_dir_name) {
-               ret = WIMLIB_ERR_NOMEM;
-               goto out;
-       }
-
-       if (real_user_prefix)
-               sprintf(staging_dir_name, "%s/%s", real_user_prefix, wim_basename);
-       else
-               strcpy(staging_dir_name, ctx->wim->filename);
+       if (!staging_dir_name)
+               return -1;
 
 
-       strcat(staging_dir_name, common_suffix);
+       p = staging_dir_name;
+       p = mempcpy(p, wim_basename, wim_basename_len);
+       p = mempcpy(p, common_suffix, sizeof(common_suffix));
+       randomize_char_array_with_alnum(p, random_suffix_len);
+       p += random_suffix_len;
+       *p = '\0';
 
 
-       randomize_char_array_with_alnum(staging_dir_name + prefix_len + common_suffix_len,
-                                       random_suffix_len);
+       if (mkdirat(parent_dir_fd, staging_dir_name, 0700))
+               goto err1;
 
 
-       staging_dir_name[staging_dir_name_len] = '\0';
+       fd = openat(parent_dir_fd, staging_dir_name,
+                   O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+       if (fd < 0)
+               goto err2;
 
 
-       if (mkdir(staging_dir_name, 0700) != 0) {
-               ERROR_WITH_ERRNO("Failed to create temporary directory `%s'",
-                                staging_dir_name);
-               ret = WIMLIB_ERR_MKDIR;
-       } else {
-               ret = 0;
-       }
-out:
-       FREE(real_user_prefix);
-       if (ret == 0) {
-               ctx->staging_dir_name = staging_dir_name;
-               ctx->staging_dir_name_len = staging_dir_name_len;
-       } else {
-               FREE(staging_dir_name);
-       }
-       return ret;
-}
+       *staging_dir_name_ret = staging_dir_name;
+       return fd;
 
 
-static int
-remove_file_or_directory(const char *fpath, const struct stat *sb,
-                        int typeflag, struct FTW *ftwbuf)
-{
-       if (remove(fpath) == 0)
-               return 0;
-       else {
-               ERROR_WITH_ERRNO("Cannot remove `%s'", fpath);
-               return WIMLIB_ERR_DELETE_STAGING_DIR;
-       }
+err2:
+       unlinkat(parent_dir_fd, staging_dir_name, AT_REMOVEDIR);
+err1:
+       FREE(staging_dir_name);
+       return -1;
 }
 
 /*
 }
 
 /*
- * Deletes the staging directory and all the files contained in it.
+ * Create the staging directory and set ctx->staging_dir_fd,
+ * ctx->staging_dir_name, and ctx->parent_dir_fd.
  */
 static int
  */
 static int
-delete_staging_dir(struct wimfs_context *ctx)
+make_staging_dir(struct wimfs_context *ctx, const char *parent_dir_path)
 {
 {
+       const char *wim_basename;
+       char *end = NULL;
        int ret;
        int ret;
-       ret = nftw(ctx->staging_dir_name, remove_file_or_directory,
-                  10, FTW_DEPTH);
-       FREE(ctx->staging_dir_name);
-       ctx->staging_dir_name = NULL;
-       return ret;
-}
 
 
-static int
-inode_close_fds(struct wim_inode *inode)
-{
-       u16 num_opened_fds = inode->i_num_opened_fds;
-       for (u16 i = 0, j = 0; j < num_opened_fds; i++) {
-               struct wimfs_fd *fd = inode->i_fds[i];
-               if (fd) {
-                       wimlib_assert(fd->f_inode == inode);
-                       int ret = close_wimfs_fd(fd);
-                       if (ret != 0)
-                               return ret;
-                       j++;
+       wim_basename = path_basename(ctx->wim->filename);
+
+       if (!parent_dir_path) {
+               /* The user did not specify a directory.  Default to creating
+                * the staging directory alongside the WIM file.  */
+               if (wim_basename > ctx->wim->filename) {
+                       parent_dir_path = ctx->wim->filename;
+                       end = (char *)(wim_basename - 1);
+                       /* *end must be a slash.  Temporarily overwrite it so we
+                        * can open the parent directory.  */
+                       *end = '\0';
+               } else {
+                       parent_dir_path = ".";
                }
        }
                }
        }
-       return 0;
-}
-
-/* Overwrites the WIM file, with changes saved. */
-static int
-rebuild_wim(struct wimfs_context *ctx, int write_flags)
-{
-       int ret;
-       struct wim_lookup_table_entry *lte, *tmp;
-       WIMStruct *wim = ctx->wim;
-       struct wim_image_metadata *imd = wim_get_current_image_metadata(ctx->wim);
 
 
-       DEBUG("Closing all staging file descriptors.");
-       image_for_each_unhashed_stream_safe(lte, tmp, imd) {
-               ret = inode_close_fds(lte->back_inode);
-               if (ret)
-                       return ret;
+       /* Open the parent directory (in which we'll create our staging
+        * directory).  */
+       ctx->parent_dir_fd = open(parent_dir_path, O_RDONLY | O_DIRECTORY);
+       if (ctx->parent_dir_fd < 0) {
+               ERROR_WITH_ERRNO("Can't open directory \"%s\"",
+                                parent_dir_path);
+               ret = WIMLIB_ERR_OPENDIR;
+               goto out_restore_wim_filename;
        }
 
        }
 
-       DEBUG("Freeing entries for zero-length streams");
-       image_for_each_unhashed_stream_safe(lte, tmp, imd) {
-               wimlib_assert(lte->unhashed);
-               if (lte->size == 0) {
-                       struct wim_lookup_table_entry **back_ptr;
-                       back_ptr = retrieve_lte_pointer(lte);
-                       *back_ptr = NULL;
-                       list_del(&lte->unhashed_list);
-                       free_lookup_table_entry(lte);
-               }
+       ctx->staging_dir_fd = make_staging_dir_at(ctx->parent_dir_fd,
+                                                 wim_basename,
+                                                 &ctx->staging_dir_name);
+       if (ctx->staging_dir_fd < 0) {
+               ERROR_WITH_ERRNO("Can't create staging directory in \"%s\"",
+                                parent_dir_path);
+               close(ctx->parent_dir_fd);
+               ret = WIMLIB_ERR_MKDIR;
+               goto out_restore_wim_filename;
        }
        }
-
-       xml_update_image_info(wim, wim->current_image);
-       ret = wimlib_overwrite(wim, write_flags, 0);
-       if (ret)
-               ERROR("Failed to commit changes to mounted WIM image");
+       ret = 0;
+out_restore_wim_filename:
+       if (end)
+               *end = '/';
        return ret;
 }
 
        return ret;
 }
 
-/* Simple function that returns the concatenation of 2 strings. */
-static char *
-strcat_dup(const char *s1, const char *s2, size_t max_len)
+/* Deletes the staging directory, undoing the effects of a succesful call to
+ * make_staging_dir().  */
+static void
+delete_staging_dir(struct wimfs_context *ctx)
 {
 {
-       size_t len = strlen(s1) + strlen(s2);
-       if (len > max_len)
-               len = max_len;
-       char *p = MALLOC(len + 1);
-       if (!p)
-               return NULL;
-       snprintf(p, len + 1, "%s%s", s1, s2);
-       return p;
+       DIR *dir;
+       struct dirent *ent;
+
+       dir = fdopendir(ctx->staging_dir_fd);
+       if (dir) {
+               while ((ent = readdir(dir)))
+                       unlinkat(ctx->staging_dir_fd, ent->d_name, 0);
+               closedir(dir);
+       } else {
+               close(ctx->staging_dir_fd);
+       }
+       if (unlinkat(ctx->parent_dir_fd, ctx->staging_dir_name, AT_REMOVEDIR))
+               WARNING_WITH_ERRNO("Could not delete staging directory");
+       FREE(ctx->staging_dir_name);
+       close(ctx->parent_dir_fd);
 }
 
 }
 
-static int
-set_message_queue_names(struct wimfs_context *ctx, const char *mount_dir)
+static void
+reassign_inode_numbers(struct wimfs_context *ctx)
 {
 {
-       static const char *u2d_prefix = "/wimlib-unmount-to-daemon-mq";
-       static const char *d2u_prefix = "/wimlib-daemon-to-unmount-mq";
-       char *dir_path;
-       char *p;
-       int ret;
-
-       dir_path = realpath(mount_dir, NULL);
-       if (!dir_path) {
-               ERROR_WITH_ERRNO("Failed to resolve path \"%s\"", mount_dir);
-               if (errno == ENOMEM)
-                       return WIMLIB_ERR_NOMEM;
-               else
-                       return WIMLIB_ERR_NOTDIR;
-       }
+       struct wim_image_metadata *imd;
+       struct wim_inode *inode;
 
 
-       for (p = dir_path; *p; p++)
-               if (*p == '/')
-                       *p = 0xff;
-
-       ctx->unmount_to_daemon_mq_name = strcat_dup(u2d_prefix, dir_path,
-                                                   NAME_MAX);
-       if (!ctx->unmount_to_daemon_mq_name) {
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_free_dir_path;
-       }
-       ctx->daemon_to_unmount_mq_name = strcat_dup(d2u_prefix, dir_path,
-                                                   NAME_MAX);
-       if (!ctx->daemon_to_unmount_mq_name) {
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_free_unmount_to_daemon_mq_name;
-       }
-
-       ret = 0;
-       goto out_free_dir_path;
-out_free_unmount_to_daemon_mq_name:
-       FREE(ctx->unmount_to_daemon_mq_name);
-       ctx->unmount_to_daemon_mq_name = NULL;
-out_free_dir_path:
-       FREE(dir_path);
-       return ret;
-}
+       ctx->next_ino = 1;
+       imd = wim_get_current_image_metadata(ctx->wim);
+       image_for_each_inode(inode, imd)
+               inode->i_ino = ctx->next_ino++;
+}
 
 static void
 
 static void
-free_message_queue_names(struct wimfs_context *ctx)
-{
-       FREE(ctx->unmount_to_daemon_mq_name);
-       FREE(ctx->daemon_to_unmount_mq_name);
-       ctx->unmount_to_daemon_mq_name = NULL;
-       ctx->daemon_to_unmount_mq_name = NULL;
-}
-
-/*
- * Opens two POSIX message queue: one for sending messages from the unmount
- * process to the daemon process, and one to go the other way.  The names of the
- * message queues, which must be system-wide unique, are be based on the mount
- * point.
- *
- * @daemon specifies whether the calling process is the filesystem daemon or the
- * unmount process.
- */
-static int
-open_message_queues(struct wimfs_context *ctx, bool daemon)
-{
-       int unmount_to_daemon_mq_flags = O_WRONLY | O_CREAT;
-       int daemon_to_unmount_mq_flags = O_RDONLY | O_CREAT;
-       mode_t mode;
-       mode_t orig_umask;
-       int ret;
-
-       if (daemon) {
-               swap(unmount_to_daemon_mq_flags, daemon_to_unmount_mq_flags);
-               mode = 0600;
-       } else {
-               mode = 0666;
-       }
-
-       orig_umask = umask(0000);
-       DEBUG("Opening message queue \"%s\"", ctx->unmount_to_daemon_mq_name);
-       ctx->unmount_to_daemon_mq = mq_open(ctx->unmount_to_daemon_mq_name,
-                                           unmount_to_daemon_mq_flags, mode, NULL);
-
-       if (ctx->unmount_to_daemon_mq == (mqd_t)-1) {
-               ERROR_WITH_ERRNO("mq_open()");
-               ret = WIMLIB_ERR_MQUEUE;
-               goto out;
-       }
-
-       DEBUG("Opening message queue \"%s\"", ctx->daemon_to_unmount_mq_name);
-       ctx->daemon_to_unmount_mq = mq_open(ctx->daemon_to_unmount_mq_name,
-                                           daemon_to_unmount_mq_flags, mode, NULL);
-
-       if (ctx->daemon_to_unmount_mq == (mqd_t)-1) {
-               ERROR_WITH_ERRNO("mq_open()");
-               mq_close(ctx->unmount_to_daemon_mq);
-               mq_unlink(ctx->unmount_to_daemon_mq_name);
-               ctx->unmount_to_daemon_mq = (mqd_t)-1;
-               ret = WIMLIB_ERR_MQUEUE;
-               goto out;
-       }
-       ret = 0;
-out:
-       umask(orig_umask);
-       return ret;
-}
-
-/* Try to determine the maximum message size of a message queue.  The return
- * value is the maximum message size, or a guess of 8192 bytes if it cannot be
- * determined. */
-static long
-mq_get_msgsize(mqd_t mq)
-{
-       static const char *msgsize_max_file = "/proc/sys/fs/mqueue/msgsize_max";
-       FILE *fp;
-       struct mq_attr attr;
-       long msgsize;
-
-       if (mq_getattr(mq, &attr) == 0) {
-               msgsize = attr.mq_msgsize;
-       } else {
-               ERROR_WITH_ERRNO("mq_getattr()");
-               ERROR("Attempting to read %s", msgsize_max_file);
-               fp = fopen(msgsize_max_file, "rb");
-               if (fp) {
-                       if (fscanf(fp, "%ld", &msgsize) != 1) {
-                               ERROR("Assuming message size of 8192");
-                               msgsize = 8192;
-                       }
-                       fclose(fp);
-               } else {
-                       ERROR_WITH_ERRNO("Failed to open the file `%s'",
-                                        msgsize_max_file);
-                       ERROR("Assuming message size of 8192");
-                       msgsize = 8192;
-               }
-       }
-       return msgsize;
-}
-
-static int
-get_mailbox(mqd_t mq, long needed_msgsize, long *msgsize_ret,
-           void **mailbox_ret)
+release_extra_refcnts(struct wimfs_context *ctx)
 {
 {
-       long msgsize;
-       void *mailbox;
-
-       msgsize = mq_get_msgsize(mq);
-
-       if (msgsize < needed_msgsize) {
-               ERROR("Message queue max size must be at least %ld!",
-                     needed_msgsize);
-               return WIMLIB_ERR_MQUEUE;
-       }
+       struct list_head *list = &ctx->orig_stream_list;
+       struct wim_lookup_table *lookup_table = ctx->wim->lookup_table;
+       struct wim_lookup_table_entry *lte, *tmp;
 
 
-       mailbox = MALLOC(msgsize);
-       if (!mailbox) {
-               ERROR("Failed to allocate %ld bytes for mailbox", msgsize);
-               return WIMLIB_ERR_NOMEM;
+       list_for_each_entry_safe(lte, tmp, list, orig_stream_list) {
+               u32 n = lte->out_refcnt;
+               while (n--)
+                       lte_decrement_refcnt(lte, lookup_table);
        }
        }
-       *msgsize_ret = msgsize;
-       *mailbox_ret = mailbox;
-       return 0;
 }
 
 static void
 }
 
 static void
-unlink_message_queues(struct wimfs_context *ctx)
+delete_empty_streams(struct wimfs_context *ctx)
 {
 {
-       mq_unlink(ctx->unmount_to_daemon_mq_name);
-       mq_unlink(ctx->daemon_to_unmount_mq_name);
-}
-
-/* Closes the message queues, which are allocated in static variables */
-static void
-close_message_queues(struct wimfs_context *ctx)
-{
-       DEBUG("Closing message queues");
-       mq_close(ctx->unmount_to_daemon_mq);
-       ctx->unmount_to_daemon_mq = (mqd_t)(-1);
-       mq_close(ctx->daemon_to_unmount_mq);
-       ctx->daemon_to_unmount_mq = (mqd_t)(-1);
-       unlink_message_queues(ctx);
-}
-
-
-struct unmount_msg_hdr {
-       u32 min_version;
-       u32 cur_version;
-       u32 msg_type;
-       u32 msg_size;
-} _packed_attribute;
-
-struct msg_unmount_request {
-       struct unmount_msg_hdr hdr;
-       u32 unmount_flags;
-       u8 want_progress_messages;
-} _packed_attribute;
-
-struct msg_daemon_info {
-       struct unmount_msg_hdr hdr;
-       pid_t daemon_pid;
-       u32 mount_flags;
-} _packed_attribute;
-
-struct msg_unmount_finished {
-       struct unmount_msg_hdr hdr;
-       s32 status;
-} _packed_attribute;
-
-struct msg_write_streams_progress {
-       struct unmount_msg_hdr hdr;
-       union wimlib_progress_info info;
-} _packed_attribute;
-
-enum {
-       MSG_TYPE_UNMOUNT_REQUEST,
-       MSG_TYPE_DAEMON_INFO,
-       MSG_TYPE_WRITE_STREAMS_PROGRESS,
-       MSG_TYPE_UNMOUNT_FINISHED,
-       MSG_TYPE_MAX,
-};
-
-struct msg_handler_context_hdr {
-       int timeout_seconds;
-};
-
-struct unmount_msg_handler_context {
-       struct msg_handler_context_hdr hdr;
-       pid_t daemon_pid;
-       int mount_flags;
-       int status;
-       wimlib_progress_func_t progfunc;
-       void *progctx;
-};
-
-struct daemon_msg_handler_context {
-       struct msg_handler_context_hdr hdr;
-       struct wimfs_context *wimfs_ctx;
-};
+       struct wim_lookup_table_entry *lte, *tmp;
+       struct wim_image_metadata *imd;
 
 
-static int
-send_unmount_request_msg(mqd_t mq, int unmount_flags, u8 want_progress_messages)
-{
-       DEBUG("Sending unmount request msg");
-       struct msg_unmount_request msg = {
-               .hdr = {
-                       .min_version = ((unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) ?
-                                               WIMLIB_MAKEVERSION(1, 6, 2) :
-                                               WIMLIB_MAKEVERSION(1, 2, 1)),
-                       .cur_version = WIMLIB_VERSION_CODE,
-                       .msg_type    = MSG_TYPE_UNMOUNT_REQUEST,
-                       .msg_size    = sizeof(msg),
-               },
-               .unmount_flags = unmount_flags,
-               .want_progress_messages = want_progress_messages,
-       };
-
-       if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) {
-               ERROR_WITH_ERRNO("Failed to communicate with filesystem daemon");
-               return WIMLIB_ERR_MQUEUE;
-       }
-       return 0;
-}
+       imd = wim_get_current_image_metadata(ctx->wim);
 
 
-static int
-send_daemon_info_msg(mqd_t mq, pid_t pid, int mount_flags)
-{
-       DEBUG("Sending daemon info msg (pid = %d, mount_flags=%x)",
-             pid, mount_flags);
-
-       struct msg_daemon_info msg = {
-               .hdr = {
-                       .min_version = WIMLIB_MAKEVERSION(1, 2, 1),
-                       .cur_version = WIMLIB_VERSION_CODE,
-                       .msg_type = MSG_TYPE_DAEMON_INFO,
-                       .msg_size = sizeof(msg),
-               },
-               .daemon_pid = pid,
-               .mount_flags = mount_flags,
-       };
-       if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) {
-               ERROR_WITH_ERRNO("Failed to send daemon info to unmount process");
-               return WIMLIB_ERR_MQUEUE;
-       }
-       return 0;
+        image_for_each_unhashed_stream_safe(lte, tmp, imd) {
+                if (!lte->size) {
+                        *retrieve_lte_pointer(lte) = NULL;
+                        list_del(&lte->unhashed_list);
+                        free_lookup_table_entry(lte);
+                }
+        }
 }
 
 static void
 }
 
 static void
-send_unmount_finished_msg(mqd_t mq, int status)
-{
-       DEBUG("Sending unmount finished msg");
-       struct msg_unmount_finished msg = {
-               .hdr = {
-                       .min_version = WIMLIB_MAKEVERSION(1, 2, 1),
-                       .cur_version = WIMLIB_VERSION_CODE,
-                       .msg_type = MSG_TYPE_UNMOUNT_FINISHED,
-                       .msg_size = sizeof(msg),
-               },
-               .status = status,
-       };
-       if (mq_send(mq, (void*)&msg, sizeof(msg), 1))
-               ERROR_WITH_ERRNO("Failed to send status to unmount process");
-}
-
-static enum wimlib_progress_status
-unmount_progress_func(enum wimlib_progress_msg msg,
-                     union wimlib_progress_info *info, void *_ignored_context)
-{
-       if (msg == WIMLIB_PROGRESS_MSG_WRITE_STREAMS) {
-               struct msg_write_streams_progress msg = {
-                       .hdr = {
-                               .min_version = WIMLIB_MAKEVERSION(1, 2, 1),
-                               .cur_version = WIMLIB_VERSION_CODE,
-                               .msg_type = MSG_TYPE_WRITE_STREAMS_PROGRESS,
-                               .msg_size = sizeof(msg),
-                       },
-                       .info = *info,
-               };
-               if (mq_send(wimfs_get_context()->daemon_to_unmount_mq,
-                           (void*)&msg, sizeof(msg), 1))
-               {
-                       ERROR_WITH_ERRNO("Failed to send progress information "
-                                        "to unmount process");
+inode_close_fds(struct wim_inode *inode)
+{
+       u16 num_open_fds = inode->i_num_opened_fds;
+       for (u16 i = 0; num_open_fds; i++) {
+               if (inode->i_fds[i]) {
+                       close_wimfs_fd(inode->i_fds[i]);
+                       num_open_fds--;
                }
        }
                }
        }
-       return WIMLIB_PROGRESS_STATUS_CONTINUE;
 }
 
 static void
 }
 
 static void
-release_extra_refcnts(struct wimfs_context *ctx)
+close_all_fds(struct wimfs_context *ctx)
 {
 {
-       struct list_head *list = &ctx->orig_stream_list;
-       struct wim_lookup_table *lookup_table = ctx->wim->lookup_table;
-       struct wim_lookup_table_entry *lte, *tmp;
+       struct wim_inode *inode, *tmp;
+       struct wim_image_metadata *imd;
 
 
-       list_for_each_entry_safe(lte, tmp, list, orig_stream_list) {
-               u32 n = lte->out_refcnt;
-               while (n--)
-                       lte_decrement_refcnt(lte, lookup_table);
-       }
+       imd = wim_get_current_image_metadata(ctx->wim);
+
+       list_for_each_entry_safe(inode, tmp, &imd->inode_list, i_list)
+               inode_close_fds(inode);
 }
 
 /* Moves the currently selected image, which may have been modified, to a new
 }
 
 /* Moves the currently selected image, which may have been modified, to a new
@@ -1211,23 +960,18 @@ static int
 renew_current_image(struct wimfs_context *ctx)
 {
        WIMStruct *wim = ctx->wim;
 renew_current_image(struct wimfs_context *ctx)
 {
        WIMStruct *wim = ctx->wim;
-       int ret;
        int idx = wim->current_image - 1;
        struct wim_image_metadata *imd = wim->image_metadata[idx];
        struct wim_image_metadata *replace_imd;
        struct wim_lookup_table_entry *new_lte;
        int idx = wim->current_image - 1;
        struct wim_image_metadata *imd = wim->image_metadata[idx];
        struct wim_image_metadata *replace_imd;
        struct wim_lookup_table_entry *new_lte;
-
-       if (imd->metadata_lte->resource_location != RESOURCE_IN_WIM) {
-               ERROR("Can't reset modified image that doesn't yet "
-                     "exist in the on-disk WIM file!");
-               return WIMLIB_ERR_METADATA_NOT_FOUND;
-       }
+       int ret;
 
        /* Create 'replace_imd' structure to use for the reset original,
         * unmodified image.  */
 
        /* Create 'replace_imd' structure to use for the reset original,
         * unmodified image.  */
+       ret = WIMLIB_ERR_NOMEM;
        replace_imd = new_image_metadata();
        if (!replace_imd)
        replace_imd = new_image_metadata();
        if (!replace_imd)
-               return WIMLIB_ERR_NOMEM;
+               goto err;
 
        /* Create new stream reference for the modified image's metadata
         * resource, which doesn't exist yet.  */
 
        /* Create new stream reference for the modified image's metadata
         * resource, which doesn't exist yet.  */
@@ -1261,386 +1005,124 @@ err_free_new_lte:
        free_lookup_table_entry(new_lte);
 err_put_replace_imd:
        put_image_metadata(replace_imd, NULL);
        free_lookup_table_entry(new_lte);
 err_put_replace_imd:
        put_image_metadata(replace_imd, NULL);
+err:
        return ret;
 }
 
        return ret;
 }
 
-static int
-msg_unmount_request_handler(const void *_msg, void *_handler_ctx)
-{
-       const struct msg_unmount_request *msg = _msg;
-       struct daemon_msg_handler_context *handler_ctx = _handler_ctx;
-       struct wimfs_context *wimfs_ctx;
-       int status = 0;
-       int ret;
-       int unmount_flags;
-
-       DEBUG("Handling unmount request msg");
-
-       wimfs_ctx = handler_ctx->wimfs_ctx;
-       if (msg->hdr.msg_size < sizeof(*msg)) {
-               status = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-               goto out;
-       }
-
-       unmount_flags = msg->unmount_flags;
-
-       wimlib_register_progress_function(wimfs_ctx->wim,
-                                         (msg->want_progress_messages ?
-                                          unmount_progress_func : NULL),
-                                         NULL);
-
-       ret = send_daemon_info_msg(wimfs_ctx->daemon_to_unmount_mq, getpid(),
-                                  wimfs_ctx->mount_flags);
-       if (ret != 0) {
-               status = ret;
-               goto out;
-       }
-
-       if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
-               if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
-
-                       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
-                               ret = renew_current_image(wimfs_ctx);
-                               if (ret) {
-                                       status = ret;
-                                       goto out;
-                               }
-                       } else {
-                               release_extra_refcnts(wimfs_ctx);
-                       }
-                       INIT_LIST_HEAD(&wimfs_ctx->orig_stream_list);
-
-                       int write_flags = 0;
-                       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY)
-                               write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
-                       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_REBUILD)
-                               write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
-                       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_RECOMPRESS)
-                               write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
-                       status = rebuild_wim(wimfs_ctx, write_flags);
-               }
-       } else {
-               DEBUG("Read-only mount");
-               status = 0;
-       }
-
-out:
-       if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
-               ret = delete_staging_dir(wimfs_ctx);
-               if (ret != 0) {
-                       ERROR("Failed to delete the staging directory");
-                       if (status == 0)
-                               status = ret;
-               }
-       }
-       wimfs_ctx->status = status;
-       wimfs_ctx->have_status = true;
-       return MSG_BREAK_LOOP;
-}
-
-static int
-msg_daemon_info_handler(const void *_msg, void *_handler_ctx)
-{
-       const struct msg_daemon_info *msg = _msg;
-       struct unmount_msg_handler_context *handler_ctx = _handler_ctx;
-
-       DEBUG("Handling daemon info msg");
-       if (msg->hdr.msg_size < sizeof(*msg))
-               return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       handler_ctx->daemon_pid = msg->daemon_pid;
-       handler_ctx->mount_flags = msg->mount_flags;
-       handler_ctx->hdr.timeout_seconds = 1;
-       DEBUG("pid of daemon is %d; mount flags were %#x",
-             handler_ctx->daemon_pid,
-             handler_ctx->mount_flags);
-       return 0;
-}
-
-static int
-msg_write_streams_progress_handler(const void *_msg, void *_handler_ctx)
-{
-       const struct msg_write_streams_progress *msg = _msg;
-       struct unmount_msg_handler_context *handler_ctx = _handler_ctx;
-
-       if (msg->hdr.msg_size < sizeof(*msg))
-               return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       return call_progress(handler_ctx->progfunc,
-                            WIMLIB_PROGRESS_MSG_WRITE_STREAMS,
-                            (union wimlib_progress_info *)&msg->info,
-                            handler_ctx->progctx);
-}
-
-static int
-msg_unmount_finished_handler(const void *_msg, void *_handler_ctx)
-{
-       const struct msg_unmount_finished *msg = _msg;
-       struct unmount_msg_handler_context *handler_ctx = _handler_ctx;
-
-       DEBUG("Handling unmount finished message");
-       if (msg->hdr.msg_size < sizeof(*msg))
-               return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       handler_ctx->status = msg->status;
-       DEBUG("status is %d", handler_ctx->status);
-       return MSG_BREAK_LOOP;
-}
-
-static int
-unmount_timed_out_cb(void *_handler_ctx)
+static enum wimlib_progress_status
+commit_progress_func(enum wimlib_progress_msg msg,
+                    union wimlib_progress_info *info, void *progctx)
 {
 {
-       const struct unmount_msg_handler_context *handler_ctx = _handler_ctx;
+       mqd_t mq = *(mqd_t *)progctx;
+       struct commit_progress_report report;
 
 
-       if (handler_ctx->daemon_pid == 0 ||
-           (kill(handler_ctx->daemon_pid, 0) != 0 && errno == ESRCH))
-       {
-               ERROR("The filesystem daemon has crashed!  Changes to the "
-                     "WIM may not have been commited.");
-               return WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED;
-       }
-
-       DEBUG("Filesystem daemon is still alive... "
-             "Waiting another %d seconds", handler_ctx->hdr.timeout_seconds);
-       return 0;
+       memset(&report, 0, sizeof(report));
+       report.msg = msg;
+       if (info)
+               report.info = *info;
+       mq_send(mq, (const char *)&report, sizeof(report), 1);
+       return WIMLIB_PROGRESS_STATUS_CONTINUE;
 }
 
 }
 
+/* Commit the mounted image to the underlying WIM file.  */
 static int
 static int
-daemon_timed_out_cb(void *_handler_ctx)
+commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq)
 {
 {
-       ERROR("Timed out waiting for unmount request! "
-             "Changes to the mounted WIM will not be committed.");
-       return WIMLIB_ERR_TIMEOUT;
-}
-
-typedef int (*msg_handler_t)(const void *_msg, void *_handler_ctx);
-
-struct msg_handler_callbacks {
-       int (*timed_out)(void * _handler_ctx);
-       msg_handler_t msg_handlers[MSG_TYPE_MAX];
-};
-
-static const struct msg_handler_callbacks unmount_msg_handler_callbacks = {
-       .timed_out = unmount_timed_out_cb,
-       .msg_handlers = {
-               [MSG_TYPE_DAEMON_INFO] = msg_daemon_info_handler,
-               [MSG_TYPE_WRITE_STREAMS_PROGRESS] = msg_write_streams_progress_handler,
-               [MSG_TYPE_UNMOUNT_FINISHED] = msg_unmount_finished_handler,
-       },
-};
-
-static const struct msg_handler_callbacks daemon_msg_handler_callbacks = {
-       .timed_out = daemon_timed_out_cb,
-       .msg_handlers = {
-               [MSG_TYPE_UNMOUNT_REQUEST] = msg_unmount_request_handler,
-       },
-};
-
-static int
-receive_message(mqd_t mq,
-               struct msg_handler_context_hdr *handler_ctx,
-               const msg_handler_t msg_handlers[],
-               long mailbox_size, void *mailbox)
-{
-       struct timeval now;
-       struct timespec timeout;
-       ssize_t bytes_received;
-       struct unmount_msg_hdr *hdr;
-       int ret;
+       int write_flags;
 
 
-       gettimeofday(&now, NULL);
-       timeout.tv_sec = now.tv_sec + handler_ctx->timeout_seconds;
-       timeout.tv_nsec = now.tv_usec * 1000;
+       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS)
+               wimlib_register_progress_function(ctx->wim,
+                                                 commit_progress_func, &mq);
+       else
+               wimlib_register_progress_function(ctx->wim, NULL, NULL);
 
 
-       bytes_received = mq_timedreceive(mq, mailbox,
-                                        mailbox_size, NULL, &timeout);
-       hdr = mailbox;
-       if (bytes_received == -1) {
-               if (errno == ETIMEDOUT) {
-                       ret = WIMLIB_ERR_TIMEOUT;
-               } else {
-                       ERROR_WITH_ERRNO("mq_timedreceive()");
-                       ret = WIMLIB_ERR_MQUEUE;
-               }
-       } else if (bytes_received < sizeof(*hdr) ||
-                  bytes_received != hdr->msg_size) {
-               ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       } else if (WIMLIB_VERSION_CODE < hdr->min_version) {
-               /*ERROR("Cannot understand the received message. "*/
-                     /*"Please upgrade wimlib to at least v%d.%d.%d",*/
-                     /*WIMLIB_GET_MAJOR_VERSION(hdr->min_version),*/
-                     /*WIMLIB_GET_MINOR_VERSION(hdr->min_version),*/
-                     /*WIMLIB_GET_PATCH_VERSION(hdr->min_version));*/
-               ret = MSG_VERSION_TOO_HIGH;
-       } else if (hdr->msg_type >= MSG_TYPE_MAX) {
-               ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       } else if (msg_handlers[hdr->msg_type] == NULL) {
-               ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
+               int ret = renew_current_image(ctx);
+               if (ret)
+                       return ret;
        } else {
        } else {
-               ret = msg_handlers[hdr->msg_type](mailbox, handler_ctx);
+               release_extra_refcnts(ctx);
        }
        }
-       return ret;
-}
-
-static int
-message_loop(mqd_t mq,
-            const struct msg_handler_callbacks *callbacks,
-            struct msg_handler_context_hdr *handler_ctx)
-{
-       static const size_t MAX_MSG_SIZE = 512;
-       long msgsize;
-       void *mailbox;
-       int ret;
-
-       DEBUG("Entering message loop");
+       INIT_LIST_HEAD(&ctx->orig_stream_list);
+       delete_empty_streams(ctx);
+       xml_update_image_info(ctx->wim, ctx->wim->current_image);
 
 
-       ret = get_mailbox(mq, MAX_MSG_SIZE, &msgsize, &mailbox);
-       if (ret != 0)
-               return ret;
-       while (1) {
-               ret = receive_message(mq, handler_ctx,
-                                     callbacks->msg_handlers,
-                                     msgsize, mailbox);
-               if (ret == 0 || ret == MSG_VERSION_TOO_HIGH) {
-                       continue;
-               } else if (ret == MSG_BREAK_LOOP) {
-                       ret = 0;
-                       break;
-               } else if (ret == WIMLIB_ERR_TIMEOUT) {
-                       if (callbacks->timed_out)
-                               ret = callbacks->timed_out(handler_ctx);
-                       if (ret == 0)
-                               continue;
-                       else
-                               break;
-               } else {
-                       ERROR_WITH_ERRNO("Error communicating with "
-                                        "filesystem daemon");
-                       break;
-               }
-       }
-       FREE(mailbox);
-       DEBUG("Exiting message loop");
-       return ret;
+       write_flags = 0;
+       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY)
+               write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
+       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_REBUILD)
+               write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
+       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_RECOMPRESS)
+               write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
+       return wimlib_overwrite(ctx->wim, write_flags, 0);
 }
 
 }
 
-/* Execute `fusermount -u', which is installed setuid root, to unmount the WIM.
- *
- * FUSE does not yet implement synchronous unmounts.  This means that fusermount
- * -u will return before the filesystem daemon returns from wimfs_destroy().
- *  This is partly what we want, because we need to send a message from this
- *  process to the filesystem daemon telling whether --commit was specified or
- *  not.  However, after that, the unmount process must wait for the filesystem
- *  daemon to finish writing the WIM file.
- */
 static int
 static int
-execute_fusermount(const char *dir, bool lazy)
+unmount_wimfs(const struct wimfs_unmount_info *info)
 {
 {
-       pid_t pid;
-       int ret;
+       struct fuse_context *fuse_ctx = fuse_get_context();
+       struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
+       int unmount_flags = info->unmount_flags;
+       mqd_t mq = (mqd_t)-1;
        int status;
 
        int status;
 
-       pid = fork();
-       if (pid == -1) {
-               ERROR_WITH_ERRNO("Failed to fork()");
-               return WIMLIB_ERR_FORK;
-       }
-       if (pid == 0) {
-               /* Child */
-               char *argv[10];
-               char **argp = argv;
-               *argp++ = "fusermount";
-               if (lazy)
-                       *argp++ = "-z";
-               *argp++ = "-u";
-               *argp++ = (char*)dir;
-               *argp = NULL;
-               execvp("fusermount", argv);
-               ERROR_WITH_ERRNO("Failed to execute `fusermount'");
-               exit(WIMLIB_ERR_FUSERMOUNT);
-       }
-
-       /* Parent */
-       ret = waitpid(pid, &status, 0);
-       if (ret == -1) {
-               ERROR_WITH_ERRNO("Failed to wait for fusermount process to "
-                                "terminate");
-               return WIMLIB_ERR_FUSERMOUNT;
-       }
+       if (fuse_ctx->uid != wimfs_ctx->owner_uid &&
+           fuse_ctx->uid != 0)
+               return -EPERM;
 
 
-       if (!WIFEXITED(status)) {
-               ERROR("'fusermount' did not terminate normally!");
-               return WIMLIB_ERR_FUSERMOUNT;
+       if (info->mq_name[0]) {
+               mq = mq_open(info->mq_name, O_WRONLY | O_NONBLOCK);
+               if (mq == (mqd_t)-1)
+                       return -errno;
        }
 
        }
 
-       status = WEXITSTATUS(status);
-
-       if (status == 0)
-               return 0;
-
-       if (status != WIMLIB_ERR_FUSERMOUNT)
-               return WIMLIB_ERR_FUSERMOUNT;
-
-       /* Try again, but with the `umount' program.  This is required on other
-        * FUSE implementations such as FreeBSD's that do not have a
-        * `fusermount' program. */
-       ERROR("Falling back to 'umount'.  Note: you may need to be "
-             "root for this to work");
-       pid = fork();
-       if (pid == -1) {
-               ERROR_WITH_ERRNO("Failed to fork()");
-               return WIMLIB_ERR_FORK;
-       }
-       if (pid == 0) {
-               /* Child */
-               char *argv[10];
-               char **argp = argv;
-               *argp++ = "umount";
-               if (lazy)
-                       *argp++ = "-l";
-               *argp++ = (char*)dir;
-               *argp = NULL;
-               execvp("umount", argv);
-               ERROR_WITH_ERRNO("Failed to execute `umount'");
-               exit(WIMLIB_ERR_FUSERMOUNT);
-       }
+       /* Ignore COMMIT if the image is mounted read-only.  */
+       if (!(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE))
+               unmount_flags &= ~WIMLIB_UNMOUNT_FLAG_COMMIT;
 
 
-       /* Parent */
-       ret = waitpid(pid, &status, 0);
-       if (ret == -1) {
-               ERROR_WITH_ERRNO("Failed to wait for `umount' process to "
-                                "terminate");
-               return WIMLIB_ERR_FUSERMOUNT;
+       if (wimfs_ctx->num_open_fds) {
+               if ((unmount_flags & (WIMLIB_UNMOUNT_FLAG_COMMIT |
+                                     WIMLIB_UNMOUNT_FLAG_FORCE))
+                                == WIMLIB_UNMOUNT_FLAG_COMMIT)
+               {
+                       status = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY;
+                       goto out_send_status;
+               }
+               close_all_fds(wimfs_ctx);
        }
 
        }
 
-       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-               ERROR("`umount' did not successfully complete");
-               return WIMLIB_ERR_FUSERMOUNT;
+       if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)
+               status = commit_image(wimfs_ctx, unmount_flags, mq);
+       else
+               status = 0;
+       fuse_exit(fuse_ctx->fuse);
+out_send_status:
+       if (mq != (mqd_t)-1) {
+               mq_send(mq, (const char *)&status, sizeof(int), 1);
+               mq_close(mq);
        }
        }
-
        return 0;
 }
 
 static int
 wimfs_chmod(const char *path, mode_t mask)
 {
        return 0;
 }
 
 static int
 wimfs_chmod(const char *path, mode_t mask)
 {
-       struct wim_dentry *dentry;
+       const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_inode *inode;
        struct wim_inode *inode;
-       struct wimfs_context *ctx = wimfs_get_context();
        struct wimlib_unix_data unix_data;
        struct wimlib_unix_data unix_data;
-       int ret;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
-               return -EPERM;
-
-       ret = wim_pathname_to_stream(ctx->wim, path, LOOKUP_FLAG_DIRECTORY_OK,
-                                    &dentry, NULL, NULL);
-       if (ret)
-               return ret;
+               return -EOPNOTSUPP;
 
 
-       inode = dentry->d_inode;
+       inode = wim_pathname_to_inode(ctx->wim, path);
+       if (!inode)
+               return -errno;
 
 
-       unix_data.uid = ctx->default_uid;
-       unix_data.gid = ctx->default_gid;
+       unix_data.uid = ctx->owner_uid;
+       unix_data.gid = ctx->owner_gid;
        unix_data.mode = mask;
        unix_data.rdev = 0;
        unix_data.mode = mask;
        unix_data.rdev = 0;
+
        if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_MODE))
                return -ENOMEM;
 
        if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_MODE))
                return -ENOMEM;
 
@@ -1650,163 +1132,173 @@ wimfs_chmod(const char *path, mode_t mask)
 static int
 wimfs_chown(const char *path, uid_t uid, gid_t gid)
 {
 static int
 wimfs_chown(const char *path, uid_t uid, gid_t gid)
 {
-       struct wim_dentry *dentry;
+       const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_inode *inode;
        struct wim_inode *inode;
-       struct wimfs_context *ctx = wimfs_get_context();
-       int which;
        struct wimlib_unix_data unix_data;
        struct wimlib_unix_data unix_data;
-       int ret;
+       int which;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
-               return -EPERM;
-
-       ret = wim_pathname_to_stream(ctx->wim, path, LOOKUP_FLAG_DIRECTORY_OK,
-                                    &dentry, NULL, NULL);
-       if (ret)
-               return ret;
+               return -EOPNOTSUPP;
 
 
-       inode = dentry->d_inode;
+       inode = wim_pathname_to_inode(ctx->wim, path);
+       if (!inode)
+               return -errno;
 
        which = 0;
 
        if (uid != (uid_t)-1)
                which |= UNIX_DATA_UID;
        else
 
        which = 0;
 
        if (uid != (uid_t)-1)
                which |= UNIX_DATA_UID;
        else
-               uid = ctx->default_uid;
+               uid = ctx->owner_uid;
 
        if (gid != (gid_t)-1)
                which |= UNIX_DATA_GID;
        else
 
        if (gid != (gid_t)-1)
                which |= UNIX_DATA_GID;
        else
-               gid = ctx->default_gid;
-
+               gid = ctx->owner_gid;
 
        unix_data.uid = uid;
        unix_data.gid = gid;
        unix_data.mode = inode_default_unix_mode(inode);
        unix_data.rdev = 0;
 
        unix_data.uid = uid;
        unix_data.gid = gid;
        unix_data.mode = inode_default_unix_mode(inode);
        unix_data.rdev = 0;
+
        if (!inode_set_unix_data(inode, &unix_data, which))
                return -ENOMEM;
 
        return 0;
 }
 
        if (!inode_set_unix_data(inode, &unix_data, which))
                return -ENOMEM;
 
        return 0;
 }
 
-/* Called when the filesystem is unmounted. */
-static void
-wimfs_destroy(void *p)
-{
-       struct wimfs_context *wimfs_ctx = wimfs_get_context();
-       if (open_message_queues(wimfs_ctx, true) == 0) {
-               struct daemon_msg_handler_context handler_ctx = {
-                       .hdr = {
-                               .timeout_seconds = 5,
-                       },
-                       .wimfs_ctx = wimfs_ctx,
-               };
-               message_loop(wimfs_ctx->unmount_to_daemon_mq,
-                            &daemon_msg_handler_callbacks,
-                            &handler_ctx.hdr);
-       }
-}
-
 static int
 static int
-wimfs_fgetattr(const char *path, struct stat *stbuf,
-              struct fuse_file_info *fi)
+wimfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
 {
 {
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
+       struct wimfs_fd *fd = WIMFS_FD(fi);
        return inode_to_stbuf(fd->f_inode, fd->f_lte, stbuf);
 }
 
 static int
 wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
 {
        return inode_to_stbuf(fd->f_inode, fd->f_lte, stbuf);
 }
 
 static int
 wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
 {
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
-       int ret = ftruncate(fd->staging_fd.fd, size);
-       if (ret)
+       struct wimfs_fd *fd = WIMFS_FD(fi);
+       if (ftruncate(fd->f_staging_fd.fd, size))
                return -errno;
        touch_inode(fd->f_inode);
        fd->f_lte->size = size;
        return 0;
 }
 
                return -errno;
        touch_inode(fd->f_inode);
        fd->f_lte->size = size;
        return 0;
 }
 
-/*
- * Fills in a `struct stat' that corresponds to a file or directory in the WIM.
- */
 static int
 wimfs_getattr(const char *path, struct stat *stbuf)
 {
 static int
 wimfs_getattr(const char *path, struct stat *stbuf)
 {
+       const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_lookup_table_entry *lte;
        int ret;
        struct wim_dentry *dentry;
        struct wim_lookup_table_entry *lte;
        int ret;
-       struct wimfs_context *ctx = wimfs_get_context();
 
        ret = wim_pathname_to_stream(ctx->wim, path,
                                     get_lookup_flags(ctx) |
                                        LOOKUP_FLAG_DIRECTORY_OK,
                                     &dentry, &lte, NULL);
 
        ret = wim_pathname_to_stream(ctx->wim, path,
                                     get_lookup_flags(ctx) |
                                        LOOKUP_FLAG_DIRECTORY_OK,
                                     &dentry, &lte, NULL);
-       if (ret != 0)
+       if (ret)
                return ret;
        return inode_to_stbuf(dentry->d_inode, lte, stbuf);
 }
 
                return ret;
        return inode_to_stbuf(dentry->d_inode, lte, stbuf);
 }
 
-#ifdef ENABLE_XATTR
-/* Read an alternate data stream through the XATTR interface, or get its size */
+static int
+copy_xattr(char *dest, size_t destsize, const void *src, size_t srcsize)
+{
+       if (!destsize)
+               return srcsize;
+       if (destsize < srcsize)
+               return -ERANGE;
+       memcpy(dest, src, srcsize);
+       return srcsize;
+}
+
 static int
 wimfs_getxattr(const char *path, const char *name, char *value,
               size_t size)
 {
 static int
 wimfs_getxattr(const char *path, const char *name, char *value,
               size_t size)
 {
-       int ret;
+       struct fuse_context *fuse_ctx = fuse_get_context();
+       const struct wimfs_context *ctx = WIMFS_CTX(fuse_ctx);
        struct wim_inode *inode;
        struct wim_ads_entry *ads_entry;
        struct wim_inode *inode;
        struct wim_ads_entry *ads_entry;
-       u64 stream_size;
        struct wim_lookup_table_entry *lte;
        struct wim_lookup_table_entry *lte;
-       struct wimfs_context *ctx = wimfs_get_context();
+
+       if (!strncmp(name, "wimfs.", 6)) {
+               /* Handle some magical extended attributes.  These really should
+                * be ioctls, but directory ioctls aren't supported until
+                * libfuse 2.9, and even then they are broken.  */
+               name += 6;
+               if (!strcmp(name, "wim_filename")) {
+                       return copy_xattr(value, size, ctx->wim->filename,
+                                         strlen(ctx->wim->filename));
+               }
+               if (!strcmp(name, "wim_info")) {
+                       struct wimlib_wim_info info;
+
+                       wimlib_get_wim_info(ctx->wim, &info);
+
+                       return copy_xattr(value, size, &info, sizeof(info));
+               }
+               if (!strcmp(name, "mounted_image")) {
+                       return copy_xattr(value, size,
+                                         &ctx->wim->current_image, sizeof(int));
+               }
+               if (!strcmp(name, "mount_flags")) {
+                       return copy_xattr(value, size,
+                                         &ctx->mount_flags, sizeof(int));
+               }
+               if (!strcmp(name, "unmount")) {
+                       struct wimfs_unmount_info info;
+                       memset(&info, 0, sizeof(info));
+                       return unmount_wimfs(&info);
+               }
+               return -ENOATTR;
+       }
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
-       if (strlen(name) <= 5 || memcmp(name, "user.", 5) != 0)
+       if (strncmp(name, "user.", 5))
                return -ENOATTR;
        name += 5;
 
                return -ENOATTR;
        name += 5;
 
+       /* Querying a named data stream  */
+
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       ads_entry = inode_get_ads_entry(inode, name, NULL);
+       ads_entry = inode_get_ads_entry(inode, name);
        if (!ads_entry)
        if (!ads_entry)
-               return -ENOATTR;
+               return (errno == ENOENT) ? -ENOATTR : -errno;
 
        lte = ads_entry->lte;
 
        lte = ads_entry->lte;
-       stream_size = lte->size;
+       if (!lte)
+               return 0;
 
 
-       if (size == 0)
-               return stream_size;
+       if (unlikely(lte->size > INT_MAX))
+               return -EFBIG;
 
 
-       if (stream_size > size)
-               return -ERANGE;
+       if (size) {
+               if (lte->size > size)
+                       return -ERANGE;
 
 
-       ret = read_full_stream_into_buf(lte, value);
-       if (ret) {
-               if (errno)
+               if (read_full_stream_into_buf(lte, value))
                        return -errno;
                        return -errno;
-               else
-                       return -EIO;
        }
        }
-       return stream_size;
+       return lte->size;
 }
 }
-#endif
 
 
-/* Create a hard link */
 static int
 static int
-wimfs_link(const char *to, const char *from)
+wimfs_link(const char *existing_path, const char *new_path)
 {
 {
-       struct wim_dentry *from_dentry, *from_dentry_parent;
-       const char *link_name;
-       struct wim_inode *inode;
        WIMStruct *wim = wimfs_get_WIMStruct();
        WIMStruct *wim = wimfs_get_WIMStruct();
-       int ret;
+       const char *new_name;
+       struct wim_inode *inode;
+       struct wim_dentry *dir;
+       struct wim_dentry *new_alias;
 
 
-       inode = wim_pathname_to_inode(wim, to);
+       inode = wim_pathname_to_inode(wim, existing_path);
        if (!inode)
                return -errno;
 
        if (!inode)
                return -errno;
 
@@ -1814,149 +1306,156 @@ wimfs_link(const char *to, const char *from)
                                   FILE_ATTRIBUTE_REPARSE_POINT))
                return -EPERM;
 
                                   FILE_ATTRIBUTE_REPARSE_POINT))
                return -EPERM;
 
-       from_dentry_parent = get_parent_dentry(wim, from, WIMLIB_CASE_SENSITIVE);
-       if (!from_dentry_parent)
+       new_name = path_basename(new_path);
+
+       dir = get_parent_dentry(wim, new_path, WIMLIB_CASE_SENSITIVE);
+       if (!dir)
                return -errno;
                return -errno;
-       if (!dentry_is_directory(from_dentry_parent))
+
+       if (!dentry_is_directory(dir))
                return -ENOTDIR;
 
                return -ENOTDIR;
 
-       link_name = path_basename(from);
-       if (get_dentry_child_with_name(from_dentry_parent, link_name,
-                                      WIMLIB_CASE_SENSITIVE))
+       if (get_dentry_child_with_name(dir, new_name, WIMLIB_CASE_SENSITIVE))
                return -EEXIST;
 
                return -EEXIST;
 
-       ret = new_dentry(link_name, &from_dentry);
-       if (ret)
+       if (new_dentry(new_name, &new_alias))
                return -ENOMEM;
 
                return -ENOMEM;
 
+       new_alias->d_inode = inode;
+       inode_add_dentry(new_alias, inode);
+       dentry_add_child(dir, new_alias);
        inode->i_nlink++;
        inode_ref_streams(inode);
        inode->i_nlink++;
        inode_ref_streams(inode);
-       from_dentry->d_inode = inode;
-       inode_add_dentry(from_dentry, inode);
-       dentry_add_child(from_dentry_parent, from_dentry);
        return 0;
 }
 
        return 0;
 }
 
-#ifdef ENABLE_XATTR
 static int
 wimfs_listxattr(const char *path, char *list, size_t size)
 {
 static int
 wimfs_listxattr(const char *path, char *list, size_t size)
 {
-       size_t needed_size;
-       struct wim_inode *inode;
-       struct wimfs_context *ctx = wimfs_get_context();
-       u16 i;
-       char *p;
-       bool size_only = (size == 0);
+       const struct wimfs_context *ctx = wimfs_get_context();
+       const struct wim_inode *inode;
+       char *p = list;
+       char *end = list + size;
+       int total_size = 0;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
-       /* List alternate data streams, or get the list size */
+       /* List named data streams, or get the list size.  We report each named
+        * data stream "X" as an extended attribute "user.X".  */
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       p = list;
-       for (i = 0; i < inode->i_num_ads; i++) {
-
-               if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
-                       continue;
-
+       for (u16 i = 0; i < inode->i_num_ads; i++) {
+               const struct wim_ads_entry *entry;
                char *stream_name_mbs;
                size_t stream_name_mbs_nbytes;
                char *stream_name_mbs;
                size_t stream_name_mbs_nbytes;
-               int ret;
 
 
-               ret = utf16le_to_tstr(inode->i_ads_entries[i].stream_name,
-                                     inode->i_ads_entries[i].stream_name_nbytes,
-                                     &stream_name_mbs,
-                                     &stream_name_mbs_nbytes);
-               if (ret)
+               entry = &inode->i_ads_entries[i];
+
+               if (!entry->stream_name_nbytes)
+                       continue;
+
+               if (utf16le_to_tstr(entry->stream_name,
+                                   entry->stream_name_nbytes,
+                                   &stream_name_mbs,
+                                   &stream_name_mbs_nbytes))
                        return -errno;
 
                        return -errno;
 
-               needed_size = stream_name_mbs_nbytes + 6;
-               if (!size_only) {
-                       if (needed_size > size) {
+               if (unlikely(INT_MAX - total_size < stream_name_mbs_nbytes + 6)) {
+                       FREE(stream_name_mbs);
+                       return -EFBIG;
+               }
+
+               total_size += stream_name_mbs_nbytes + 6;
+               if (size) {
+                       if (end - p < stream_name_mbs_nbytes + 6) {
                                FREE(stream_name_mbs);
                                return -ERANGE;
                        }
                                FREE(stream_name_mbs);
                                return -ERANGE;
                        }
-                       sprintf(p, "user.%s", stream_name_mbs);
-                       size -= needed_size;
+                       p = mempcpy(p, "user.", 5);
+                       p = mempcpy(p, stream_name_mbs, stream_name_mbs_nbytes);
+                       *p++ = '\0';
                }
                }
-               p += needed_size;
                FREE(stream_name_mbs);
        }
                FREE(stream_name_mbs);
        }
-       return p - list;
+       return total_size;
 }
 }
-#endif
 
 
-
-/* Create a directory in the WIM image. */
 static int
 wimfs_mkdir(const char *path, mode_t mode)
 {
 static int
 wimfs_mkdir(const char *path, mode_t mode)
 {
+       /* Note: according to fuse.h, mode may not include S_IFDIR  */
        return create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
                             FILE_ATTRIBUTE_DIRECTORY, NULL);
 }
 
        return create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
                             FILE_ATTRIBUTE_DIRECTORY, NULL);
 }
 
-/* Create a non-directory, non-symbolic-link file or alternate data stream in
- * the WIM image.  */
 static int
 wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
 {
 static int
 wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
 {
-       const char *stream_name;
        struct fuse_context *fuse_ctx = fuse_get_context();
        struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
        struct fuse_context *fuse_ctx = fuse_get_context();
        struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
+       const char *stream_name;
 
        if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
 
        if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
-            && (stream_name = path_stream_name(path))) {
+            && (stream_name = path_stream_name(path)))
+       {
+               struct wim_ads_entry *old_entry;
+               struct wim_ads_entry *new_entry;
+               struct wim_inode *inode;
+               char *p;
+
+               /* Create a named data stream.  */
 
                if (!S_ISREG(mode))
 
                if (!S_ISREG(mode))
-                       return -EPERM;
+                       return -EOPNOTSUPP;
 
 
-               /* Make an alternate data stream */
-               struct wim_ads_entry *new_entry;
-               struct wim_inode *inode;
+               p = (char *)stream_name - 1;
 
 
-               char *p = (char*)stream_name - 1;
-               wimlib_assert(*p == ':');
                *p = '\0';
                *p = '\0';
-
                inode = wim_pathname_to_inode(wimfs_ctx->wim, path);
                inode = wim_pathname_to_inode(wimfs_ctx->wim, path);
+               *p = ':';
                if (!inode)
                        return -errno;
                if (!inode)
                        return -errno;
-               if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
-                       return -ENOENT;
-               if (inode_get_ads_entry(inode, stream_name, NULL))
+
+               old_entry = inode_get_ads_entry(inode, stream_name);
+               if (old_entry)
                        return -EEXIST;
                        return -EEXIST;
+               if (errno != ENOENT)
+                       return -errno;
+
                new_entry = inode_add_ads(inode, stream_name);
                if (!new_entry)
                new_entry = inode_add_ads(inode, stream_name);
                if (!new_entry)
-                       return -ENOMEM;
+                       return -errno;
                return 0;
        } else {
                return 0;
        } else {
+               /* Create a regular file, device node, named pipe, or socket.
+                */
+
                if (!S_ISREG(mode) &&
                    !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
                        return -EPERM;
 
                if (!S_ISREG(mode) &&
                    !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
                        return -EPERM;
 
-               /* Make a regular file, device node, named pipe, or socket.  */
+               /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes,
+                * named pipes, and sockets.  The real mode is in the UNIX
+                * metadata.  */
                return create_dentry(fuse_ctx, path, mode, rdev,
                                     FILE_ATTRIBUTE_NORMAL, NULL);
        }
 }
 
                return create_dentry(fuse_ctx, path, mode, rdev,
                                     FILE_ATTRIBUTE_NORMAL, NULL);
        }
 }
 
-/* Open a file.  */
 static int
 wimfs_open(const char *path, struct fuse_file_info *fi)
 {
 static int
 wimfs_open(const char *path, struct fuse_file_info *fi)
 {
+       struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_dentry *dentry;
-       struct wim_lookup_table_entry *lte;
-       int ret;
-       struct wimfs_fd *fd;
        struct wim_inode *inode;
        struct wim_inode *inode;
+       struct wim_lookup_table_entry *lte;
        u16 stream_idx;
        u16 stream_idx;
-       u32 stream_id;
-       struct wimfs_context *ctx = wimfs_get_context();
-       struct wim_lookup_table_entry **back_ptr;
+       struct wimfs_fd *fd;
+       int ret;
 
        ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
                                     &dentry, &lte, &stream_idx);
 
        ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
                                     &dentry, &lte, &stream_idx);
@@ -1965,59 +1464,51 @@ wimfs_open(const char *path, struct fuse_file_info *fi)
 
        inode = dentry->d_inode;
 
 
        inode = dentry->d_inode;
 
-       if (stream_idx == 0) {
-               stream_id = 0;
-               back_ptr = &inode->i_lte;
-       } else {
-               stream_id = inode->i_ads_entries[stream_idx - 1].stream_id;
-               back_ptr = &inode->i_ads_entries[stream_idx - 1].lte;
-       }
-
        /* The file resource may be in the staging directory (read-write mounts
         * only) or in the WIM.  If it's in the staging directory, we need to
         * open a native file descriptor for the corresponding file.  Otherwise,
         * we can read the file resource directly from the WIM file if we are
         * opening it read-only, but we need to extract the resource to the
        /* The file resource may be in the staging directory (read-write mounts
         * only) or in the WIM.  If it's in the staging directory, we need to
         * open a native file descriptor for the corresponding file.  Otherwise,
         * we can read the file resource directly from the WIM file if we are
         * opening it read-only, but we need to extract the resource to the
-        * staging directory if we are opening it writable. */
+        * staging directory if we are opening it writable.  */
 
        if (flags_writable(fi->flags) &&
             (!lte || lte->resource_location != RESOURCE_IN_STAGING_FILE)) {
 
        if (flags_writable(fi->flags) &&
             (!lte || lte->resource_location != RESOURCE_IN_STAGING_FILE)) {
-               u64 size = (lte) ? lte->size : 0;
-               ret = extract_resource_to_staging_dir(inode, stream_id,
-                                                     &lte, size, ctx);
+               ret = extract_resource_to_staging_dir(inode,
+                                                     stream_idx,
+                                                     &lte,
+                                                     lte ? lte->size : 0,
+                                                     ctx);
                if (ret)
                        return ret;
                if (ret)
                        return ret;
-               *back_ptr = lte;
        }
 
        }
 
-       ret = alloc_wimfs_fd(inode, stream_id, lte, &fd);
+       ret = alloc_wimfs_fd(inode, inode_stream_idx_to_id(inode, stream_idx),
+                            lte, &fd);
        if (ret)
                return ret;
 
        if (lte && lte->resource_location == RESOURCE_IN_STAGING_FILE) {
                int raw_fd;
 
        if (ret)
                return ret;
 
        if (lte && lte->resource_location == RESOURCE_IN_STAGING_FILE) {
                int raw_fd;
 
-               raw_fd = open(lte->staging_file_name, fi->flags);
+               raw_fd = openat(lte->staging_dir_fd, lte->staging_file_name,
+                               (fi->flags & O_ACCMODE) | O_NOFOLLOW);
                if (raw_fd < 0) {
                if (raw_fd < 0) {
-                       int errno_save = errno;
                        close_wimfs_fd(fd);
                        close_wimfs_fd(fd);
-                       return -errno_save;
+                       return -errno;
                }
                }
-               filedes_init(&fd->staging_fd, raw_fd);
+               filedes_init(&fd->f_staging_fd, raw_fd);
        }
        fi->fh = (uintptr_t)fd;
        return 0;
 }
 
        }
        fi->fh = (uintptr_t)fd;
        return 0;
 }
 
-/* Opens a directory. */
 static int
 wimfs_opendir(const char *path, struct fuse_file_info *fi)
 {
 static int
 wimfs_opendir(const char *path, struct fuse_file_info *fi)
 {
+       WIMStruct *wim = wimfs_get_WIMStruct();
        struct wim_inode *inode;
        struct wim_inode *inode;
+       struct wimfs_fd *fd;
        int ret;
        int ret;
-       struct wimfs_fd *fd = NULL;
-       struct wimfs_context *ctx = wimfs_get_context();
-       WIMStruct *wim = ctx->wim;
 
        inode = wim_pathname_to_inode(wim, path);
        if (!inode)
 
        inode = wim_pathname_to_inode(wim, path);
        if (!inode)
@@ -2025,79 +1516,65 @@ wimfs_opendir(const char *path, struct fuse_file_info *fi)
        if (!inode_is_directory(inode))
                return -ENOTDIR;
        ret = alloc_wimfs_fd(inode, 0, NULL, &fd);
        if (!inode_is_directory(inode))
                return -ENOTDIR;
        ret = alloc_wimfs_fd(inode, 0, NULL, &fd);
+       if (ret)
+               return ret;
        fi->fh = (uintptr_t)fd;
        fi->fh = (uintptr_t)fd;
-       return ret;
+       return 0;
 }
 
 }
 
-
-/*
- * Read data from a file in the WIM or in the staging directory.
- */
 static int
 wimfs_read(const char *path, char *buf, size_t size,
           off_t offset, struct fuse_file_info *fi)
 {
 static int
 wimfs_read(const char *path, char *buf, size_t size,
           off_t offset, struct fuse_file_info *fi)
 {
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
+       struct wimfs_fd *fd = WIMFS_FD(fi);
+       const struct wim_lookup_table_entry *lte;
        ssize_t ret;
        ssize_t ret;
-       u64 stream_size;
 
 
-       if (!fd)
-               return -EBADF;
-
-       if (size == 0)
+       lte = fd->f_lte;
+       if (!lte)
                return 0;
 
                return 0;
 
-       if (fd->f_lte)
-               stream_size = fd->f_lte->size;
-       else
-               stream_size = 0;
+       if (offset >= lte->size)
+               return 0;
 
 
-       if (offset > stream_size)
-               return -EOVERFLOW;
+       if (size > lte->size - offset)
+               size = lte->size - offset;
 
 
-       size = min(size, stream_size - offset);
-       if (size == 0)
+       if (!size)
                return 0;
 
                return 0;
 
-       switch (fd->f_lte->resource_location) {
-       case RESOURCE_IN_STAGING_FILE:
-               ret = raw_pread(&fd->staging_fd, buf, size, offset);
-               if (ret == -1)
-                       ret = -errno;
-               break;
+       switch (lte->resource_location) {
        case RESOURCE_IN_WIM:
        case RESOURCE_IN_WIM:
-               if (read_partial_wim_stream_into_buf(fd->f_lte, size,
-                                                    offset, buf))
-                       ret = errno ? -errno : -EIO;
+               if (read_partial_wim_stream_into_buf(lte, size, offset, buf))
+                       ret = -errno;
                else
                        ret = size;
                break;
                else
                        ret = size;
                break;
+       case RESOURCE_IN_STAGING_FILE:
+               ret = raw_pread(&fd->f_staging_fd, buf, size, offset);
+               if (ret < 0)
+                       ret = -errno;
+               break;
        case RESOURCE_IN_ATTACHED_BUFFER:
        case RESOURCE_IN_ATTACHED_BUFFER:
-               memcpy(buf, fd->f_lte->attached_buffer + offset, size);
+               memcpy(buf, lte->attached_buffer + offset, size);
                ret = size;
                break;
        default:
                ret = size;
                break;
        default:
-               ERROR("Invalid resource location");
-               ret = -EIO;
+               ret = -EINVAL;
                break;
        }
        return ret;
 }
 
                break;
        }
        return ret;
 }
 
-/* Fills in the entries of the directory specified by @path using the
- * FUSE-provided function @filler.  */
 static int
 wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
 {
 static int
 wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
 {
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
-       struct wim_inode *inode;
-       struct wim_dentry *child;
+       struct wimfs_fd *fd = WIMFS_FD(fi);
+       const struct wim_inode *inode;
+       const struct wim_dentry *child;
        int ret;
 
        int ret;
 
-       if (!fd)
-               return -EBADF;
-
        inode = fd->f_inode;
 
        ret = filler(buf, ".", NULL, 0);
        inode = fd->f_inode;
 
        ret = filler(buf, ".", NULL, 0);
@@ -2126,13 +1603,14 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
        return 0;
 }
 
        return 0;
 }
 
-
 static int
 wimfs_readlink(const char *path, char *buf, size_t buf_len)
 {
 static int
 wimfs_readlink(const char *path, char *buf, size_t buf_len)
 {
-       struct wimfs_context *ctx = wimfs_get_context();
-       struct wim_inode *inode = wim_pathname_to_inode(ctx->wim, path);
-       int ret;
+       WIMStruct *wim = wimfs_get_WIMStruct();
+       const struct wim_inode *inode;
+       ssize_t ret;
+
+       inode = wim_pathname_to_inode(wim, path);
        if (!inode)
                return -errno;
        if (!inode_is_symlink(inode))
        if (!inode)
                return -errno;
        if (!inode_is_symlink(inode))
@@ -2141,7 +1619,6 @@ wimfs_readlink(const char *path, char *buf, size_t buf_len)
                return -EINVAL;
        ret = wim_inode_readlink(inode, buf, buf_len - 1, NULL);
        if (ret >= 0) {
                return -EINVAL;
        ret = wim_inode_readlink(inode, buf, buf_len - 1, NULL);
        if (ret >= 0) {
-               wimlib_assert(ret <= buf_len - 1);
                buf[ret] = '\0';
                ret = 0;
        } else if (ret == -ENAMETOOLONG) {
                buf[ret] = '\0';
                ret = 0;
        } else if (ret == -ENAMETOOLONG) {
@@ -2150,52 +1627,42 @@ wimfs_readlink(const char *path, char *buf, size_t buf_len)
        return ret;
 }
 
        return ret;
 }
 
-/* Close a file. */
+/* We use this for both release() and releasedir(), since in both cases we
+ * simply need to close the file descriptor.  */
 static int
 wimfs_release(const char *path, struct fuse_file_info *fi)
 {
 static int
 wimfs_release(const char *path, struct fuse_file_info *fi)
 {
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
-       return close_wimfs_fd(fd);
-}
-
-/* Close a directory */
-static int
-wimfs_releasedir(const char *path, struct fuse_file_info *fi)
-{
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
-       return close_wimfs_fd(fd);
+       return close_wimfs_fd(WIMFS_FD(fi));
 }
 
 }
 
-#ifdef ENABLE_XATTR
-/* Remove an alternate data stream through the XATTR interface */
 static int
 wimfs_removexattr(const char *path, const char *name)
 {
 static int
 wimfs_removexattr(const char *path, const char *name)
 {
+       struct wimfs_context *ctx = wimfs_get_context();
        struct wim_inode *inode;
        struct wim_ads_entry *ads_entry;
        struct wim_inode *inode;
        struct wim_ads_entry *ads_entry;
-       u16 ads_idx;
-       struct wimfs_context *ctx = wimfs_get_context();
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
-       if (strlen(name) < 5 || memcmp(name, "user.", 5) != 0)
+       if (strncmp(name, "user.", 5))
                return -ENOATTR;
        name += 5;
 
                return -ENOATTR;
        name += 5;
 
+       /* Removing a named data stream.  */
+
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       ads_entry = inode_get_ads_entry(inode, name, &ads_idx);
+       ads_entry = inode_get_ads_entry(inode, name);
        if (!ads_entry)
        if (!ads_entry)
-               return -ENOATTR;
-       inode_remove_ads(inode, ads_idx, ctx->wim->lookup_table);
+               return (errno == ENOENT) ? -ENOATTR : -errno;
+
+       inode_remove_ads(inode, ads_entry, ctx->wim->lookup_table);
        return 0;
 }
        return 0;
 }
-#endif
 
 
-/* Renames a file or directory.  See rename (3) */
 static int
 wimfs_rename(const char *from, const char *to)
 {
 static int
 wimfs_rename(const char *from, const char *to)
 {
@@ -2203,12 +1670,11 @@ wimfs_rename(const char *from, const char *to)
                               WIMLIB_CASE_SENSITIVE, NULL);
 }
 
                               WIMLIB_CASE_SENSITIVE, NULL);
 }
 
-/* Remove a directory */
 static int
 wimfs_rmdir(const char *path)
 {
 static int
 wimfs_rmdir(const char *path)
 {
-       struct wim_dentry *dentry;
        WIMStruct *wim = wimfs_get_WIMStruct();
        WIMStruct *wim = wimfs_get_WIMStruct();
+       struct wim_dentry *dentry;
 
        dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
        if (!dentry)
 
        dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
        if (!dentry)
@@ -2224,49 +1690,58 @@ wimfs_rmdir(const char *path)
        return 0;
 }
 
        return 0;
 }
 
-#ifdef ENABLE_XATTR
-/* Write an alternate data stream through the XATTR interface */
 static int
 wimfs_setxattr(const char *path, const char *name,
               const char *value, size_t size, int flags)
 {
 static int
 wimfs_setxattr(const char *path, const char *name,
               const char *value, size_t size, int flags)
 {
-       struct wim_ads_entry *existing_ads_entry;
-       struct wim_inode *inode;
-       u16 ads_idx;
        struct wimfs_context *ctx = wimfs_get_context();
        struct wimfs_context *ctx = wimfs_get_context();
-       int ret;
+       struct wim_inode *inode;
+       struct wim_ads_entry *existing_entry;
+
+       if (!strncmp(name, "wimfs.", 6)) {
+               /* Handle some magical extended attributes.  These really should
+                * be ioctls, but directory ioctls aren't supported until
+                * libfuse 2.9, and even then they are broken.  */
+               name += 6;
+               if (!strcmp(name, "unmount")) {
+                       if (size < sizeof(struct wimfs_unmount_info))
+                               return -EINVAL;
+                       return unmount_wimfs((const void *)value);
+               }
+               return -ENOATTR;
+       }
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
-       if (strlen(name) <= 5 || memcmp(name, "user.", 5) != 0)
+       if (strncmp(name, "user.", 5))
                return -ENOATTR;
        name += 5;
 
                return -ENOATTR;
        name += 5;
 
+       /* Setting the contents of a named data stream.  */
+
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       existing_ads_entry = inode_get_ads_entry(inode, name, &ads_idx);
-       if (existing_ads_entry) {
+       existing_entry = inode_get_ads_entry(inode, name);
+       if (existing_entry) {
                if (flags & XATTR_CREATE)
                        return -EEXIST;
        } else {
                if (flags & XATTR_CREATE)
                        return -EEXIST;
        } else {
+               if (errno != ENOENT)
+                       return -errno;
                if (flags & XATTR_REPLACE)
                        return -ENOATTR;
        }
 
                if (flags & XATTR_REPLACE)
                        return -ENOATTR;
        }
 
-       ret = inode_add_ads_with_data(inode, name, value,
-                                     size, ctx->wim->lookup_table);
-       if (ret == 0) {
-               if (existing_ads_entry)
-                       inode_remove_ads(inode, ads_idx, ctx->wim->lookup_table);
-       } else {
-               ret = -ENOMEM;
-       }
-       return ret;
+       if (!inode_add_ads_with_data(inode, name, value,
+                                    size, ctx->wim->lookup_table))
+               return -errno;
+       if (existing_entry)
+               inode_remove_ads(inode, existing_entry, ctx->wim->lookup_table);
+       return 0;
 }
 }
-#endif
 
 static int
 wimfs_symlink(const char *to, const char *from)
 
 static int
 wimfs_symlink(const char *to, const char *from)
@@ -2278,89 +1753,77 @@ wimfs_symlink(const char *to, const char *from)
 
        ret = create_dentry(fuse_ctx, from, S_IFLNK | 0777, 0,
                            FILE_ATTRIBUTE_REPARSE_POINT, &dentry);
 
        ret = create_dentry(fuse_ctx, from, S_IFLNK | 0777, 0,
                            FILE_ATTRIBUTE_REPARSE_POINT, &dentry);
-       if (ret == 0) {
-               dentry->d_inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
-               ret = wim_inode_set_symlink(dentry->d_inode, to,
-                                           wimfs_ctx->wim->lookup_table);
-               if (ret) {
-                       remove_dentry(dentry, wimfs_ctx->wim->lookup_table);
-                       if (ret == WIMLIB_ERR_NOMEM)
-                               ret = -ENOMEM;
-                       else
-                               ret = -EIO;
-               }
+       if (ret)
+               return ret;
+       dentry->d_inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+       ret = wim_inode_set_symlink(dentry->d_inode, to,
+                                   wimfs_ctx->wim->lookup_table);
+       if (ret) {
+               remove_dentry(dentry, wimfs_ctx->wim->lookup_table);
+               if (ret == WIMLIB_ERR_NOMEM)
+                       ret = -ENOMEM;
+               else
+                       ret = -EINVAL;
        }
        return ret;
 }
 
        }
        return ret;
 }
 
-
-/* Reduce the size of a file */
 static int
 wimfs_truncate(const char *path, off_t size)
 {
 static int
 wimfs_truncate(const char *path, off_t size)
 {
+       const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_lookup_table_entry *lte;
        struct wim_dentry *dentry;
        struct wim_lookup_table_entry *lte;
-       int ret;
        u16 stream_idx;
        u16 stream_idx;
-       u32 stream_id;
-       struct wim_inode *inode;
-       struct wimfs_context *ctx = wimfs_get_context();
+       int ret;
+       int fd;
 
        ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
                                     &dentry, &lte, &stream_idx);
 
 
        ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
                                     &dentry, &lte, &stream_idx);
 
-       if (ret != 0)
+       if (ret)
                return ret;
 
                return ret;
 
-       if (lte == NULL && size == 0)
+       if (!lte && !size)
                return 0;
 
                return 0;
 
-       if (lte != NULL && lte->resource_location == RESOURCE_IN_STAGING_FILE) {
-               ret = truncate(lte->staging_file_name, size);
-               if (ret)
-                       ret = -errno;
-               else
-                       lte->size = size;
-       } else {
-               /* File in WIM.  Extract it to the staging directory, but only
-                * the first @size bytes of it. */
-               struct wim_lookup_table_entry **back_ptr;
-
-               inode = dentry->d_inode;
-               if (stream_idx == 0) {
-                       stream_id = 0;
-                       back_ptr = &inode->i_lte;
-               } else {
-                       stream_id = inode->i_ads_entries[stream_idx - 1].stream_id;
-                       back_ptr = &inode->i_ads_entries[stream_idx - 1].lte;
-               }
-               ret = extract_resource_to_staging_dir(inode, stream_id,
-                                                     &lte, size, ctx);
-               *back_ptr = lte;
+       if (!lte || lte->resource_location != RESOURCE_IN_STAGING_FILE) {
+               return extract_resource_to_staging_dir(dentry->d_inode,
+                                                      stream_idx, &lte,
+                                                      size, ctx);
        }
        }
-       return ret;
+
+       /* Truncate the staging file.  */
+       fd = openat(lte->staging_dir_fd, lte->staging_file_name,
+                   O_WRONLY | O_NOFOLLOW);
+       if (fd < 0)
+               return -errno;
+       ret = ftruncate(fd, size);
+       if (close(fd) || ret)
+               return -errno;
+       lte->size = size;
+       return 0;
 }
 
 }
 
-/* Unlink a non-directory or alternate data stream */
 static int
 wimfs_unlink(const char *path)
 {
 static int
 wimfs_unlink(const char *path)
 {
+       const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_dentry *dentry;
-       struct wim_lookup_table_entry *lte;
-       int ret;
        u16 stream_idx;
        u16 stream_idx;
-       struct wimfs_context *ctx = wimfs_get_context();
+       int ret;
 
        ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
 
        ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
-                                    &dentry, &lte, &stream_idx);
+                                    &dentry, NULL, &stream_idx);
 
 
-       if (ret != 0)
+       if (ret)
                return ret;
 
        if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0)
                remove_dentry(dentry, ctx->wim->lookup_table);
        else
                return ret;
 
        if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0)
                remove_dentry(dentry, ctx->wim->lookup_table);
        else
-               inode_remove_ads(dentry->d_inode, stream_idx - 1,
+               inode_remove_ads(dentry->d_inode,
+                                &dentry->d_inode->i_ads_entries[stream_idx - 1],
                                 ctx->wim->lookup_table);
        return 0;
 }
                                 ctx->wim->lookup_table);
        return 0;
 }
@@ -2374,8 +1837,8 @@ wimfs_unlink(const char *path)
 static int
 wimfs_utimens(const char *path, const struct timespec tv[2])
 {
 static int
 wimfs_utimens(const char *path, const struct timespec tv[2])
 {
-       struct wim_inode *inode;
        WIMStruct *wim = wimfs_get_WIMStruct();
        WIMStruct *wim = wimfs_get_WIMStruct();
+       struct wim_inode *inode;
 
        inode = wim_pathname_to_inode(wim, path);
        if (!inode)
 
        inode = wim_pathname_to_inode(wim, path);
        if (!inode)
@@ -2399,50 +1862,33 @@ wimfs_utimens(const char *path, const struct timespec tv[2])
 static int
 wimfs_utime(const char *path, struct utimbuf *times)
 {
 static int
 wimfs_utime(const char *path, struct utimbuf *times)
 {
-       struct wim_inode *inode;
        WIMStruct *wim = wimfs_get_WIMStruct();
        WIMStruct *wim = wimfs_get_WIMStruct();
+       struct wim_inode *inode;
 
        inode = wim_pathname_to_inode(wim, path);
        if (!inode)
                return -errno;
 
 
        inode = wim_pathname_to_inode(wim, path);
        if (!inode)
                return -errno;
 
-       inode->i_last_write_time = unix_timestamp_to_wim(times->modtime);
        inode->i_last_access_time = unix_timestamp_to_wim(times->actime);
        inode->i_last_access_time = unix_timestamp_to_wim(times->actime);
+       inode->i_last_write_time = unix_timestamp_to_wim(times->modtime);
        return 0;
 }
 #endif /* !HAVE_UTIMENSAT */
 
        return 0;
 }
 #endif /* !HAVE_UTIMENSAT */
 
-/* Writes to a file in the WIM filesystem.
- * It may be an alternate data stream, but here we don't even notice because we
- * just get a lookup table entry. */
 static int
 wimfs_write(const char *path, const char *buf, size_t size,
            off_t offset, struct fuse_file_info *fi)
 {
 static int
 wimfs_write(const char *path, const char *buf, size_t size,
            off_t offset, struct fuse_file_info *fi)
 {
-       struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
-       int ret;
-
-       if (!fd)
-               return -EBADF;
-
-       wimlib_assert(fd->f_lte != NULL);
-       wimlib_assert(fd->f_lte->staging_file_name != NULL);
-       wimlib_assert(filedes_valid(&fd->staging_fd));
-       wimlib_assert(fd->f_inode != NULL);
+       struct wimfs_fd *fd = WIMFS_FD(fi);
+       ssize_t ret;
 
 
-       /* Write the data. */
-       ret = raw_pwrite(&fd->staging_fd, buf, size, offset);
-       if (ret == -1)
+       ret = raw_pwrite(&fd->f_staging_fd, buf, size, offset);
+       if (ret < 0)
                return -errno;
 
                return -errno;
 
-       /* Update file size */
-       if (offset + size > fd->f_lte->size) {
-               DEBUG("Update file size %"PRIu64 " => %"PRIu64"",
-                     fd->f_lte->size, offset + size);
+       if (offset + size > fd->f_lte->size)
                fd->f_lte->size = offset + size;
                fd->f_lte->size = offset + size;
-       }
 
 
-       /* Update timestamps */
        touch_inode(fd->f_inode);
        return ret;
 }
        touch_inode(fd->f_inode);
        return ret;
 }
@@ -2450,17 +1896,12 @@ wimfs_write(const char *path, const char *buf, size_t size,
 static struct fuse_operations wimfs_operations = {
        .chmod       = wimfs_chmod,
        .chown       = wimfs_chown,
 static struct fuse_operations wimfs_operations = {
        .chmod       = wimfs_chmod,
        .chown       = wimfs_chown,
-       .destroy     = wimfs_destroy,
        .fgetattr    = wimfs_fgetattr,
        .ftruncate   = wimfs_ftruncate,
        .getattr     = wimfs_getattr,
        .fgetattr    = wimfs_fgetattr,
        .ftruncate   = wimfs_ftruncate,
        .getattr     = wimfs_getattr,
-#ifdef ENABLE_XATTR
        .getxattr    = wimfs_getxattr,
        .getxattr    = wimfs_getxattr,
-#endif
        .link        = wimfs_link,
        .link        = wimfs_link,
-#ifdef ENABLE_XATTR
        .listxattr   = wimfs_listxattr,
        .listxattr   = wimfs_listxattr,
-#endif
        .mkdir       = wimfs_mkdir,
        .mknod       = wimfs_mknod,
        .open        = wimfs_open,
        .mkdir       = wimfs_mkdir,
        .mknod       = wimfs_mknod,
        .open        = wimfs_open,
@@ -2469,15 +1910,11 @@ static struct fuse_operations wimfs_operations = {
        .readdir     = wimfs_readdir,
        .readlink    = wimfs_readlink,
        .release     = wimfs_release,
        .readdir     = wimfs_readdir,
        .readlink    = wimfs_readlink,
        .release     = wimfs_release,
-       .releasedir  = wimfs_releasedir,
-#ifdef ENABLE_XATTR
+       .releasedir  = wimfs_release,
        .removexattr = wimfs_removexattr,
        .removexattr = wimfs_removexattr,
-#endif
        .rename      = wimfs_rename,
        .rmdir       = wimfs_rmdir,
        .rename      = wimfs_rename,
        .rmdir       = wimfs_rmdir,
-#ifdef ENABLE_XATTR
        .setxattr    = wimfs_setxattr,
        .setxattr    = wimfs_setxattr,
-#endif
        .symlink     = wimfs_symlink,
        .truncate    = wimfs_truncate,
        .unlink      = wimfs_unlink,
        .symlink     = wimfs_symlink,
        .truncate    = wimfs_truncate,
        .unlink      = wimfs_unlink,
@@ -2488,9 +1925,9 @@ static struct fuse_operations wimfs_operations = {
 #endif
        .write       = wimfs_write,
 
 #endif
        .write       = wimfs_write,
 
-       /* wimfs keeps file descriptor structures (struct wimfs_fd), so there is
-        * no need to have the file path provided on operations such as read()
-        * where only the file descriptor is needed. */
+       /* We keep track of file descriptor structures (struct wimfs_fd), so
+        * there is no need to have the file path provided on operations such as
+        * read().  */
 #if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
        .flag_nullpath_ok = 1,
 #endif
 #if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
        .flag_nullpath_ok = 1,
 #endif
@@ -2500,24 +1937,18 @@ static struct fuse_operations wimfs_operations = {
 #endif
 };
 
 #endif
 };
 
-
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
 wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
                   int mount_flags, const char *staging_dir)
 {
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
 wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
                   int mount_flags, const char *staging_dir)
 {
-       int argc;
-       char *argv[16];
        int ret;
        int ret;
-       char *dir_copy;
        struct wim_image_metadata *imd;
        struct wimfs_context ctx;
        struct wim_image_metadata *imd;
        struct wimfs_context ctx;
-       struct wim_inode *inode;
+       char *fuse_argv[16];
+       int fuse_argc;
 
 
-       DEBUG("Mount: wim = %p, image = %d, dir = %s, flags = %d, ",
-             wim, image, dir, mount_flags);
-
-       if (!wim || !dir)
+       if (!wim || !dir || !*dir)
                return WIMLIB_ERR_INVALID_PARAM;
 
        if (mount_flags & ~(WIMLIB_MOUNT_FLAG_READWRITE |
                return WIMLIB_ERR_INVALID_PARAM;
 
        if (mount_flags & ~(WIMLIB_MOUNT_FLAG_READWRITE |
@@ -2529,247 +1960,423 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
                            WIMLIB_MOUNT_FLAG_ALLOW_OTHER))
                return WIMLIB_ERR_INVALID_PARAM;
 
                            WIMLIB_MOUNT_FLAG_ALLOW_OTHER))
                return WIMLIB_ERR_INVALID_PARAM;
 
+       /* For read-write mount, check for write access to the WIM.  */
        if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
                ret = can_delete_from_wim(wim);
                if (ret)
                        return ret;
        }
 
        if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
                ret = can_delete_from_wim(wim);
                if (ret)
                        return ret;
        }
 
+       /* Select the image to mount.  */
        ret = select_wim_image(wim, image);
        if (ret)
                return ret;
 
        ret = select_wim_image(wim, image);
        if (ret)
                return ret;
 
-       DEBUG("Selected image %d", image);
-
+       /* Get the metadata for the image to mount.  */
        imd = wim_get_current_image_metadata(wim);
 
        if (imd->modified) {
        imd = wim_get_current_image_metadata(wim);
 
        if (imd->modified) {
-               /* wimfs_read() only supports a limited number of stream
-                * locations, not including RESOURCE_IN_FILE_ON_DISK,
-                * RESOURCE_IN_NTFS_VOLUME, etc. that might appear if files were
-                * added to the WIM image.  */
-               ERROR("Cannot mount an image with newly added files!");
+               /* To avoid complicating things, we don't support mounting
+                * images to which in-memory modifications have already been
+                * made.  */
+               ERROR("Cannot mount a modified WIM image!");
                return WIMLIB_ERR_INVALID_PARAM;
        }
 
                return WIMLIB_ERR_INVALID_PARAM;
        }
 
-       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
-               ret = lock_wim(wim, wim->in_fd.fd);
-               if (ret)
-                       return ret;
-       }
+       ret = lock_wim_for_append(wim, wim->in_fd.fd);
+       if (ret)
+               return ret;
 
 
-       /* Use default stream interface if one was not specified */
+       /* If the user did not specify an interface for accessing named
+        * data streams, use the default (extended attributes).  */
        if (!(mount_flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE |
        if (!(mount_flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE |
-                      WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR |
-                      WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)))
+                            WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR |
+                            WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)))
                mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
 
                mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
 
-       DEBUG("Initializing struct wimfs_context");
-       init_wimfs_context(&ctx);
+       /* Start initializing the wimfs_context.  */
+       memset(&ctx, 0, sizeof(struct wimfs_context));
        ctx.wim = wim;
        ctx.mount_flags = mount_flags;
        ctx.wim = wim;
        ctx.mount_flags = mount_flags;
-       ctx.image_inode_list = &imd->inode_list;
-       ctx.default_uid = getuid();
-       ctx.default_gid = getgid();
-       wimlib_assert(list_empty(&imd->unhashed_streams));
        if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
                ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK;
        if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
                ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK;
+       /* For read-write mount, create the staging directory.  */
+       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+               ret = make_staging_dir(&ctx, staging_dir);
+               if (ret)
+                       goto out_unlock;
+       }
+       ctx.owner_uid = getuid();
+       ctx.owner_gid = getgid();
 
 
-       DEBUG("Unlinking message queues in case they already exist");
-       ret = set_message_queue_names(&ctx, dir);
-       if (ret)
-               goto out_unlock;
-       unlink_message_queues(&ctx);
+       /* Add each stream referenced by files in the image to a list and
+        * preemptively double the number of references to each.  The latter is
+        * done to allow implementing the WIMLIB_UNMOUNT_FLAG_NEW_IMAGE
+        * semantics.  */
+       INIT_LIST_HEAD(&ctx.orig_stream_list);
+       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+               unsigned i;
+               struct wim_inode *inode;
+               struct wim_lookup_table_entry *lte;
 
 
-       DEBUG("Preparing arguments to fuse_main()");
+               image_for_each_inode(inode, imd) {
+                       for (i = 0; i <= inode->i_num_ads; i++) {
+                               lte = inode_stream_lte(inode, i,
+                                                      wim->lookup_table);
+                               if (lte)
+                                       lte->out_refcnt = 0;
+                       }
+               }
 
 
-       dir_copy = STRDUP(dir);
-       if (!dir_copy) {
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_free_message_queue_names;
+               image_for_each_inode(inode, imd) {
+                       for (i = 0; i <= inode->i_num_ads; i++) {
+                               lte = inode_stream_lte(inode, i,
+                                                      wim->lookup_table);
+                               if (lte) {
+                                       if (lte->out_refcnt == 0)
+                                               list_add(&lte->orig_stream_list,
+                                                        &ctx.orig_stream_list);
+                                       lte->out_refcnt += inode->i_nlink;
+                                       lte->refcnt += inode->i_nlink;
+                               }
+                       }
+               }
        }
 
        }
 
-       argc = 0;
-       argv[argc++] = "wimlib";
-       argv[argc++] = dir_copy;
+       /* Assign new inode numbers.  */
+       reassign_inode_numbers(&ctx);
+
+       /* If a read-write mount, mark the image as modified.  */
+       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
+               imd->modified = 1;
+
+       /* Build the FUSE command line.  */
 
 
-       /* disable multi-threaded operation */
-       argv[argc++] = "-s";
+       fuse_argc = 0;
+       fuse_argv[fuse_argc++] = "wimlib";
+       fuse_argv[fuse_argc++] = (char *)dir;
 
 
+       /* Disable multi-threaded operation.  */
+       fuse_argv[fuse_argc++] = "-s";
+
+       /* Enable FUSE debug mode (don't fork) if requested by the user.  */
        if (mount_flags & WIMLIB_MOUNT_FLAG_DEBUG)
        if (mount_flags & WIMLIB_MOUNT_FLAG_DEBUG)
-               argv[argc++] = "-d";
+               fuse_argv[fuse_argc++] = "-d";
 
        /*
 
        /*
-        * We provide the use_ino option to the FUSE mount because we are going
-        * to assign inode numbers ourselves. */
+        * Build the FUSE mount options:
+        *
+        * use_ino
+        *      FUSE will use the inode numbers we provide.  We want this,
+        *      because we have inodes and will number them ourselves.
+        *
+        * subtype=wimfs
+        *      Name for our filesystem (main type is "fuse").
+        *
+        * hard_remove
+        *      If an open file is unlinked, unlink it for real rather than
+        *      renaming it to a hidden file.  Our code supports this; an
+        *      unlinked inode is retained until all its file descriptors have
+        *      been closed.
+        *
+        * default_permissions
+        *      FUSE will perform permission checking.  Useful when
+        *      WIMLIB_MOUNT_FLAG_UNIX_DATA is provided and the WIM image
+        *      contains the UNIX permissions for each file.
+        *
+        * kernel_cache
+        *      Cache the contents of files.  This will speed up repeated access
+        *      to files on a mounted WIM image, since they won't need to be
+        *      decompressed repeatedly.  This option is valid because data in
+        *      the WIM image should never be changed externally.  (Although, if
+        *      someone really wanted to they could modify the WIM file or mess
+        *      with the staging directory; but then they're asking for
+        *      trouble.)
+        *
+        * entry_timeout=1000000000
+        *      Cache positive name lookups indefinitely, since names can only
+        *      be added, removed, or modified through the mounted filesystem
+        *      itself.
+        *
+        * negative_timeout=1000000000
+        *      Cache negative name lookups indefinitely, since names can only
+        *      be added, removed, or modified through the mounted filesystem
+        *      itself.
+        *
+        * attr_timeout=0
+        *      Don't cache file/directory attributes.  This is needed as a
+        *      workaround for the fact that when caching attributes, the high
+        *      level interface to libfuse considers a file which has several
+        *      hard-linked names as several different files.  (Otherwise, we
+        *      could cache our file/directory attributes indefinitely, since
+        *      they can only be changed through the mounted filesystem itself.)
+        */
        char optstring[256] =
                "use_ino"
                ",subtype=wimfs"
                ",attr_timeout=0"
        char optstring[256] =
                "use_ino"
                ",subtype=wimfs"
                ",attr_timeout=0"
-#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
                ",hard_remove"
                ",hard_remove"
-#endif
                ",default_permissions"
                ",default_permissions"
+               ",kernel_cache"
+               ",entry_timeout=1000000000"
+               ",negative_timeout=1000000000"
+               ",attr_timeout=0"
                ;
                ;
-       argv[argc++] = "-o";
-       argv[argc++] = optstring;
-       if ((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) {
-               /* Read-write mount.  Make the staging directory */
-               ret = make_staging_dir(&ctx, staging_dir);
-               if (ret)
-                       goto out_free_dir_copy;
-       } else {
-               /* Read-only mount */
+       fuse_argv[fuse_argc++] = "-o";
+       fuse_argv[fuse_argc++] = optstring;
+       if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE))
                strcat(optstring, ",ro");
                strcat(optstring, ",ro");
-       }
        if (mount_flags & WIMLIB_MOUNT_FLAG_ALLOW_OTHER)
                strcat(optstring, ",allow_other");
        if (mount_flags & WIMLIB_MOUNT_FLAG_ALLOW_OTHER)
                strcat(optstring, ",allow_other");
-       argv[argc] = NULL;
+       fuse_argv[fuse_argc] = NULL;
 
 
-#ifdef ENABLE_DEBUG
-       {
-               int i;
-               DEBUG("FUSE command line (argc = %d): ", argc);
-               for (i = 0; i < argc; i++) {
-                       fputs(argv[i], stdout);
-                       putchar(' ');
+       /* Mount our filesystem.  */
+       ret = fuse_main(fuse_argc, fuse_argv, &wimfs_operations, &ctx);
+
+       /* Cleanup and return.  */
+       if (ret)
+               ret = WIMLIB_ERR_FUSE;
+       release_extra_refcnts(&ctx);
+       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
+               delete_staging_dir(&ctx);
+out_unlock:
+       unlock_wim_for_append(wim, wim->in_fd.fd);
+       return ret;
+}
+
+struct commit_progress_thread_args {
+       mqd_t mq;
+       wimlib_progress_func_t progfunc;
+       void *progctx;
+       int status;
+};
+
+static void *
+commit_progress_thread_proc(void *_args)
+{
+       struct commit_progress_thread_args *args = _args;
+       struct commit_progress_report report;
+       ssize_t ret;
+
+       args->status = WIMLIB_ERR_NOT_A_MOUNTPOINT;
+       for (;;) {
+               ret = mq_receive(args->mq,
+                                (char *)&report, sizeof(report), NULL);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       break;
+               }
+               if (ret == sizeof(int)) {
+                       args->status = *(int *)&report;
+                       break;
                }
                }
-               putchar('\n');
-               fflush(stdout);
+               if (ret < sizeof(report))
+                       continue;
+               call_progress(args->progfunc, report.msg,
+                             &report.info, args->progctx);
        }
        }
-#endif
+       return NULL;
+}
 
 
-       /* Assign inode numbers.  Also, if a read-write mount was requested,
-        * mark the dentry tree as modified, and add each stream referenced by
-        * files in the image to a list and preemptively double the number of
-        * references to each.  The latter is done to allow implementing the
-        * WIMLIB_UNMOUNT_FLAG_NEW_IMAGE semantics.  */
-       ctx.next_ino = 1;
-       INIT_LIST_HEAD(&ctx.orig_stream_list);
-       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
-               imd->modified = 1;
-               image_for_each_inode(inode, imd) {
-                       inode->i_ino = ctx.next_ino++;
-                       for (unsigned i = 0; i <= inode->i_num_ads; i++) {
-                               struct wim_lookup_table_entry *lte;
+static void
+generate_message_queue_name(char name[WIMFS_MQUEUE_NAME_LEN + 1])
+{
+       name[0] = '/';
+       memcpy(name + 1, "wimfs-", 6);
+       randomize_char_array_with_alnum(name + 7, WIMFS_MQUEUE_NAME_LEN - 7);
+       name[WIMFS_MQUEUE_NAME_LEN] = '\0';
+}
 
 
-                               lte = inode_stream_lte(inode, i, wim->lookup_table);
-                               if (lte)
-                                       lte->out_refcnt = 0;
-                       }
-               }
-               image_for_each_inode(inode, imd) {
-                       for (unsigned i = 0; i <= inode->i_num_ads; i++) {
-                               struct wim_lookup_table_entry *lte;
+static mqd_t
+create_message_queue(const char *name, bool have_progfunc)
+{
+       bool am_root = (getuid() == 0);
+       mode_t umask_save = 0;
+       mode_t mode = 0600;
+       struct mq_attr attr;
+       mqd_t mq;
 
 
-                               lte = inode_stream_lte(inode, i,
-                                                      wim->lookup_table);
-                               if (lte) {
-                                       if (lte->out_refcnt == 0)
-                                               list_add(&lte->orig_stream_list,
-                                                        &ctx.orig_stream_list);
-                                       lte->out_refcnt += inode->i_nlink;
-                                       lte->refcnt += inode->i_nlink;
-                               }
-                       }
+       memset(&attr, 0, sizeof(attr));
+       attr.mq_maxmsg = 8;
+       if (have_progfunc)
+               attr.mq_msgsize = sizeof(struct commit_progress_report);
+       else
+               attr.mq_msgsize = sizeof(int);
+
+       if (am_root) {
+               /* Filesystem mounted as normal user with --allow-other should
+                * be able to send messages to root user, if they're doing the
+                * unmount.  */
+               umask_save = umask(0);
+               mode = 0666;
+       }
+       mq = mq_open(name, O_RDWR | O_CREAT | O_EXCL, mode, &attr);
+       if (am_root)
+               umask(umask_save);
+       return mq;
+}
+
+/* Unmount a read-write mounted WIM image, committing the changes.  */
+static int
+do_unmount_commit(const char *dir, int unmount_flags,
+                 wimlib_progress_func_t progfunc, void *progctx)
+{
+       struct wimfs_unmount_info unmount_info;
+       mqd_t mq;
+       struct commit_progress_thread_args args;
+       pthread_t commit_progress_tid;
+       int ret;
+
+       memset(&unmount_info, 0, sizeof(unmount_info));
+       unmount_info.unmount_flags = unmount_flags;
+       generate_message_queue_name(unmount_info.mq_name);
+
+       mq = create_message_queue(unmount_info.mq_name, progfunc != NULL);
+       if (mq == (mqd_t)-1) {
+               ERROR_WITH_ERRNO("Can't create POSIX message queue");
+               return WIMLIB_ERR_MQUEUE;
+       }
+
+       /* The current thread will be stuck in setxattr() until the image is
+        * committed.  Create a thread to handle the progress messages.  */
+       if (progfunc) {
+               args.mq = mq;
+               args.progfunc = progfunc;
+               args.progctx = progctx;
+               ret = pthread_create(&commit_progress_tid, NULL,
+                                    commit_progress_thread_proc, &args);
+               if (ret) {
+                       errno = ret;
+                       ERROR_WITH_ERRNO("Can't create thread");
+                       ret = WIMLIB_ERR_NOMEM;
+                       goto out_delete_mq;
                }
                }
-       } else {
-               image_for_each_inode(inode, imd)
-                       inode->i_ino = ctx.next_ino++;
+               unmount_info.unmount_flags |= WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS;
        }
 
        }
 
-       DEBUG("(next_ino = %"PRIu64")", ctx.next_ino);
+       if (!setxattr(dir, "wimfs.unmount",
+                    (const char *)&unmount_info, sizeof(unmount_info), 0))
+               ret = 0;
+       else if (errno == EACCES || errno == EPERM)
+               ret = WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT;
+       else
+               ret = WIMLIB_ERR_NOT_A_MOUNTPOINT;
+
+       if (progfunc) {
+               /* Terminate the progress thread and retrieve final unmount
+                * status.  */
 
 
-       DEBUG("Calling fuse_main()");
+               int tmp = -1;
+               mq_send(mq, (const char *)&tmp, sizeof(int), 1);
 
 
-       ret = fuse_main(argc, argv, &wimfs_operations, &ctx);
+               pthread_join(commit_progress_tid, NULL);
+               if (!ret && args.status != -1)
+                       ret = args.status;
+       } else if (!ret) {
+               /* Retrieve the final unmount status.  */
 
 
-       DEBUG("Returned from fuse_main() (ret = %d)", ret);
+               int tmp = -1;
+               int len;
 
 
-       if (ret) {
-               ret = WIMLIB_ERR_FUSE;
-       } else {
-               if (ctx.have_status)
-                       ret = ctx.status;
+               mq_send(mq, (const char *)&tmp, sizeof(int), 1);
+               len = mq_receive(mq, (char *)&tmp, sizeof(int), NULL);
+
+               if (len == 4 && tmp != -1)
+                       ret = tmp;
                else
                else
-                       ret = WIMLIB_ERR_TIMEOUT;
-       }
-       if (ctx.daemon_to_unmount_mq != (mqd_t)(-1)) {
-               send_unmount_finished_msg(ctx.daemon_to_unmount_mq, ret);
-               close_message_queues(&ctx);
+                       ret = WIMLIB_ERR_NOT_A_MOUNTPOINT;
        }
        }
+out_delete_mq:
+       mq_close(mq);
+       mq_unlink(unmount_info.mq_name);
+       return ret;
+}
 
 
-       release_extra_refcnts(&ctx);
+/* Unmount a read-only or read-write mounted WIM image, discarding any changes.
+ */
+static int
+do_unmount_discard(const char *dir)
+{
+       if (!getxattr(dir, "wimfs.unmount", NULL, 0))
+               return 0;
+       else if (errno == EACCES || errno == EPERM)
+               return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT;
+       else
+               return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+}
 
 
-       /* Try to delete the staging directory if a deletion wasn't yet
-        * attempted due to an earlier error */
-       if (ctx.staging_dir_name)
-               delete_staging_dir(&ctx);
-out_free_dir_copy:
-       FREE(dir_copy);
-out_unlock:
-       wim->wim_locked = 0;
-out_free_message_queue_names:
-       free_message_queue_names(&ctx);
-       return ret;
+static int
+begin_unmount(const char *dir, int unmount_flags, int *mount_flags_ret,
+             wimlib_progress_func_t progfunc, void *progctx)
+{
+       int mount_flags;
+       int mounted_image;
+       int wim_filename_len;
+       union wimlib_progress_info progress;
+
+       if (getxattr(dir, "wimfs.mount_flags",
+                    &mount_flags, sizeof(int)) != sizeof(int))
+               return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+
+       *mount_flags_ret = mount_flags;
+
+       if (!progfunc)
+               return 0;
+
+       if (getxattr(dir, "wimfs.mounted_image",
+                    &mounted_image, sizeof(int)) != sizeof(int))
+               return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+
+       wim_filename_len = getxattr(dir, "wimfs.wim_filename", NULL, 0);
+       if (wim_filename_len < 0)
+               return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+
+       char wim_filename[wim_filename_len + 1];
+       if (getxattr(dir, "wimfs.wim_filename",
+                    wim_filename, wim_filename_len) != wim_filename_len)
+               return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+       wim_filename[wim_filename_len] = '\0';
+
+       progress.unmount.mountpoint = dir;
+       progress.unmount.mounted_wim = wim_filename;
+       progress.unmount.mounted_image = mounted_image;
+       progress.unmount.mount_flags = mount_flags;
+       progress.unmount.unmount_flags = unmount_flags;
+
+       return call_progress(progfunc, WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN,
+                            &progress, progctx);
 }
 
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
 }
 
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
-wimlib_unmount_image_with_progress(const tchar *dir, int unmount_flags,
+wimlib_unmount_image_with_progress(const char *dir, int unmount_flags,
                                   wimlib_progress_func_t progfunc, void *progctx)
 {
                                   wimlib_progress_func_t progfunc, void *progctx)
 {
+       int mount_flags;
        int ret;
        int ret;
-       struct wimfs_context wimfs_ctx;
+
+       wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
 
        if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY |
                              WIMLIB_UNMOUNT_FLAG_COMMIT |
                              WIMLIB_UNMOUNT_FLAG_REBUILD |
                              WIMLIB_UNMOUNT_FLAG_RECOMPRESS |
 
        if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY |
                              WIMLIB_UNMOUNT_FLAG_COMMIT |
                              WIMLIB_UNMOUNT_FLAG_REBUILD |
                              WIMLIB_UNMOUNT_FLAG_RECOMPRESS |
-                             WIMLIB_UNMOUNT_FLAG_LAZY |
+                             WIMLIB_UNMOUNT_FLAG_FORCE |
                              WIMLIB_UNMOUNT_FLAG_NEW_IMAGE))
                return WIMLIB_ERR_INVALID_PARAM;
 
                              WIMLIB_UNMOUNT_FLAG_NEW_IMAGE))
                return WIMLIB_ERR_INVALID_PARAM;
 
-       init_wimfs_context(&wimfs_ctx);
-
-       ret = set_message_queue_names(&wimfs_ctx, dir);
-       if (ret != 0)
-               goto out;
-
-       ret = open_message_queues(&wimfs_ctx, false);
-       if (ret != 0)
-               goto out_free_message_queue_names;
-
-       ret = send_unmount_request_msg(wimfs_ctx.unmount_to_daemon_mq,
-                                      unmount_flags,
-                                      progfunc != NULL);
-       if (ret != 0)
-               goto out_close_message_queues;
-
-       ret = execute_fusermount(dir, (unmount_flags & WIMLIB_UNMOUNT_FLAG_LAZY) != 0);
-       if (ret != 0)
-               goto out_close_message_queues;
-
-       struct unmount_msg_handler_context handler_ctx = {
-               .hdr = {
-                       .timeout_seconds = 5,
-               },
-               .daemon_pid = 0,
-               .progfunc = progfunc,
-               .progctx = progctx,
-       };
-
-       ret = message_loop(wimfs_ctx.daemon_to_unmount_mq,
-                          &unmount_msg_handler_callbacks,
-                          &handler_ctx.hdr);
-       if (ret == 0)
-               ret = handler_ctx.status;
-out_close_message_queues:
-       close_message_queues(&wimfs_ctx);
-out_free_message_queue_names:
-       free_message_queue_names(&wimfs_ctx);
-out:
-       return ret;
+       ret = begin_unmount(dir, unmount_flags, &mount_flags,
+                           progfunc, progctx);
+       if (ret)
+               return ret;
+
+       if ((unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) &&
+           (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE))
+               return do_unmount_commit(dir, unmount_flags,
+                                        progfunc, progctx);
+       else
+               return do_unmount_discard(dir);
 }
 
 #else /* WITH_FUSE */
 }
 
 #else /* WITH_FUSE */
@@ -2803,7 +2410,6 @@ wimlib_mount_image(WIMStruct *wim, int image, const tchar *dir,
 
 #endif /* !WITH_FUSE */
 
 
 #endif /* !WITH_FUSE */
 
-
 WIMLIBAPI int
 wimlib_unmount_image(const tchar *dir, int unmount_flags)
 {
 WIMLIBAPI int
 wimlib_unmount_image(const tchar *dir, int unmount_flags)
 {
index 7631dd492309283bb7d8fa72a569004f64ae619d..5c0225ab6d55023c5c8ef9c60bdda7b280fdc5ac 100644 (file)
@@ -193,6 +193,7 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec,
                      "expected power-of-2 chunk size (got %"PRIu32")",
                      chunk_size);
                ret = WIMLIB_ERR_INVALID_CHUNK_SIZE;
                      "expected power-of-2 chunk size (got %"PRIu32")",
                      chunk_size);
                ret = WIMLIB_ERR_INVALID_CHUNK_SIZE;
+               errno = EINVAL;
                goto out_free_memory;
        }
 
                goto out_free_memory;
        }
 
@@ -207,8 +208,11 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec,
        } else {
                ret = wimlib_create_decompressor(ctype, chunk_size, NULL,
                                                 &decompressor);
        } else {
                ret = wimlib_create_decompressor(ctype, chunk_size, NULL,
                                                 &decompressor);
-               if (ret)
+               if (ret) {
+                       if (ret != WIMLIB_ERR_NOMEM)
+                               errno = EINVAL;
                        goto out_free_memory;
                        goto out_free_memory;
+               }
        }
 
        const u32 chunk_order = bsr32(chunk_size);
        }
 
        const u32 chunk_order = bsr32(chunk_size);
@@ -789,6 +793,34 @@ read_file_on_disk_prefix(const struct wim_lookup_table_entry *lte, u64 size,
        return ret;
 }
 
        return ret;
 }
 
+#ifdef WITH_FUSE
+static int
+read_staging_file_prefix(const struct wim_lookup_table_entry *lte, u64 size,
+                        consume_data_callback_t cb, void *cb_ctx)
+{
+       int raw_fd;
+       struct filedes fd;
+       int ret;
+
+       wimlib_assert(size <= lte->size);
+
+       DEBUG("Reading %"PRIu64" bytes from staging file \"%s\"",
+             size, lte->staging_file_name);
+
+       raw_fd = openat(lte->staging_dir_fd, lte->staging_file_name,
+                       O_RDONLY | O_NOFOLLOW);
+       if (raw_fd < 0) {
+               ERROR_WITH_ERRNO("Can't open staging file \"%s\"",
+                                lte->staging_file_name);
+               return WIMLIB_ERR_OPEN;
+       }
+       filedes_init(&fd, raw_fd);
+       ret = read_raw_file_data(&fd, 0, size, cb, cb_ctx);
+       filedes_close(&fd);
+       return ret;
+}
+#endif
+
 /* This function handles the trivial case of reading stream data that is, in
  * fact, already located in an in-memory buffer.  */
 static int
 /* This function handles the trivial case of reading stream data that is, in
  * fact, already located in an in-memory buffer.  */
 static int
@@ -828,7 +860,7 @@ read_stream_prefix(const struct wim_lookup_table_entry *lte, u64 size,
                [RESOURCE_IN_FILE_ON_DISK]    = read_file_on_disk_prefix,
                [RESOURCE_IN_ATTACHED_BUFFER] = read_buffer_prefix,
        #ifdef WITH_FUSE
                [RESOURCE_IN_FILE_ON_DISK]    = read_file_on_disk_prefix,
                [RESOURCE_IN_ATTACHED_BUFFER] = read_buffer_prefix,
        #ifdef WITH_FUSE
-               [RESOURCE_IN_STAGING_FILE]    = read_file_on_disk_prefix,
+               [RESOURCE_IN_STAGING_FILE]    = read_staging_file_prefix,
        #endif
        #ifdef WITH_NTFS_3G
                [RESOURCE_IN_NTFS_VOLUME]     = read_ntfs_file_prefix,
        #endif
        #ifdef WITH_NTFS_3G
                [RESOURCE_IN_NTFS_VOLUME]     = read_ntfs_file_prefix,
index 7916f40a77b8a0b333af90c163e4b6d2ca8458a5..f6d3253d790f3d7af898ee3e18d66d631882ec86 100644 (file)
@@ -352,6 +352,12 @@ static const tchar *error_strings[] = {
                = T("The user-provided progress function returned an unrecognized value"),
        [WIMLIB_ERR_MKNOD]
                = T("Unable to create a special file (e.g. device node or socket)"),
                = T("The user-provided progress function returned an unrecognized value"),
        [WIMLIB_ERR_MKNOD]
                = T("Unable to create a special file (e.g. device node or socket)"),
+       [WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY]
+               = T("There are still files open on the mounted WIM image"),
+       [WIMLIB_ERR_NOT_A_MOUNTPOINT]
+               = T("There is not a WIM image mounted on the directory"),
+       [WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT]
+               = T("The current user does not have permission to unmount the WIM image"),
 };
 
 /* API function documented in wimlib.h  */
 };
 
 /* API function documented in wimlib.h  */
index ff4ac29be3e53b7c3d981924ee8f452be0b1dcdc..2942863c938859e44d2ba62f2c4c2073492e395a 100644 (file)
--- a/src/wim.c
+++ b/src/wim.c
@@ -328,8 +328,22 @@ new_image_metadata_array(unsigned num_images)
 }
 
 
 }
 
 
-/* Load the metadata for the specified WIM image into memory and set it as the
- * WIMStruct's currently selected WIM image.  */
+/*
+ * Load the metadata for the specified WIM image into memory and set it
+ * as the WIMStruct's currently selected image.
+ *
+ * @wim
+ *     The WIMStruct for the WIM.
+ * @image
+ *     The 1-based index of the image in the WIM to select.
+ *
+ * On success, 0 will be returned, wim->current_image will be set to
+ * @image, and wim_get_current_image_metadata() can be used to retrieve
+ * metadata information for the image.
+ *
+ * On failure, WIMLIB_ERR_INVALID_IMAGE, WIMLIB_ERR_METADATA_NOT_FOUND,
+ * or another error code will be returned.
+ */
 int
 select_wim_image(WIMStruct *wim, int image)
 {
 int
 select_wim_image(WIMStruct *wim, int image)
 {
index 27463d90d059879426f9df0f17121df8e08f4fea..0114618f1658384dc1a99401bc7ba615f35413bd 100644 (file)
@@ -2401,27 +2401,25 @@ finish_write(WIMStruct *wim, int image, int write_flags,
 
 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
 int
 
 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
 int
-lock_wim(WIMStruct *wim, int fd)
+lock_wim_for_append(WIMStruct *wim, int fd)
 {
 {
-       int ret = 0;
-       if (fd != -1 && !wim->wim_locked) {
-               ret = flock(fd, LOCK_EX | LOCK_NB);
-               if (ret != 0) {
-                       if (errno == EWOULDBLOCK) {
-                               ERROR("`%"TS"' is already being modified or has been "
-                                     "mounted read-write\n"
-                                     "        by another process!", wim->filename);
-                               ret = WIMLIB_ERR_ALREADY_LOCKED;
-                       } else {
-                               WARNING_WITH_ERRNO("Failed to lock `%"TS"'",
-                                                  wim->filename);
-                               ret = 0;
-                       }
-               } else {
-                       wim->wim_locked = 1;
-               }
+       if (wim->locked_for_append)
+               return 0;
+       if (!flock(fd, LOCK_EX | LOCK_NB)) {
+               wim->locked_for_append = 1;
+               return 0;
+       }
+       if (errno != EWOULDBLOCK)
+               return 0;
+       return WIMLIB_ERR_ALREADY_LOCKED;
+}
+void
+unlock_wim_for_append(WIMStruct *wim, int fd)
+{
+       if (wim->locked_for_append) {
+               flock(fd, LOCK_UN);
+               wim->locked_for_append = 0;
        }
        }
-       return ret;
 }
 #endif
 
 }
 #endif
 
@@ -3062,7 +3060,7 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        if (ret)
                goto out_restore_memory_hdr;
 
        if (ret)
                goto out_restore_memory_hdr;
 
-       ret = lock_wim(wim, wim->out_fd.fd);
+       ret = lock_wim_for_append(wim, wim->out_fd.fd);
        if (ret)
                goto out_close_wim;
 
        if (ret)
                goto out_close_wim;
 
@@ -3098,7 +3096,8 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        if (ret)
                goto out_truncate;
 
        if (ret)
                goto out_truncate;
 
-       wim->wim_locked = 0;
+       /* lock was dropped when file descriptor was closed  */
+       wim->locked_for_append = 0;
        return 0;
 
 out_truncate:
        return 0;
 
 out_truncate:
@@ -3112,7 +3111,8 @@ out_truncate:
 out_restore_physical_hdr:
        (void)write_wim_header_flags(hdr_save.flags, &wim->out_fd);
 out_unlock_wim:
 out_restore_physical_hdr:
        (void)write_wim_header_flags(hdr_save.flags, &wim->out_fd);
 out_unlock_wim:
-       wim->wim_locked = 0;
+       /* lock is dropped when close_wim_writable() closes the file  */
+       wim->locked_for_append = 0;
 out_close_wim:
        (void)close_wim_writable(wim, write_flags);
 out_restore_memory_hdr:
 out_close_wim:
        (void)close_wim_writable(wim, write_flags);
 out_restore_memory_hdr: