X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fmount_image.c;h=ac993e17b2f7f54cc366780832c84b30ee5a865a;hp=41478d92a43dd8566c83c53a3f15bc35ff48c01a;hb=ad8c3f70361e25b7c1bbc46d4429749c7215fa12;hpb=dabcd6fe9a32f58709c513d6bf641773baa8f6aa diff --git a/src/mount_image.c b/src/mount_image.c index 41478d92..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. * @@ -114,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 @@ -176,7 +180,7 @@ flags_writable(int open_flags) * @lte: Lookup table entry for the stream (may be NULL) * @fd_ret: Return the allocated file descriptor if successful. * - * 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, @@ -186,7 +190,6 @@ alloc_wimfs_fd(struct wim_inode *inode, { 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)", @@ -197,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; @@ -219,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); @@ -234,12 +234,9 @@ alloc_wimfs_fd(struct wim_inode *inode, if (lte) lte->num_opened_fds++; DEBUG("Allocated fd (idx = %u)", fd->idx); - ret = 0; - break; + return 0; } } -out: - return ret; } static void @@ -394,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); } @@ -1107,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), @@ -1187,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) { @@ -1221,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; @@ -1962,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 @@ -1996,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; - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); + for_inode_child(child, inode) { + char *file_name_mbs; + size_t file_name_mbs_nbytes; - return for_dentry_in_rbtree(inode->i_children.rb_node, - dentry_fuse_fill, &fill_params); + ret = utf16le_to_tstr(child->file_name, + child->file_name_nbytes, + &file_name_mbs, + &file_name_mbs_nbytes); + if (ret) + return -errno; + + ret = filler(buf, file_name_mbs, NULL, 0); + FREE(file_name_mbs); + if (ret) + return ret; + } + return 0; } @@ -2262,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) @@ -2289,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); @@ -2531,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()"); @@ -2562,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) @@ -2587,7 +2689,8 @@ wimlib_unmount_image(const char *dir, int unmount_flags, WIMLIB_UNMOUNT_FLAG_COMMIT | WIMLIB_UNMOUNT_FLAG_REBUILD | WIMLIB_UNMOUNT_FLAG_RECOMPRESS | - WIMLIB_UNMOUNT_FLAG_LAZY)) + WIMLIB_UNMOUNT_FLAG_LAZY | + WIMLIB_UNMOUNT_FLAG_NEW_IMAGE)) return WIMLIB_ERR_INVALID_PARAM; init_wimfs_context(&wimfs_ctx);