X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fmount_image.c;h=734afe48073c21bdabcab74a89390e6fa43e3161;hb=ca5539953d4927c61c7a45f719eab2d6e139cb5b;hp=86cc8153e65d5bc37882487b0033bc957e6de6c2;hpb=d444f2e5ddee51e7d9d0401cffcf88477c180422;p=wimlib diff --git a/src/mount_image.c b/src/mount_image.c index 86cc8153..734afe48 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -1,10 +1,10 @@ /* * mount_image.c * - * This file implements mounting of WIM files using FUSE, which stands for - * Filesystem in Userspace. FUSE allows a filesystem to be implemented in a - * userspace process by implementing the filesystem primitives--- read(), - * write(), readdir(), etc. + * This file implements mounting of WIM images using FUSE + * (Filesystem in Userspace). See http://fuse.sourceforge.net/. + * + * Currently it is only expected to work on Linux. */ /* @@ -31,6 +31,7 @@ #endif #include "wimlib.h" +#include "wimlib/error.h" #ifdef WITH_FUSE @@ -161,6 +162,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 +200,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 +355,80 @@ 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 char *stream_name = NULL; + struct wim_inode *inode; + char *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 = (char *)stream_name - 1; + *p = '\0'; + } + } + + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); + if (p) + *p = ':'; + if (!dentry) + return -errno; + + inode = dentry->d_inode; + + 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. * @@ -431,8 +504,7 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, dentry_add_child(parent, new_dentry); - if (dentry_ret) - *dentry_ret = new_dentry; + *dentry_ret = new_dentry; return 0; } @@ -537,6 +609,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. * @@ -864,7 +942,7 @@ out_restore_wim_filename: return ret; } -/* Deletes the staging directory, undoing the effects of a succesful call to +/* Deletes the staging directory, undoing the effects of a successful call to * make_staging_dir(). */ static void delete_staging_dir(struct wimfs_context *ctx) @@ -886,6 +964,7 @@ 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) { @@ -912,6 +991,8 @@ release_extra_refcnts(struct wimfs_context *ctx) } } +/* Delete the 'struct wim_lookup_table_entry' for any stream that was modified + * or created in the read-write mounted image and had a final size of 0. */ static void delete_empty_streams(struct wimfs_context *ctx) { @@ -929,6 +1010,9 @@ delete_empty_streams(struct wimfs_context *ctx) } } +/* Close all file descriptors open to the specified inode. + * + * Note: closing the last file descriptor might free the inode. */ static void inode_close_fds(struct wim_inode *inode) { @@ -941,6 +1025,7 @@ inode_close_fds(struct wim_inode *inode) } } +/* Close all file descriptors open to the mounted image. */ static void close_all_fds(struct wimfs_context *ctx) { @@ -989,7 +1074,7 @@ renew_current_image(struct wimfs_context *ctx) if (ret) goto err_free_new_lte; - ret = xml_add_image(wim, T("")); + ret = xml_add_image(wim, ""); if (ret) goto err_undo_append; @@ -1048,60 +1133,89 @@ commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq) xml_update_image_info(ctx->wim, ctx->wim->current_image); write_flags = 0; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_REBUILD) write_flags |= WIMLIB_WRITE_FLAG_REBUILD; + if (unmount_flags & WIMLIB_UNMOUNT_FLAG_RECOMPRESS) write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; + 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); +} + +/* Unmount the mounted image, called from the daemon process. */ 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) { + + /* There are still open file descriptors to the image. */ + + /* With COMMIT, refuse to unmount unless FORCE is also + * specified. */ 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; } + + /* Force-close all file descriptors. */ 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; /* Read-only mount, or discarding changes to + a read-write mount */ + +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 +1307,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; @@ -1205,11 +1317,11 @@ wimfs_getattr(const char *path, struct stat *stbuf) static int copy_xattr(char *dest, size_t destsize, const void *src, size_t srcsize) { - if (!destsize) - return srcsize; - if (destsize < srcsize) - return -ERANGE; - memcpy(dest, src, srcsize); + if (destsize) { + if (destsize < srcsize) + return -ERANGE; + memcpy(dest, src, srcsize); + } return srcsize; } @@ -1217,8 +1329,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 +1359,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; } @@ -1280,11 +1399,11 @@ wimfs_getxattr(const char *path, const char *name, char *value, return -EFBIG; if (size) { - if (lte->size > size) + if (size < lte->size) return -ERANGE; if (read_full_stream_into_buf(lte, value)) - return -errno; + return errno ? -errno : -EIO; } return lte->size; } @@ -1324,6 +1443,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 +1507,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 +1560,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 +1570,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 +1590,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; @@ -1546,7 +1678,7 @@ wimfs_read(const char *path, char *buf, size_t size, switch (lte->resource_location) { case RESOURCE_IN_WIM: if (read_partial_wim_stream_into_buf(lte, size, offset, buf)) - ret = -errno; + ret = errno ? -errno : -EIO; else ret = size; break; @@ -1686,6 +1818,7 @@ wimfs_rmdir(const char *path) if (dentry_has_children(dentry)) return -ENOTEMPTY; + touch_parent(dentry); remove_dentry(dentry, wim->lookup_table); return 0; } @@ -1701,12 +1834,17 @@ wimfs_setxattr(const char *path, const char *name, if (!strncmp(name, "wimfs.", 6)) { /* Handle some magical extended attributes. These really should * be ioctls, but directory ioctls aren't supported until - * libfuse 2.9, and even then they are broken. */ + * libfuse 2.9, and even then they are broken. [Fixed by + * libfuse commit e3b7d4c278a26520be63d99d6ea84b26906fe73d] */ 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 +1902,8 @@ wimfs_symlink(const char *to, const char *from) ret = -ENOMEM; else ret = -EINVAL; + } else { + touch_parent(dentry); } return ret; } @@ -1778,9 +1918,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 +1951,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; } @@ -1962,6 +2100,8 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, /* For read-write mount, check for write access to the WIM. */ if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { + if (!wim->filename) + return WIMLIB_ERR_NO_FILENAME; ret = can_delete_from_wim(wim); if (ret) return ret; @@ -1983,9 +2123,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 +2152,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; @@ -2114,7 +2256,6 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, char optstring[256] = "use_ino" ",subtype=wimfs" - ",attr_timeout=0" ",hard_remove" ",default_permissions" ",kernel_cache" @@ -2140,7 +2281,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 +2289,6 @@ struct commit_progress_thread_args { mqd_t mq; wimlib_progress_func_t progfunc; void *progctx; - int status; }; static void * @@ -2158,23 +2298,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; } @@ -2189,27 +2322,27 @@ generate_message_queue_name(char name[WIMFS_MQUEUE_NAME_LEN + 1]) } static mqd_t -create_message_queue(const char *name, bool have_progfunc) +create_message_queue(const char *name) { - bool am_root = (getuid() == 0); - mode_t umask_save = 0; - mode_t mode = 0600; + bool am_root; + mode_t umask_save; + mode_t mode; struct mq_attr attr; mqd_t mq; memset(&attr, 0, sizeof(attr)); attr.mq_maxmsg = 8; - if (have_progfunc) - attr.mq_msgsize = sizeof(struct commit_progress_report); - else - attr.mq_msgsize = sizeof(int); + attr.mq_msgsize = sizeof(struct commit_progress_report); + am_root = (geteuid() == 0); if (am_root) { /* Filesystem mounted as normal user with --allow-other should * be able to send messages to root user, if they're doing the * unmount. */ umask_save = umask(0); mode = 0666; + } else { + mode = 0600; } mq = mq_open(name, O_RDWR | O_CREAT | O_EXCL, mode, &attr); if (am_root) @@ -2217,6 +2350,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 +2407,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); + 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 +2432,23 @@ 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. */ + char empty[0]; + mq_send(mq, empty, 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) @@ -2356,7 +2499,9 @@ wimlib_unmount_image_with_progress(const char *dir, int unmount_flags, int mount_flags; int ret; - wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8); + ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8); + if (ret) + return ret; if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY | WIMLIB_UNMOUNT_FLAG_COMMIT |