X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fmount_image.c;h=ac993e17b2f7f54cc366780832c84b30ee5a865a;hp=02a863c9b6ac55b47e09e01080bad6f085eaaefc;hb=cd433ce49f582175063141cc3673840bf321c453;hpb=1fc939b7bd0b37900d974b1cd5b11df128df71f5 diff --git a/src/mount_image.c b/src/mount_image.c index 02a863c9..ac993e17 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,6 +41,8 @@ #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" @@ -112,6 +114,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 +158,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 +168,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 +179,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 +200,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 +220,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 +231,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 @@ -399,7 +391,7 @@ remove_dentry(struct wim_dentry *dentry, unsigned i; for (i = 0; i <= inode->i_num_ads; i++) { - lte = inode_stream_lte_resolved(inode, i); + lte = inode_stream_lte(inode, i, lookup_table); if (lte) lte_decrement_refcnt(lte, lookup_table); } @@ -673,13 +665,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; @@ -1111,7 +1104,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), @@ -1191,6 +1186,80 @@ unmount_progress_func(enum wimlib_progress_msg msg, return 0; } +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 msg_unmount_request_handler(const void *_msg, void *_handler_ctx) { @@ -1225,6 +1294,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; @@ -1304,25 +1385,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 @@ -1532,10 +1607,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; } @@ -1876,8 +1953,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; @@ -1911,7 +1987,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; } @@ -1971,32 +2047,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 @@ -2005,22 +2055,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; + + for_inode_child(child, inode) { + char *file_name_mbs; + size_t file_name_mbs_nbytes; - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); + 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; } @@ -2035,7 +2101,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); @@ -2271,14 +2337,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, WIMLIB_CASE_SENSITIVE); - 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) @@ -2298,14 +2362,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, WIMLIB_CASE_SENSITIVE); - 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); @@ -2421,6 +2483,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) @@ -2435,15 +2506,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; } @@ -2467,7 +2535,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; @@ -2480,8 +2547,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"; @@ -2533,16 +2602,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()"); @@ -2564,6 +2662,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) @@ -2585,6 +2685,14 @@ wimlib_unmount_image(const char *dir, int unmount_flags, 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);