]> wimlib.net Git - wimlib/blobdiff - src/mount_image.c
Improve random number generation
[wimlib] / src / mount_image.c
index 3aac49d11d6942797b51b5174ad09cc5042588a7..8ad4fdc8663a4cc50b76df2b37d908ac513df6aa 100644 (file)
@@ -1,29 +1,27 @@
 /*
  * mount_image.c
  *
- * This file implements mounting of WIM files using FUSE, which stands for
- * Filesystem in Userspace.  FUSE allows a filesystem to be implemented in a
- * userspace process by implementing the filesystem primitives--- read(),
- * write(), readdir(), etc.
+ * This file implements mounting of WIM images using FUSE
+ * (Filesystem in Userspace).  See http://fuse.sourceforge.net/.
+ *
+ * Currently it is only expected to work on Linux.
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014 Eric Biggers
- *
- * This file is part of wimlib, a library for working with WIM files.
+ * Copyright (C) 2012-2016 Eric Biggers
  *
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
  *
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  * details.
  *
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -31,6 +29,7 @@
 #endif
 
 #include "wimlib.h"
+#include "wimlib/error.h"
 
 #ifdef WITH_FUSE
 
 #  error "FUSE mount not supported on Windows!  Please configure --without-fuse"
 #endif
 
-#include "wimlib/dentry.h"
-#include "wimlib/encoding.h"
-#include "wimlib/metadata.h"
-#include "wimlib/paths.h"
-#include "wimlib/progress.h"
-#include "wimlib/reparse.h"
-#include "wimlib/timestamp.h"
-#include "wimlib/unix_data.h"
-#include "wimlib/write.h"
-#include "wimlib/xml.h"
+#define FUSE_USE_VERSION 26
 
+#include <sys/types.h> /* sometimes required before <sys/xattr.h> */
+#include <sys/xattr.h>
 #include <dirent.h>
 #include <errno.h>
+#include <fuse.h>
 #include <limits.h>
 #include <mqueue.h>
 #include <pthread.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <unistd.h>
 #include <utime.h>
 
-#define FUSE_USE_VERSION 26
-#include <fuse.h>
-#include <attr/xattr.h>
+#include "wimlib/blob_table.h"
+#include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
+#include "wimlib/metadata.h"
+#include "wimlib/paths.h"
+#include "wimlib/progress.h"
+#include "wimlib/reparse.h"
+#include "wimlib/timestamp.h"
+#include "wimlib/unix_data.h"
+#include "wimlib/write.h"
+#include "wimlib/xml.h"
 
 #ifndef O_NOFOLLOW
 #  define O_NOFOLLOW 0  /* Security only...  */
 #endif
 
+#ifndef ENOATTR
+#  define ENOATTR ENODATA
+#endif
+
 #define WIMFS_MQUEUE_NAME_LEN 32
 
 #define WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS 0x80000000
@@ -97,12 +101,12 @@ struct wimfs_fd {
         * that reference it.  */
        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,
+       /* Pointer to the blob descriptor for the data stream that has been
+        * opened.  'num_opened_fds' of the blob descriptor 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;
+        * not have a blob descriptor.  */
+       struct blob_descriptor *f_blob;
 
        /* If valid (filedes_valid(&f_staging_fd)), this contains the
         * corresponding native file descriptor for the staging file that has
@@ -121,8 +125,8 @@ struct wimfs_fd {
        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.  */
+        * even if the indices of the inode's streams are changed by a deletion.
+        */
        u32 f_stream_id;
 };
 
@@ -144,6 +148,11 @@ struct wimfs_context {
        uid_t owner_uid;
        gid_t owner_gid;
 
+       /* Absolute path to the mountpoint directory (may be needed for absolute
+        * symbolic link fixups)  */
+       char *mountpoint_abspath;
+       size_t mountpoint_abspath_nchars;
+
        /* Information about the staging directory for a read-write mount.  */
        int parent_dir_fd;
        int staging_dir_fd;
@@ -158,9 +167,9 @@ struct wimfs_context {
        /* Number of file descriptors open to the mounted WIM image.  */
        unsigned long num_open_fds;
 
-       /* 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;
+       /* For read-write mounts, the original metadata resource of the mounted
+        * image.  */
+       struct blob_descriptor *metadata_resource;
 
        /* Parameters for unmounting the image (can be set via extended
         * attribute "wimfs.unmount_info").  */
@@ -220,12 +229,9 @@ fuse_mask_mode(mode_t mode, const struct fuse_context *fuse_ctx)
  * Allocate a file descriptor to a data stream in the mounted WIM image.
  *
  * @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.
+ *     The inode containing the stream being opened
+ * @strm
+ *     The stream of the inode being opened
  * @fd_ret
  *     On success, a pointer to the new file descriptor will be stored here.
  *
@@ -233,8 +239,7 @@ fuse_mask_mode(mode_t mode, const struct fuse_context *fuse_ctx)
  */
 static int
 alloc_wimfs_fd(struct wim_inode *inode,
-              u32 stream_id,
-              struct wim_lookup_table_entry *lte,
+              struct wim_inode_stream *strm,
               struct wimfs_fd **fd_ret)
 {
        static const u16 min_fds_per_alloc = 8;
@@ -282,15 +287,15 @@ alloc_wimfs_fd(struct wim_inode *inode,
                return -ENOMEM;
 
        fd->f_inode     = inode;
-       fd->f_lte       = lte;
+       fd->f_blob      = stream_blob_resolved(strm);
        filedes_invalidate(&fd->f_staging_fd);
        fd->f_idx       = i;
-       fd->f_stream_id = stream_id;
+       fd->f_stream_id = strm->stream_id;
        *fd_ret         = fd;
        inode->i_fds[i] = fd;
        inode->i_num_opened_fds++;
-       if (lte)
-               lte->num_opened_fds++;
+       if (fd->f_blob)
+               fd->f_blob->num_opened_fds++;
        wimfs_inc_num_open_fds();
        inode->i_next_fd = i + 1;
        return 0;
@@ -312,9 +317,9 @@ close_wimfs_fd(struct wimfs_fd *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);
+       /* Release this file descriptor from its blob descriptor.  */
+       if (fd->f_blob)
+               blob_decrement_num_opened_fds(fd->f_blob);
 
        wimfs_dec_num_open_fds();
 
@@ -324,15 +329,7 @@ close_wimfs_fd(struct wimfs_fd *fd)
        if (fd->f_idx < inode->i_next_fd)
                inode->i_next_fd = fd->f_idx;
        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)
-                       /* No links to this inode remain.  Get rid of it.  */
-                       free_inode(inode);
-       }
+       inode_dec_num_opened_fds(inode);
        return ret;
 }
 
@@ -360,72 +357,88 @@ wim_pathname_to_inode(WIMStruct *wim, const char *path)
 /* Can look up directory (otherwise get -ENOTDIR)  */
 #define LOOKUP_FLAG_DIRECTORY_OK       0x02
 
+/* Get the data stream of the specified name from the specified inode.  Returns
+ * NULL with errno set if not found.  */
+static struct wim_inode_stream *
+inode_get_data_stream_tstr(const struct wim_inode *inode,
+                          const char *stream_name)
+{
+       struct wim_inode_stream *strm;
+
+       if (!stream_name || !*stream_name) {
+               strm = inode_get_unnamed_data_stream(inode);
+       } else {
+               const utf16lechar *uname;
+
+               if (tstr_get_utf16le(stream_name, &uname))
+                       return NULL;
+               strm = inode_get_stream(inode, STREAM_TYPE_DATA, uname);
+               tstr_put_utf16le(uname);
+       }
+       if (!strm)
+               errno = ENOENT;
+       return strm;
+}
+
 /*
- * Translate a path into the corresponding dentry, lookup table entry, and
- * stream index in the mounted WIM image.
+ * Translate a path into the corresponding dentry and stream in the mounted WIM
+ * image.
  *
- * Returns 0 or a -errno code.  All of @dentry_ret, @lte_ret, and
- * @stream_idx_ret are optional.
+ * Returns 0 or a -errno code.  @dentry_ret and @strm_ret are both optional.
  */
 static int
-wim_pathname_to_stream(const struct wimfs_context *ctx, const char *path,
+wim_pathname_to_stream(const struct wimfs_context *ctx,
+                      const char *path,
                       int lookup_flags,
                       struct wim_dentry **dentry_ret,
-                      struct wim_lookup_table_entry **lte_ret,
-                      u16 *stream_idx_ret)
+                      struct wim_inode_stream **strm_ret)
 {
        WIMStruct *wim = ctx->wim;
        struct wim_dentry *dentry;
-       struct wim_lookup_table_entry *lte;
-       u16 stream_idx;
-       const tchar *stream_name = NULL;
        struct wim_inode *inode;
-       tchar *p = NULL;
+       struct wim_inode_stream *strm;
+       const char *stream_name = NULL;
+       char *p = NULL;
 
        lookup_flags |= ctx->default_lookup_flags;
 
        if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
                stream_name = path_stream_name(path);
                if (stream_name) {
-                       p = (tchar*)stream_name - 1;
-                       *p = T('\0');
+                       p = (char *)stream_name - 1;
+                       *p = '\0';
                }
        }
 
        dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
        if (p)
-               *p = T(':');
+               *p = ':';
        if (!dentry)
                return -errno;
 
        inode = dentry->d_inode;
 
-       if (!inode->i_resolved)
-               if (inode_resolve_streams(inode, wim->lookup_table, false))
-                       return -EIO;
+       if (inode_resolve_streams(inode, wim->blob_table, false))
+               return -EIO;
 
        if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
              && inode_is_directory(inode))
                return -EISDIR;
 
-       if (stream_name) {
-               struct wim_ads_entry *ads_entry;
-
-               ads_entry = inode_get_ads_entry(inode, stream_name);
-               if (!ads_entry)
+       strm = inode_get_data_stream_tstr(inode, stream_name);
+       if (!strm) {
+               /* Force creation of an unnamed data stream  */
+               if (!stream_name)
+                       strm = inode_add_stream(inode, STREAM_TYPE_DATA,
+                                               NO_STREAM_NAME, NULL);
+               if (!strm)
                        return -errno;
-
-               stream_idx = ads_entry - inode->i_ads_entries + 1;
-               lte = ads_entry->lte;
-       } else {
-               lte = inode_unnamed_stream_resolved(inode, &stream_idx);
        }
+
        if (dentry_ret)
                *dentry_ret = dentry;
-       if (lte_ret)
-               *lte_ret = lte;
-       if (stream_idx_ret)
-               *stream_idx_ret = stream_idx;
+       if (strm_ret)
+               *strm_ret = strm;
        return 0;
 }
 
@@ -438,14 +451,12 @@ wim_pathname_to_stream(const struct wimfs_context *ctx, const char *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
+ *     The UNIX mode for the new file.  This is only fully 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
@@ -454,15 +465,14 @@ wim_pathname_to_stream(const struct wimfs_context *ctx, const char *path,
  * Returns 0 or a -errno code.
  */
 static int
-create_dentry(struct fuse_context *fuse_ctx, const char *path,
-             mode_t mode, dev_t rdev, u32 attributes,
-             struct wim_dentry **dentry_ret)
+create_file(struct fuse_context *fuse_ctx, const char *path,
+           mode_t mode, dev_t rdev, struct wim_dentry **dentry_ret)
 {
        struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
        struct wim_dentry *parent;
        const char *basename;
-       struct wim_dentry *new_dentry;
-       struct wim_inode *new_inode;
+       struct wim_dentry *dentry;
+       struct wim_inode *inode;
 
        parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE);
        if (!parent)
@@ -476,14 +486,19 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path,
        if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE))
                return -EEXIST;
 
-       if (new_dentry_with_inode(basename, &new_dentry))
+       if (new_dentry_with_new_inode(basename, true, &dentry))
                return -ENOMEM;
 
-       new_inode = new_dentry->d_inode;
+       inode = dentry->d_inode;
+
+       inode->i_ino = wimfs_ctx->next_ino++;
 
-       new_inode->i_resolved = 1;
-       new_inode->i_ino = wimfs_ctx->next_ino++;
-       new_inode->i_attributes = attributes;
+       /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes, named
+        * pipes, and sockets.  The real mode is in the UNIX metadata.  */
+       if (S_ISDIR(mode))
+               inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
+       else
+               inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
 
        if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
                struct wimlib_unix_data unix_data;
@@ -492,20 +507,19 @@ 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;
-               if (!inode_set_unix_data(new_inode, &unix_data, UNIX_DATA_ALL))
+               if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_ALL))
                {
-                       free_dentry(new_dentry);
+                       free_dentry(dentry);
                        return -ENOMEM;
                }
        }
 
-       list_add_tail(&new_inode->i_list,
-                     &wim_get_current_image_metadata(wimfs_ctx->wim)->inode_list);
+       hlist_add_head(&inode->i_hlist_node,
+                      &wim_get_current_image_metadata(wimfs_ctx->wim)->inode_list);
 
-       dentry_add_child(parent, new_dentry);
+       dentry_add_child(parent, dentry);
 
-       if (dentry_ret)
-               *dentry_ret = new_dentry;
+       *dentry_ret = dentry;
        return 0;
 }
 
@@ -514,11 +528,10 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path,
  * inode.
  */
 static void
-remove_dentry(struct wim_dentry *dentry,
-             struct wim_lookup_table *lookup_table)
+remove_dentry(struct wim_dentry *dentry, struct blob_table *blob_table)
 {
-       /* Drop the reference to each stream the inode contains.  */
-       inode_unref_streams(dentry->d_inode, lookup_table);
+       /* Drop blob references.  */
+       inode_unref_blobs(dentry->d_inode, blob_table);
 
        /* Unlink the dentry from the image's dentry tree.  */
        unlink_dentry(dentry);
@@ -548,19 +561,37 @@ inode_default_unix_mode(const struct wim_inode *inode)
        return inode_unix_file_type(inode) | 0777;
 }
 
+static u64
+blob_size(const struct blob_descriptor *blob)
+{
+       if (!blob)
+               return 0;
+       return blob->size;
+}
+
+static u64
+blob_stored_size(const struct blob_descriptor *blob)
+{
+       if (!blob)
+               return 0;
+       if (blob->blob_location == BLOB_IN_WIM &&
+           blob->size == blob->rdesc->uncompressed_size)
+               return blob->rdesc->size_in_wim;
+       return blob->size;
+}
+
 /*
  * Retrieve standard UNIX metadata ('struct stat') for a WIM inode.
  *
- * @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.
+ * @blob is the blob descriptor for the stream of the inode that is being
+ * queried, or NULL.  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,
-              struct stat *stbuf)
+              const struct blob_descriptor *blob, struct stat *stbuf)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wimlib_unix_data unix_data;
@@ -586,18 +617,17 @@ inode_to_stbuf(const struct wim_inode *inode,
        }
        stbuf->st_ino = inode->i_ino;
        stbuf->st_nlink = inode->i_nlink;
-       if (lte)
-               stbuf->st_size = lte->size;
+       stbuf->st_size = blob_size(blob);
 #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);
        stbuf->st_ctim = stbuf->st_mtim;
 #else
-       stbuf->st_atime = wim_timestamp_to_unix(inode->i_last_access_time);
-       stbuf->st_mtime = wim_timestamp_to_unix(inode->i_last_write_time);
+       stbuf->st_atime = wim_timestamp_to_time_t(inode->i_last_access_time);
+       stbuf->st_mtime = wim_timestamp_to_time_t(inode->i_last_write_time);
        stbuf->st_ctime = stbuf->st_mtime;
 #endif
-       stbuf->st_blocks = (stbuf->st_size + 511) / 512;
+       stbuf->st_blocks = DIV_ROUND_UP(blob_stored_size(blob), 512);
        return 0;
 }
 
@@ -605,7 +635,7 @@ inode_to_stbuf(const struct wim_inode *inode,
 static void
 touch_inode(struct wim_inode *inode)
 {
-       u64 now = get_wim_timestamp();
+       u64 now = now_as_wim_timestamp();
        inode->i_last_access_time = now;
        inode->i_last_write_time = now;
 }
@@ -639,7 +669,7 @@ create_staging_file(const struct wimfs_context *ctx, char **name_ret)
        name[STAGING_FILE_NAME_LEN] = '\0';
 
 retry:
-       randomize_char_array_with_alnum(name, STAGING_FILE_NAME_LEN);
+       get_random_alnum_chars(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)) {
@@ -654,49 +684,37 @@ retry:
 }
 
 /*
- * Extract a WIM resource to the staging directory.
- * This is necessary if a stream using the resource is being opened for writing.
+ * Extract a blob to the staging directory.  This is necessary when a stream
+ * using the blob is being opened for writing and the blob has not already been
+ * extracted to the staging directory.
  *
  * @inode
  *     The inode containing the stream being opened for writing.
- *
- * @stream_idx
- *     The index of the stream in @inode being opened for writing.
- *
- * @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.
- *
+ * @strm
+ *     The stream being opened for writing.  The blob descriptor to which the
+ *     stream refers will be changed by this function.
  * @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.
+ *     Number of bytes of the blob to extract and include in the staging file.
+ *     It may be less than the actual blob length, in which case only a prefix
+ *     of the blob will be extracted.  It may also be more than the actual blob
+ *     length, in which case the extra space will be zero-filled.
  *
  * Returns 0 or a -errno code.
  */
 static int
-extract_resource_to_staging_dir(struct wim_inode *inode,
-                               u16 stream_idx,
-                               struct wim_lookup_table_entry **lte_ptr,
-                               off_t size,
-                               const struct wimfs_context *ctx)
-{
-       struct wim_lookup_table_entry *old_lte;
-       struct wim_lookup_table_entry *new_lte;
+extract_blob_to_staging_dir(struct wim_inode *inode,
+                           struct wim_inode_stream *strm,
+                           off_t size, const struct wimfs_context *ctx)
+{
+       struct blob_descriptor *old_blob;
+       struct blob_descriptor *new_blob;
        char *staging_file_name;
        int staging_fd;
        off_t extract_size;
        int result;
-       u32 stream_id;
        int ret;
 
-       old_lte = *lte_ptr;
+       old_blob = stream_blob_resolved(strm);
 
        /* Create the staging file.  */
        staging_fd = create_staging_file(ctx, &staging_file_name);
@@ -704,13 +722,13 @@ extract_resource_to_staging_dir(struct wim_inode *inode,
                return -errno;
 
        /* Extract the stream to the staging file (possibly truncated).  */
-       if (old_lte) {
+       if (old_blob) {
                struct filedes fd;
 
                filedes_init(&fd, staging_fd);
                errno = 0;
-               extract_size = min(old_lte->size, size);
-               result = extract_stream_to_fd(old_lte, &fd, extract_size);
+               extract_size = min(old_blob->size, size);
+               result = extract_blob_prefix_to_fd(old_blob, extract_size, &fd);
        } else {
                extract_size = 0;
                result = 0;
@@ -727,106 +745,74 @@ extract_resource_to_staging_dir(struct wim_inode *inode,
 
        /* If an error occurred, unlink the staging file.  */
        if (unlikely(result)) {
-               /* extract_stream_to_fd() should set errno, but if it didn't,
+               /* extract_blob_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
-        * existing entry, but we may have to create a new one instead.  */
+       /* Create a blob descriptor for the staging file.  */
+       new_blob = new_blob_descriptor();
+       if (unlikely(!new_blob)) {
+               ret = -ENOMEM;
+               goto out_delete_staging_file;
+       }
 
-       stream_id = inode_stream_idx_to_id(inode, stream_idx);
+       /* There may already be open file descriptors to this stream if it's
+        * previously been opened read-only, but just now we're opening it
+        * read-write.  Identify those file descriptors, update them to use the
+        * new blob descriptor, and open staging file descriptors for them.  */
+       for (u16 i = 0, j = 0; j < inode->i_num_opened_fds; i++) {
+               struct wimfs_fd *fd;
+               int raw_fd;
 
-       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
-                * table entry correspond to the stream we're trying to extract,
-                * so the lookup table entry can be re-used.  */
-               lookup_table_unlink(ctx->wim->lookup_table, old_lte);
-               lte_put_resource(old_lte);
-               new_lte = old_lte;
-       } else {
-               /* 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.  */
+               fd = inode->i_fds[i];
+               if (!fd)
+                       continue;
 
-               new_lte = new_lookup_table_entry();
-               if (unlikely(!new_lte)) {
-                       ret = -ENOMEM;
-                       goto out_delete_staging_file;
-               }
+               j++;
 
-               /* There may already be open file descriptors to this stream if
-                * it's previously been opened read-only, but just now we're
-                * opening it read-write.  Identify those file descriptors and
-                * change their lookup table entry pointers to point to the new
-                * lookup table entry, and open staging file descriptors for
-                * them.
-                *
-                * 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.  */
-               for (u16 i = 0, j = 0; j < inode->i_num_opened_fds; i++) {
-                       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);
-               }
-               if (old_lte) {
-                       old_lte->num_opened_fds -= new_lte->num_opened_fds;
-                       old_lte->refcnt -= inode->i_nlink;
+               if (fd->f_stream_id != strm->stream_id)
+                       continue;
+
+               /* This is a readonly fd for the same stream.  */
+               fd->f_blob = new_blob;
+               new_blob->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);
        }
 
-       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;
+       if (old_blob)
+               old_blob->num_opened_fds -= new_blob->num_opened_fds;
 
-       add_unhashed_stream(new_lte, inode, stream_id,
-                           &wim_get_current_image_metadata(ctx->wim)->unhashed_streams);
-       if (stream_idx == 0)
-               inode->i_lte = new_lte;
-       else
-               inode->i_ads_entries[stream_idx - 1].lte = new_lte;
-       *lte_ptr = new_lte;
+       new_blob->blob_location     = BLOB_IN_STAGING_FILE;
+       new_blob->staging_file_name = staging_file_name;
+       new_blob->staging_dir_fd    = ctx->staging_dir_fd;
+       new_blob->size              = size;
+
+       prepare_unhashed_blob(new_blob, inode, strm->stream_id,
+                             &wim_get_current_image_metadata(ctx->wim)->unhashed_blobs);
+       inode_replace_stream_blob(inode, strm, new_blob, ctx->wim->blob_table);
        return 0;
 
 out_revert_fd_changes:
-       for (u16 i = 0; new_lte->num_opened_fds; i++) {
+       for (u16 i = 0; new_blob->num_opened_fds; i++) {
                struct wimfs_fd *fd = inode->i_fds[i];
-               if (fd && fd->f_stream_id == stream_id) {
-                       fd->f_lte = old_lte;
+               if (fd && fd->f_stream_id == strm->stream_id) {
+                       fd->f_blob = old_blob;
                        if (filedes_valid(&fd->f_staging_fd)) {
                                filedes_close(&fd->f_staging_fd);
                                filedes_invalidate(&fd->f_staging_fd);
                        }
-                       new_lte->num_opened_fds--;
+                       new_blob->num_opened_fds--;
                }
        }
-       free_lookup_table_entry(new_lte);
+       free_blob_descriptor(new_blob);
 out_delete_staging_file:
        unlinkat(ctx->staging_dir_fd, staging_file_name, 0);
        FREE(staging_file_name);
@@ -867,7 +853,7 @@ make_staging_dir_at(int parent_dir_fd, const char *wim_basename,
        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);
+       get_random_alnum_chars(p, random_suffix_len);
        p += random_suffix_len;
        *p = '\0';
 
@@ -943,7 +929,7 @@ out_restore_wim_filename:
        return ret;
 }
 
-/* Deletes the staging directory, undoing the effects of a succesful call to
+/* Deletes the staging directory, undoing the effects of a successful call to
  * make_staging_dir().  */
 static void
 delete_staging_dir(struct wimfs_context *ctx)
@@ -966,48 +952,43 @@ delete_staging_dir(struct wimfs_context *ctx)
 }
 
 static void
-reassign_inode_numbers(struct wimfs_context *ctx)
+prepare_inodes(struct wimfs_context *ctx)
 {
        struct wim_image_metadata *imd;
        struct wim_inode *inode;
 
        ctx->next_ino = 1;
        imd = wim_get_current_image_metadata(ctx->wim);
-       image_for_each_inode(inode, imd)
+       image_for_each_inode(inode, imd) {
                inode->i_ino = ctx->next_ino++;
-}
-
-static void
-release_extra_refcnts(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;
-
-       list_for_each_entry_safe(lte, tmp, list, orig_stream_list) {
-               u32 n = lte->out_refcnt;
-               while (n--)
-                       lte_decrement_refcnt(lte, lookup_table);
+               inode->i_num_opened_fds = 0;
+               inode->i_num_allocated_fds = 0;
+               inode->i_fds = NULL;
        }
 }
 
+/* Delete the 'struct blob_descriptor' for any stream that was modified
+ * or created in the read-write mounted image and had a final size of 0.  */
 static void
-delete_empty_streams(struct wimfs_context *ctx)
+delete_empty_blobs(struct wimfs_context *ctx)
 {
-       struct wim_lookup_table_entry *lte, *tmp;
+       struct blob_descriptor *blob, *tmp;
        struct wim_image_metadata *imd;
 
        imd = wim_get_current_image_metadata(ctx->wim);
 
-        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);
-                }
-        }
+       image_for_each_unhashed_blob_safe(blob, tmp, imd) {
+               if (!blob->size) {
+                       *retrieve_pointer_to_unhashed_blob(blob) = NULL;
+                       list_del(&blob->unhashed_list);
+                       free_blob_descriptor(blob);
+               }
+       }
 }
 
+/* Close all file descriptors open to the specified inode.
+ *
+ * Note: closing the last file descriptor might free the inode.  */
 static void
 inode_close_fds(struct wim_inode *inode)
 {
@@ -1020,15 +1001,17 @@ inode_close_fds(struct wim_inode *inode)
        }
 }
 
+/* Close all file descriptors open to the mounted image.  */
 static void
 close_all_fds(struct wimfs_context *ctx)
 {
-       struct wim_inode *inode, *tmp;
+       struct wim_inode *inode;
+       struct hlist_node *tmp;
        struct wim_image_metadata *imd;
 
        imd = wim_get_current_image_metadata(ctx->wim);
 
-       list_for_each_entry_safe(inode, tmp, &imd->inode_list, i_list)
+       image_for_each_inode_safe(inode, tmp, imd)
                inode_close_fds(inode);
 }
 
@@ -1039,51 +1022,56 @@ static int
 renew_current_image(struct wimfs_context *ctx)
 {
        WIMStruct *wim = ctx->wim;
-       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 image = wim->current_image;
+       struct wim_image_metadata *imd;
+       struct wim_inode *inode;
        int ret;
 
-       /* Create 'replace_imd' structure to use for the reset original,
-        * unmodified image.  */
        ret = WIMLIB_ERR_NOMEM;
-       replace_imd = new_image_metadata();
-       if (!replace_imd)
+       imd = new_unloaded_image_metadata(ctx->metadata_resource);
+       if (!imd)
                goto err;
 
-       /* Create new stream reference for the modified image's metadata
-        * resource, which doesn't exist yet.  */
-       ret = WIMLIB_ERR_NOMEM;
-       new_lte = new_lookup_table_entry();
-       if (!new_lte)
-               goto err_put_replace_imd;
-       new_lte->flags = WIM_RESHDR_FLAG_METADATA;
-       new_lte->unhashed = 1;
-
-       /* Make the image being moved available at a new index.  Increments the
-        * WIM's image count, but does not increment the reference count of the
-        * 'struct image_metadata'.  */
-       ret = append_image_metadata(wim, imd);
+       ret = append_image_metadata(wim, wim->image_metadata[image - 1]);
        if (ret)
-               goto err_free_new_lte;
+               goto err_put_imd;
 
-       ret = xml_add_image(wim, T(""));
+       ret = xml_export_image(wim->xml_info, image,
+                              wim->xml_info, NULL, NULL, false);
        if (ret)
                goto err_undo_append;
 
-       replace_imd->metadata_lte = imd->metadata_lte;
-       imd->metadata_lte = new_lte;
-       wim->image_metadata[idx] = replace_imd;
+       wim->image_metadata[image - 1] = imd;
        wim->current_image = wim->hdr.image_count;
+
+       ret = select_wim_image(wim, image);
+       if (ret)
+               goto err_undo_export;
+
+       image_for_each_inode(inode, imd) {
+               for (unsigned i = 0; i < inode->i_num_streams; i++) {
+                       struct blob_descriptor *blob;
+
+                       blob = stream_blob(&inode->i_streams[i],
+                                          wim->blob_table);
+                       if (blob)
+                               blob->refcnt += inode->i_nlink;
+               }
+       }
+
+       select_wim_image(wim, wim->hdr.image_count);
+       ctx->metadata_resource = NULL;
        return 0;
 
+err_undo_export:
+       xml_delete_image(wim->xml_info, wim->hdr.image_count);
+       wim->image_metadata[image - 1] = wim->image_metadata[wim->hdr.image_count - 1];
+       wim->current_image = image;
 err_undo_append:
        wim->hdr.image_count--;
-err_free_new_lte:
-       free_lookup_table_entry(new_lte);
-err_put_replace_imd:
-       put_image_metadata(replace_imd, NULL);
+err_put_imd:
+       imd->metadata_blob = NULL;
+       put_image_metadata(imd);
 err:
        return ret;
 }
@@ -1119,24 +1107,24 @@ commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq)
                int ret = renew_current_image(ctx);
                if (ret)
                        return ret;
-       } else {
-               release_extra_refcnts(ctx);
        }
-       INIT_LIST_HEAD(&ctx->orig_stream_list);
-       delete_empty_streams(ctx);
-       xml_update_image_info(ctx->wim, ctx->wim->current_image);
+       delete_empty_blobs(ctx);
 
        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);
 }
 
-/* In the case of an allow_other mount, only the owner and root should be
+/* In the case of an allow_other mount, only the mount owner and root are
  * allowed to unmount the filesystem.  */
 static bool
 may_unmount_wimfs(void)
@@ -1148,6 +1136,7 @@ may_unmount_wimfs(void)
                fuse_ctx->uid == 0);
 }
 
+/* Unmount the mounted image, called from the daemon process.  */
 static int
 unmount_wimfs(void)
 {
@@ -1171,6 +1160,11 @@ unmount_wimfs(void)
        }
 
        if (wimfs_ctx->num_open_fds) {
+
+               /* There are still open file descriptors to the image.  */
+
+               /* With COMMIT, refuse to unmount unless FORCE is also
+                * specified.  */
                if ((unmount_flags & (WIMLIB_UNMOUNT_FLAG_COMMIT |
                                      WIMLIB_UNMOUNT_FLAG_FORCE))
                                 == WIMLIB_UNMOUNT_FLAG_COMMIT)
@@ -1178,16 +1172,20 @@ unmount_wimfs(void)
                        ret = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY;
                        goto out;
                }
+
+               /* Force-close all file descriptors.  */
                close_all_fds(wimfs_ctx);
        }
 
        if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)
                ret = commit_image(wimfs_ctx, unmount_flags, mq);
        else
-               ret = 0;
+               ret = 0;  /* Read-only mount, or discarding changes to
+                            a read-write mount  */
+
 out:
        /* Leave the image mounted if commit failed, unless this is a
-        * forced unmount.  The user can retry without commit if they
+        * forced unmount.  The user can retry without COMMIT if they
         * want.  */
        if (!ret || (unmount_flags & WIMLIB_UNMOUNT_FLAG_FORCE)) {
                unlock_wim_for_append(wimfs_ctx->wim);
@@ -1265,7 +1263,7 @@ static int
 wimfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
 {
        struct wimfs_fd *fd = WIMFS_FD(fi);
-       return inode_to_stbuf(fd->f_inode, fd->f_lte, stbuf);
+       return inode_to_stbuf(fd->f_inode, fd->f_blob, stbuf);
 }
 
 static int
@@ -1275,7 +1273,7 @@ wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
        if (ftruncate(fd->f_staging_fd.fd, size))
                return -errno;
        touch_inode(fd->f_inode);
-       fd->f_lte->size = size;
+       fd->f_blob->size = size;
        return 0;
 }
 
@@ -1284,24 +1282,26 @@ 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;
+       struct wim_inode_stream *strm;
        int ret;
 
        ret = wim_pathname_to_stream(ctx, path, LOOKUP_FLAG_DIRECTORY_OK,
-                                    &dentry, &lte, NULL);
+                                    &dentry, &strm);
        if (ret)
                return ret;
-       return inode_to_stbuf(dentry->d_inode, lte, stbuf);
+
+       return inode_to_stbuf(dentry->d_inode,
+                             stream_blob_resolved(strm), stbuf);
 }
 
 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);
+       if (destsize) {
+               if (destsize < srcsize)
+                       return -ERANGE;
+               memcpy(dest, src, srcsize);
+       }
        return srcsize;
 }
 
@@ -1310,9 +1310,9 @@ wimfs_getxattr(const char *path, const char *name, char *value,
               size_t size)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
-       struct wim_inode *inode;
-       struct wim_ads_entry *ads_entry;
-       struct wim_lookup_table_entry *lte;
+       const struct wim_inode *inode;
+       const struct wim_inode_stream *strm;
+       const struct blob_descriptor *blob;
 
        if (!strncmp(name, "wimfs.", 6)) {
                /* Handle some magical extended attributes.  These really should
@@ -1361,31 +1361,34 @@ wimfs_getxattr(const char *path, const char *name, char *value,
                return -ENOATTR;
        name += 5;
 
+       if (!*name)
+               return -ENOATTR;
+
        /* Querying a named data stream  */
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       ads_entry = inode_get_ads_entry(inode, name);
-       if (!ads_entry)
+       strm = inode_get_data_stream_tstr(inode, name);
+       if (!strm)
                return (errno == ENOENT) ? -ENOATTR : -errno;
 
-       lte = ads_entry->lte;
-       if (!lte)
+       blob = stream_blob_resolved(strm);
+       if (!blob)
                return 0;
 
-       if (unlikely(lte->size > INT_MAX))
+       if (unlikely(blob->size > INT_MAX))
                return -EFBIG;
 
        if (size) {
-               if (lte->size > size)
+               if (size < blob->size)
                        return -ERANGE;
 
-               if (read_full_stream_into_buf(lte, value))
-                       return -errno;
+               if (read_blob_into_buf(blob, value))
+                       return errno ? -errno : -EIO;
        }
-       return lte->size;
+       return blob->size;
 }
 
 static int
@@ -1417,15 +1420,11 @@ wimfs_link(const char *existing_path, const char *new_path)
        if (get_dentry_child_with_name(dir, new_name, WIMLIB_CASE_SENSITIVE))
                return -EEXIST;
 
-       if (new_dentry(new_name, &new_alias))
+       if (new_dentry_with_existing_inode(new_name, inode, &new_alias))
                return -ENOMEM;
 
-       new_alias->d_inode = inode;
-       inode_add_dentry(new_alias, inode);
        dentry_add_child(dir, new_alias);
        touch_inode(dir->d_inode);
-       inode->i_nlink++;
-       inode_ref_streams(inode);
        return 0;
 }
 
@@ -1448,18 +1447,18 @@ wimfs_listxattr(const char *path, char *list, size_t size)
        if (!inode)
                return -errno;
 
-       for (u16 i = 0; i < inode->i_num_ads; i++) {
-               const struct wim_ads_entry *entry;
+       for (unsigned i = 0; i < inode->i_num_streams; i++) {
+               const struct wim_inode_stream *strm;
                char *stream_name_mbs;
                size_t stream_name_mbs_nbytes;
 
-               entry = &inode->i_ads_entries[i];
+               strm = &inode->i_streams[i];
 
-               if (!entry->stream_name_nbytes)
+               if (!stream_is_named_data_stream(strm))
                        continue;
 
-               if (utf16le_to_tstr(entry->stream_name,
-                                   entry->stream_name_nbytes,
+               if (utf16le_to_tstr(strm->stream_name,
+                                   utf16le_len_bytes(strm->stream_name),
                                    &stream_name_mbs,
                                    &stream_name_mbs_nbytes))
                        return -errno;
@@ -1491,8 +1490,7 @@ wimfs_mkdir(const char *path, mode_t mode)
        int ret;
 
        /* Note: according to fuse.h, mode may not include S_IFDIR  */
-       ret = create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
-                           FILE_ATTRIBUTE_DIRECTORY, &dentry);
+       ret = create_file(fuse_get_context(), path, mode | S_IFDIR, 0, &dentry);
        if (ret)
                return ret;
        touch_parent(dentry);
@@ -1509,10 +1507,11 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
        if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
             && (stream_name = path_stream_name(path)))
        {
-               struct wim_ads_entry *old_entry;
-               struct wim_ads_entry *new_entry;
                struct wim_inode *inode;
+               struct wim_inode_stream *existing_strm;
+               struct wim_inode_stream *new_strm;
                char *p;
+               const utf16lechar *uname;
 
                /* Create a named data stream.  */
 
@@ -1527,14 +1526,20 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                if (!inode)
                        return -errno;
 
-               old_entry = inode_get_ads_entry(inode, stream_name);
-               if (old_entry)
-                       return -EEXIST;
-               if (errno != ENOENT)
+               if (tstr_get_utf16le(stream_name, &uname))
                        return -errno;
 
-               new_entry = inode_add_ads(inode, stream_name);
-               if (!new_entry)
+               existing_strm = inode_get_stream(inode, STREAM_TYPE_DATA, uname);
+               if (existing_strm) {
+                       tstr_put_utf16le(uname);
+                       return -EEXIST;
+               }
+
+               new_strm = inode_add_stream(inode, STREAM_TYPE_DATA, uname, NULL);
+
+               tstr_put_utf16le(uname);
+
+               if (!new_strm)
                        return -errno;
                return 0;
        } else {
@@ -1547,11 +1552,7 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                    !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
                        return -EPERM;
 
-               /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes,
-                * named pipes, and sockets.  The real mode is in the UNIX
-                * metadata.  */
-               ret = create_dentry(fuse_ctx, path, mode, rdev,
-                                   FILE_ATTRIBUTE_NORMAL, &dentry);
+               ret = create_file(fuse_ctx, path, mode, rdev, &dentry);
                if (ret)
                        return ret;
                touch_parent(dentry);
@@ -1565,44 +1566,45 @@ wimfs_open(const char *path, struct fuse_file_info *fi)
        struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_inode *inode;
-       struct wim_lookup_table_entry *lte;
-       u16 stream_idx;
+       struct wim_inode_stream *strm;
+       struct blob_descriptor *blob;
        struct wimfs_fd *fd;
        int ret;
 
-       ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &lte, &stream_idx);
+       ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm);
        if (ret)
                return ret;
 
        inode = dentry->d_inode;
+       blob = stream_blob_resolved(strm);
 
-       /* 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.  */
+       /* The data of the file being opened 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 data directly
+        * from the WIM file if we are opening it read-only, but we need to
+        * extract the data to the staging directory if we are opening it
+        * writable.  */
 
        if (flags_writable(fi->flags) &&
-            (!lte || lte->resource_location != RESOURCE_IN_STAGING_FILE)) {
-               ret = extract_resource_to_staging_dir(inode,
-                                                     stream_idx,
-                                                     &lte,
-                                                     lte ? lte->size : 0,
-                                                     ctx);
+            (!blob || blob->blob_location != BLOB_IN_STAGING_FILE)) {
+               ret = extract_blob_to_staging_dir(inode,
+                                                 strm,
+                                                 blob_size(blob),
+                                                 ctx);
                if (ret)
                        return ret;
+               blob = stream_blob_resolved(strm);
        }
 
-       ret = alloc_wimfs_fd(inode, inode_stream_idx_to_id(inode, stream_idx),
-                            lte, &fd);
+       ret = alloc_wimfs_fd(inode, strm, &fd);
        if (ret)
                return ret;
 
-       if (lte && lte->resource_location == RESOURCE_IN_STAGING_FILE) {
+       if (blob && blob->blob_location == BLOB_IN_STAGING_FILE) {
                int raw_fd;
 
-               raw_fd = openat(lte->staging_dir_fd, lte->staging_file_name,
+               raw_fd = openat(blob->staging_dir_fd, blob->staging_file_name,
                                (fi->flags & O_ACCMODE) | O_NOFOLLOW);
                if (raw_fd < 0) {
                        close_wimfs_fd(fd);
@@ -1619,6 +1621,7 @@ wimfs_opendir(const char *path, struct fuse_file_info *fi)
 {
        WIMStruct *wim = wimfs_get_WIMStruct();
        struct wim_inode *inode;
+       struct wim_inode_stream *strm;
        struct wimfs_fd *fd;
        int ret;
 
@@ -1627,7 +1630,10 @@ wimfs_opendir(const char *path, struct fuse_file_info *fi)
                return -errno;
        if (!inode_is_directory(inode))
                return -ENOTDIR;
-       ret = alloc_wimfs_fd(inode, 0, NULL, &fd);
+       strm = inode_get_unnamed_data_stream(inode);
+       if (!strm)
+               return -ENOTDIR;
+       ret = alloc_wimfs_fd(inode, strm, &fd);
        if (ret)
                return ret;
        fi->fh = (uintptr_t)fd;
@@ -1639,36 +1645,36 @@ wimfs_read(const char *path, char *buf, size_t size,
           off_t offset, struct fuse_file_info *fi)
 {
        struct wimfs_fd *fd = WIMFS_FD(fi);
-       const struct wim_lookup_table_entry *lte;
+       const struct blob_descriptor *blob;
        ssize_t ret;
 
-       lte = fd->f_lte;
-       if (!lte)
+       blob = fd->f_blob;
+       if (!blob)
                return 0;
 
-       if (offset >= lte->size)
+       if (offset >= blob->size)
                return 0;
 
-       if (size > lte->size - offset)
-               size = lte->size - offset;
+       if (size > blob->size - offset)
+               size = blob->size - offset;
 
        if (!size)
                return 0;
 
-       switch (lte->resource_location) {
-       case RESOURCE_IN_WIM:
-               if (read_partial_wim_stream_into_buf(lte, size, offset, buf))
-                       ret = -errno;
+       switch (blob->blob_location) {
+       case BLOB_IN_WIM:
+               if (read_partial_wim_blob_into_buf(blob, offset, size, buf))
+                       ret = errno ? -errno : -EIO;
                else
                        ret = size;
                break;
-       case RESOURCE_IN_STAGING_FILE:
-               ret = raw_pread(&fd->f_staging_fd, buf, size, offset);
+       case BLOB_IN_STAGING_FILE:
+               ret = pread(fd->f_staging_fd.fd, buf, size, offset);
                if (ret < 0)
                        ret = -errno;
                break;
-       case RESOURCE_IN_ATTACHED_BUFFER:
-               memcpy(buf, lte->attached_buffer + offset, size);
+       case BLOB_IN_ATTACHED_BUFFER:
+               memcpy(buf, blob->attached_buffer + offset, size);
                ret = size;
                break;
        default:
@@ -1697,18 +1703,15 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                return ret;
 
        for_inode_child(child, inode) {
-               char *file_name_mbs;
-               size_t file_name_mbs_nbytes;
+               char *name;
+               size_t name_nbytes;
 
-               ret = utf16le_to_tstr(child->file_name,
-                                     child->file_name_nbytes,
-                                     &file_name_mbs,
-                                     &file_name_mbs_nbytes);
-               if (ret)
+               if (utf16le_to_tstr(child->d_name, child->d_name_nbytes,
+                                   &name, &name_nbytes))
                        return -errno;
 
-               ret = filler(buf, file_name_mbs, NULL, 0);
-               FREE(file_name_mbs);
+               ret = filler(buf, name, NULL, 0);
+               FREE(name);
                if (ret)
                        return ret;
        }
@@ -1716,27 +1719,24 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 }
 
 static int
-wimfs_readlink(const char *path, char *buf, size_t buf_len)
+wimfs_readlink(const char *path, char *buf, size_t bufsize)
 {
-       WIMStruct *wim = wimfs_get_WIMStruct();
+       struct wimfs_context *ctx = wimfs_get_context();
        const struct wim_inode *inode;
-       ssize_t ret;
+       int ret;
 
-       inode = wim_pathname_to_inode(wim, path);
+       inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
-       if (!inode_is_symlink(inode))
-               return -EINVAL;
-       if (buf_len == 0)
+       if (bufsize <= 0)
                return -EINVAL;
-       ret = wim_inode_readlink(inode, buf, buf_len - 1, NULL);
-       if (ret >= 0) {
-               buf[ret] = '\0';
-               ret = 0;
-       } else if (ret == -ENAMETOOLONG) {
-               buf[buf_len - 1] = '\0';
-       }
-       return ret;
+       ret = wim_inode_readlink(inode, buf, bufsize - 1, NULL,
+                                ctx->mountpoint_abspath,
+                                ctx->mountpoint_abspath_nchars);
+       if (ret < 0)
+               return ret;
+       buf[ret] = '\0';
+       return 0;
 }
 
 /* We use this for both release() and releasedir(), since in both cases we
@@ -1752,7 +1752,7 @@ 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_stream *strm;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
@@ -1761,17 +1761,20 @@ wimfs_removexattr(const char *path, const char *name)
                return -ENOATTR;
        name += 5;
 
+       if (!*name)
+               return -ENOATTR;
+
        /* Removing a named data stream.  */
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       ads_entry = inode_get_ads_entry(inode, name);
-       if (!ads_entry)
+       strm = inode_get_data_stream_tstr(inode, name);
+       if (!strm)
                return (errno == ENOENT) ? -ENOATTR : -errno;
 
-       inode_remove_ads(inode, ads_entry, ctx->wim->lookup_table);
+       inode_remove_stream(inode, strm, ctx->wim->blob_table);
        return 0;
 }
 
@@ -1799,7 +1802,7 @@ wimfs_rmdir(const char *path)
                return -ENOTEMPTY;
 
        touch_parent(dentry);
-       remove_dentry(dentry, wim->lookup_table);
+       remove_dentry(dentry, wim->blob_table);
        return 0;
 }
 
@@ -1809,12 +1812,15 @@ wimfs_setxattr(const char *path, const char *name,
 {
        struct wimfs_context *ctx = wimfs_get_context();
        struct wim_inode *inode;
-       struct wim_ads_entry *existing_entry;
+       struct wim_inode_stream *strm;
+       const utf16lechar *uname;
+       int ret;
 
        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.  */
+                * libfuse 2.9, and even then they are broken.  [Fixed by
+                * libfuse commit e3b7d4c278a26520be63d99d6ea84b26906fe73d]  */
                name += 6;
                if (!strcmp(name, "unmount_info")) {
                        if (!may_unmount_wimfs())
@@ -1835,29 +1841,50 @@ wimfs_setxattr(const char *path, const char *name,
                return -ENOATTR;
        name += 5;
 
+       if (!*name)
+               return -ENOATTR;
+
        /* Setting the contents of a named data stream.  */
 
        inode = wim_pathname_to_inode(ctx->wim, path);
        if (!inode)
                return -errno;
 
-       existing_entry = inode_get_ads_entry(inode, name);
-       if (existing_entry) {
+       ret = tstr_get_utf16le(name, &uname);
+       if (ret)
+               return -errno;
+
+       strm = inode_get_stream(inode, STREAM_TYPE_DATA, uname);
+       if (strm) {
+               ret = -EEXIST;
                if (flags & XATTR_CREATE)
-                       return -EEXIST;
+                       goto out_put_uname;
        } else {
-               if (errno != ENOENT)
-                       return -errno;
+               ret = -ENOATTR;
                if (flags & XATTR_REPLACE)
-                       return -ENOATTR;
+                       goto out_put_uname;
        }
 
-       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;
+       if (strm) {
+               if (!inode_replace_stream_data(inode, strm, value, size,
+                                              ctx->wim->blob_table))
+               {
+                       ret = -errno;
+                       goto out_put_uname;
+               }
+       } else {
+               if (!inode_add_stream_with_data(inode, STREAM_TYPE_DATA, uname,
+                                               value, size, ctx->wim->blob_table))
+               {
+                       ret = -errno;
+                       goto out_put_uname;
+               }
+       }
+
+       ret = 0;
+out_put_uname:
+       tstr_put_utf16le(uname);
+       return ret;
 }
 
 static int
@@ -1868,15 +1895,13 @@ wimfs_symlink(const char *to, const char *from)
        struct wim_dentry *dentry;
        int ret;
 
-       ret = create_dentry(fuse_ctx, from, S_IFLNK | 0777, 0,
-                           FILE_ATTRIBUTE_REPARSE_POINT, &dentry);
+       ret = create_file(fuse_ctx, from, S_IFLNK | 0777, 0, &dentry);
        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);
+                                   wimfs_ctx->wim->blob_table);
        if (ret) {
-               remove_dentry(dentry, wimfs_ctx->wim->lookup_table);
+               remove_dentry(dentry, wimfs_ctx->wim->blob_table);
                if (ret == WIMLIB_ERR_NOMEM)
                        ret = -ENOMEM;
                else
@@ -1892,33 +1917,35 @@ 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;
-       u16 stream_idx;
+       struct wim_inode_stream *strm;
+       struct blob_descriptor *blob;
        int ret;
        int fd;
 
-       ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &lte, &stream_idx);
+       ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm);
        if (ret)
                return ret;
 
-       if (!lte && !size)
+       blob = stream_blob_resolved(strm);
+
+       if (!blob && !size)
                return 0;
 
-       if (!lte || lte->resource_location != RESOURCE_IN_STAGING_FILE) {
-               return extract_resource_to_staging_dir(dentry->d_inode,
-                                                      stream_idx, &lte,
-                                                      size, ctx);
+       if (!blob || blob->blob_location != BLOB_IN_STAGING_FILE) {
+               return extract_blob_to_staging_dir(dentry->d_inode,
+                                                  strm, size, ctx);
        }
 
        /* Truncate the staging file.  */
-       fd = openat(lte->staging_dir_fd, lte->staging_file_name,
+       fd = openat(blob->staging_dir_fd, blob->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;
+       blob->size = size;
+       touch_inode(dentry->d_inode);
        return 0;
 }
 
@@ -1927,20 +1954,19 @@ wimfs_unlink(const char *path)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
-       u16 stream_idx;
+       struct wim_inode_stream *strm;
        int ret;
 
-       ret = wim_pathname_to_stream(ctx, path, 0, &dentry, NULL, &stream_idx);
+       ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm);
        if (ret)
                return ret;
 
-       if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0) {
-               touch_parent(dentry);
-               remove_dentry(dentry, ctx->wim->lookup_table);
+       if (stream_is_named(strm)) {
+               inode_remove_stream(dentry->d_inode, strm,
+                                   ctx->wim->blob_table);
        } else {
-               inode_remove_ads(dentry->d_inode,
-                                &dentry->d_inode->i_ads_entries[stream_idx - 1],
-                                ctx->wim->lookup_table);
+               touch_parent(dentry);
+               remove_dentry(dentry, ctx->wim->blob_table);
        }
        return 0;
 }
@@ -1963,15 +1989,15 @@ wimfs_utimens(const char *path, const struct timespec tv[2])
 
        if (tv[0].tv_nsec != UTIME_OMIT) {
                if (tv[0].tv_nsec == UTIME_NOW)
-                       inode->i_last_access_time = get_wim_timestamp();
+                       inode->i_last_access_time = now_as_wim_timestamp();
                else
-                       inode->i_last_access_time = timespec_to_wim_timestamp(tv[0]);
+                       inode->i_last_access_time = timespec_to_wim_timestamp(&tv[0]);
        }
        if (tv[1].tv_nsec != UTIME_OMIT) {
                if (tv[1].tv_nsec == UTIME_NOW)
-                       inode->i_last_write_time = get_wim_timestamp();
+                       inode->i_last_write_time = now_as_wim_timestamp();
                else
-                       inode->i_last_write_time = timespec_to_wim_timestamp(tv[1]);
+                       inode->i_last_write_time = timespec_to_wim_timestamp(&tv[1]);
        }
        return 0;
 }
@@ -1986,8 +2012,8 @@ wimfs_utime(const char *path, struct utimbuf *times)
        if (!inode)
                return -errno;
 
-       inode->i_last_access_time = unix_timestamp_to_wim(times->actime);
-       inode->i_last_write_time = unix_timestamp_to_wim(times->modtime);
+       inode->i_last_access_time = time_t_to_wim_timestamp(times->actime);
+       inode->i_last_write_time = time_t_to_wim_timestamp(times->modtime);
        return 0;
 }
 #endif /* !HAVE_UTIMENSAT */
@@ -1999,18 +2025,18 @@ wimfs_write(const char *path, const char *buf, size_t size,
        struct wimfs_fd *fd = WIMFS_FD(fi);
        ssize_t ret;
 
-       ret = raw_pwrite(&fd->f_staging_fd, buf, size, offset);
+       ret = pwrite(fd->f_staging_fd.fd, buf, size, offset);
        if (ret < 0)
                return -errno;
 
-       if (offset + size > fd->f_lte->size)
-               fd->f_lte->size = offset + size;
+       if (offset + size > fd->f_blob->size)
+               fd->f_blob->size = offset + size;
 
        touch_inode(fd->f_inode);
        return ret;
 }
 
-static struct fuse_operations wimfs_operations = {
+static const struct fuse_operations wimfs_operations = {
        .chmod       = wimfs_chmod,
        .chown       = wimfs_chown,
        .fgetattr    = wimfs_fgetattr,
@@ -2079,7 +2105,9 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
 
        /* 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 (!wim->filename)
+                       return WIMLIB_ERR_NO_FILENAME;
+               ret = can_modify_wim(wim);
                if (ret)
                        return ret;
        }
@@ -2092,20 +2120,26 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        /* Get the metadata for the image to mount.  */
        imd = wim_get_current_image_metadata(wim);
 
-       if (imd->modified) {
-               /* To avoid complicating things, we don't support mounting
-                * images to which in-memory modifications have already been
-                * made.  */
+       /* To avoid complicating things, we don't support mounting images to
+        * which in-memory modifications have already been made.  */
+       if (is_image_dirty(imd)) {
                ERROR("Cannot mount a modified WIM image!");
                return WIMLIB_ERR_INVALID_PARAM;
        }
 
        if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+               if (imd->refcnt > 1)
+                       return WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES;
                ret = lock_wim_for_append(wim);
                if (ret)
                        return ret;
        }
 
+       if (wim_has_solid_resources(wim)) {
+               WARNING("Mounting a WIM file containing solid-compressed data; "
+                       "file access may be slow.");
+       }
+
        /* 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 |
@@ -2119,55 +2153,31 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        ctx.mount_flags = mount_flags;
        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.  */
+
+       /* For read-write mounts, create the staging directory, save a reference
+        * to the image's metadata resource, and mark the image dirty.  */
        if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
                ret = make_staging_dir(&ctx, staging_dir);
                if (ret)
-                       goto out_unlock;
+                       goto out;
+               ret = WIMLIB_ERR_NOMEM;
+               ctx.metadata_resource = clone_blob_descriptor(
+                                                       imd->metadata_blob);
+               if (!ctx.metadata_resource)
+                       goto out;
+               mark_image_dirty(imd);
        }
        ctx.owner_uid = getuid();
        ctx.owner_gid = getgid();
 
-       /* Add each stream referenced by files in the image to a list and
-        * preemptively double the number of references to each.  This 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;
-
-               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;
-                       }
-               }
+       /* Number the inodes in the mounted image sequentially and initialize
+        * the file descriptor arrays  */
+       prepare_inodes(&ctx);
 
-               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;
-                               }
-                       }
-               }
-       }
-
-       /* 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;
+       /* Save the absolute path to the mountpoint directory.  */
+       ctx.mountpoint_abspath = realpath(dir, NULL);
+       if (ctx.mountpoint_abspath)
+               ctx.mountpoint_abspath_nchars = strlen(ctx.mountpoint_abspath);
 
        /* Build the FUSE command line.  */
 
@@ -2233,7 +2243,6 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        char optstring[256] =
                "use_ino"
                ",subtype=wimfs"
-               ",attr_timeout=0"
                ",hard_remove"
                ",default_permissions"
                ",kernel_cache"
@@ -2255,10 +2264,11 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        /* Cleanup and return.  */
        if (ret)
                ret = WIMLIB_ERR_FUSE;
-       release_extra_refcnts(&ctx);
-       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
+out:
+       FREE(ctx.mountpoint_abspath);
+       free_blob_descriptor(ctx.metadata_resource);
+       if (ctx.staging_dir_name)
                delete_staging_dir(&ctx);
-out_unlock:
        unlock_wim_for_append(wim);
        return ret;
 }
@@ -2295,32 +2305,32 @@ 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);
+       get_random_alnum_chars(name + 7, WIMFS_MQUEUE_NAME_LEN - 7);
        name[WIMFS_MQUEUE_NAME_LEN] = '\0';
 }
 
 static mqd_t
-create_message_queue(const char *name, bool have_progfunc)
+create_message_queue(const char *name)
 {
-       bool am_root = (getuid() == 0);
-       mode_t umask_save = 0;
-       mode_t mode = 0600;
+       bool am_root;
+       mode_t umask_save;
+       mode_t mode;
        struct mq_attr attr;
        mqd_t mq;
 
        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);
+       attr.mq_msgsize = sizeof(struct commit_progress_report);
 
+       am_root = (geteuid() == 0);
        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;
+       } else {
+               mode = 0600;
        }
        mq = mq_open(name, O_RDWR | O_CREAT | O_EXCL, mode, &attr);
        if (am_root)
@@ -2391,7 +2401,7 @@ do_unmount_commit(const char *dir, int unmount_flags,
        if (progfunc) {
                generate_message_queue_name(unmount_info.mq_name);
 
-               mq = create_message_queue(unmount_info.mq_name, progfunc != NULL);
+               mq = create_message_queue(unmount_info.mq_name);
                if (mq == (mqd_t)-1) {
                        ERROR_WITH_ERRNO("Can't create POSIX message queue");
                        return WIMLIB_ERR_MQUEUE;
@@ -2415,7 +2425,8 @@ do_unmount_commit(const char *dir, int unmount_flags,
                ret = do_unmount(dir);
        if (progfunc) {
                /* Terminate the progress thread.  */
-               mq_send(mq, NULL, 0, 1);
+               char empty[1];
+               mq_send(mq, empty, 0, 1);
                pthread_join(commit_progress_tid, NULL);
        }
 out_delete_mq:
@@ -2476,7 +2487,9 @@ wimlib_unmount_image_with_progress(const char *dir, int unmount_flags,
        int mount_flags;
        int ret;
 
-       wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
+       ret = wimlib_global_init(0);
+       if (ret)
+               return ret;
 
        if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY |
                              WIMLIB_UNMOUNT_FLAG_COMMIT |