X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fmount_image.c;h=e1ff886f583d89298eacd851e3da8d61bf528dd6;hp=7543b6e424b67770a7d35376b753f3e7b76386a1;hb=b5b9681794d1f5f13350e3567f6f6e74f5c779cf;hpb=7cfb9777313e1a2f60a49d6b9ef87910f27f1a51 diff --git a/src/mount_image.c b/src/mount_image.c index 7543b6e4..e1ff886f 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2012, 2013 Eric Biggers + * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is part of wimlib, a library for working with WIM files. * @@ -41,9 +41,12 @@ #include "wimlib/encoding.h" #include "wimlib/file_io.h" +#include "wimlib/dentry.h" +#include "wimlib/inode.h" #include "wimlib/lookup_table.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" +#include "wimlib/progress.h" #include "wimlib/reparse.h" #include "wimlib/resource.h" #include "wimlib/timestamp.h" @@ -112,6 +115,10 @@ struct wimfs_context { /* List of inodes in the mounted image */ struct list_head *image_inode_list; + /* Original list of streams in the mounted image, linked by + * mount_orig_stream_list. */ + struct list_head orig_stream_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 @@ -152,12 +159,6 @@ wimfs_get_WIMStruct(void) return wimfs_get_context()->wim; } -static inline bool -wimfs_ctx_readonly(const struct wimfs_context *ctx) -{ - return (ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) == 0; -} - static inline int get_lookup_flags(const struct wimfs_context *ctx) { @@ -168,7 +169,8 @@ get_lookup_flags(const struct wimfs_context *ctx) static inline int flags_writable(int open_flags) { - return open_flags & (O_RDWR | O_WRONLY); + int accmode = (open_flags & O_ACCMODE); + return (accmode == O_RDWR || accmode == O_WRONLY); } /* @@ -178,20 +180,17 @@ flags_writable(int open_flags) * @stream_id: ID of the stream we're opening * @lte: Lookup table entry for the stream (may be NULL) * @fd_ret: Return the allocated file descriptor if successful. - * @readonly: True if this is a read-only mount. * - * Return 0 iff successful or error code if unsuccessful. + * Return 0 iff successful or negative error code if unsuccessful. */ static int alloc_wimfs_fd(struct wim_inode *inode, u32 stream_id, struct wim_lookup_table_entry *lte, - struct wimfs_fd **fd_ret, - bool readonly) + struct wimfs_fd **fd_ret) { static const u16 fds_per_alloc = 8; static const u16 max_fds = 0xffff; - int ret; DEBUG("Allocating fd for stream ID %u from inode %#"PRIx64" " "(open = %u, allocated = %u)", @@ -202,20 +201,18 @@ alloc_wimfs_fd(struct wim_inode *inode, struct wimfs_fd **fds; u16 num_new_fds; - if (inode->i_num_allocated_fds == max_fds) { - ret = -EMFILE; - goto out; - } + if (inode->i_num_allocated_fds == max_fds) + return -EMFILE; + num_new_fds = min(fds_per_alloc, max_fds - inode->i_num_allocated_fds); fds = REALLOC(inode->i_fds, (inode->i_num_allocated_fds + num_new_fds) * sizeof(inode->i_fds[0])); - if (!fds) { - ret = -ENOMEM; - goto out; - } + if (!fds) + return -ENOMEM; + memset(&fds[inode->i_num_allocated_fds], 0, num_new_fds * sizeof(fds[0])); inode->i_fds = fds; @@ -224,10 +221,9 @@ alloc_wimfs_fd(struct wim_inode *inode, for (u16 i = 0; ; i++) { if (!inode->i_fds[i]) { struct wimfs_fd *fd = CALLOC(1, sizeof(*fd)); - if (!fd) { - ret = -ENOMEM; - break; - } + if (!fd) + return -ENOMEM; + fd->f_inode = inode; fd->f_lte = lte; filedes_invalidate(&fd->staging_fd); @@ -236,15 +232,12 @@ alloc_wimfs_fd(struct wim_inode *inode, *fd_ret = fd; inode->i_fds[i] = fd; inode->i_num_opened_fds++; - if (lte && !readonly) + if (lte) lte->num_opened_fds++; DEBUG("Allocated fd (idx = %u)", fd->idx); - ret = 0; - break; + return 0; } } -out: - return ret; } static void @@ -329,7 +322,7 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); int ret; - parent = get_parent_dentry(wimfs_ctx->wim, path); + parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE); if (!parent) return -errno; @@ -337,7 +330,7 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, return -ENOTDIR; basename = path_basename(path); - if (get_dentry_child_with_name(parent, basename)) + if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE)) return -EEXIST; ret = new_dentry_with_inode(basename, &new); @@ -349,16 +342,9 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, new->d_inode->i_attributes = attributes; if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) { - if (inode_set_unix_data(new->d_inode, - fuse_ctx->uid, - fuse_ctx->gid, - fuse_mask_mode(mode, fuse_ctx), - wimfs_ctx->wim->lookup_table, - UNIX_DATA_ALL | UNIX_DATA_CREATE)) - { - free_dentry(new); - return -ENOMEM; - } + new->d_inode->i_unix_data.uid = fuse_ctx->uid; + new->d_inode->i_unix_data.gid = fuse_ctx->gid; + new->d_inode->i_unix_data.mode = fuse_mask_mode(mode, fuse_ctx); } dentry_add_child(parent, new); list_add_tail(&new->d_inode->i_list, wimfs_ctx->image_inode_list); @@ -367,44 +353,50 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, return 0; } -/* Remove a dentry from a mounted WIM image; i.e. remove an alias for the - * corresponding inode. - * - * If there are no remaining references to the inode either through dentries or - * open file descriptors, the inode is freed. Otherwise, the inode is not - * removed, but the dentry is unlinked and freed. - * - * Either way, all lookup table entries referenced by the inode have their - * reference count decremented. If a lookup table entry has no open file - * descriptors and no references remaining, it is freed, and the corresponding - * staging file is unlinked. +static struct wim_inode * +wim_pathname_to_inode(WIMStruct *wim, const tchar *path) +{ + struct wim_dentry *dentry; + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); + if (dentry) + return dentry->d_inode; + else + return NULL; +} + +/* + * Remove a dentry from a mounted WIM image; i.e. remove an alias for an inode. */ static void remove_dentry(struct wim_dentry *dentry, struct wim_lookup_table *lookup_table) { - struct wim_inode *inode = dentry->d_inode; - struct wim_lookup_table_entry *lte; - unsigned i; + /* Put a reference to each stream the inode contains. */ + inode_unref_streams(dentry->d_inode, lookup_table); - for (i = 0; i <= inode->i_num_ads; i++) { - lte = inode_stream_lte_resolved(inode, i); - if (lte) - lte_decrement_refcnt(lte, lookup_table); - } + /* Unlink the dentry from the image's dentry tree. */ unlink_dentry(dentry); + + /* Delete the dentry. This will also decrement the link count of the + * corresponding inode. */ free_dentry(dentry); } static mode_t -inode_default_unix_mode(const struct wim_inode *inode) +inode_unix_file_type(const struct wim_inode *inode) { if (inode_is_symlink(inode)) - return S_IFLNK | 0777; + return S_IFLNK; else if (inode_is_directory(inode)) - return S_IFDIR | 0777; + return S_IFDIR; else - return S_IFREG | 0777; + return S_IFREG; +} + +static mode_t +inode_default_unix_mode(const struct wim_inode *inode) +{ + return inode_unix_file_type(inode) | 0777; } /* Transfers file attributes from a struct wim_inode to a `stat' buffer. @@ -420,16 +412,16 @@ inode_to_stbuf(const struct wim_inode *inode, const struct wimfs_context *ctx = wimfs_get_context(); memset(stbuf, 0, sizeof(struct stat)); - stbuf->st_mode = inode_default_unix_mode(inode); - stbuf->st_uid = ctx->default_uid; - stbuf->st_gid = ctx->default_gid; - if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) { - struct wimlib_unix_data unix_data; - if (inode_get_unix_data(inode, &unix_data, NULL) == 0) { - stbuf->st_uid = unix_data.uid; - stbuf->st_gid = unix_data.gid; - stbuf->st_mode = unix_data.mode; - } + if ((ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) && + inode_has_unix_data(inode)) + { + stbuf->st_uid = inode->i_unix_data.uid; + stbuf->st_gid = inode->i_unix_data.gid; + stbuf->st_mode = inode->i_unix_data.mode; + } else { + stbuf->st_uid = ctx->default_uid; + stbuf->st_gid = ctx->default_gid; + stbuf->st_mode = inode_default_unix_mode(inode); } stbuf->st_ino = (ino_t)inode->i_ino; stbuf->st_nlink = inode->i_nlink; @@ -662,13 +654,14 @@ extract_resource_to_staging_dir(struct wim_inode *inode, } } + lte_put_resource(new_lte); new_lte->refcnt = inode->i_nlink; new_lte->resource_location = RESOURCE_IN_STAGING_FILE; new_lte->staging_file_name = staging_file_name; new_lte->size = size; - lookup_table_insert_unhashed(ctx->wim->lookup_table, new_lte, - inode, stream_id); + add_unhashed_stream(new_lte, inode, stream_id, + &wim_get_current_image_metadata(ctx->wim)->unhashed_streams); *retrieve_lte_pointer(new_lte) = new_lte; *lte = new_lte; return 0; @@ -806,8 +799,7 @@ inode_close_fds(struct wim_inode *inode) /* Overwrites the WIM file, with changes saved. */ static int -rebuild_wim(struct wimfs_context *ctx, int write_flags, - wimlib_progress_func_t progress_func) +rebuild_wim(struct wimfs_context *ctx, int write_flags) { int ret; struct wim_lookup_table_entry *lte, *tmp; @@ -834,7 +826,7 @@ rebuild_wim(struct wimfs_context *ctx, int write_flags, } xml_update_image_info(wim, wim->current_image); - ret = wimlib_overwrite(wim, write_flags, 0, progress_func); + ret = wimlib_overwrite(wim, write_flags, 0); if (ret) ERROR("Failed to commit changes to mounted WIM image"); return ret; @@ -1086,7 +1078,8 @@ struct unmount_msg_handler_context { pid_t daemon_pid; int mount_flags; int status; - wimlib_progress_func_t progress_func; + wimlib_progress_func_t progfunc; + void *progctx; }; struct daemon_msg_handler_context { @@ -1100,7 +1093,9 @@ 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, 1), + .min_version = ((unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) ? + WIMLIB_MAKEVERSION(1, 6, 2) : + WIMLIB_MAKEVERSION(1, 2, 1)), .cur_version = WIMLIB_VERSION_CODE, .msg_type = MSG_TYPE_UNMOUNT_REQUEST, .msg_size = sizeof(msg), @@ -1156,9 +1151,9 @@ send_unmount_finished_msg(mqd_t mq, int status) ERROR_WITH_ERRNO("Failed to send status to unmount process"); } -static int +static enum wimlib_progress_status unmount_progress_func(enum wimlib_progress_msg msg, - const union wimlib_progress_info *info) + union wimlib_progress_info *info, void *_ignored_context) { if (msg == WIMLIB_PROGRESS_MSG_WRITE_STREAMS) { struct msg_write_streams_progress msg = { @@ -1177,7 +1172,81 @@ unmount_progress_func(enum wimlib_progress_msg msg, "to unmount process"); } } + return WIMLIB_PROGRESS_STATUS_CONTINUE; +} + +static void +release_extra_refcnts(struct wimfs_context *ctx) +{ + struct list_head *list = &ctx->orig_stream_list; + struct wim_lookup_table *lookup_table = ctx->wim->lookup_table; + struct wim_lookup_table_entry *lte, *tmp; + + list_for_each_entry_safe(lte, tmp, list, orig_stream_list) { + u32 n = lte->out_refcnt; + while (n--) + lte_decrement_refcnt(lte, lookup_table); + } +} + +/* Moves the currently selected image, which may have been modified, to a new + * index, and sets the original index to refer to a reset (unmodified) copy of + * the image. */ +static int +renew_current_image(struct wimfs_context *ctx) +{ + WIMStruct *wim = ctx->wim; + int ret; + int idx = wim->current_image - 1; + struct wim_image_metadata *imd = wim->image_metadata[idx]; + struct wim_image_metadata *replace_imd; + struct wim_lookup_table_entry *new_lte; + + if (imd->metadata_lte->resource_location != RESOURCE_IN_WIM) { + ERROR("Can't reset modified image that doesn't yet " + "exist in the on-disk WIM file!"); + return WIMLIB_ERR_METADATA_NOT_FOUND; + } + + /* Create 'replace_imd' structure to use for the reset original, + * unmodified image. */ + replace_imd = new_image_metadata(); + if (!replace_imd) + return WIMLIB_ERR_NOMEM; + + /* Create new stream reference for the modified image's metadata + * resource, which doesn't exist yet. */ + ret = WIMLIB_ERR_NOMEM; + new_lte = new_lookup_table_entry(); + if (!new_lte) + goto err_put_replace_imd; + new_lte->flags = WIM_RESHDR_FLAG_METADATA; + new_lte->unhashed = 1; + + /* Make the image being moved available at a new index. Increments the + * WIM's image count, but does not increment the reference count of the + * 'struct image_metadata'. */ + ret = append_image_metadata(wim, imd); + if (ret) + goto err_free_new_lte; + + ret = xml_add_image(wim, T("")); + if (ret) + goto err_undo_append; + + replace_imd->metadata_lte = imd->metadata_lte; + imd->metadata_lte = new_lte; + wim->image_metadata[idx] = replace_imd; + wim->current_image = wim->hdr.image_count; return 0; + +err_undo_append: + wim->hdr.image_count--; +err_free_new_lte: + free_lookup_table_entry(new_lte); +err_put_replace_imd: + put_image_metadata(replace_imd, NULL); + return ret; } static int @@ -1189,7 +1258,6 @@ msg_unmount_request_handler(const void *_msg, void *_handler_ctx) int status = 0; int ret; int unmount_flags; - wimlib_progress_func_t progress_func; DEBUG("Handling unmount request msg"); @@ -1200,10 +1268,11 @@ msg_unmount_request_handler(const void *_msg, void *_handler_ctx) } unmount_flags = msg->unmount_flags; - if (msg->want_progress_messages) - progress_func = unmount_progress_func; - else - progress_func = NULL; + + wimlib_register_progress_function(wimfs_ctx->wim, + (msg->want_progress_messages ? + unmount_progress_func : NULL), + NULL); ret = send_daemon_info_msg(wimfs_ctx->daemon_to_unmount_mq, getpid(), wimfs_ctx->mount_flags); @@ -1214,6 +1283,18 @@ msg_unmount_request_handler(const void *_msg, void *_handler_ctx) if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) { + + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) { + ret = renew_current_image(wimfs_ctx); + if (ret) { + status = ret; + goto out; + } + } else { + release_extra_refcnts(wimfs_ctx); + } + INIT_LIST_HEAD(&wimfs_ctx->orig_stream_list); + int write_flags = 0; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; @@ -1221,8 +1302,7 @@ msg_unmount_request_handler(const void *_msg, void *_handler_ctx) 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); + status = rebuild_wim(wimfs_ctx, write_flags); } } else { DEBUG("Read-only mount"); @@ -1269,11 +1349,10 @@ msg_write_streams_progress_handler(const void *_msg, void *_handler_ctx) if (msg->hdr.msg_size < sizeof(*msg)) return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE; - if (handler_ctx->progress_func) { - handler_ctx->progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, - &msg->info); - } - return 0; + return call_progress(handler_ctx->progfunc, + WIMLIB_PROGRESS_MSG_WRITE_STREAMS, + (union wimlib_progress_info *)&msg->info, + handler_ctx->progctx); } static int @@ -1293,25 +1372,19 @@ msg_unmount_finished_handler(const void *_msg, void *_handler_ctx) static int unmount_timed_out_cb(void *_handler_ctx) { - struct unmount_msg_handler_context *handler_ctx = _handler_ctx; + const struct unmount_msg_handler_context *handler_ctx = _handler_ctx; - if (handler_ctx->daemon_pid == 0) { - goto out_crashed; - } else { - kill(handler_ctx->daemon_pid, 0); - if (errno == ESRCH) { - goto out_crashed; - } else { - DEBUG("Filesystem daemon is still alive... " - "Waiting another %d seconds\n", - handler_ctx->hdr.timeout_seconds); - return 0; - } + if (handler_ctx->daemon_pid == 0 || + (kill(handler_ctx->daemon_pid, 0) != 0 && errno == ESRCH)) + { + ERROR("The filesystem daemon has crashed! Changes to the " + "WIM may not have been commited."); + return WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED; } -out_crashed: - ERROR("The filesystem daemon has crashed! Changes to the " - "WIM may not have been commited."); - return WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED; + + DEBUG("Filesystem daemon is still alive... " + "Waiting another %d seconds", handler_ctx->hdr.timeout_seconds); + return 0; } static int @@ -1521,10 +1594,12 @@ execute_fusermount(const char *dir, bool lazy) "terminate"); return WIMLIB_ERR_FUSERMOUNT; } - if (status != 0) { + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { ERROR("`umount' did not successfully complete"); return WIMLIB_ERR_FUSERMOUNT; } + return 0; } @@ -1532,6 +1607,7 @@ static int wimfs_chmod(const char *path, mode_t mask) { struct wim_dentry *dentry; + struct wim_inode *inode; struct wimfs_context *ctx = wimfs_get_context(); int ret; @@ -1543,16 +1619,21 @@ wimfs_chmod(const char *path, mode_t mask) if (ret) return ret; - ret = inode_set_unix_data(dentry->d_inode, ctx->default_uid, - ctx->default_gid, mask, - ctx->wim->lookup_table, UNIX_DATA_MODE); - return ret ? -ENOMEM : 0; + inode = dentry->d_inode; + + if (!inode_has_unix_data(inode)) { + inode->i_unix_data.uid = ctx->default_uid; + inode->i_unix_data.gid = ctx->default_gid; + } + inode->i_unix_data.mode = mask; + return 0; } static int wimfs_chown(const char *path, uid_t uid, gid_t gid) { struct wim_dentry *dentry; + struct wim_inode *inode; struct wimfs_context *ctx = wimfs_get_context(); int ret; @@ -1564,11 +1645,13 @@ wimfs_chown(const char *path, uid_t uid, gid_t gid) if (ret) return ret; - ret = inode_set_unix_data(dentry->d_inode, uid, gid, - inode_default_unix_mode(dentry->d_inode), - ctx->wim->lookup_table, - UNIX_DATA_UID | UNIX_DATA_GID); - return ret ? -ENOMEM : 0; + inode = dentry->d_inode; + + if (!inode_has_unix_data(inode)) + inode->i_unix_data.mode = inode_default_unix_mode(inode); + inode->i_unix_data.uid = uid; + inode->i_unix_data.gid = gid; + return 0; } /* Called when the filesystem is unmounted. */ @@ -1695,14 +1778,15 @@ wimfs_link(const char *to, const char *from) FILE_ATTRIBUTE_REPARSE_POINT)) return -EPERM; - from_dentry_parent = get_parent_dentry(wim, from); + from_dentry_parent = get_parent_dentry(wim, from, WIMLIB_CASE_SENSITIVE); if (!from_dentry_parent) return -errno; if (!dentry_is_directory(from_dentry_parent)) return -ENOTDIR; link_name = path_basename(from); - if (get_dentry_child_with_name(from_dentry_parent, link_name)) + if (get_dentry_child_with_name(from_dentry_parent, link_name, + WIMLIB_CASE_SENSITIVE)) return -EEXIST; ret = new_dentry(link_name, &from_dentry); @@ -1864,8 +1948,7 @@ wimfs_open(const char *path, struct fuse_file_info *fi) *back_ptr = lte; } - ret = alloc_wimfs_fd(inode, stream_id, lte, &fd, - wimfs_ctx_readonly(ctx)); + ret = alloc_wimfs_fd(inode, stream_id, lte, &fd); if (ret) return ret; @@ -1899,7 +1982,7 @@ wimfs_opendir(const char *path, struct fuse_file_info *fi) return -errno; if (!inode_is_directory(inode)) return -ENOTDIR; - ret = alloc_wimfs_fd(inode, 0, NULL, &fd, wimfs_ctx_readonly(ctx)); + ret = alloc_wimfs_fd(inode, 0, NULL, &fd); fi->fh = (uintptr_t)fd; return ret; } @@ -1943,7 +2026,7 @@ wimfs_read(const char *path, char *buf, size_t size, case RESOURCE_IN_WIM: if (read_partial_wim_stream_into_buf(fd->f_lte, size, offset, buf)) - ret = -errno; + ret = errno ? -errno : -EIO; else ret = size; break; @@ -1959,32 +2042,6 @@ wimfs_read(const char *path, char *buf, size_t size, return ret; } -struct fill_params { - void *buf; - fuse_fill_dir_t filler; -}; - -static int -dentry_fuse_fill(struct wim_dentry *dentry, void *arg) -{ - struct fill_params *fill_params = arg; - - char *file_name_mbs; - size_t file_name_mbs_nbytes; - int ret; - - ret = utf16le_to_tstr(dentry->file_name, - dentry->file_name_nbytes, - &file_name_mbs, - &file_name_mbs_nbytes); - if (ret) - return -errno; - - ret = fill_params->filler(fill_params->buf, file_name_mbs, NULL, 0); - FREE(file_name_mbs); - return ret; -} - /* Fills in the entries of the directory specified by @path using the * FUSE-provided function @filler. */ static int @@ -1993,22 +2050,38 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, { struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh; struct wim_inode *inode; + struct wim_dentry *child; + int ret; if (!fd) return -EBADF; inode = fd->f_inode; - struct fill_params fill_params = { - .buf = buf, - .filler = filler, - }; + ret = filler(buf, ".", NULL, 0); + if (ret) + return ret; + ret = filler(buf, "..", NULL, 0); + if (ret) + return ret; - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); + for_inode_child(child, inode) { + char *file_name_mbs; + size_t file_name_mbs_nbytes; + + ret = utf16le_to_tstr(child->file_name, + child->file_name_nbytes, + &file_name_mbs, + &file_name_mbs_nbytes); + if (ret) + return -errno; - return for_dentry_in_rbtree(inode->i_children.rb_node, - dentry_fuse_fill, &fill_params); + ret = filler(buf, file_name_mbs, NULL, 0); + FREE(file_name_mbs); + if (ret) + return ret; + } + return 0; } @@ -2023,7 +2096,7 @@ wimfs_readlink(const char *path, char *buf, size_t buf_len) if (!inode_is_symlink(inode)) return -EINVAL; if (buf_len == 0) - return -ENAMETOOLONG; + return -EINVAL; ret = wim_inode_readlink(inode, buf, buf_len - 1, NULL); if (ret >= 0) { wimlib_assert(ret <= buf_len - 1); @@ -2084,7 +2157,8 @@ wimfs_removexattr(const char *path, const char *name) static int wimfs_rename(const char *from, const char *to) { - return rename_wim_path(wimfs_get_WIMStruct(), from, to); + return rename_wim_path(wimfs_get_WIMStruct(), from, to, + WIMLIB_CASE_SENSITIVE, NULL); } /* Remove a directory */ @@ -2094,7 +2168,7 @@ wimfs_rmdir(const char *path) struct wim_dentry *dentry; WIMStruct *wim = wimfs_get_WIMStruct(); - dentry = get_dentry(wim, path); + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (!dentry) return -errno; @@ -2258,14 +2332,12 @@ wimfs_unlink(const char *path) static int wimfs_utimens(const char *path, const struct timespec tv[2]) { - struct wim_dentry *dentry; struct wim_inode *inode; WIMStruct *wim = wimfs_get_WIMStruct(); - dentry = get_dentry(wim, path); - if (!dentry) + inode = wim_pathname_to_inode(wim, path); + if (!inode) return -errno; - inode = dentry->d_inode; if (tv[0].tv_nsec != UTIME_OMIT) { if (tv[0].tv_nsec == UTIME_NOW) @@ -2285,14 +2357,12 @@ wimfs_utimens(const char *path, const struct timespec tv[2]) static int wimfs_utime(const char *path, struct utimbuf *times) { - struct wim_dentry *dentry; struct wim_inode *inode; WIMStruct *wim = wimfs_get_WIMStruct(); - dentry = get_dentry(wim, path); - if (!dentry) + inode = wim_pathname_to_inode(wim, path); + if (!inode) return -errno; - inode = dentry->d_inode; inode->i_last_write_time = unix_timestamp_to_wim(times->modtime); inode->i_last_access_time = unix_timestamp_to_wim(times->actime); @@ -2408,6 +2478,15 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, if (!wim || !dir) return WIMLIB_ERR_INVALID_PARAM; + if (mount_flags & ~(WIMLIB_MOUNT_FLAG_READWRITE | + WIMLIB_MOUNT_FLAG_DEBUG | + WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE | + WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR | + WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS | + WIMLIB_MOUNT_FLAG_UNIX_DATA | + WIMLIB_MOUNT_FLAG_ALLOW_OTHER)) + return WIMLIB_ERR_INVALID_PARAM; + if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { ret = can_delete_from_wim(wim); if (ret) @@ -2422,15 +2501,12 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, imd = wim_get_current_image_metadata(wim); - if (imd->refcnt != 1) { - ERROR("Cannot mount image that was just exported with " - "wimlib_export_image()"); - return WIMLIB_ERR_INVALID_PARAM; - } - if (imd->modified) { - ERROR("Cannot mount image that was added " - "with wimlib_add_image()"); + /* wimfs_read() only supports a limited number of stream + * locations, not including RESOURCE_IN_FILE_ON_DISK, + * RESOURCE_IN_NTFS_VOLUME, etc. that might appear if files were + * added to the WIM image. */ + ERROR("Cannot mount an image with newly added files!"); return WIMLIB_ERR_INVALID_PARAM; } @@ -2454,7 +2530,6 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, ctx.default_uid = getuid(); ctx.default_gid = getgid(); wimlib_assert(list_empty(&imd->unhashed_streams)); - ctx.wim->lookup_table->unhashed_streams = &imd->unhashed_streams; if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK; @@ -2467,8 +2542,10 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, DEBUG("Preparing arguments to fuse_main()"); dir_copy = STRDUP(dir); - if (!dir_copy) + if (!dir_copy) { + ret = WIMLIB_ERR_NOMEM; goto out_free_message_queue_names; + } argc = 0; argv[argc++] = "wimlib"; @@ -2520,16 +2597,45 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, } #endif - /* Mark dentry tree as modified if read-write mount. */ - if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) + /* Assign inode numbers. Also, if a read-write mount was requested, + * mark the dentry tree as modified, and 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. */ + ctx.next_ino = 1; + INIT_LIST_HEAD(&ctx.orig_stream_list); + if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { imd->modified = 1; + image_for_each_inode(inode, imd) { + inode->i_ino = ctx.next_ino++; + for (unsigned i = 0; i <= inode->i_num_ads; i++) { + struct wim_lookup_table_entry *lte; + + lte = inode_stream_lte(inode, i, wim->lookup_table); + if (lte) + lte->out_refcnt = 0; + } + } + image_for_each_inode(inode, imd) { + for (unsigned i = 0; i <= inode->i_num_ads; i++) { + struct wim_lookup_table_entry *lte; + + lte = inode_stream_lte(inode, i, + wim->lookup_table); + if (lte) { + if (lte->out_refcnt == 0) + list_add(<e->orig_stream_list, + &ctx.orig_stream_list); + lte->out_refcnt += inode->i_nlink; + lte->refcnt += inode->i_nlink; + } + } + } + } else { + image_for_each_inode(inode, imd) + inode->i_ino = ctx.next_ino++; + } - /* Resolve the lookup table entries for every inode in the image, and - * assign inode numbers */ - DEBUG("Resolving lookup table entries and assigning inode numbers"); - ctx.next_ino = 1; - image_for_each_inode(inode, imd) - inode->i_ino = ctx.next_ino++; DEBUG("(next_ino = %"PRIu64")", ctx.next_ino); DEBUG("Calling fuse_main()"); @@ -2551,6 +2657,8 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, close_message_queues(&ctx); } + release_extra_refcnts(&ctx); + /* Try to delete the staging directory if a deletion wasn't yet * attempted due to an earlier error */ if (ctx.staging_dir_name) @@ -2566,12 +2674,20 @@ out_free_message_queue_names: /* API function documented in wimlib.h */ WIMLIBAPI int -wimlib_unmount_image(const char *dir, int unmount_flags, - wimlib_progress_func_t progress_func) +wimlib_unmount_image_with_progress(const tchar *dir, int unmount_flags, + wimlib_progress_func_t progfunc, void *progctx) { int ret; struct wimfs_context wimfs_ctx; + if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY | + WIMLIB_UNMOUNT_FLAG_COMMIT | + WIMLIB_UNMOUNT_FLAG_REBUILD | + WIMLIB_UNMOUNT_FLAG_RECOMPRESS | + WIMLIB_UNMOUNT_FLAG_LAZY | + WIMLIB_UNMOUNT_FLAG_NEW_IMAGE)) + return WIMLIB_ERR_INVALID_PARAM; + init_wimfs_context(&wimfs_ctx); ret = set_message_queue_names(&wimfs_ctx, dir); @@ -2584,7 +2700,7 @@ wimlib_unmount_image(const char *dir, int unmount_flags, ret = send_unmount_request_msg(wimfs_ctx.unmount_to_daemon_mq, unmount_flags, - progress_func != NULL); + progfunc != NULL); if (ret != 0) goto out_close_message_queues; @@ -2597,7 +2713,8 @@ wimlib_unmount_image(const char *dir, int unmount_flags, .timeout_seconds = 5, }, .daemon_pid = 0, - .progress_func = progress_func, + .progfunc = progfunc, + .progctx = progctx, }; ret = message_loop(wimfs_ctx.daemon_to_unmount_mq, @@ -2629,8 +2746,8 @@ mount_unsupported_error(void) } WIMLIBAPI int -wimlib_unmount_image(const tchar *dir, int unmount_flags, - wimlib_progress_func_t progress_func) +wimlib_unmount_image_with_progress(const tchar *dir, int unmount_flags, + wimlib_progress_func_t progfunc, void *progctx) { return mount_unsupported_error(); } @@ -2643,3 +2760,10 @@ wimlib_mount_image(WIMStruct *wim, int image, const tchar *dir, } #endif /* !WITH_FUSE */ + + +WIMLIBAPI int +wimlib_unmount_image(const tchar *dir, int unmount_flags) +{ + return wimlib_unmount_image_with_progress(dir, unmount_flags, NULL, NULL); +}