+ return ret;
+}
+
+/* Execute `fusermount -u', which is installed setuid root, to unmount the WIM.
+ *
+ * FUSE does not yet implement synchronous unmounts. This means that fusermount
+ * -u will return before the filesystem daemon returns from wimfs_destroy().
+ * This is partly what we want, because we need to send a message from this
+ * process to the filesystem daemon telling whether --commit was specified or
+ * not. However, after that, the unmount process must wait for the filesystem
+ * daemon to finish writing the WIM file.
+ */
+static int execute_fusermount(const char *dir)
+{
+ pid_t pid;
+ int ret;
+ int status;
+
+ pid = fork();
+ if (pid == -1) {
+ ERROR_WITH_ERRNO("Failed to fork()");
+ return WIMLIB_ERR_FORK;
+ }
+ if (pid == 0) {
+ /* Child */
+ execlp("fusermount", "fusermount", "-u", dir, NULL);
+ ERROR_WITH_ERRNO("Failed to execute `fusermount'");
+ exit(WIMLIB_ERR_FUSERMOUNT);
+ }
+
+ /* Parent */
+ ret = wait(&status);
+ if (ret == -1) {
+ ERROR_WITH_ERRNO("Failed to wait for fusermount process to "
+ "terminate");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+
+ if (status != 0) {
+ if (status == WIMLIB_ERR_FUSERMOUNT)
+ ERROR("Could not find the `fusermount' program");
+ else
+ ERROR("fusermount exited with status %d", status);
+
+ /* Try again, but with the `umount' program. This is required
+ * on other FUSE implementations such as FreeBSD's that do not
+ * have a `fusermount' program. */
+
+ pid = fork();
+ if (pid == -1) {
+ ERROR_WITH_ERRNO("Failed to fork()");
+ return WIMLIB_ERR_FORK;
+ }
+ if (pid == 0) {
+ /* Child */
+ execlp("umount", "umount", dir, NULL);
+ ERROR_WITH_ERRNO("Failed to execute `umount'");
+ exit(WIMLIB_ERR_FUSERMOUNT);
+ }
+
+ /* Parent */
+ ret = wait(&status);
+ if (ret == -1) {
+ ERROR_WITH_ERRNO("Failed to wait for `umount' process to "
+ "terminate");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+ if (status != 0) {
+ ERROR("`umount' exited with failure status");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+ }
+ return 0;
+}
+
+static int wimfs_access(const char *path, int mask)
+{
+ /* Permissions not implemented */
+ return 0;
+}
+
+static int wimfs_chmod(const char *path, mode_t mask)
+{
+ struct dentry *dentry;
+ struct wimfs_context *ctx = wimfs_get_context();
+ struct inode *inode;
+ struct stat stbuf;
+ int ret;
+
+ ret = lookup_resource(ctx->wim, path,
+ get_lookup_flags(ctx) | LOOKUP_FLAG_DIRECTORY_OK,
+ &dentry, NULL, NULL);
+ if (ret != 0)
+ return ret;
+ inode = dentry->d_inode;
+ inode_to_stbuf(inode, NULL, &stbuf);
+ if (mask == stbuf.st_mode)
+ return 0;
+ else
+ return -EPERM;
+}
+
+/* Called when the filesystem is unmounted. */
+static void wimfs_destroy(void *p)
+{
+ /* The `imagex unmount' command, which is running in a separate process
+ * and is executing the wimlib_unmount() function, will send this
+ * process a `struct unmount_request' through a message queue that
+ * specifies some unmount options, such as other to commit changes or
+ * not. */
+
+ int ret;
+ int unmount_flags;
+ struct wimfs_context *ctx;
+ int status;
+
+ ctx = wimfs_get_context();
+ if (open_message_queues(ctx, true))
+ return;
+
+ ret = receive_unmount_request(ctx, &unmount_flags);
+ status = ret;
+ if (ret == 0) {
+ if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+ if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
+ int 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;
+ status = rebuild_wim(ctx, write_flags);
+ }
+ ret = delete_staging_dir(ctx);
+ if (ret != 0) {
+ ERROR("Failed to delete the staging directory");
+ if (status == 0)
+ status = ret;
+ }
+ } else {
+ DEBUG("Read-only mount");
+ }
+ }
+ send_unmount_response(ctx, status);