X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fmount_image.c;h=3641b204c3aec966d938b57d86517f181c5d9589;hp=cc1bb5f9efe6d68935dadfe8f79b4e566d2f2147;hb=139f42ea23511d81a44e8925a24f7f218318010c;hpb=2fc33f535a398ea85964c0e483c5692821d775f3 diff --git a/src/mount_image.c b/src/mount_image.c index cc1bb5f9..3641b204 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers + * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -39,7 +39,8 @@ #define FUSE_USE_VERSION 26 -#include +#include /* sometimes required before */ +#include #include #include #include @@ -50,7 +51,6 @@ #include #include #include -#include #include #include @@ -70,6 +70,10 @@ # define O_NOFOLLOW 0 /* Security only... */ #endif +#ifndef ENOATTR +# define ENOATTR ENODATA +#endif + #define WIMFS_MQUEUE_NAME_LEN 32 #define WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS 0x80000000 @@ -144,6 +148,11 @@ struct wimfs_context { uid_t owner_uid; gid_t owner_gid; + /* Absolute path to the mountpoint directory (may be needed for absolute + * symbolic link fixups) */ + char *mountpoint_abspath; + size_t mountpoint_abspath_nchars; + /* Information about the staging directory for a read-write mount. */ int parent_dir_fd; int staging_dir_fd; @@ -158,9 +167,9 @@ struct wimfs_context { /* Number of file descriptors open to the mounted WIM image. */ unsigned long num_open_fds; - /* Original list of blobs in the mounted image, linked by - * 'struct blob_descriptor'.orig_blob_list. */ - struct list_head orig_blob_list; + /* For read-write mounts, the original metadata resource of the mounted + * image. */ + struct blob_descriptor *metadata_resource; /* Parameters for unmounting the image (can be set via extended * attribute "wimfs.unmount_info"). */ @@ -442,14 +451,12 @@ wim_pathname_to_stream(const struct wimfs_context *ctx, * The path at which to create the first link to the new file. If a file * already exists at this path, -EEXIST is returned. * @mode - * The UNIX mode for the new file. This is only honored if + * The UNIX mode for the new file. This is only fully honored if * WIMLIB_MOUNT_FLAG_UNIX_DATA was passed to wimlib_mount_image(). * @rdev * The device ID for the new file, encoding the major and minor device * numbers. This is only honored if WIMLIB_MOUNT_FLAG_UNIX_DATA was passed * to wimlib_mount_image(). - * @attributes - * Windows file attributes to use for the new file. * @dentry_ret * On success, a pointer to the new dentry is returned here. Its d_inode * member will point to the new inode that was created for it and added to @@ -459,14 +466,13 @@ wim_pathname_to_stream(const struct wimfs_context *ctx, */ static int create_file(struct fuse_context *fuse_ctx, const char *path, - mode_t mode, dev_t rdev, u32 attributes, - struct wim_dentry **dentry_ret) + mode_t mode, dev_t rdev, struct wim_dentry **dentry_ret) { struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); struct wim_dentry *parent; const char *basename; - struct wim_dentry *new_dentry; - struct wim_inode *new_inode; + struct wim_dentry *dentry; + struct wim_inode *inode; parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE); if (!parent) @@ -480,13 +486,19 @@ create_file(struct fuse_context *fuse_ctx, const char *path, if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE)) return -EEXIST; - if (new_dentry_with_new_inode(basename, true, &new_dentry)) + if (new_dentry_with_new_inode(basename, true, &dentry)) return -ENOMEM; - new_inode = new_dentry->d_inode; + inode = dentry->d_inode; + + inode->i_ino = wimfs_ctx->next_ino++; - new_inode->i_ino = wimfs_ctx->next_ino++; - new_inode->i_attributes = attributes; + /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes, named + * pipes, and sockets. The real mode is in the UNIX metadata. */ + if (S_ISDIR(mode)) + inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; + else + inode->i_attributes = FILE_ATTRIBUTE_NORMAL; if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) { struct wimlib_unix_data unix_data; @@ -495,19 +507,19 @@ create_file(struct fuse_context *fuse_ctx, const char *path, unix_data.gid = fuse_ctx->gid; unix_data.mode = fuse_mask_mode(mode, fuse_ctx); unix_data.rdev = rdev; - if (!inode_set_unix_data(new_inode, &unix_data, UNIX_DATA_ALL)) + if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_ALL)) { - free_dentry(new_dentry); + free_dentry(dentry); return -ENOMEM; } } - hlist_add_head(&new_inode->i_hlist, + hlist_add_head(&inode->i_hlist_node, &wim_get_current_image_metadata(wimfs_ctx->wim)->inode_list); - dentry_add_child(parent, new_dentry); + dentry_add_child(parent, dentry); - *dentry_ret = new_dentry; + *dentry_ret = dentry; return 0; } @@ -716,7 +728,7 @@ extract_blob_to_staging_dir(struct wim_inode *inode, filedes_init(&fd, staging_fd); errno = 0; extract_size = min(old_blob->size, size); - result = extract_blob_to_fd(old_blob, &fd, extract_size); + result = extract_blob_prefix_to_fd(old_blob, extract_size, &fd); } else { extract_size = 0; result = 0; @@ -939,28 +951,20 @@ delete_staging_dir(struct wimfs_context *ctx) close(ctx->parent_dir_fd); } -/* Number the inodes in the mounted image sequentially. */ static void -reassign_inode_numbers(struct wimfs_context *ctx) +prepare_inodes(struct wimfs_context *ctx) { struct wim_image_metadata *imd; struct wim_inode *inode; ctx->next_ino = 1; imd = wim_get_current_image_metadata(ctx->wim); - image_for_each_inode(inode, imd) + image_for_each_inode(inode, imd) { inode->i_ino = ctx->next_ino++; -} - -static void -release_extra_refcnts(struct wimfs_context *ctx) -{ - struct list_head *list = &ctx->orig_blob_list; - struct blob_table *blob_table = ctx->wim->blob_table; - struct blob_descriptor *blob, *tmp; - - list_for_each_entry_safe(blob, tmp, list, orig_blob_list) - blob_subtract_refcnt(blob, blob_table, blob->out_refcnt); + inode->i_num_opened_fds = 0; + inode->i_num_allocated_fds = 0; + inode->i_fds = NULL; + } } /* Delete the 'struct blob_descriptor' for any stream that was modified @@ -1018,53 +1022,56 @@ static int renew_current_image(struct wimfs_context *ctx) { WIMStruct *wim = ctx->wim; - int idx = wim->current_image - 1; - struct wim_image_metadata *imd = wim->image_metadata[idx]; - struct wim_image_metadata *replace_imd; - struct blob_descriptor *new_blob; + int image = wim->current_image; + struct wim_image_metadata *imd; + struct wim_inode *inode; int ret; - /* Create 'replace_imd' structure to use for the reset original, - * unmodified image. */ ret = WIMLIB_ERR_NOMEM; - replace_imd = new_image_metadata(); - if (!replace_imd) + imd = new_unloaded_image_metadata(ctx->metadata_resource); + if (!imd) goto err; - /* Create new blob descriptor for the modified image's metadata - * resource, which doesn't exist yet. */ - ret = WIMLIB_ERR_NOMEM; - new_blob = new_blob_descriptor(); - if (!new_blob) - goto err_put_replace_imd; - - new_blob->refcnt = 1; - new_blob->unhashed = 1; - new_blob->is_metadata = 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); + ret = append_image_metadata(wim, wim->image_metadata[image - 1]); if (ret) - goto err_free_new_blob; + goto err_put_imd; - ret = xml_add_image(wim, ""); + ret = xml_export_image(wim->xml_info, image, + wim->xml_info, NULL, NULL, false); if (ret) goto err_undo_append; - replace_imd->metadata_blob = imd->metadata_blob; - imd->metadata_blob = new_blob; - wim->image_metadata[idx] = replace_imd; + wim->image_metadata[image - 1] = imd; wim->current_image = wim->hdr.image_count; + + ret = select_wim_image(wim, image); + if (ret) + goto err_undo_export; + + image_for_each_inode(inode, imd) { + for (unsigned i = 0; i < inode->i_num_streams; i++) { + struct blob_descriptor *blob; + + blob = stream_blob(&inode->i_streams[i], + wim->blob_table); + if (blob) + blob->refcnt += inode->i_nlink; + } + } + + select_wim_image(wim, wim->hdr.image_count); + ctx->metadata_resource = NULL; return 0; +err_undo_export: + xml_delete_image(wim->xml_info, wim->hdr.image_count); + wim->image_metadata[image - 1] = wim->image_metadata[wim->hdr.image_count - 1]; + wim->current_image = image; err_undo_append: wim->hdr.image_count--; -err_free_new_blob: - free_blob_descriptor(new_blob); -err_put_replace_imd: - put_image_metadata(replace_imd, NULL); +err_put_imd: + imd->metadata_blob = NULL; + put_image_metadata(imd); err: return ret; } @@ -1100,12 +1107,8 @@ commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq) int ret = renew_current_image(ctx); if (ret) return ret; - } else { - release_extra_refcnts(ctx); } - INIT_LIST_HEAD(&ctx->orig_blob_list); delete_empty_blobs(ctx); - xml_update_image_info(ctx->wim, ctx->wim->current_image); write_flags = 0; @@ -1307,9 +1310,9 @@ wimfs_getxattr(const char *path, const char *name, char *value, size_t size) { const struct wimfs_context *ctx = wimfs_get_context(); - struct wim_inode *inode; - struct wim_inode_stream *strm; - struct blob_descriptor *blob; + const struct wim_inode *inode; + const struct wim_inode_stream *strm; + const struct blob_descriptor *blob; if (!strncmp(name, "wimfs.", 6)) { /* Handle some magical extended attributes. These really should @@ -1382,7 +1385,7 @@ wimfs_getxattr(const char *path, const char *name, char *value, if (size < blob->size) return -ERANGE; - if (read_full_blob_into_buf(blob, value)) + if (read_blob_into_buf(blob, value)) return errno ? -errno : -EIO; } return blob->size; @@ -1487,8 +1490,7 @@ wimfs_mkdir(const char *path, mode_t mode) int ret; /* Note: according to fuse.h, mode may not include S_IFDIR */ - ret = create_file(fuse_get_context(), path, mode | S_IFDIR, 0, - FILE_ATTRIBUTE_DIRECTORY, &dentry); + ret = create_file(fuse_get_context(), path, mode | S_IFDIR, 0, &dentry); if (ret) return ret; touch_parent(dentry); @@ -1550,11 +1552,7 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev) !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA)) return -EPERM; - /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes, - * named pipes, and sockets. The real mode is in the UNIX - * metadata. */ - ret = create_file(fuse_ctx, path, mode, rdev, - FILE_ATTRIBUTE_NORMAL, &dentry); + ret = create_file(fuse_ctx, path, mode, rdev, &dentry); if (ret) return ret; touch_parent(dentry); @@ -1665,13 +1663,13 @@ wimfs_read(const char *path, char *buf, size_t size, switch (blob->blob_location) { case BLOB_IN_WIM: - if (read_partial_wim_blob_into_buf(blob, size, offset, buf)) + if (read_partial_wim_blob_into_buf(blob, offset, size, buf)) ret = errno ? -errno : -EIO; else ret = size; break; case BLOB_IN_STAGING_FILE: - ret = raw_pread(&fd->f_staging_fd, buf, size, offset); + ret = pread(fd->f_staging_fd.fd, buf, size, offset); if (ret < 0) ret = -errno; break; @@ -1705,18 +1703,15 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, return ret; for_inode_child(child, inode) { - char *file_name_mbs; - size_t file_name_mbs_nbytes; + char *name; + size_t name_nbytes; - ret = utf16le_to_tstr(child->file_name, - child->file_name_nbytes, - &file_name_mbs, - &file_name_mbs_nbytes); - if (ret) + if (utf16le_to_tstr(child->d_name, child->d_name_nbytes, + &name, &name_nbytes)) return -errno; - ret = filler(buf, file_name_mbs, NULL, 0); - FREE(file_name_mbs); + ret = filler(buf, name, NULL, 0); + FREE(name); if (ret) return ret; } @@ -1724,27 +1719,24 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, } static int -wimfs_readlink(const char *path, char *buf, size_t buf_len) +wimfs_readlink(const char *path, char *buf, size_t bufsize) { - WIMStruct *wim = wimfs_get_WIMStruct(); + struct wimfs_context *ctx = wimfs_get_context(); const struct wim_inode *inode; - ssize_t ret; + int ret; - inode = wim_pathname_to_inode(wim, path); + inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; - if (!inode_is_symlink(inode)) + if (bufsize <= 0) return -EINVAL; - if (buf_len == 0) - return -EINVAL; - ret = wim_inode_readlink(inode, buf, buf_len - 1, NULL); - if (ret >= 0) { - buf[ret] = '\0'; - ret = 0; - } else if (ret == -ENAMETOOLONG) { - buf[buf_len - 1] = '\0'; - } - return ret; + ret = wim_inode_readlink(inode, buf, bufsize - 1, NULL, + ctx->mountpoint_abspath, + ctx->mountpoint_abspath_nchars); + if (ret < 0) + return ret; + buf[ret] = '\0'; + return 0; } /* We use this for both release() and releasedir(), since in both cases we @@ -1903,11 +1895,9 @@ wimfs_symlink(const char *to, const char *from) struct wim_dentry *dentry; int ret; - ret = create_file(fuse_ctx, from, S_IFLNK | 0777, 0, - FILE_ATTRIBUTE_REPARSE_POINT, &dentry); + ret = create_file(fuse_ctx, from, S_IFLNK | 0777, 0, &dentry); if (ret) return ret; - dentry->d_inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK; ret = wim_inode_set_symlink(dentry->d_inode, to, wimfs_ctx->wim->blob_table); if (ret) { @@ -2035,7 +2025,7 @@ wimfs_write(const char *path, const char *buf, size_t size, struct wimfs_fd *fd = WIMFS_FD(fi); ssize_t ret; - ret = raw_pwrite(&fd->f_staging_fd, buf, size, offset); + ret = pwrite(fd->f_staging_fd.fd, buf, size, offset); if (ret < 0) return -errno; @@ -2130,15 +2120,16 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, /* Get the metadata for the image to mount. */ imd = wim_get_current_image_metadata(wim); - if (imd->modified) { - /* To avoid complicating things, we don't support mounting - * images to which in-memory modifications have already been - * made. */ + /* To avoid complicating things, we don't support mounting images to + * which in-memory modifications have already been made. */ + if (is_image_dirty(imd)) { ERROR("Cannot mount a modified WIM image!"); return WIMLIB_ERR_INVALID_PARAM; } if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { + if (imd->refcnt > 1) + return WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES; ret = lock_wim_for_append(wim); if (ret) return ret; @@ -2162,55 +2153,31 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, ctx.mount_flags = mount_flags; if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK; - /* For read-write mount, create the staging directory. */ + + /* For read-write mounts, create the staging directory, save a reference + * to the image's metadata resource, and mark the image dirty. */ if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { ret = make_staging_dir(&ctx, staging_dir); if (ret) - goto out_unlock; + goto out; + ret = WIMLIB_ERR_NOMEM; + ctx.metadata_resource = clone_blob_descriptor( + imd->metadata_blob); + if (!ctx.metadata_resource) + goto out; + mark_image_dirty(imd); } ctx.owner_uid = getuid(); ctx.owner_gid = getgid(); - /* Add each blob referenced by files in the image to a list and - * preemptively double the number of references to each. This is done - * to allow implementing the WIMLIB_UNMOUNT_FLAG_NEW_IMAGE semantics. - */ - INIT_LIST_HEAD(&ctx.orig_blob_list); - if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { - unsigned i; - struct wim_inode *inode; - struct blob_descriptor *blob; - - image_for_each_inode(inode, imd) { - for (i = 0; i < inode->i_num_streams; i++) { - blob = stream_blob(&inode->i_streams[i], - wim->blob_table); - if (blob) - blob->out_refcnt = 0; - } - } - - image_for_each_inode(inode, imd) { - for (i = 0; i < inode->i_num_streams; i++) { - blob = stream_blob(&inode->i_streams[i], - wim->blob_table); - if (blob) { - if (blob->out_refcnt == 0) - list_add(&blob->orig_blob_list, - &ctx.orig_blob_list); - blob->out_refcnt += inode->i_nlink; - blob->refcnt += inode->i_nlink; - } - } - } - } - - /* Assign new inode numbers. */ - reassign_inode_numbers(&ctx); + /* Number the inodes in the mounted image sequentially and initialize + * the file descriptor arrays */ + prepare_inodes(&ctx); - /* If a read-write mount, mark the image as modified. */ - if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) - imd->modified = 1; + /* Save the absolute path to the mountpoint directory. */ + ctx.mountpoint_abspath = realpath(dir, NULL); + if (ctx.mountpoint_abspath) + ctx.mountpoint_abspath_nchars = strlen(ctx.mountpoint_abspath); /* Build the FUSE command line. */ @@ -2297,10 +2264,11 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, /* Cleanup and return. */ if (ret) ret = WIMLIB_ERR_FUSE; - release_extra_refcnts(&ctx); - if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) +out: + FREE(ctx.mountpoint_abspath); + free_blob_descriptor(ctx.metadata_resource); + if (ctx.staging_dir_name) delete_staging_dir(&ctx); -out_unlock: unlock_wim_for_append(wim); return ret; } @@ -2519,7 +2487,7 @@ wimlib_unmount_image_with_progress(const char *dir, int unmount_flags, int mount_flags; int ret; - ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8); + ret = wimlib_global_init(0); if (ret) return ret;