* mount_image.c
*
* This file implements mounting of WIM images using FUSE
- * (Filesystem in Userspace). See http://fuse.sourceforge.net/.
+ * (Filesystem in Userspace). See https://github.com/libfuse/libfuse
*
* Currently it is only expected to work on Linux.
*/
/*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
*
* 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
* details.
*
* 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/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
*/
#ifdef HAVE_CONFIG_H
#ifdef WITH_FUSE
-#ifdef __WIN32__
+#ifdef _WIN32
# error "FUSE mount not supported on Windows! Please configure --without-fuse"
#endif
-#define FUSE_USE_VERSION 26
+#define FUSE_USE_VERSION 30
-#include <sys/types.h> /* sometimes required before <attr/xattr.h> */
-
-#include <attr/xattr.h>
+#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 <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "wimlib/paths.h"
#include "wimlib/progress.h"
#include "wimlib/reparse.h"
+#include "wimlib/threads.h"
#include "wimlib/timestamp.h"
#include "wimlib/unix_data.h"
#include "wimlib/write.h"
# define O_NOFOLLOW 0 /* Security only... */
#endif
+#ifndef ENOATTR
+# define ENOATTR ENODATA
+#endif
+
+#ifndef RENAME_NOREPLACE
+# define RENAME_NOREPLACE (1 << 0)
+#endif
+#ifndef RENAME_EXCHANGE
+# define RENAME_EXCHANGE (1 << 1)
+#endif
+
#define WIMFS_MQUEUE_NAME_LEN 32
#define WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS 0x80000000
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;
/* Number of file descriptors open to the mounted WIM image. */
unsigned long num_open_fds;
- /* Original list of blobs in the mounted image, linked by
- * 'struct blob_descriptor'.orig_blob_list. */
- struct list_head orig_blob_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"). */
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;
+ return mode & ~fuse_ctx->umask;
}
/*
* 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
*/
static int
create_file(struct fuse_context *fuse_ctx, const char *path,
- mode_t mode, dev_t rdev, u32 attributes,
- struct wim_dentry **dentry_ret)
+ 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)
if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE))
return -EEXIST;
- if (new_dentry_with_new_inode(basename, true, &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_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;
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;
}
}
- hlist_add_head(&new_inode->i_hlist,
+ 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);
- *dentry_ret = new_dentry;
+ *dentry_ret = dentry;
return 0;
}
touch_inode(dentry->d_parent->d_inode);
}
+/*
+ * Update inode metadata after a regular file's contents have changed:
+ *
+ * - Update the timestamps
+ * - Clear the setuid and setgid bits
+ */
+static void
+file_contents_changed(struct wim_inode *inode)
+{
+ struct wimlib_unix_data unix_data;
+ bool ok;
+
+ touch_inode(inode);
+
+ if (inode_get_unix_data(inode, &unix_data)) {
+ unix_data.mode &= ~(S_ISUID | S_ISGID);
+ ok = inode_set_unix_data(inode, &unix_data, UNIX_DATA_MODE);
+ /*
+ * This cannot fail because no memory allocation should have
+ * been required, as the UNIX data already exists.
+ */
+ wimlib_assert(ok);
+ } /* Else, set[ug]id can't be set, so there's nothing to do. */
+}
+
/*
* Create a new file in the staging directory for a read-write mounted image.
*
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)) {
filedes_init(&fd, staging_fd);
errno = 0;
extract_size = min(old_blob->size, size);
- result = extract_blob_to_fd(old_blob, &fd, extract_size);
+ result = extract_blob_prefix_to_fd(old_blob, extract_size, &fd);
} else {
extract_size = 0;
result = 0;
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);
+ if (size != blob_size(old_blob))
+ file_contents_changed(inode);
return 0;
out_revert_fd_changes:
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';
}
}
-static void
-release_extra_refcnts(struct wimfs_context *ctx)
-{
- struct list_head *list = &ctx->orig_blob_list;
- struct blob_table *blob_table = ctx->wim->blob_table;
- struct blob_descriptor *blob, *tmp;
-
- list_for_each_entry_safe(blob, tmp, list, orig_blob_list)
- blob_subtract_refcnt(blob, blob_table, blob->out_refcnt);
-}
-
/* 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
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 blob_descriptor *new_blob;
+ 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 blob descriptor for the modified image's metadata
- * resource, which doesn't exist yet. */
- ret = WIMLIB_ERR_NOMEM;
- new_blob = new_blob_descriptor();
- if (!new_blob)
- goto err_put_replace_imd;
-
- new_blob->refcnt = 1;
- new_blob->unhashed = 1;
- new_blob->is_metadata = 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_blob;
+ goto err_put_imd;
- ret = xml_add_image(wim, "");
+ ret = xml_export_image(wim->xml_info, image,
+ wim->xml_info, NULL, NULL, false);
if (ret)
goto err_undo_append;
- replace_imd->metadata_blob = imd->metadata_blob;
- imd->metadata_blob = new_blob;
- 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_blob:
- free_blob_descriptor(new_blob);
-err_put_replace_imd:
- put_image_metadata(replace_imd, NULL);
+err_put_imd:
+ imd->metadata_blob = NULL;
+ put_image_metadata(imd);
err:
return ret;
}
int ret = renew_current_image(ctx);
if (ret)
return ret;
- } else {
- release_extra_refcnts(ctx);
}
- INIT_LIST_HEAD(&ctx->orig_blob_list);
delete_empty_blobs(ctx);
- xml_update_image_info(ctx->wim, ctx->wim->current_image);
write_flags = 0;
return ret;
}
+static void *
+wimfs_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
+{
+ /*
+ * Cache positive name lookups indefinitely, since names can only be
+ * added, removed, or modified through the mounted filesystem itself.
+ */
+ cfg->entry_timeout = 1000000000;
+
+ /*
+ * Cache negative name lookups indefinitely, since names can only be
+ * added, removed, or modified through the mounted filesystem itself.
+ */
+ cfg->negative_timeout = 1000000000;
+
+ /*
+ * 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.)
+ */
+ cfg->attr_timeout = 0;
+
+ /*
+ * 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.
+ */
+ cfg->hard_remove = 1;
+
+ /*
+ * Make FUSE use the inode numbers we provide. We want this, because we
+ * have inodes and will number them ourselves.
+ */
+ cfg->use_ino = 1;
+
+ /*
+ * 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.)
+ */
+ cfg->kernel_cache = 1;
+
+ /*
+ * 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().
+ */
+ cfg->nullpath_ok = 1;
+
+ return wimfs_get_context();
+}
+
static int
-wimfs_chmod(const char *path, mode_t mask)
+wimfs_chmod(const char *path, mode_t mask, struct fuse_file_info *fi)
{
const struct wimfs_context *ctx = wimfs_get_context();
struct wim_inode *inode;
if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
return -EOPNOTSUPP;
- inode = wim_pathname_to_inode(ctx->wim, path);
- if (!inode)
- return -errno;
-
+ if (fi) {
+ inode = WIMFS_FD(fi)->f_inode;
+ } else {
+ inode = wim_pathname_to_inode(ctx->wim, path);
+ if (!inode)
+ return -errno;
+ }
unix_data.uid = ctx->owner_uid;
unix_data.gid = ctx->owner_gid;
unix_data.mode = mask;
}
static int
-wimfs_chown(const char *path, uid_t uid, gid_t gid)
+wimfs_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi)
{
const struct wimfs_context *ctx = wimfs_get_context();
struct wim_inode *inode;
if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
return -EOPNOTSUPP;
- inode = wim_pathname_to_inode(ctx->wim, path);
- if (!inode)
- return -errno;
+ if (fi) {
+ inode = WIMFS_FD(fi)->f_inode;
+ } else {
+ inode = wim_pathname_to_inode(ctx->wim, path);
+ if (!inode)
+ return -errno;
+ }
which = 0;
}
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_blob, stbuf);
-}
-
-static int
-wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
-{
- struct wimfs_fd *fd = WIMFS_FD(fi);
- if (ftruncate(fd->f_staging_fd.fd, size))
- return -errno;
- touch_inode(fd->f_inode);
- fd->f_blob->size = size;
- return 0;
-}
-
-static int
-wimfs_getattr(const char *path, struct stat *stbuf)
+wimfs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
const struct wimfs_context *ctx = wimfs_get_context();
- struct wim_dentry *dentry;
- struct wim_inode_stream *strm;
+ const struct wim_inode *inode;
+ const struct blob_descriptor *blob;
int ret;
- ret = wim_pathname_to_stream(ctx, path, LOOKUP_FLAG_DIRECTORY_OK,
- &dentry, &strm);
- if (ret)
- return ret;
+ if (fi) {
+ const struct wimfs_fd *fd = WIMFS_FD(fi);
- return inode_to_stbuf(dentry->d_inode,
- stream_blob_resolved(strm), stbuf);
+ inode = fd->f_inode;
+ blob = fd->f_blob;
+ } else {
+ struct wim_dentry *dentry;
+ struct wim_inode_stream *strm;
+
+ ret = wim_pathname_to_stream(ctx, path,
+ LOOKUP_FLAG_DIRECTORY_OK,
+ &dentry, &strm);
+ if (ret)
+ return ret;
+ inode = dentry->d_inode;
+ blob = stream_blob_resolved(strm);
+ }
+
+ return inode_to_stbuf(inode, blob, stbuf);
}
static int
size_t size)
{
const struct wimfs_context *ctx = wimfs_get_context();
- struct wim_inode *inode;
- struct wim_inode_stream *strm;
- struct blob_descriptor *blob;
+ 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
if (size < blob->size)
return -ERANGE;
- if (read_full_blob_into_buf(blob, value))
+ if (read_blob_into_buf(blob, value))
return errno ? -errno : -EIO;
}
return blob->size;
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))
total_size += stream_name_mbs_nbytes + 6;
if (size) {
- if (end - p < stream_name_mbs_nbytes + 6) {
+ if (list + size - p < stream_name_mbs_nbytes + 6) {
FREE(stream_name_mbs);
return -ERANGE;
}
int ret;
/* Note: according to fuse.h, mode may not include S_IFDIR */
- ret = create_file(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);
!(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_file(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);
int raw_fd;
raw_fd = openat(blob->staging_dir_fd, blob->staging_file_name,
- (fi->flags & O_ACCMODE) | O_NOFOLLOW);
+ (fi->flags & (O_ACCMODE | O_TRUNC)) |
+ O_NOFOLLOW);
if (raw_fd < 0) {
close_wimfs_fd(fd);
return -errno;
}
filedes_init(&fd->f_staging_fd, raw_fd);
+ if (fi->flags & O_TRUNC) {
+ blob->size = 0;
+ file_contents_changed(inode);
+ }
}
fi->fh = (uintptr_t)fd;
return 0;
switch (blob->blob_location) {
case BLOB_IN_WIM:
- if (read_partial_wim_blob_into_buf(blob, size, offset, buf))
+ if (read_partial_wim_blob_into_buf(blob, offset, size, buf))
ret = errno ? -errno : -EIO;
else
ret = size;
break;
case BLOB_IN_STAGING_FILE:
- ret = raw_pread(&fd->f_staging_fd, buf, size, offset);
+ ret = pread(fd->f_staging_fd.fd, buf, size, offset);
if (ret < 0)
ret = -errno;
break;
static int
wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi)
+ off_t offset, struct fuse_file_info *fi,
+ enum fuse_readdir_flags flags)
{
struct wimfs_fd *fd = WIMFS_FD(fi);
const struct wim_inode *inode;
inode = fd->f_inode;
- ret = filler(buf, ".", NULL, 0);
+ ret = filler(buf, ".", NULL, 0, 0);
if (ret)
return ret;
- ret = filler(buf, "..", NULL, 0);
+ ret = filler(buf, "..", NULL, 0, 0);
if (ret)
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, 0);
+ FREE(name);
if (ret)
return ret;
}
}
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))
+ if (bufsize <= 0)
return -EINVAL;
- if (buf_len == 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
}
static int
-wimfs_rename(const char *from, const char *to)
+wimfs_rename(const char *from, const char *to, unsigned int flags)
{
+ if (flags & RENAME_EXCHANGE)
+ return -EINVAL;
return rename_wim_path(wimfs_get_WIMStruct(), from, to,
- WIMLIB_CASE_SENSITIVE, NULL);
+ WIMLIB_CASE_SENSITIVE,
+ (flags & RENAME_NOREPLACE), NULL);
}
static int
struct wim_dentry *dentry;
int ret;
- ret = create_file(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->blob_table);
if (ret) {
}
static int
-wimfs_truncate(const char *path, off_t size)
+do_truncate(int staging_fd, off_t size,
+ struct wim_inode *inode, struct blob_descriptor *blob)
+{
+ if (ftruncate(staging_fd, size))
+ return -errno;
+ file_contents_changed(inode);
+ blob->size = size;
+ return 0;
+}
+
+static int
+wimfs_truncate(const char *path, off_t size, struct fuse_file_info *fi)
{
const struct wimfs_context *ctx = wimfs_get_context();
struct wim_dentry *dentry;
struct wim_inode_stream *strm;
struct blob_descriptor *blob;
int ret;
- int fd;
+ int staging_fd;
+
+ if (fi) {
+ struct wimfs_fd *fd = WIMFS_FD(fi);
+
+ return do_truncate(fd->f_staging_fd.fd, size, fd->f_inode,
+ fd->f_blob);
+ }
ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm);
if (ret)
}
/* Truncate the staging file. */
- 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)
+ staging_fd = openat(blob->staging_dir_fd, blob->staging_file_name,
+ O_WRONLY | O_NOFOLLOW);
+ if (staging_fd < 0)
return -errno;
- blob->size = size;
- touch_inode(dentry->d_inode);
- return 0;
+ ret = do_truncate(staging_fd, size, dentry->d_inode, blob);
+ if (close(staging_fd) && !ret)
+ ret = -errno;
+ return ret;
}
static int
return 0;
}
-#ifdef HAVE_UTIMENSAT
/*
* Change the timestamp on a file dentry.
*
* Note that alternate data streams do not have their own timestamps.
*/
static int
-wimfs_utimens(const char *path, const struct timespec tv[2])
+wimfs_utimens(const char *path, const struct timespec tv[2],
+ struct fuse_file_info *fi)
{
- WIMStruct *wim = wimfs_get_WIMStruct();
struct wim_inode *inode;
- inode = wim_pathname_to_inode(wim, path);
- if (!inode)
- return -errno;
+ if (fi) {
+ inode = WIMFS_FD(fi)->f_inode;
+ } else {
+ inode = wim_pathname_to_inode(wimfs_get_WIMStruct(), path);
+ if (!inode)
+ return -errno;
+ }
if (tv[0].tv_nsec != UTIME_OMIT) {
if (tv[0].tv_nsec == UTIME_NOW)
}
return 0;
}
-#else /* HAVE_UTIMENSAT */
-static int
-wimfs_utime(const char *path, struct utimbuf *times)
-{
- WIMStruct *wim = wimfs_get_WIMStruct();
- struct wim_inode *inode;
-
- inode = wim_pathname_to_inode(wim, path);
- if (!inode)
- return -errno;
-
- 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 */
static int
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_blob->size)
fd->f_blob->size = offset + size;
- touch_inode(fd->f_inode);
+ file_contents_changed(fd->f_inode);
return ret;
}
-static struct fuse_operations wimfs_operations = {
+static const struct fuse_operations wimfs_operations = {
+ .init = wimfs_init,
.chmod = wimfs_chmod,
.chown = wimfs_chown,
- .fgetattr = wimfs_fgetattr,
- .ftruncate = wimfs_ftruncate,
.getattr = wimfs_getattr,
.getxattr = wimfs_getxattr,
.link = wimfs_link,
.symlink = wimfs_symlink,
.truncate = wimfs_truncate,
.unlink = wimfs_unlink,
-#ifdef HAVE_UTIMENSAT
.utimens = wimfs_utimens,
-#else
- .utime = wimfs_utime,
-#endif
.write = wimfs_write,
- /* 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 >= 9)
- .flag_nopath = 1,
- .flag_utime_omit_ok = 1,
-#endif
};
/* API function documented in wimlib.h */
/* 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;
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 blob 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_blob_list);
- if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
- unsigned i;
- struct wim_inode *inode;
- struct blob_descriptor *blob;
-
- image_for_each_inode(inode, imd) {
- for (i = 0; i < inode->i_num_streams; i++) {
- blob = stream_blob(&inode->i_streams[i],
- wim->blob_table);
- if (blob)
- blob->out_refcnt = 0;
- }
- }
-
- image_for_each_inode(inode, imd) {
- for (i = 0; i < inode->i_num_streams; i++) {
- blob = stream_blob(&inode->i_streams[i],
- wim->blob_table);
- if (blob) {
- if (blob->out_refcnt == 0)
- list_add(&blob->orig_blob_list,
- &ctx.orig_blob_list);
- blob->out_refcnt += inode->i_nlink;
- blob->refcnt += inode->i_nlink;
- }
- }
- }
- }
-
/* Number the inodes in the mounted image sequentially and initialize
* the file descriptor arrays */
prepare_inodes(&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. */
/*
* 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"
- ",hard_remove"
- ",default_permissions"
- ",kernel_cache"
- ",entry_timeout=1000000000"
- ",negative_timeout=1000000000"
- ",attr_timeout=0"
- ;
+ char optstring[128] = "subtype=wimfs,default_permissions";
fuse_argv[fuse_argc++] = "-o";
fuse_argv[fuse_argc++] = optstring;
if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE))
/* 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;
}
{
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';
}
wimlib_progress_func_t progfunc, void *progctx)
{
struct wimfs_unmount_info unmount_info;
- mqd_t mq;
+ mqd_t mq = (mqd_t)-1;
struct commit_progress_thread_args args;
- pthread_t commit_progress_tid;
+ struct thread commit_progress_tid;
int ret;
memset(&unmount_info, 0, sizeof(unmount_info));
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");
+ if (!thread_create(&commit_progress_tid,
+ commit_progress_thread_proc, &args)) {
ret = WIMLIB_ERR_NOMEM;
goto out_delete_mq;
}
/* Terminate the progress thread. */
char empty[1];
mq_send(mq, empty, 0, 1);
- pthread_join(commit_progress_tid, NULL);
+ thread_join(&commit_progress_tid);
}
out_delete_mq:
if (progfunc) {
int mount_flags;
int ret;
- ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
+ ret = wimlib_global_init(0);
if (ret)
return ret;
static int
mount_unsupported_error(void)
{
-#if defined(__WIN32__)
+#ifdef _WIN32
ERROR("Sorry-- Mounting WIM images is not supported on Windows!");
#else
ERROR("wimlib was compiled with --without-fuse, which disables support "