/*
* 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.
- *
- * 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
# 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 <attr/xattr.h>
#include <dirent.h>
#include <errno.h>
+#include <fuse.h>
#include <limits.h>
#include <mqueue.h>
#include <pthread.h>
#include <unistd.h>
#include <utime.h>
-#define FUSE_USE_VERSION 26
-#include <fuse.h>
-#include <attr/xattr.h>
+#include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
+#include "wimlib/lookup_table.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... */
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;
}
int lookup_flags,
struct wim_dentry **dentry_ret,
struct wim_lookup_table_entry **lte_ret,
- u16 *stream_idx_ret)
+ unsigned *stream_idx_ret)
{
WIMStruct *wim = ctx->wim;
struct wim_dentry *dentry;
struct wim_lookup_table_entry *lte;
- u16 stream_idx;
+ unsigned stream_idx;
const char *stream_name = NULL;
struct wim_inode *inode;
char *p = NULL;
stream_name = path_stream_name(path);
if (stream_name) {
p = (char *)stream_name - 1;
- *p = T('\0');
+ *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->lookup_table, false))
+ return -EIO;
if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
&& inode_is_directory(inode))
dentry_add_child(parent, new_dentry);
- if (dentry_ret)
- *dentry_ret = new_dentry;
+ *dentry_ret = new_dentry;
return 0;
}
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(stbuf->st_size, 512);
return 0;
}
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;
}
*/
static int
extract_resource_to_staging_dir(struct wim_inode *inode,
- u16 stream_idx,
+ unsigned stream_idx,
struct wim_lookup_table_entry **lte_ptr,
off_t size,
const struct wimfs_context *ctx)
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)
close(ctx->parent_dir_fd);
}
+/* Number the inodes in the mounted image sequentially. */
static void
reassign_inode_numbers(struct wimfs_context *ctx)
{
}
}
+/* Delete the 'struct wim_lookup_table_entry' 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)
{
}
}
+/* 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)
{
}
}
+/* Close all file descriptors open to the mounted image. */
static void
close_all_fds(struct wimfs_context *ctx)
{
if (ret)
goto err_free_new_lte;
- ret = xml_add_image(wim, T(""));
+ ret = xml_add_image(wim, "");
if (ret)
goto err_undo_append;
xml_update_image_info(ctx->wim, ctx->wim->current_image);
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)
fuse_ctx->uid == 0);
}
+/* Unmount the mounted image, called from the daemon process. */
static int
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)
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);
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;
}
return -EFBIG;
if (size) {
- if (lte->size > size)
+ if (size < lte->size)
return -ERANGE;
if (read_full_stream_into_buf(lte, value))
- return -errno;
+ return errno ? -errno : -EIO;
}
return lte->size;
}
if (new_dentry(new_name, &new_alias))
return -ENOMEM;
- new_alias->d_inode = inode;
- inode_add_dentry(new_alias, inode);
+ inode_ref_streams(inode);
+ d_associate(new_alias, inode);
dentry_add_child(dir, new_alias);
touch_inode(dir->d_inode);
- inode->i_nlink++;
- inode_ref_streams(inode);
return 0;
}
struct wim_dentry *dentry;
struct wim_inode *inode;
struct wim_lookup_table_entry *lte;
- u16 stream_idx;
+ unsigned stream_idx;
struct wimfs_fd *fd;
int ret;
switch (lte->resource_location) {
case RESOURCE_IN_WIM:
if (read_partial_wim_stream_into_buf(lte, size, offset, buf))
- ret = -errno;
+ ret = errno ? -errno : -EIO;
else
ret = size;
break;
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())
const struct wimfs_context *ctx = wimfs_get_context();
struct wim_dentry *dentry;
struct wim_lookup_table_entry *lte;
- u16 stream_idx;
+ unsigned stream_idx;
int ret;
int fd;
{
const struct wimfs_context *ctx = wimfs_get_context();
struct wim_dentry *dentry;
- u16 stream_idx;
+ unsigned stream_idx;
int ret;
ret = wim_pathname_to_stream(ctx, path, 0, &dentry, NULL, &stream_idx);
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;
}
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 */
/* 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;
}
char optstring[256] =
"use_ino"
",subtype=wimfs"
- ",attr_timeout=0"
",hard_remove"
",default_permissions"
",kernel_cache"
}
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)
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;
ret = do_unmount(dir);
if (progfunc) {
/* Terminate the progress thread. */
- char empty[0];
+ char empty[1];
mq_send(mq, empty, 0, 1);
pthread_join(commit_progress_tid, NULL);
}
int mount_flags;
int ret;
- wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
+ ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
+ if (ret)
+ return ret;
if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY |
WIMLIB_UNMOUNT_FLAG_COMMIT |