X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fmount_image.c;h=e361ddb8abb47a650b3befa8baf4874737843125;hp=57983c5cccb2e1f931394855bdb234a9a63d9ed8;hb=d86acf5e9930ee5cdf5150f30b51701fc81c2204;hpb=8f16067f71da5f45eda84813e9a07e68641bc283 diff --git a/src/mount_image.c b/src/mount_image.c index 57983c5c..e361ddb8 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -47,11 +47,15 @@ #include #include #include +#include #ifdef ENABLE_XATTR #include #endif +#define MSG_VERSION_TOO_HIGH -1 +#define MSG_BREAK_LOOP -2 + /* File descriptor to a file open on the WIM filesystem. */ struct wimlib_fd { struct inode *f_inode; @@ -76,12 +80,17 @@ struct wimfs_context { /* Flags passed to wimlib_mount(). */ int mount_flags; + int default_lookup_flags; + /* Next inode number to be assigned. */ u64 next_ino; /* List of lookup table entries in the staging directory */ struct list_head staging_list; + /* List of inodes in the mounted image */ + struct hlist_head *image_inode_list; + /* Name and message queue descriptors for message queues between the filesystem * daemon process and the unmount process. These are used when the filesystem * is unmounted and the process running wimlib_mount() (i.e. the `imagex @@ -119,10 +128,7 @@ static inline bool wimfs_ctx_readonly(const struct wimfs_context *ctx) static inline int get_lookup_flags(const struct wimfs_context *ctx) { - if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) - return LOOKUP_FLAG_ADS_OK; - else - return 0; + return ctx->default_lookup_flags; } /* Returns nonzero if write permission is requested on the file open flags */ @@ -210,7 +216,6 @@ out: static void inode_put_fd(struct inode *inode, struct wimlib_fd *fd) { - wimlib_assert(fd != NULL); wimlib_assert(inode != NULL); pthread_mutex_lock(&inode->i_mutex); @@ -232,7 +237,6 @@ static void inode_put_fd(struct inode *inode, struct wimlib_fd *fd) static int lte_put_fd(struct lookup_table_entry *lte, struct wimlib_fd *fd) { - wimlib_assert(fd != NULL); wimlib_assert(fd->f_lte == lte); if (!lte) /* Empty stream with no lookup table entry */ @@ -256,7 +260,6 @@ static int lte_put_fd(struct lookup_table_entry *lte, struct wimlib_fd *fd) static int close_wimlib_fd(struct wimlib_fd *fd) { int ret; - wimlib_assert(fd != NULL); DEBUG("Closing fd (inode = %lu, opened = %u, allocated = %u)", fd->f_inode->ino, fd->f_inode->num_opened_fds, fd->f_inode->num_allocated_fds); @@ -268,6 +271,36 @@ static int close_wimlib_fd(struct wimlib_fd *fd) return 0; } +static int create_dentry(struct wimfs_context *ctx, const char *path, + struct dentry **dentry_ret) +{ + struct dentry *parent; + struct dentry *new; + const char *basename; + + parent = get_parent_dentry(ctx->wim, path); + if (!parent) + return -ENOENT; + + if (!dentry_is_directory(parent)) + return -ENOTDIR; + + basename = path_basename(path); + if (get_dentry_child_with_name(parent, basename)) + return -EEXIST; + + new = new_dentry_with_inode(basename); + if (!new) + return -ENOMEM; + + new->d_inode->resolved = 1; + new->d_inode->ino = ctx->next_ino++; + dentry_add_child(parent, new); + hlist_add_head(&new->d_inode->hlist, ctx->image_inode_list); + *dentry_ret = new; + return 0; +} + /* Remove a dentry; i.e. remove a reference to the corresponding inode. * * If there are no remaining references to the inode either through dentries or @@ -317,7 +350,6 @@ static int inode_to_stbuf(const struct inode *inode, if (lte) { if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { - wimlib_assert(lte->staging_file_name); struct stat native_stat; if (stat(lte->staging_file_name, &native_stat) != 0) { DEBUG("Failed to stat `%s': %m", @@ -407,7 +439,12 @@ static int create_staging_file(char **name_ret, int open_flags, * extract, or NULL if there was no lookup table entry present for the stream * * @size: Number of bytes of the stream we want to extract (this supports the - * wimfs_truncate() function). + * wimfs_truncate() function). It may be more than the actual stream length, in + * which case the extra space is filled with zeroes. + * + * @ctx: Context for the WIM filesystem. + * + * Returns 0 on success or a negative error code on failure. */ static int extract_resource_to_staging_dir(struct inode *inode, u32 stream_id, @@ -419,19 +456,38 @@ static int extract_resource_to_staging_dir(struct inode *inode, int ret; int fd; struct lookup_table_entry *old_lte, *new_lte; + off_t extract_size; DEBUG("Extracting resource to staging dir: inode %"PRIu64", " "stream id %"PRIu32, inode->ino, stream_id); old_lte = *lte; + + wimlib_assert(old_lte == NULL || + old_lte->resource_location != RESOURCE_IN_STAGING_FILE); + + /* Create the staging file */ fd = create_staging_file(&staging_file_name, O_WRONLY, ctx); if (fd == -1) return -errno; - if (old_lte) - ret = extract_wim_resource_to_fd(old_lte, fd, size); - else + /* Extract the stream to the staging file (possibly truncated) */ + if (old_lte) { + extract_size = min(wim_resource_size(old_lte), size); + ret = extract_wim_resource_to_fd(old_lte, fd, extract_size); + } else { ret = 0; + extract_size = 0; + } + + /* In the case of truncate() to more than the file length, extend the + * file with zeroes by calling ftruncate() on the underlying staging + * file */ + if (ret == 0 && size > extract_size) + ret = ftruncate(fd, size); + + /* Close the staging file descriptor and check for errors. If there's + * an error, unlink the staging file. */ if (ret != 0 || close(fd) != 0) { if (errno != 0) ret = -errno; @@ -441,28 +497,29 @@ static int extract_resource_to_staging_dir(struct inode *inode, 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. */ + if (old_lte && inode->link_count == 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. */ + /* 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. */ DEBUG("Re-using lookup table entry"); lookup_table_unlink(ctx->wim->lookup_table, old_lte); new_lte = old_lte; } else { if (old_lte) { /* There's an existing lookup table entry, but its - * reference count is creater than the link count for + * reference count is greater than the link count for * the inode containing a stream we're opening. * Therefore, we need to split the lookup table entry. - * */ + */ wimlib_assert(old_lte->refcnt > inode->link_count); DEBUG("Splitting lookup table entry " "(inode->link_count = %u, old_lte->refcnt = %u)", inode->link_count, old_lte->refcnt); - } new_lte = new_lookup_table_entry(); @@ -507,7 +564,6 @@ static int extract_resource_to_staging_dir(struct inode *inode, } } - new_lte->resource_entry.original_size = size; new_lte->refcnt = inode->link_count; new_lte->resource_location = RESOURCE_IN_STAGING_FILE; new_lte->staging_file_name = staging_file_name; @@ -548,8 +604,7 @@ out_delete_staging_file: * Creates a randomly named staging directory and saves its name in the * filesystem context structure. */ -static int make_staging_dir(struct wimfs_context *ctx, - const char *user_prefix) +static int make_staging_dir(struct wimfs_context *ctx, const char *user_prefix) { static const size_t random_suffix_len = 10; static const char *common_suffix = ".staging"; @@ -625,14 +680,12 @@ static int remove_file_or_directory(const char *fpath, const struct stat *sb, } } - /* * Deletes the staging directory and all the files contained in it. */ static int delete_staging_dir(struct wimfs_context *ctx) { int ret; - wimlib_assert(ctx->staging_dir_name != NULL); ret = nftw(ctx->staging_dir_name, remove_file_or_directory, 10, FTW_DEPTH); FREE(ctx->staging_dir_name); @@ -640,6 +693,108 @@ static int delete_staging_dir(struct wimfs_context *ctx) return ret; } +static void inode_update_lte_ptr(struct inode *inode, + struct lookup_table_entry *old_lte, + struct lookup_table_entry *new_lte) +{ + if (inode->lte == old_lte) { + inode->lte = new_lte; + } else { + for (unsigned i = 0; i < inode->num_ads; i++) { + if (inode->ads_entries[i].lte == old_lte) { + inode->ads_entries[i].lte = new_lte; + break; + } + } + } +} + +static int update_lte_of_staging_file(struct lookup_table_entry *lte, + struct lookup_table *table) +{ + struct lookup_table_entry *duplicate_lte; + int ret; + u8 hash[SHA1_HASH_SIZE]; + struct stat stbuf; + + ret = sha1sum(lte->staging_file_name, hash); + if (ret != 0) + return ret; + lookup_table_unlink(table, lte); + duplicate_lte = __lookup_resource(table, hash); + if (duplicate_lte) { + /* Merge duplicate lookup table entries */ + duplicate_lte->refcnt += lte->refcnt; + inode_update_lte_ptr(lte->lte_inode, lte, duplicate_lte); + free_lookup_table_entry(lte); + } else { + if (stat(lte->staging_file_name, &stbuf) != 0) { + ERROR_WITH_ERRNO("Failed to stat `%s'", lte->staging_file_name); + return WIMLIB_ERR_STAT; + } + if (stbuf.st_size == 0) { + /* Zero-length stream. No lookup table entry needed. */ + inode_update_lte_ptr(lte->lte_inode, lte, NULL); + free_lookup_table_entry(lte); + } else { + wimlib_assert(<e->file_on_disk == <e->staging_file_name); + lte->resource_entry.original_size = stbuf.st_size; + lte->resource_entry.size = stbuf.st_size; + lte->resource_location = RESOURCE_IN_FILE_ON_DISK; + lte->file_on_disk_fp = NULL; + copy_hash(lte->hash, hash); + lookup_table_insert(table, lte); + } + } + return 0; +} + +static int inode_close_fds(struct inode *inode) +{ + u16 num_opened_fds = inode->num_opened_fds; + for (u16 i = 0, j = 0; j < num_opened_fds; i++) { + struct wimlib_fd *fd = inode->fds[i]; + if (fd) { + wimlib_assert(fd->f_inode == inode); + int ret = close_wimlib_fd(fd); + if (ret != 0) + return ret; + j++; + } + } + return 0; +} + +/* Overwrites the WIM file, with changes saved. */ +static int rebuild_wim(struct wimfs_context *ctx, int write_flags, + wimlib_progress_func_t progress_func) +{ + int ret; + struct lookup_table_entry *lte, *tmp; + WIMStruct *w = ctx->wim; + + DEBUG("Closing all staging file descriptors."); + list_for_each_entry_safe(lte, tmp, &ctx->staging_list, staging_list) { + ret = inode_close_fds(lte->lte_inode); + if (ret != 0) + return ret; + } + + DEBUG("Calculating SHA1 checksums for all new staging files."); + list_for_each_entry(lte, &ctx->staging_list, staging_list) { + ret = update_lte_of_staging_file(lte, w->lookup_table); + if (ret != 0) + return ret; + } + + xml_update_image_info(w, w->current_image); + ret = wimlib_overwrite(w, write_flags, 0, progress_func); + if (ret != 0) + ERROR("Failed to commit changes to mounted WIM image"); + return ret; +} + + /* Simple function that returns the concatenation of 2 strings. */ static char *strcat_dup(const char *s1, const char *s2, size_t max_len) @@ -714,57 +869,40 @@ static void free_message_queue_names(struct wimfs_context *ctx) * Opens two POSIX message queue: one for sending messages from the unmount * process to the daemon process, and one to go the other way. The names of the * message queues, which must be system-wide unique, are be based on the mount - * point. (There of course is still a possibility of a collision if one were to - * unmount two identically named directories simultaneously...) + * point. * * @daemon specifies whether the calling process is the filesystem daemon or the * unmount process. */ static int open_message_queues(struct wimfs_context *ctx, bool daemon) { - int flags; - int ret; - - wimlib_assert(ctx->unmount_to_daemon_mq_name != NULL && - ctx->daemon_to_unmount_mq_name != NULL); + int unmount_to_daemon_mq_flags = O_WRONLY | O_CREAT; + int daemon_to_unmount_mq_flags = O_RDONLY | O_CREAT; if (daemon) - flags = O_RDONLY | O_CREAT; - else - flags = O_WRONLY | O_CREAT; + swap(unmount_to_daemon_mq_flags, daemon_to_unmount_mq_flags); DEBUG("Opening message queue \"%s\"", ctx->unmount_to_daemon_mq_name); ctx->unmount_to_daemon_mq = mq_open(ctx->unmount_to_daemon_mq_name, - flags, 0700, NULL); + unmount_to_daemon_mq_flags, 0700, NULL); if (ctx->unmount_to_daemon_mq == (mqd_t)-1) { ERROR_WITH_ERRNO("mq_open()"); - ret = WIMLIB_ERR_MQUEUE; - goto out; + return WIMLIB_ERR_MQUEUE; } - if (daemon) - flags = O_WRONLY | O_CREAT; - else - flags = O_RDONLY | O_CREAT; - DEBUG("Opening message queue \"%s\"", ctx->daemon_to_unmount_mq_name); ctx->daemon_to_unmount_mq = mq_open(ctx->daemon_to_unmount_mq_name, - flags, 0700, NULL); + daemon_to_unmount_mq_flags, 0700, NULL); if (ctx->daemon_to_unmount_mq == (mqd_t)-1) { ERROR_WITH_ERRNO("mq_open()"); - ret = WIMLIB_ERR_MQUEUE; - goto out_close_unmount_to_daemon_mq; + mq_close(ctx->unmount_to_daemon_mq); + mq_unlink(ctx->unmount_to_daemon_mq_name); + ctx->unmount_to_daemon_mq = (mqd_t)-1; + return WIMLIB_ERR_MQUEUE; } - ret = 0; - goto out; -out_close_unmount_to_daemon_mq: - mq_close(ctx->unmount_to_daemon_mq); - mq_unlink(ctx->unmount_to_daemon_mq_name); - ctx->unmount_to_daemon_mq = (mqd_t)-1; -out: - return ret; + return 0; } /* Try to determine the maximum message size of a message queue. The return @@ -832,6 +970,7 @@ static void unlink_message_queues(struct wimfs_context *ctx) /* Closes the message queues, which are allocated in static variables */ static void close_message_queues(struct wimfs_context *ctx) { + DEBUG("Closing message queues"); mq_close(ctx->unmount_to_daemon_mq); ctx->unmount_to_daemon_mq = (mqd_t)(-1); mq_close(ctx->daemon_to_unmount_mq); @@ -839,316 +978,378 @@ static void close_message_queues(struct wimfs_context *ctx) unlink_message_queues(ctx); } -struct unmount_message_header { + +struct unmount_msg_hdr { u32 min_version; u32 cur_version; - u32 data_size; + u32 msg_type; + u32 msg_size; +} PACKED; + +struct msg_unmount_request { + struct unmount_msg_hdr hdr; + u32 unmount_flags; + u8 want_progress_messages; +} PACKED; + +struct msg_daemon_info { + struct unmount_msg_hdr hdr; + pid_t daemon_pid; + u32 mount_flags; +} PACKED; + +struct msg_unmount_finished { + struct unmount_msg_hdr hdr; + int32_t status; +} PACKED; + +struct msg_write_streams_progress { + struct unmount_msg_hdr hdr; + union wimlib_progress_info info; +} PACKED; + +enum { + MSG_TYPE_UNMOUNT_REQUEST, + MSG_TYPE_DAEMON_INFO, + MSG_TYPE_WRITE_STREAMS_PROGRESS, + MSG_TYPE_UNMOUNT_FINISHED, + MSG_TYPE_MAX, }; -struct unmount_request { - struct unmount_message_header hdr; - struct { - u32 unmount_flags; - } data; +struct msg_handler_context { + int timeout_seconds; + union { + struct { + pid_t daemon_pid; + int mount_flags; + int status; + wimlib_progress_func_t progress_func; + } unmount; + struct { + struct wimfs_context *wimfs_ctx; + } daemon; + }; }; -struct unmount_response { - struct unmount_message_header hdr; - struct { - int32_t status; - u32 mount_flags; - } data; -}; +static int send_unmount_request_msg(mqd_t mq, int unmount_flags, + u8 want_progress_messages) +{ + DEBUG("Sending unmount request msg"); + struct msg_unmount_request msg = { + .hdr = { + .min_version = WIMLIB_MAKEVERSION(1, 2, 0), + .cur_version = WIMLIB_VERSION_CODE, + .msg_type = MSG_TYPE_UNMOUNT_REQUEST, + .msg_size = sizeof(msg), + }, + .unmount_flags = unmount_flags, + .want_progress_messages = want_progress_messages, + }; -static int wimfs_access(const char *path, int mask) -{ - /* Permissions not implemented */ + if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) { + ERROR_WITH_ERRNO("Failed to communicate with filesystem daemon"); + return WIMLIB_ERR_MQUEUE; + } return 0; } -static int wimfs_chmod(const char *path, mode_t mask) +static int send_daemon_info_msg(mqd_t mq, pid_t pid, int mount_flags) { - struct dentry *dentry; - struct wimfs_context *ctx = wimfs_get_context(); - struct inode *inode; - struct stat stbuf; - int ret; - - ret = lookup_resource(ctx->wim, path, - get_lookup_flags(ctx) | LOOKUP_FLAG_DIRECTORY_OK, - &dentry, NULL, NULL); - if (ret != 0) - return ret; - inode = dentry->d_inode; - inode_to_stbuf(inode, NULL, &stbuf); - if (mask == stbuf.st_mode) - return 0; - else - return -EPERM; + DEBUG("Sending daemon info msg (pid = %d, mount_flags=%x)", + pid, mount_flags); + struct msg_daemon_info msg = { + .hdr = { + .min_version = WIMLIB_MAKEVERSION(1, 2, 0), + .cur_version = WIMLIB_VERSION_CODE, + .msg_type = MSG_TYPE_DAEMON_INFO, + .msg_size = sizeof(msg), + }, + .daemon_pid = pid, + .mount_flags = mount_flags, + }; + if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) { + ERROR_WITH_ERRNO("Failed to send daemon info to unmount process"); + return WIMLIB_ERR_MQUEUE; + } + return 0; } -static void inode_update_lte_ptr(struct inode *inode, - struct lookup_table_entry *old_lte, - struct lookup_table_entry *new_lte) +static void send_unmount_finished_msg(mqd_t mq, int status) { - if (inode->lte == old_lte) { - inode->lte = new_lte; - } else { - for (unsigned i = 0; i < inode->num_ads; i++) { - if (inode->ads_entries[i].lte == old_lte) { - inode->ads_entries[i].lte = new_lte; - break; - } + DEBUG("Sending unmount finished msg"); + struct msg_unmount_finished msg = { + .hdr = { + .min_version = WIMLIB_MAKEVERSION(1, 2, 0), + .cur_version = WIMLIB_VERSION_CODE, + .msg_type = MSG_TYPE_UNMOUNT_FINISHED, + .msg_size = sizeof(msg), + }, + .status = status, + }; + if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) + ERROR_WITH_ERRNO("Failed to send status to unmount process"); +} + +static int unmount_progress_func(enum wimlib_progress_msg msg, + const union wimlib_progress_info *info) +{ + if (msg == WIMLIB_PROGRESS_MSG_WRITE_STREAMS) { + struct msg_write_streams_progress msg = { + .hdr = { + .min_version = WIMLIB_MAKEVERSION(1, 2, 0), + .cur_version = WIMLIB_VERSION_CODE, + .msg_type = MSG_TYPE_WRITE_STREAMS_PROGRESS, + .msg_size = sizeof(msg), + }, + .info = *info, + }; + if (mq_send(wimfs_get_context()->daemon_to_unmount_mq, + (void*)&msg, sizeof(msg), 1)) + { + ERROR_WITH_ERRNO("Failed to send status to unmount process"); } } + return 0; } -static int update_lte_of_staging_file(struct lookup_table_entry *lte, - struct lookup_table *table) +static int msg_unmount_request_handler(const void *_msg, + struct msg_handler_context *handler_ctx) { - struct lookup_table_entry *duplicate_lte; + const struct msg_unmount_request *msg; + struct wimfs_context *wimfs_ctx; + int status = 0; int ret; - u8 hash[SHA1_HASH_SIZE]; - struct stat stbuf; + int unmount_flags; + wimlib_progress_func_t progress_func; - wimlib_assert(lte->resource_location == RESOURCE_IN_STAGING_FILE); - wimlib_assert(lte->staging_file_name); + DEBUG("Handling unmount request msg"); - ret = sha1sum(lte->staging_file_name, hash); - if (ret != 0) - return ret; + msg = _msg; + wimfs_ctx = handler_ctx->daemon.wimfs_ctx; - lookup_table_unlink(table, lte); + if (msg->hdr.msg_size < sizeof(*msg)) { + status = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; + goto out; + } - duplicate_lte = __lookup_resource(table, hash); + unmount_flags = msg->unmount_flags; + if (msg->want_progress_messages) + progress_func = unmount_progress_func; + else + progress_func = NULL; - if (duplicate_lte) { - /* Merge duplicate lookup table entries */ - duplicate_lte->refcnt += lte->refcnt; - list_del(<e->staging_list); - inode_update_lte_ptr(lte->lte_inode, lte, duplicate_lte); - free_lookup_table_entry(lte); - } else { - if (stat(lte->staging_file_name, &stbuf) != 0) { - ERROR_WITH_ERRNO("Failed to stat `%s'", lte->staging_file_name); - return WIMLIB_ERR_STAT; + ret = send_daemon_info_msg(wimfs_ctx->daemon_to_unmount_mq, getpid(), + wimfs_ctx->mount_flags); + if (ret != 0) { + status = ret; + goto out; + } + + if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) { + int write_flags = 0; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) + write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_REBUILD) + write_flags |= WIMLIB_WRITE_FLAG_REBUILD; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_RECOMPRESS) + write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; + status = rebuild_wim(wimfs_ctx, write_flags, + progress_func); } - if (stbuf.st_size == 0) { - /* Zero-length stream. No lookup table entry needed. */ - inode_update_lte_ptr(lte->lte_inode, lte, NULL); - free_lookup_table_entry(lte); - } else { - wimlib_assert(<e->file_on_disk == <e->staging_file_name); - lte->resource_entry.original_size = stbuf.st_size; - lte->resource_entry.size = stbuf.st_size; - lte->resource_location = RESOURCE_IN_FILE_ON_DISK; - lte->lte_inode = NULL; - copy_hash(lte->hash, hash); - lookup_table_insert(table, lte); + } else { + DEBUG("Read-only mount"); + status = 0; + } + +out: + if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { + ret = delete_staging_dir(wimfs_ctx); + if (ret != 0) { + ERROR("Failed to delete the staging directory"); + if (status == 0) + status = ret; } } + send_unmount_finished_msg(wimfs_ctx->daemon_to_unmount_mq, status); + return MSG_BREAK_LOOP; +} + +static int msg_daemon_info_handler(const void *_msg, + struct msg_handler_context *handler_ctx) +{ + const struct msg_daemon_info *msg = _msg; + DEBUG("Handling daemon info msg"); + if (msg->hdr.msg_size < sizeof(*msg)) + return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; + handler_ctx->unmount.daemon_pid = msg->daemon_pid; + handler_ctx->unmount.mount_flags = msg->mount_flags; + handler_ctx->timeout_seconds = 1; + DEBUG("pid of daemon is %d; mount flags were %#x", + handler_ctx->unmount.daemon_pid, + handler_ctx->unmount.mount_flags); return 0; } -static int inode_close_fds(struct inode *inode) +static int msg_write_streams_progress_handler(const void *_msg, + struct msg_handler_context *handler_ctx) { - u16 num_opened_fds = inode->num_opened_fds; - for (u16 i = 0, j = 0; j < num_opened_fds; i++) { - struct wimlib_fd *fd = inode->fds[i]; - if (fd) { - wimlib_assert(fd->f_inode == inode); - int ret = close_wimlib_fd(fd); - if (ret != 0) - return ret; - j++; - } + const struct msg_write_streams_progress *msg = _msg; + if (msg->hdr.msg_size < sizeof(*msg)) + return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; + if (handler_ctx->unmount.progress_func) { + handler_ctx->unmount.progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, + &msg->info); } return 0; } -/* Overwrites the WIM file, with changes saved. */ -static int rebuild_wim(struct wimfs_context *ctx, int write_flags) +static int msg_unmount_finished_handler(const void *_msg, + struct msg_handler_context *handler_ctx) { - int ret; - struct lookup_table_entry *lte, *tmp; - WIMStruct *w = ctx->wim; - - - DEBUG("Closing all staging file descriptors."); - list_for_each_entry_safe(lte, tmp, &ctx->staging_list, staging_list) { - ret = inode_close_fds(lte->lte_inode); - if (ret != 0) - return ret; - } - - DEBUG("Calculating SHA1 checksums for all new staging files."); - list_for_each_entry_safe(lte, tmp, &ctx->staging_list, staging_list) { - ret = update_lte_of_staging_file(lte, ctx->wim->lookup_table); - if (ret != 0) - return ret; - } - - xml_update_image_info(w, w->current_image); + const struct msg_unmount_finished *msg = _msg; + DEBUG("Handling unmount finished message"); + if (msg->hdr.msg_size < sizeof(*msg)) + return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; + handler_ctx->unmount.status = msg->status; + DEBUG("status is %d", handler_ctx->unmount.status); + return MSG_BREAK_LOOP; +} - ret = wimlib_overwrite(w, write_flags, 0, NULL); - if (ret != 0) { - ERROR("Failed to commit changes"); - return ret; +static int unmount_timed_out_cb(struct msg_handler_context *handler_ctx) +{ + if (handler_ctx->unmount.daemon_pid == 0) { + goto out_crashed; + } else { + kill(handler_ctx->unmount.daemon_pid, 0); + if (errno == ESRCH) { + goto out_crashed; + } else { + DEBUG("Filesystem daemon is still alive... " + "Waiting another %d seconds\n", + handler_ctx->timeout_seconds); + return 0; + } } - return ret; +out_crashed: + ERROR("The filesystem daemon has crashed! Changes to the " + "WIM may not have been commited."); + return WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED; } -/* Send a message to the filesystem daemon saying whether to commit or not. */ -static int send_unmount_request(struct wimfs_context *ctx, int unmount_flags) +static int daemon_timed_out_cb(struct msg_handler_context *handler_ctx) { - int ret; - struct unmount_request msg; + DEBUG("Timed out"); + return WIMLIB_ERR_TIMEOUT; +} - msg.hdr.min_version = WIMLIB_MAKEVERSION(1, 2, 0); - msg.hdr.cur_version = WIMLIB_VERSION_CODE; - msg.hdr.data_size = sizeof(msg.data); - msg.data.unmount_flags = unmount_flags; +typedef int (*msg_handler_t)(const void *_msg, + struct msg_handler_context *handler_ctx); - ret = mq_send(ctx->unmount_to_daemon_mq, (void*)&msg, - sizeof(msg), 1); - if (ret == -1) { - ERROR_WITH_ERRNO("Failed to notify filesystem daemon whether " - "we want to commit changes or not"); - return WIMLIB_ERR_MQUEUE; - } - return 0; -} +struct msg_handler_callbacks { + int (*timed_out)(struct msg_handler_context *); + msg_handler_t msg_handlers[MSG_TYPE_MAX]; +}; + +static const struct msg_handler_callbacks unmount_msg_handler_callbacks = { + .timed_out = unmount_timed_out_cb, + .msg_handlers = { + [MSG_TYPE_DAEMON_INFO] = msg_daemon_info_handler, + [MSG_TYPE_WRITE_STREAMS_PROGRESS] = msg_write_streams_progress_handler, + [MSG_TYPE_UNMOUNT_FINISHED] = msg_unmount_finished_handler, + }, +}; + +static const struct msg_handler_callbacks daemon_msg_handler_callbacks = { + .timed_out = daemon_timed_out_cb, + .msg_handlers = { + [MSG_TYPE_UNMOUNT_REQUEST] = msg_unmount_request_handler, + }, +}; -static int receive_unmount_request(struct wimfs_context *ctx, - int *unmount_flags) +static int receive_message(mqd_t mq, struct msg_handler_context *handler_ctx, + const msg_handler_t msg_handlers[], + long mailbox_size, void *mailbox) { - void *mailbox; - long msgsize; struct timeval now; struct timespec timeout; ssize_t bytes_received; - struct unmount_request *msg; + struct unmount_msg_hdr *hdr; int ret; - ret = get_mailbox(ctx->unmount_to_daemon_mq, - sizeof(struct unmount_request), - &msgsize, &mailbox); - if (ret != 0) - return ret; - - /* Wait at most 3 seconds before giving up and discarding changes. */ gettimeofday(&now, NULL); - timeout.tv_sec = now.tv_sec + 3; + timeout.tv_sec = now.tv_sec + handler_ctx->timeout_seconds; timeout.tv_nsec = now.tv_usec * 1000; - bytes_received = mq_timedreceive(ctx->unmount_to_daemon_mq, mailbox, - msgsize, NULL, &timeout); - msg = mailbox; + bytes_received = mq_timedreceive(mq, mailbox, + mailbox_size, NULL, &timeout); + hdr = mailbox; if (bytes_received == -1) { - ERROR_WITH_ERRNO("mq_timedreceive()"); - ERROR("Not committing."); - ret = WIMLIB_ERR_MQUEUE; - } else if (bytes_received < sizeof(struct unmount_message_header) || - bytes_received != sizeof(struct unmount_message_header) + msg->hdr.data_size) { - ERROR("Received invalid unmount request"); + if (errno == ETIMEDOUT) { + ret = WIMLIB_ERR_TIMEOUT; + } else { + ERROR_WITH_ERRNO("mq_timedreceive()"); + ret = WIMLIB_ERR_MQUEUE; + } + } else if (bytes_received < sizeof(*hdr) || + bytes_received != hdr->msg_size) { ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; - } else if (WIMLIB_VERSION_CODE < msg->hdr.min_version) { - ERROR("Cannot understand the unmount request. " - "Please upgrade wimlib to at least v%d.%d.%d", - WIMLIB_GET_MAJOR_VERSION(msg->hdr.min_version), - WIMLIB_GET_MINOR_VERSION(msg->hdr.min_version), - WIMLIB_GET_PATCH_VERSION(msg->hdr.min_version)); + } else if (WIMLIB_VERSION_CODE < hdr->min_version) { + /*ERROR("Cannot understand the received message. "*/ + /*"Please upgrade wimlib to at least v%d.%d.%d",*/ + /*WIMLIB_GET_MAJOR_VERSION(hdr->min_version),*/ + /*WIMLIB_GET_MINOR_VERSION(hdr->min_version),*/ + /*WIMLIB_GET_PATCH_VERSION(hdr->min_version));*/ + ret = MSG_VERSION_TOO_HIGH; + } else if (hdr->msg_type >= MSG_TYPE_MAX) { + ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; + } else if (msg_handlers[hdr->msg_type] == NULL) { ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; } else { - *unmount_flags = msg->data.unmount_flags; - ret = 0; + ret = msg_handlers[hdr->msg_type](mailbox, handler_ctx); } - FREE(mailbox); return ret; } -static void send_unmount_response(struct wimfs_context *ctx, int status) -{ - struct unmount_response response; - response.hdr.min_version = WIMLIB_MAKEVERSION(1, 2, 0); - response.hdr.cur_version = WIMLIB_VERSION_CODE; - response.hdr.data_size = sizeof(response.data); - response.data.status = status; - response.data.mount_flags = ctx->mount_flags; - if (mq_send(ctx->daemon_to_unmount_mq, (void*)&response, - sizeof(response), 1)) - ERROR_WITH_ERRNO("Failed to send status to unmount process"); -} - -/* Wait for a message from the filesytem daemon indicating whether the - * filesystem was unmounted successfully. This may - * take a long time if a big WIM file needs to be rewritten. - * - * Wait at most 600??? seconds before giving up and returning an error. Either - * it's a really big WIM file, or (more likely) the filesystem daemon has - * crashed or failed for some reason. - * - * XXX come up with some method to determine if the filesystem daemon has really - * crashed or not. - * - * XXX Idea: have mount daemon write its PID into the WIM file header? No, this - * wouldn't work because we know the directory but not the WIM file... - */ -static int receive_unmount_response(struct wimfs_context *ctx) +static int message_loop(mqd_t mq, + const struct msg_handler_callbacks *callbacks, + struct msg_handler_context *handler_ctx) { + static const size_t MAX_MSG_SIZE = 512; long msgsize; void *mailbox; - struct timeval now; - struct timespec timeout; - ssize_t bytes_received; int ret; - struct unmount_response *msg; - ret = get_mailbox(ctx->daemon_to_unmount_mq, - sizeof(struct unmount_response), &msgsize, &mailbox); + DEBUG("Entering message loop"); + + ret = get_mailbox(mq, MAX_MSG_SIZE, &msgsize, &mailbox); if (ret != 0) return ret; - - DEBUG("Waiting for message telling us whether the unmount was " - "successful or not."); - - gettimeofday(&now, NULL); - timeout.tv_sec = now.tv_sec + 600; - timeout.tv_nsec = now.tv_usec * 1000; - bytes_received = mq_timedreceive(ctx->daemon_to_unmount_mq, - mailbox, msgsize, NULL, &timeout); - msg = mailbox; - if (bytes_received == -1) { - if (errno == ETIMEDOUT) { - ERROR("Timed out- probably the filesystem daemon " - "crashed and the WIM was not written " - "successfully."); - ret = WIMLIB_ERR_TIMEOUT; + while (1) { + ret = receive_message(mq, handler_ctx, + callbacks->msg_handlers, + msgsize, mailbox); + if (ret == 0 || ret == MSG_VERSION_TOO_HIGH) { + continue; + } else if (ret == MSG_BREAK_LOOP) { + ret = 0; + break; + } else if (ret == WIMLIB_ERR_TIMEOUT) { + if (callbacks->timed_out) + ret = callbacks->timed_out(handler_ctx); + if (ret == 0) + continue; + else + break; } else { - ERROR_WITH_ERRNO("mq_receive()"); - ret = WIMLIB_ERR_MQUEUE; - } - } else if (bytes_received < sizeof(struct unmount_message_header) || - bytes_received != sizeof(struct unmount_message_header) + msg->hdr.data_size || - WIMLIB_VERSION_CODE < msg->hdr.min_version) { - ERROR("Received invalid unmount response"); - ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; - } else if (WIMLIB_VERSION_CODE < msg->hdr.min_version) { - ERROR("Cannot understand the unmount response. " - "Please upgrade wimlib to at least v%d.%d.%d", - WIMLIB_GET_MAJOR_VERSION(msg->hdr.min_version), - WIMLIB_GET_MINOR_VERSION(msg->hdr.min_version), - WIMLIB_GET_PATCH_VERSION(msg->hdr.min_version)); - ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; - } else { - ret = msg->data.status; - if (ret) { - ERROR("A failure status (%d) was returned " - "by the filesystem daemon", ret); + ERROR_WITH_ERRNO("Error communicating with " + "filesystem daemon"); + break; } } FREE(mailbox); + DEBUG("Exiting message loop"); return ret; } @@ -1224,48 +1425,55 @@ static int execute_fusermount(const char *dir) return 0; } +static int wimfs_access(const char *path, int mask) +{ + /* Permissions not implemented */ + return 0; +} + +static int wimfs_chmod(const char *path, mode_t mask) +{ + struct dentry *dentry; + struct wimfs_context *ctx = wimfs_get_context(); + struct inode *inode; + struct stat stbuf; + int ret; + + ret = lookup_resource(ctx->wim, path, + get_lookup_flags(ctx) | LOOKUP_FLAG_DIRECTORY_OK, + &dentry, NULL, NULL); + if (ret != 0) + return ret; + inode = dentry->d_inode; + inode_to_stbuf(inode, NULL, &stbuf); + if (mask == stbuf.st_mode) + return 0; + else + return -EPERM; +} + /* Called when the filesystem is unmounted. */ static void wimfs_destroy(void *p) { - /* The `imagex unmount' command, which is running in a separate process - * and is executing the wimlib_unmount() function, will send this - * process a `struct unmount_request' through a message queue that - * specifies some unmount options, such as other to commit changes or - * not. */ + struct wimfs_context *wimfs_ctx; - int ret; - int unmount_flags; - struct wimfs_context *ctx; - int status; + wimfs_ctx = wimfs_get_context(); - ctx = wimfs_get_context(); - if (open_message_queues(ctx, true)) + if (open_message_queues(wimfs_ctx, true)) return; - ret = receive_unmount_request(ctx, &unmount_flags); - status = ret; - if (ret == 0) { - if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { - if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) { - int write_flags; - if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) - write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; - else - write_flags = 0; - status = rebuild_wim(ctx, write_flags); - } - ret = delete_staging_dir(ctx); - if (ret != 0) { - ERROR("Failed to delete the staging directory"); - if (status == 0) - status = ret; - } - } else { - DEBUG("Read-only mount"); - } - } - send_unmount_response(ctx, status); - close_message_queues(ctx); + struct msg_handler_context handler_ctx = { + .timeout_seconds = 5, + .daemon = { + .wimfs_ctx = wimfs_ctx, + }, + }; + + message_loop(wimfs_ctx->unmount_to_daemon_mq, + &daemon_msg_handler_callbacks, + &handler_ctx); + + close_message_queues(wimfs_ctx); } #if 0 @@ -1369,12 +1577,16 @@ static int wimfs_link(const char *to, const char *from) struct inode *inode; struct lookup_table_entry *lte; WIMStruct *w = wimfs_get_WIMStruct(); + u16 i; inode = wim_pathname_to_inode(w, to); if (!inode) return -ENOENT; - if (!inode_is_regular_file(inode)) + if (inode->attributes & FILE_ATTRIBUTE_REPARSE_POINT) + return -EEXIST; + + if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) return -EPERM; from_dentry_parent = get_parent_dentry(w, from); @@ -1390,17 +1602,15 @@ static int wimfs_link(const char *to, const char *from) if (!from_dentry) return -ENOMEM; - inode_add_dentry(from_dentry, inode); from_dentry->d_inode = inode; inode->link_count++; - for (unsigned i = 0; i <= inode->num_ads; i++) { + for (i = 0; i <= inode->num_ads; i++) { lte = inode_stream_lte_resolved(inode, i); if (lte) lte->refcnt++; } - dentry_add_child(from_dentry_parent, from_dentry); return 0; } @@ -1411,6 +1621,8 @@ static int wimfs_listxattr(const char *path, char *list, size_t size) size_t needed_size; struct inode *inode; struct wimfs_context *ctx = wimfs_get_context(); + u16 i; + char *p; if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; @@ -1423,12 +1635,12 @@ static int wimfs_listxattr(const char *path, char *list, size_t size) if (size == 0) { needed_size = 0; - for (u16 i = 0; i < inode->num_ads; i++) + for (i = 0; i < inode->num_ads; i++) needed_size += inode->ads_entries[i].stream_name_utf8_len + 6; return needed_size; } else { - char *p = list; - for (u16 i = 0; i < inode->num_ads; i++) { + p = list; + for (i = 0; i < inode->num_ads; i++) { needed_size = inode->ads_entries[i].stream_name_utf8_len + 6; if (needed_size > size) return -ERANGE; @@ -1441,37 +1653,22 @@ static int wimfs_listxattr(const char *path, char *list, size_t size) } #endif -/* - * Create a directory in the WIM. - * @mode is currently ignored. - */ + +/* Create a directory in the WIM. + * @mode is currently ignored. */ static int wimfs_mkdir(const char *path, mode_t mode) { - struct dentry *parent; - struct dentry *newdir; - const char *basename; - struct wimfs_context *ctx = wimfs_get_context(); - - parent = get_parent_dentry(ctx->wim, path); - if (!parent) - return -ENOENT; - - if (!dentry_is_directory(parent)) - return -ENOTDIR; - - basename = path_basename(path); - if (get_dentry_child_with_name(parent, basename)) - return -EEXIST; + struct dentry *dentry; + int ret; - newdir = new_dentry_with_inode(basename); - newdir->d_inode->attributes |= FILE_ATTRIBUTE_DIRECTORY; - newdir->d_inode->resolved = true; - newdir->d_inode->ino = ctx->next_ino++; - dentry_add_child(parent, newdir); - return 0; + ret = create_dentry(wimfs_get_context(), path, &dentry); + if (ret == 0) + dentry->d_inode->attributes = FILE_ATTRIBUTE_DIRECTORY; + return ret; } -/* Creates a regular file. */ +/* Create a regular file in the WIM. + * @mode is currently ignored. */ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev) { const char *stream_name; @@ -1489,39 +1686,25 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev) inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -ENOENT; - if (!inode_is_regular_file(inode)) + if (inode->attributes & + (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) return -ENOENT; if (inode_get_ads_entry(inode, stream_name, NULL)) return -EEXIST; new_entry = inode_add_ads(inode, stream_name); if (!new_entry) return -ENOMEM; + return 0; } else { - struct dentry *dentry, *parent; - const char *basename; + struct dentry *dentry; + int ret; /* Make a normal file (not an alternate data stream) */ - - /* Make sure that the parent of @path exists and is a directory, and - * that the dentry named by @path does not already exist. */ - parent = get_parent_dentry(ctx->wim, path); - if (!parent) - return -ENOENT; - if (!dentry_is_directory(parent)) - return -ENOTDIR; - - basename = path_basename(path); - if (get_dentry_child_with_name(parent, path)) - return -EEXIST; - - dentry = new_dentry_with_inode(basename); - if (!dentry) - return -ENOMEM; - dentry->d_inode->resolved = true; - dentry->d_inode->ino = ctx->next_ino++; - dentry_add_child(parent, dentry); + ret = create_dentry(ctx, path, &dentry); + if (ret == 0) + dentry->d_inode->attributes = FILE_ATTRIBUTE_NORMAL; + return ret; } - return 0; } @@ -1573,8 +1756,9 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi) if (lte && lte->resource_location == RESOURCE_IN_STAGING_FILE) { fd->staging_fd = open(lte->staging_file_name, fi->flags); if (fd->staging_fd == -1) { + int errno_save = errno; close_wimlib_fd(fd); - return -errno; + return -errno_save; } } fi->fh = (uintptr_t)fd; @@ -1608,6 +1792,7 @@ static int wimfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh; + ssize_t ret; if (!fd) return -EBADF; @@ -1621,7 +1806,6 @@ static int wimfs_read(const char *path, char *buf, size_t size, wimlib_assert(fd->f_lte->staging_file_name); wimlib_assert(fd->staging_fd != -1); - ssize_t ret; DEBUG("Seek to offset %zu", offset); if (lseek(fd->staging_fd, offset, SEEK_SET) == -1) @@ -1632,16 +1816,10 @@ static int wimfs_read(const char *path, char *buf, size_t size, return ret; } else { /* Read from WIM */ - - wimlib_assert(fd->f_lte->resource_location == RESOURCE_IN_WIM); - u64 res_size = wim_resource_size(fd->f_lte); - if (offset > res_size) return -EOVERFLOW; - size = min(size, res_size - offset); - if (read_wim_resource(fd->f_lte, (u8*)buf, size, offset, WIMLIB_RESOURCE_FLAG_MULTITHREADED) != 0) @@ -1767,7 +1945,6 @@ static int wimfs_rename(const char *from, const char *to) dst = get_dentry(w, to); - ret = get_names(&file_name_utf16, &file_name_utf8, &file_name_utf16_len, &file_name_utf8_len, path_basename(to)); @@ -1904,39 +2081,23 @@ static int wimfs_setxattr(const char *path, const char *name, static int wimfs_symlink(const char *to, const char *from) { - struct dentry *dentry_parent, *dentry; - const char *link_name; - struct inode *inode; struct wimfs_context *ctx = wimfs_get_context(); + struct dentry *dentry; + int ret; - dentry_parent = get_parent_dentry(ctx->wim, from); - if (!dentry_parent) - return -ENOENT; - if (!dentry_is_directory(dentry_parent)) - return -ENOTDIR; - - link_name = path_basename(from); - - if (get_dentry_child_with_name(dentry_parent, link_name)) - return -EEXIST; - dentry = new_dentry_with_inode(link_name); - if (!dentry) - return -ENOMEM; - inode = dentry->d_inode; - - inode->attributes = FILE_ATTRIBUTE_REPARSE_POINT; - inode->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK; - inode->ino = ctx->next_ino++; - inode->resolved = true; - - if (inode_set_symlink(inode, to, ctx->wim->lookup_table, NULL) != 0) - goto out_free_dentry; - - dentry_add_child(dentry_parent, dentry); - return 0; -out_free_dentry: - free_dentry(dentry); - return -ENOMEM; + ret = create_dentry(ctx, from, &dentry); + if (ret == 0) { + dentry->d_inode->attributes = FILE_ATTRIBUTE_REPARSE_POINT; + dentry->d_inode->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK; + if (inode_set_symlink(dentry->d_inode, to, + ctx->wim->lookup_table, NULL)) + { + unlink_dentry(dentry); + free_dentry(dentry); + ret = -ENOMEM; + } + } + return ret; } @@ -1957,24 +2118,20 @@ static int wimfs_truncate(const char *path, off_t size) if (ret != 0) return ret; - if (!lte) /* Already a zero-length file */ + if (lte == NULL && size == 0) return 0; inode = dentry->d_inode; - if (stream_idx == 0) stream_id = 0; else stream_id = inode->ads_entries[stream_idx - 1].stream_id; if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { - wimlib_assert(lte->staging_file_name); ret = truncate(lte->staging_file_name, size); if (ret != 0) - return -errno; - lte->resource_entry.original_size = size; + ret = -errno; } else { - wimlib_assert(lte->resource_location == RESOURCE_IN_WIM); /* File in WIM. Extract it to the staging directory, but only * the first @size bytes of it. */ ret = extract_resource_to_staging_dir(inode, stream_id, @@ -1983,7 +2140,7 @@ static int wimfs_truncate(const char *path, off_t size) return ret; } -/* Remove a regular file */ +/* Unlink a non-directory or alternate data stream */ static int wimfs_unlink(const char *path) { struct dentry *dentry; @@ -2190,14 +2347,13 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, } ret = select_wim_image(wim, image); - if (ret != 0) goto out; - imd = &wim->image_metadata[image - 1]; - DEBUG("Selected image %d", image); + imd = wim_get_current_image_metadata(wim); + if (imd->root_dentry->refcnt != 1) { ERROR("Cannot mount image that was just exported with " "wimlib_export_image()"); @@ -2213,7 +2369,7 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, } if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { - ret = lock_wim(wim->fp, wim->filename); + ret = lock_wim(wim, wim->fp); if (ret != 0) goto out; } @@ -2228,11 +2384,15 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, init_wimfs_context(&ctx); ctx.wim = wim; ctx.mount_flags = mount_flags; + ctx.image_inode_list = &imd->inode_list; + + if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) + ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK; DEBUG("Unlinking message queues in case they already exist"); ret = set_message_queue_names(&ctx, dir); if (ret != 0) - goto out; + goto out_unlock; unlink_message_queues(&ctx); DEBUG("Preparing arguments to fuse_main()"); @@ -2257,7 +2417,14 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, * assign_inode_numbers() function, and the static variable @next_ino is * set to the next available inode number. */ - char optstring[256] = "use_ino,subtype=wimfs,attr_timeout=0"; + char optstring[256] = + "use_ino" + ",subtype=wimfs" + ",attr_timeout=0" +#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8) + ",hard_remove" +#endif + ; argv[argc++] = "-o"; argv[argc++] = optstring; if ((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) { @@ -2311,6 +2478,8 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, ret = WIMLIB_ERR_FUSE; out_free_dir_copy: FREE(dir_copy); +out_unlock: + wim->wim_locked = 0; out_free_message_queue_names: free_message_queue_names(&ctx); out: @@ -2329,30 +2498,45 @@ WIMLIBAPI int wimlib_unmount_image(const char *dir, int unmount_flags, wimlib_progress_func_t progress_func) { int ret; - struct wimfs_context ctx; + struct wimfs_context wimfs_ctx; - init_wimfs_context(&ctx); + init_wimfs_context(&wimfs_ctx); - ret = set_message_queue_names(&ctx, dir); + ret = set_message_queue_names(&wimfs_ctx, dir); if (ret != 0) goto out; - ret = open_message_queues(&ctx, false); + ret = open_message_queues(&wimfs_ctx, false); if (ret != 0) goto out_free_message_queue_names; - ret = send_unmount_request(&ctx, unmount_flags); + ret = send_unmount_request_msg(wimfs_ctx.unmount_to_daemon_mq, + unmount_flags, + progress_func != NULL); if (ret != 0) goto out_close_message_queues; ret = execute_fusermount(dir); if (ret != 0) goto out_close_message_queues; - ret = receive_unmount_response(&ctx); + + struct msg_handler_context handler_ctx = { + .timeout_seconds = 5, + .unmount = { + .daemon_pid = 0, + .progress_func = progress_func, + }, + }; + + ret = message_loop(wimfs_ctx.daemon_to_unmount_mq, + &unmount_msg_handler_callbacks, + &handler_ctx); + if (ret == 0) + ret = handler_ctx.unmount.status; out_close_message_queues: - close_message_queues(&ctx); + close_message_queues(&wimfs_ctx); out_free_message_queue_names: - free_message_queue_names(&ctx); + free_message_queue_names(&wimfs_ctx); out: return ret; }