#endif
#include "wimlib.h"
+#include "wimlib/error.h"
#ifdef WITH_FUSE
/* 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;
+
+ /* Parameters for unmounting the image (can be set via extended
+ * attribute "wimfs.unmount_info"). */
+ struct wimfs_unmount_info unmount_info;
};
#define WIMFS_CTX(fuse_ctx) ((struct wimfs_context*)(fuse_ctx)->private_data)
return wimfs_get_context()->wim;
}
-static inline int
-get_lookup_flags(const struct wimfs_context *ctx)
-{
- return ctx->default_lookup_flags;
-}
-
/* Is write permission requested on the file? */
static inline bool
flags_writable(int open_flags)
return dentry->d_inode;
}
+/* Can look up named data stream with colon syntax */
+#define LOOKUP_FLAG_ADS_OK 0x01
+
+/* Can look up directory (otherwise get -ENOTDIR) */
+#define LOOKUP_FLAG_DIRECTORY_OK 0x02
+
+/*
+ * Translate a path into the corresponding dentry, lookup table entry, and
+ * stream index in the mounted WIM image.
+ *
+ * Returns 0 or a -errno code. All of @dentry_ret, @lte_ret, and
+ * @stream_idx_ret are optional.
+ */
+static int
+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)
+{
+ WIMStruct *wim = ctx->wim;
+ struct wim_dentry *dentry;
+ struct wim_lookup_table_entry *lte;
+ u16 stream_idx;
+ const char *stream_name = NULL;
+ struct wim_inode *inode;
+ 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 = (char *)stream_name - 1;
+ *p = T('\0');
+ }
+ }
+
+ dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
+ if (p)
+ *p = T(':');
+ if (!dentry)
+ return -errno;
+
+ inode = dentry->d_inode;
+
+ if (!inode->i_resolved)
+ if (inode_resolve_streams(inode, wim->lookup_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)
+ 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;
+ return 0;
+}
+
/*
* Create a new file in the mounted WIM image.
*
inode->i_last_write_time = now;
}
+static void
+touch_parent(struct wim_dentry *dentry)
+{
+ touch_inode(dentry->d_parent->d_inode);
+}
+
/*
* Create a new file in the staging directory for a read-write mounted image.
*
return wimlib_overwrite(ctx->wim, write_flags, 0);
}
+/* In the case of an allow_other mount, only the owner and root should be
+ * allowed to unmount the filesystem. */
+static bool
+may_unmount_wimfs(void)
+{
+ const struct fuse_context *fuse_ctx = fuse_get_context();
+ const struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
+
+ return (fuse_ctx->uid == wimfs_ctx->owner_uid ||
+ fuse_ctx->uid == 0);
+}
+
static int
-unmount_wimfs(const struct wimfs_unmount_info *info)
+unmount_wimfs(void)
{
struct fuse_context *fuse_ctx = fuse_get_context();
struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
+ const struct wimfs_unmount_info *info = &wimfs_ctx->unmount_info;
int unmount_flags = info->unmount_flags;
mqd_t mq = (mqd_t)-1;
- int status;
-
- if (fuse_ctx->uid != wimfs_ctx->owner_uid &&
- fuse_ctx->uid != 0)
- return -EPERM;
-
- if (info->mq_name[0]) {
- mq = mq_open(info->mq_name, O_WRONLY | O_NONBLOCK);
- if (mq == (mqd_t)-1)
- return -errno;
- }
+ int ret;
/* Ignore COMMIT if the image is mounted read-only. */
if (!(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE))
unmount_flags &= ~WIMLIB_UNMOUNT_FLAG_COMMIT;
+ if (unmount_flags & WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS) {
+ mq = mq_open(info->mq_name, O_WRONLY | O_NONBLOCK);
+ if (mq == (mqd_t)-1) {
+ ret = WIMLIB_ERR_MQUEUE;
+ goto out;
+ }
+ }
+
if (wimfs_ctx->num_open_fds) {
if ((unmount_flags & (WIMLIB_UNMOUNT_FLAG_COMMIT |
WIMLIB_UNMOUNT_FLAG_FORCE))
== WIMLIB_UNMOUNT_FLAG_COMMIT)
{
- status = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY;
- goto out_send_status;
+ ret = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY;
+ goto out;
}
close_all_fds(wimfs_ctx);
}
if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)
- status = commit_image(wimfs_ctx, unmount_flags, mq);
+ ret = commit_image(wimfs_ctx, unmount_flags, mq);
else
- status = 0;
- fuse_exit(fuse_ctx->fuse);
-out_send_status:
- if (mq != (mqd_t)-1) {
- mq_send(mq, (const char *)&status, sizeof(int), 1);
- mq_close(mq);
+ ret = 0;
+out:
+ /* Leave the image mounted if commit failed, unless this is a
+ * 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);
+ fuse_exit(fuse_ctx->fuse);
}
- return 0;
+ if (mq != (mqd_t)-1)
+ mq_close(mq);
+ return ret;
}
static int
struct wim_lookup_table_entry *lte;
int ret;
- ret = wim_pathname_to_stream(ctx->wim, path,
- get_lookup_flags(ctx) |
- LOOKUP_FLAG_DIRECTORY_OK,
+ ret = wim_pathname_to_stream(ctx, path, LOOKUP_FLAG_DIRECTORY_OK,
&dentry, <e, NULL);
if (ret)
return ret;
wimfs_getxattr(const char *path, const char *name, char *value,
size_t size)
{
- struct fuse_context *fuse_ctx = fuse_get_context();
- const struct wimfs_context *ctx = WIMFS_CTX(fuse_ctx);
+ const struct wimfs_context *ctx = wimfs_get_context();
struct wim_inode *inode;
struct wim_ads_entry *ads_entry;
struct wim_lookup_table_entry *lte;
&ctx->mount_flags, sizeof(int));
}
if (!strcmp(name, "unmount")) {
- struct wimfs_unmount_info info;
- memset(&info, 0, sizeof(info));
- return unmount_wimfs(&info);
+ if (!may_unmount_wimfs())
+ return -EPERM;
+ if (size) {
+ int status;
+
+ if (size < sizeof(int))
+ return -ERANGE;
+ status = unmount_wimfs();
+ memcpy(value, &status, sizeof(int));
+ }
+ return sizeof(int);
}
return -ENOATTR;
}
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;
static int
wimfs_mkdir(const char *path, mode_t mode)
{
+ struct wim_dentry *dentry;
+ int ret;
+
/* Note: according to fuse.h, mode may not include S_IFDIR */
- return create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
- FILE_ATTRIBUTE_DIRECTORY, NULL);
+ ret = create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
+ FILE_ATTRIBUTE_DIRECTORY, &dentry);
+ if (ret)
+ return ret;
+ touch_parent(dentry);
+ return 0;
}
static int
} else {
/* Create a regular file, device node, named pipe, or socket.
*/
+ struct wim_dentry *dentry;
+ int ret;
if (!S_ISREG(mode) &&
!(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
/* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes,
* named pipes, and sockets. The real mode is in the UNIX
* metadata. */
- return create_dentry(fuse_ctx, path, mode, rdev,
- FILE_ATTRIBUTE_NORMAL, NULL);
+ ret = create_dentry(fuse_ctx, path, mode, rdev,
+ FILE_ATTRIBUTE_NORMAL, &dentry);
+ if (ret)
+ return ret;
+ touch_parent(dentry);
+ return 0;
}
}
struct wimfs_fd *fd;
int ret;
- ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
- &dentry, <e, &stream_idx);
+ ret = wim_pathname_to_stream(ctx, path, 0, &dentry, <e, &stream_idx);
if (ret)
return ret;
if (dentry_has_children(dentry))
return -ENOTEMPTY;
+ touch_parent(dentry);
remove_dentry(dentry, wim->lookup_table);
return 0;
}
* be ioctls, but directory ioctls aren't supported until
* libfuse 2.9, and even then they are broken. */
name += 6;
- if (!strcmp(name, "unmount")) {
+ if (!strcmp(name, "unmount_info")) {
+ if (!may_unmount_wimfs())
+ return -EPERM;
if (size < sizeof(struct wimfs_unmount_info))
return -EINVAL;
- return unmount_wimfs((const void *)value);
+ memcpy(&ctx->unmount_info, value,
+ sizeof(struct wimfs_unmount_info));
+ return 0;
}
return -ENOATTR;
}
ret = -ENOMEM;
else
ret = -EINVAL;
+ } else {
+ touch_parent(dentry);
}
return ret;
}
int ret;
int fd;
- ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
- &dentry, <e, &stream_idx);
-
+ ret = wim_pathname_to_stream(ctx, path, 0, &dentry, <e, &stream_idx);
if (ret)
return ret;
u16 stream_idx;
int ret;
- ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx),
- &dentry, NULL, &stream_idx);
-
+ ret = wim_pathname_to_stream(ctx, path, 0, &dentry, NULL, &stream_idx);
if (ret)
return ret;
- if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0)
+ if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0) {
+ touch_parent(dentry);
remove_dentry(dentry, ctx->wim->lookup_table);
- else
+ } else {
inode_remove_ads(dentry->d_inode,
&dentry->d_inode->i_ads_entries[stream_idx - 1],
ctx->wim->lookup_table);
+ }
return 0;
}
return WIMLIB_ERR_INVALID_PARAM;
}
- ret = lock_wim_for_append(wim, wim->in_fd.fd);
- if (ret)
- return ret;
+ if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+ ret = lock_wim_for_append(wim);
+ if (ret)
+ return ret;
+ }
/* If the user did not specify an interface for accessing named
* data streams, use the default (extended attributes). */
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. The latter is
- * done to allow implementing the WIMLIB_UNMOUNT_FLAG_NEW_IMAGE
- * semantics. */
+ * 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;
if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
delete_staging_dir(&ctx);
out_unlock:
- unlock_wim_for_append(wim, wim->in_fd.fd);
+ unlock_wim_for_append(wim);
return ret;
}
mqd_t mq;
wimlib_progress_func_t progfunc;
void *progctx;
- int status;
};
static void *
struct commit_progress_report report;
ssize_t ret;
- args->status = WIMLIB_ERR_NOT_A_MOUNTPOINT;
for (;;) {
ret = mq_receive(args->mq,
(char *)&report, sizeof(report), NULL);
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- break;
- }
- if (ret == sizeof(int)) {
- args->status = *(int *)&report;
- break;
+ if (ret == sizeof(report)) {
+ call_progress(args->progfunc, report.msg,
+ &report.info, args->progctx);
+ } else {
+ if (ret == 0 || (ret < 0 && errno != EINTR))
+ break;
}
- if (ret < sizeof(report))
- continue;
- call_progress(args->progfunc, report.msg,
- &report.info, args->progctx);
}
return NULL;
}
return mq;
}
+/* Unmount a read-only or read-write mounted WIM image. */
+static int
+do_unmount(const char *dir)
+{
+ int status;
+ ssize_t len;
+
+ len = getxattr(dir, "wimfs.unmount", &status, sizeof(int));
+ if (len == sizeof(int))
+ return status;
+ else if (len < 0 && (errno == EACCES || errno == EPERM))
+ return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT;
+ else
+ return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+}
+
+static int
+set_unmount_info(const char *dir, const struct wimfs_unmount_info *unmount_info)
+{
+ if (!setxattr(dir, "wimfs.unmount_info",
+ unmount_info, sizeof(struct wimfs_unmount_info), 0))
+ return 0;
+ else if (errno == EROFS)
+ return 0;
+ else if (errno == EACCES || errno == EPERM)
+ return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT;
+ else
+ return WIMLIB_ERR_NOT_A_MOUNTPOINT;
+}
+
+static int
+do_unmount_discard(const char *dir)
+{
+ int ret;
+ struct wimfs_unmount_info unmount_info;
+
+ memset(&unmount_info, 0, sizeof(unmount_info));
+
+ ret = set_unmount_info(dir, &unmount_info);
+ if (ret)
+ return ret;
+ return do_unmount(dir);
+}
+
/* Unmount a read-write mounted WIM image, committing the changes. */
static int
do_unmount_commit(const char *dir, int unmount_flags,
memset(&unmount_info, 0, sizeof(unmount_info));
unmount_info.unmount_flags = unmount_flags;
- generate_message_queue_name(unmount_info.mq_name);
- mq = create_message_queue(unmount_info.mq_name, progfunc != NULL);
- if (mq == (mqd_t)-1) {
- ERROR_WITH_ERRNO("Can't create POSIX message queue");
- return WIMLIB_ERR_MQUEUE;
- }
-
- /* The current thread will be stuck in setxattr() until the image is
+ /* The current thread will be stuck in getxattr() until the image is
* committed. Create a thread to handle the progress messages. */
if (progfunc) {
+ generate_message_queue_name(unmount_info.mq_name);
+
+ mq = create_message_queue(unmount_info.mq_name, progfunc != NULL);
+ if (mq == (mqd_t)-1) {
+ ERROR_WITH_ERRNO("Can't create POSIX message queue");
+ return WIMLIB_ERR_MQUEUE;
+ }
args.mq = mq;
args.progfunc = progfunc;
args.progctx = progctx;
unmount_info.unmount_flags |= WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS;
}
- if (!setxattr(dir, "wimfs.unmount",
- (const char *)&unmount_info, sizeof(unmount_info), 0))
- ret = 0;
- else if (errno == EACCES || errno == EPERM)
- ret = WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT;
- else
- ret = WIMLIB_ERR_NOT_A_MOUNTPOINT;
-
+ ret = set_unmount_info(dir, &unmount_info);
+ if (!ret)
+ ret = do_unmount(dir);
if (progfunc) {
- /* Terminate the progress thread and retrieve final unmount
- * status. */
-
- int tmp = -1;
- mq_send(mq, (const char *)&tmp, sizeof(int), 1);
-
+ /* Terminate the progress thread. */
+ char empty[0];
+ mq_send(mq, empty, 0, 1);
pthread_join(commit_progress_tid, NULL);
- if (!ret && args.status != -1)
- ret = args.status;
- } else if (!ret) {
- /* Retrieve the final unmount status. */
-
- int tmp = -1;
- int len;
-
- mq_send(mq, (const char *)&tmp, sizeof(int), 1);
- len = mq_receive(mq, (char *)&tmp, sizeof(int), NULL);
-
- if (len == 4 && tmp != -1)
- ret = tmp;
- else
- ret = WIMLIB_ERR_NOT_A_MOUNTPOINT;
}
out_delete_mq:
- mq_close(mq);
- mq_unlink(unmount_info.mq_name);
+ if (progfunc) {
+ mq_close(mq);
+ mq_unlink(unmount_info.mq_name);
+ }
return ret;
}
-/* Unmount a read-only or read-write mounted WIM image, discarding any changes.
- */
-static int
-do_unmount_discard(const char *dir)
-{
- if (!getxattr(dir, "wimfs.unmount", NULL, 0))
- return 0;
- else if (errno == EACCES || errno == EPERM)
- return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT;
- else
- return WIMLIB_ERR_NOT_A_MOUNTPOINT;
-}
-
static int
begin_unmount(const char *dir, int unmount_flags, int *mount_flags_ret,
wimlib_progress_func_t progfunc, void *progctx)