X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fmount_image.c;h=3aac49d11d6942797b51b5174ad09cc5042588a7;hb=a5388cc58647a6506e62acddc648ed6ca7b76fa5;hp=86cc8153e65d5bc37882487b0033bc957e6de6c2;hpb=d444f2e5ddee51e7d9d0401cffcf88477c180422;p=wimlib diff --git a/src/mount_image.c b/src/mount_image.c index 86cc8153..3aac49d1 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -161,6 +161,10 @@ struct wimfs_context { /* Original list of single-instance streams in the mounted image, linked * by 'struct wim_lookup_table_entry'.orig_stream_list. */ struct list_head orig_stream_list; + + /* Parameters for unmounting the image (can be set via extended + * attribute "wimfs.unmount_info"). */ + struct wimfs_unmount_info unmount_info; }; #define WIMFS_CTX(fuse_ctx) ((struct wimfs_context*)(fuse_ctx)->private_data) @@ -195,12 +199,6 @@ wimfs_get_WIMStruct(void) return wimfs_get_context()->wim; } -static inline int -get_lookup_flags(const struct wimfs_context *ctx) -{ - return ctx->default_lookup_flags; -} - /* Is write permission requested on the file? */ static inline bool flags_writable(int open_flags) @@ -356,6 +354,81 @@ wim_pathname_to_inode(WIMStruct *wim, const char *path) return dentry->d_inode; } +/* Can look up named data stream with colon syntax */ +#define LOOKUP_FLAG_ADS_OK 0x01 + +/* Can look up directory (otherwise get -ENOTDIR) */ +#define LOOKUP_FLAG_DIRECTORY_OK 0x02 + +/* + * Translate a path into the corresponding dentry, lookup table entry, and + * stream index in the mounted WIM image. + * + * Returns 0 or a -errno code. All of @dentry_ret, @lte_ret, and + * @stream_idx_ret are optional. + */ +static int +wim_pathname_to_stream(const struct wimfs_context *ctx, const char *path, + int lookup_flags, + struct wim_dentry **dentry_ret, + struct wim_lookup_table_entry **lte_ret, + u16 *stream_idx_ret) +{ + WIMStruct *wim = ctx->wim; + struct wim_dentry *dentry; + struct wim_lookup_table_entry *lte; + u16 stream_idx; + const tchar *stream_name = NULL; + struct wim_inode *inode; + tchar *p = NULL; + + lookup_flags |= ctx->default_lookup_flags; + + if (lookup_flags & LOOKUP_FLAG_ADS_OK) { + stream_name = path_stream_name(path); + if (stream_name) { + p = (tchar*)stream_name - 1; + *p = T('\0'); + } + } + + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); + if (p) + *p = T(':'); + if (!dentry) + return -errno; + + inode = dentry->d_inode; + + if (!inode->i_resolved) + if (inode_resolve_streams(inode, wim->lookup_table, false)) + return -EIO; + + if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK) + && inode_is_directory(inode)) + return -EISDIR; + + if (stream_name) { + struct wim_ads_entry *ads_entry; + + ads_entry = inode_get_ads_entry(inode, stream_name); + if (!ads_entry) + return -errno; + + stream_idx = ads_entry - inode->i_ads_entries + 1; + lte = ads_entry->lte; + } else { + lte = inode_unnamed_stream_resolved(inode, &stream_idx); + } + if (dentry_ret) + *dentry_ret = dentry; + if (lte_ret) + *lte_ret = lte; + if (stream_idx_ret) + *stream_idx_ret = stream_idx; + return 0; +} + /* * Create a new file in the mounted WIM image. * @@ -537,6 +610,12 @@ touch_inode(struct wim_inode *inode) inode->i_last_write_time = now; } +static void +touch_parent(struct wim_dentry *dentry) +{ + touch_inode(dentry->d_parent->d_inode); +} + /* * Create a new file in the staging directory for a read-write mounted image. * @@ -1057,51 +1136,66 @@ commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq) return wimlib_overwrite(ctx->wim, write_flags, 0); } +/* In the case of an allow_other mount, only the owner and root should be + * allowed to unmount the filesystem. */ +static bool +may_unmount_wimfs(void) +{ + const struct fuse_context *fuse_ctx = fuse_get_context(); + const struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); + + return (fuse_ctx->uid == wimfs_ctx->owner_uid || + fuse_ctx->uid == 0); +} + static int -unmount_wimfs(const struct wimfs_unmount_info *info) +unmount_wimfs(void) { struct fuse_context *fuse_ctx = fuse_get_context(); struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); + const struct wimfs_unmount_info *info = &wimfs_ctx->unmount_info; int unmount_flags = info->unmount_flags; mqd_t mq = (mqd_t)-1; - int status; - - if (fuse_ctx->uid != wimfs_ctx->owner_uid && - fuse_ctx->uid != 0) - return -EPERM; - - if (info->mq_name[0]) { - mq = mq_open(info->mq_name, O_WRONLY | O_NONBLOCK); - if (mq == (mqd_t)-1) - return -errno; - } + int ret; /* Ignore COMMIT if the image is mounted read-only. */ if (!(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) unmount_flags &= ~WIMLIB_UNMOUNT_FLAG_COMMIT; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS) { + mq = mq_open(info->mq_name, O_WRONLY | O_NONBLOCK); + if (mq == (mqd_t)-1) { + ret = WIMLIB_ERR_MQUEUE; + goto out; + } + } + if (wimfs_ctx->num_open_fds) { if ((unmount_flags & (WIMLIB_UNMOUNT_FLAG_COMMIT | WIMLIB_UNMOUNT_FLAG_FORCE)) == WIMLIB_UNMOUNT_FLAG_COMMIT) { - status = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY; - goto out_send_status; + ret = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY; + goto out; } close_all_fds(wimfs_ctx); } if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) - status = commit_image(wimfs_ctx, unmount_flags, mq); + ret = commit_image(wimfs_ctx, unmount_flags, mq); else - status = 0; - fuse_exit(fuse_ctx->fuse); -out_send_status: - if (mq != (mqd_t)-1) { - mq_send(mq, (const char *)&status, sizeof(int), 1); - mq_close(mq); + ret = 0; +out: + /* Leave the image mounted if commit failed, unless this is a + * forced unmount. The user can retry without commit if they + * want. */ + if (!ret || (unmount_flags & WIMLIB_UNMOUNT_FLAG_FORCE)) { + unlock_wim_for_append(wimfs_ctx->wim); + fuse_exit(fuse_ctx->fuse); } - return 0; + if (mq != (mqd_t)-1) + mq_close(mq); + return ret; } static int @@ -1193,9 +1287,7 @@ wimfs_getattr(const char *path, struct stat *stbuf) struct wim_lookup_table_entry *lte; int ret; - ret = wim_pathname_to_stream(ctx->wim, path, - get_lookup_flags(ctx) | - LOOKUP_FLAG_DIRECTORY_OK, + ret = wim_pathname_to_stream(ctx, path, LOOKUP_FLAG_DIRECTORY_OK, &dentry, <e, NULL); if (ret) return ret; @@ -1217,8 +1309,7 @@ static int wimfs_getxattr(const char *path, const char *name, char *value, size_t size) { - struct fuse_context *fuse_ctx = fuse_get_context(); - const struct wimfs_context *ctx = WIMFS_CTX(fuse_ctx); + const struct wimfs_context *ctx = wimfs_get_context(); struct wim_inode *inode; struct wim_ads_entry *ads_entry; struct wim_lookup_table_entry *lte; @@ -1248,9 +1339,17 @@ wimfs_getxattr(const char *path, const char *name, char *value, &ctx->mount_flags, sizeof(int)); } if (!strcmp(name, "unmount")) { - struct wimfs_unmount_info info; - memset(&info, 0, sizeof(info)); - return unmount_wimfs(&info); + if (!may_unmount_wimfs()) + return -EPERM; + if (size) { + int status; + + if (size < sizeof(int)) + return -ERANGE; + status = unmount_wimfs(); + memcpy(value, &status, sizeof(int)); + } + return sizeof(int); } return -ENOATTR; } @@ -1324,6 +1423,7 @@ wimfs_link(const char *existing_path, const char *new_path) new_alias->d_inode = inode; inode_add_dentry(new_alias, inode); dentry_add_child(dir, new_alias); + touch_inode(dir->d_inode); inode->i_nlink++; inode_ref_streams(inode); return 0; @@ -1387,9 +1487,16 @@ wimfs_listxattr(const char *path, char *list, size_t size) static int wimfs_mkdir(const char *path, mode_t mode) { + struct wim_dentry *dentry; + int ret; + /* Note: according to fuse.h, mode may not include S_IFDIR */ - return create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0, - FILE_ATTRIBUTE_DIRECTORY, NULL); + ret = create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0, + FILE_ATTRIBUTE_DIRECTORY, &dentry); + if (ret) + return ret; + touch_parent(dentry); + return 0; } static int @@ -1433,6 +1540,8 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev) } else { /* Create a regular file, device node, named pipe, or socket. */ + struct wim_dentry *dentry; + int ret; if (!S_ISREG(mode) && !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA)) @@ -1441,8 +1550,12 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev) /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes, * named pipes, and sockets. The real mode is in the UNIX * metadata. */ - return create_dentry(fuse_ctx, path, mode, rdev, - FILE_ATTRIBUTE_NORMAL, NULL); + ret = create_dentry(fuse_ctx, path, mode, rdev, + FILE_ATTRIBUTE_NORMAL, &dentry); + if (ret) + return ret; + touch_parent(dentry); + return 0; } } @@ -1457,8 +1570,7 @@ wimfs_open(const char *path, struct fuse_file_info *fi) struct wimfs_fd *fd; int ret; - ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx), - &dentry, <e, &stream_idx); + ret = wim_pathname_to_stream(ctx, path, 0, &dentry, <e, &stream_idx); if (ret) return ret; @@ -1686,6 +1798,7 @@ wimfs_rmdir(const char *path) if (dentry_has_children(dentry)) return -ENOTEMPTY; + touch_parent(dentry); remove_dentry(dentry, wim->lookup_table); return 0; } @@ -1703,10 +1816,14 @@ wimfs_setxattr(const char *path, const char *name, * be ioctls, but directory ioctls aren't supported until * libfuse 2.9, and even then they are broken. */ name += 6; - if (!strcmp(name, "unmount")) { + if (!strcmp(name, "unmount_info")) { + if (!may_unmount_wimfs()) + return -EPERM; if (size < sizeof(struct wimfs_unmount_info)) return -EINVAL; - return unmount_wimfs((const void *)value); + memcpy(&ctx->unmount_info, value, + sizeof(struct wimfs_unmount_info)); + return 0; } return -ENOATTR; } @@ -1764,6 +1881,8 @@ wimfs_symlink(const char *to, const char *from) ret = -ENOMEM; else ret = -EINVAL; + } else { + touch_parent(dentry); } return ret; } @@ -1778,9 +1897,7 @@ wimfs_truncate(const char *path, off_t size) int ret; int fd; - ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx), - &dentry, <e, &stream_idx); - + ret = wim_pathname_to_stream(ctx, path, 0, &dentry, <e, &stream_idx); if (ret) return ret; @@ -1813,18 +1930,18 @@ wimfs_unlink(const char *path) u16 stream_idx; int ret; - ret = wim_pathname_to_stream(ctx->wim, path, get_lookup_flags(ctx), - &dentry, NULL, &stream_idx); - + ret = wim_pathname_to_stream(ctx, path, 0, &dentry, NULL, &stream_idx); if (ret) return ret; - if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0) + if (inode_stream_name_nbytes(dentry->d_inode, stream_idx) == 0) { + touch_parent(dentry); remove_dentry(dentry, ctx->wim->lookup_table); - else + } else { inode_remove_ads(dentry->d_inode, &dentry->d_inode->i_ads_entries[stream_idx - 1], ctx->wim->lookup_table); + } return 0; } @@ -1983,9 +2100,11 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, return WIMLIB_ERR_INVALID_PARAM; } - ret = lock_wim_for_append(wim, wim->in_fd.fd); - if (ret) - return ret; + if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { + ret = lock_wim_for_append(wim); + if (ret) + return ret; + } /* If the user did not specify an interface for accessing named * data streams, use the default (extended attributes). */ @@ -2010,9 +2129,9 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, ctx.owner_gid = getgid(); /* 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. */ + * 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_stream_list); if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { unsigned i; @@ -2140,7 +2259,7 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) delete_staging_dir(&ctx); out_unlock: - unlock_wim_for_append(wim, wim->in_fd.fd); + unlock_wim_for_append(wim); return ret; } @@ -2148,7 +2267,6 @@ struct commit_progress_thread_args { mqd_t mq; wimlib_progress_func_t progfunc; void *progctx; - int status; }; static void * @@ -2158,23 +2276,16 @@ commit_progress_thread_proc(void *_args) struct commit_progress_report report; ssize_t ret; - args->status = WIMLIB_ERR_NOT_A_MOUNTPOINT; for (;;) { ret = mq_receive(args->mq, (char *)&report, sizeof(report), NULL); - if (ret < 0) { - if (errno == EINTR) - continue; - break; - } - if (ret == sizeof(int)) { - args->status = *(int *)&report; - break; + if (ret == sizeof(report)) { + call_progress(args->progfunc, report.msg, + &report.info, args->progctx); + } else { + if (ret == 0 || (ret < 0 && errno != EINTR)) + break; } - if (ret < sizeof(report)) - continue; - call_progress(args->progfunc, report.msg, - &report.info, args->progctx); } return NULL; } @@ -2217,6 +2328,50 @@ create_message_queue(const char *name, bool have_progfunc) return mq; } +/* Unmount a read-only or read-write mounted WIM image. */ +static int +do_unmount(const char *dir) +{ + int status; + ssize_t len; + + len = getxattr(dir, "wimfs.unmount", &status, sizeof(int)); + if (len == sizeof(int)) + return status; + else if (len < 0 && (errno == EACCES || errno == EPERM)) + return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT; + else + return WIMLIB_ERR_NOT_A_MOUNTPOINT; +} + +static int +set_unmount_info(const char *dir, const struct wimfs_unmount_info *unmount_info) +{ + if (!setxattr(dir, "wimfs.unmount_info", + unmount_info, sizeof(struct wimfs_unmount_info), 0)) + return 0; + else if (errno == EROFS) + return 0; + else if (errno == EACCES || errno == EPERM) + return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT; + else + return WIMLIB_ERR_NOT_A_MOUNTPOINT; +} + +static int +do_unmount_discard(const char *dir) +{ + int ret; + struct wimfs_unmount_info unmount_info; + + memset(&unmount_info, 0, sizeof(unmount_info)); + + ret = set_unmount_info(dir, &unmount_info); + if (ret) + return ret; + return do_unmount(dir); +} + /* Unmount a read-write mounted WIM image, committing the changes. */ static int do_unmount_commit(const char *dir, int unmount_flags, @@ -2230,17 +2385,17 @@ do_unmount_commit(const char *dir, int unmount_flags, memset(&unmount_info, 0, sizeof(unmount_info)); unmount_info.unmount_flags = unmount_flags; - generate_message_queue_name(unmount_info.mq_name); - mq = create_message_queue(unmount_info.mq_name, progfunc != NULL); - if (mq == (mqd_t)-1) { - ERROR_WITH_ERRNO("Can't create POSIX message queue"); - return WIMLIB_ERR_MQUEUE; - } - - /* The current thread will be stuck in setxattr() until the image is + /* The current thread will be stuck in getxattr() until the image is * committed. Create a thread to handle the progress messages. */ if (progfunc) { + generate_message_queue_name(unmount_info.mq_name); + + mq = create_message_queue(unmount_info.mq_name, progfunc != NULL); + if (mq == (mqd_t)-1) { + ERROR_WITH_ERRNO("Can't create POSIX message queue"); + return WIMLIB_ERR_MQUEUE; + } args.mq = mq; args.progfunc = progfunc; args.progctx = progctx; @@ -2255,57 +2410,22 @@ do_unmount_commit(const char *dir, int unmount_flags, unmount_info.unmount_flags |= WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS; } - if (!setxattr(dir, "wimfs.unmount", - (const char *)&unmount_info, sizeof(unmount_info), 0)) - ret = 0; - else if (errno == EACCES || errno == EPERM) - ret = WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT; - else - ret = WIMLIB_ERR_NOT_A_MOUNTPOINT; - + ret = set_unmount_info(dir, &unmount_info); + if (!ret) + ret = do_unmount(dir); if (progfunc) { - /* Terminate the progress thread and retrieve final unmount - * status. */ - - int tmp = -1; - mq_send(mq, (const char *)&tmp, sizeof(int), 1); - + /* Terminate the progress thread. */ + mq_send(mq, NULL, 0, 1); pthread_join(commit_progress_tid, NULL); - if (!ret && args.status != -1) - ret = args.status; - } else if (!ret) { - /* Retrieve the final unmount status. */ - - int tmp = -1; - int len; - - mq_send(mq, (const char *)&tmp, sizeof(int), 1); - len = mq_receive(mq, (char *)&tmp, sizeof(int), NULL); - - if (len == 4 && tmp != -1) - ret = tmp; - else - ret = WIMLIB_ERR_NOT_A_MOUNTPOINT; } out_delete_mq: - mq_close(mq); - mq_unlink(unmount_info.mq_name); + if (progfunc) { + mq_close(mq); + mq_unlink(unmount_info.mq_name); + } return ret; } -/* Unmount a read-only or read-write mounted WIM image, discarding any changes. - */ -static int -do_unmount_discard(const char *dir) -{ - if (!getxattr(dir, "wimfs.unmount", NULL, 0)) - return 0; - else if (errno == EACCES || errno == EPERM) - return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT; - else - return WIMLIB_ERR_NOT_A_MOUNTPOINT; -} - static int begin_unmount(const char *dir, int unmount_flags, int *mount_flags_ret, wimlib_progress_func_t progfunc, void *progctx)