+ if (handler_ctx->daemon_pid == 0 ||
+ (kill(handler_ctx->daemon_pid, 0) != 0 && errno == ESRCH))
+ {
+ ERROR("The filesystem daemon has crashed! Changes to the "
+ "WIM may not have been commited.");
+ return WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED;
+ }
+
+ DEBUG("Filesystem daemon is still alive... "
+ "Waiting another %d seconds", handler_ctx->hdr.timeout_seconds);
+ return 0;
+}
+
+static int
+daemon_timed_out_cb(void *_handler_ctx)
+{
+ ERROR("Timed out waiting for unmount request! "
+ "Changes to the mounted WIM will not be committed.");
+ return WIMLIB_ERR_TIMEOUT;
+}
+
+typedef int (*msg_handler_t)(const void *_msg, void *_handler_ctx);
+
+struct msg_handler_callbacks {
+ int (*timed_out)(void * _handler_ctx);
+ msg_handler_t msg_handlers[MSG_TYPE_MAX];
+};
+
+static const struct msg_handler_callbacks unmount_msg_handler_callbacks = {
+ .timed_out = unmount_timed_out_cb,
+ .msg_handlers = {
+ [MSG_TYPE_DAEMON_INFO] = msg_daemon_info_handler,
+ [MSG_TYPE_WRITE_STREAMS_PROGRESS] = msg_write_streams_progress_handler,
+ [MSG_TYPE_UNMOUNT_FINISHED] = msg_unmount_finished_handler,
+ },
+};
+
+static const struct msg_handler_callbacks daemon_msg_handler_callbacks = {
+ .timed_out = daemon_timed_out_cb,
+ .msg_handlers = {
+ [MSG_TYPE_UNMOUNT_REQUEST] = msg_unmount_request_handler,
+ },
+};
+
+static int
+receive_message(mqd_t mq,
+ struct msg_handler_context_hdr *handler_ctx,
+ const msg_handler_t msg_handlers[],
+ long mailbox_size, void *mailbox)
+{
+ struct timeval now;
+ struct timespec timeout;
+ ssize_t bytes_received;
+ struct unmount_msg_hdr *hdr;
+ int ret;
+
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec + handler_ctx->timeout_seconds;
+ timeout.tv_nsec = now.tv_usec * 1000;
+
+ bytes_received = mq_timedreceive(mq, mailbox,
+ mailbox_size, NULL, &timeout);
+ hdr = mailbox;
+ if (bytes_received == -1) {
+ if (errno == ETIMEDOUT) {
+ ret = WIMLIB_ERR_TIMEOUT;
+ } else {
+ ERROR_WITH_ERRNO("mq_timedreceive()");
+ ret = WIMLIB_ERR_MQUEUE;
+ }
+ } else if (bytes_received < sizeof(*hdr) ||
+ bytes_received != hdr->msg_size) {
+ ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+ } else if (WIMLIB_VERSION_CODE < hdr->min_version) {
+ /*ERROR("Cannot understand the received message. "*/
+ /*"Please upgrade wimlib to at least v%d.%d.%d",*/
+ /*WIMLIB_GET_MAJOR_VERSION(hdr->min_version),*/
+ /*WIMLIB_GET_MINOR_VERSION(hdr->min_version),*/
+ /*WIMLIB_GET_PATCH_VERSION(hdr->min_version));*/
+ ret = MSG_VERSION_TOO_HIGH;
+ } else if (hdr->msg_type >= MSG_TYPE_MAX) {
+ ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+ } else if (msg_handlers[hdr->msg_type] == NULL) {
+ ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+ } else {
+ ret = msg_handlers[hdr->msg_type](mailbox, handler_ctx);
+ }
+ return ret;
+}
+
+static int
+message_loop(mqd_t mq,
+ const struct msg_handler_callbacks *callbacks,
+ struct msg_handler_context_hdr *handler_ctx)
+{
+ static const size_t MAX_MSG_SIZE = 512;
+ long msgsize;
+ void *mailbox;
+ int ret;
+
+ DEBUG("Entering message loop");
+
+ ret = get_mailbox(mq, MAX_MSG_SIZE, &msgsize, &mailbox);
+ if (ret != 0)
+ return ret;
+ while (1) {
+ ret = receive_message(mq, handler_ctx,
+ callbacks->msg_handlers,
+ msgsize, mailbox);
+ if (ret == 0 || ret == MSG_VERSION_TOO_HIGH) {
+ continue;
+ } else if (ret == MSG_BREAK_LOOP) {
+ ret = 0;
+ break;
+ } else if (ret == WIMLIB_ERR_TIMEOUT) {
+ if (callbacks->timed_out)
+ ret = callbacks->timed_out(handler_ctx);
+ if (ret == 0)
+ continue;
+ else
+ break;
+ } else {
+ ERROR_WITH_ERRNO("Error communicating with "
+ "filesystem daemon");
+ break;
+ }
+ }
+ FREE(mailbox);
+ DEBUG("Exiting message loop");
+ 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, bool lazy)
+{
+ 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 */
+ char *argv[10];
+ char **argp = argv;
+ *argp++ = "fusermount";
+ if (lazy)
+ *argp++ = "-z";
+ *argp++ = "-u";
+ *argp++ = (char*)dir;
+ *argp = NULL;
+ execvp("fusermount", argv);
+ ERROR_WITH_ERRNO("Failed to execute `fusermount'");
+ exit(WIMLIB_ERR_FUSERMOUNT);
+ }
+
+ /* Parent */
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1) {
+ ERROR_WITH_ERRNO("Failed to wait for fusermount process to "
+ "terminate");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+
+ if (!WIFEXITED(status)) {
+ ERROR("'fusermount' did not terminate normally!");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+
+ status = WEXITSTATUS(status);
+
+ if (status == 0)
+ return 0;
+
+ if (status != WIMLIB_ERR_FUSERMOUNT)
+ return WIMLIB_ERR_FUSERMOUNT;
+
+ /* 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. */
+ ERROR("Falling back to 'umount'. Note: you may need to be "
+ "root for this to work");
+ pid = fork();
+ if (pid == -1) {
+ ERROR_WITH_ERRNO("Failed to fork()");
+ return WIMLIB_ERR_FORK;
+ }
+ if (pid == 0) {
+ /* Child */
+ char *argv[10];
+ char **argp = argv;
+ *argp++ = "umount";
+ if (lazy)
+ *argp++ = "-l";
+ *argp++ = (char*)dir;
+ *argp = NULL;
+ execvp("umount", argv);
+ ERROR_WITH_ERRNO("Failed to execute `umount'");
+ exit(WIMLIB_ERR_FUSERMOUNT);
+ }
+
+ /* Parent */
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1) {
+ ERROR_WITH_ERRNO("Failed to wait for `umount' process to "
+ "terminate");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ ERROR("`umount' did not successfully complete");
+ return WIMLIB_ERR_FUSERMOUNT;
+ }
+
+ return 0;
+}
+
+static int
+wimfs_chmod(const char *path, mode_t mask)
+{
+ struct wim_dentry *dentry;
+ struct wimfs_context *ctx = wimfs_get_context();
+ int ret;
+
+ if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
+ return -EPERM;
+
+ ret = wim_pathname_to_stream(ctx->wim, path, LOOKUP_FLAG_DIRECTORY_OK,
+ &dentry, NULL, NULL);
+ if (ret)
+ return ret;
+
+ ret = inode_set_unix_data(dentry->d_inode, ctx->default_uid,
+ ctx->default_gid, mask,
+ ctx->wim->lookup_table, UNIX_DATA_MODE);
+ return ret ? -ENOMEM : 0;
+}
+
+static int
+wimfs_chown(const char *path, uid_t uid, gid_t gid)
+{
+ struct wim_dentry *dentry;
+ struct wimfs_context *ctx = wimfs_get_context();
+ int ret;
+
+ if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
+ return -EPERM;
+
+ ret = wim_pathname_to_stream(ctx->wim, path, LOOKUP_FLAG_DIRECTORY_OK,
+ &dentry, NULL, NULL);
+ if (ret)
+ return ret;
+
+ ret = inode_set_unix_data(dentry->d_inode, uid, gid,
+ inode_default_unix_mode(dentry->d_inode),
+ ctx->wim->lookup_table,
+ UNIX_DATA_UID | UNIX_DATA_GID);
+ return ret ? -ENOMEM : 0;
+}
+
+/* Called when the filesystem is unmounted. */
+static void
+wimfs_destroy(void *p)
+{
+ struct wimfs_context *wimfs_ctx = wimfs_get_context();
+ if (open_message_queues(wimfs_ctx, true) == 0) {
+ struct daemon_msg_handler_context handler_ctx = {
+ .hdr = {
+ .timeout_seconds = 5,
+ },
+ .wimfs_ctx = wimfs_ctx,
+ };
+ message_loop(wimfs_ctx->unmount_to_daemon_mq,
+ &daemon_msg_handler_callbacks,
+ &handler_ctx.hdr);
+ }
+}
+
+static int
+wimfs_fgetattr(const char *path, struct stat *stbuf,
+ struct fuse_file_info *fi)
+{
+ struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
+ return inode_to_stbuf(fd->f_inode, fd->f_lte, stbuf);
+}
+
+static int
+wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
+{
+ struct wimfs_fd *fd = (struct wimfs_fd*)(uintptr_t)fi->fh;
+ int ret = ftruncate(fd->staging_fd.fd, size);
+ if (ret)
+ return -errno;
+ touch_inode(fd->f_inode);
+ fd->f_lte->size = size;
+ return 0;
+}
+
+/*
+ * Fills in a `struct stat' that corresponds to a file or directory in the WIM.
+ */
+static int
+wimfs_getattr(const char *path, struct stat *stbuf)
+{
+ struct wim_dentry *dentry;
+ struct wim_lookup_table_entry *lte;
+ int ret;
+ struct wimfs_context *ctx = wimfs_get_context();
+
+ ret = wim_pathname_to_stream(ctx->wim, path,
+ get_lookup_flags(ctx) |
+ LOOKUP_FLAG_DIRECTORY_OK,
+ &dentry, <e, NULL);