mount_image.c: More message queue changes
authorEric Biggers <ebiggers3@gmail.com>
Tue, 18 Dec 2012 05:56:41 +0000 (23:56 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 18 Dec 2012 05:56:41 +0000 (23:56 -0600)
Yet another rewrite of the message sending/receiving functions.  Everything is
now sent through a generic message loop function with callbacks.  At the same
time, another message has been introduced that is sent from the daemon to the
unmount process to provide its pid.  Then, the unmount process can detect if the
filesystem daemon has crashed by polling this pid every second.  (This should
solve the 10 minute timeout problem.)

src/mount_image.c
src/util.c
src/util.h
src/wimlib.h

index 74b751c..7bede83 100644 (file)
@@ -52,6 +52,9 @@
 #include <attr/xattr.h>
 #endif
 
+#define MSG_VERSION_TOO_HIGH   -1
+#define MSG_BREAK_LOOP         -2
+
 /* File descriptor to a file open on the WIM filesystem. */
 struct wimlib_fd {
        struct inode *f_inode;
@@ -676,7 +679,6 @@ static int remove_file_or_directory(const char *fpath, const struct stat *sb,
        }
 }
 
-
 /*
  * Deletes the staging directory and all the files contained in it.
  */
@@ -690,6 +692,107 @@ static int delete_staging_dir(struct wimfs_context *ctx)
        return ret;
 }
 
+static void inode_update_lte_ptr(struct inode *inode,
+                                struct lookup_table_entry *old_lte,
+                                struct lookup_table_entry *new_lte)
+{
+       if (inode->lte == old_lte) {
+               inode->lte = new_lte;
+       } else {
+               for (unsigned i = 0; i < inode->num_ads; i++) {
+                       if (inode->ads_entries[i].lte == old_lte) {
+                               inode->ads_entries[i].lte = new_lte;
+                               break;
+                       }
+               }
+       }
+}
+
+static int update_lte_of_staging_file(struct lookup_table_entry *lte,
+                                     struct lookup_table *table)
+{
+       struct lookup_table_entry *duplicate_lte;
+       int ret;
+       u8 hash[SHA1_HASH_SIZE];
+       struct stat stbuf;
+
+       ret = sha1sum(lte->staging_file_name, hash);
+       if (ret != 0)
+               return ret;
+       lookup_table_unlink(table, lte);
+       duplicate_lte = __lookup_resource(table, hash);
+       if (duplicate_lte) {
+               /* Merge duplicate lookup table entries */
+               duplicate_lte->refcnt += lte->refcnt;
+               inode_update_lte_ptr(lte->lte_inode, lte, duplicate_lte);
+               free_lookup_table_entry(lte);
+       } else {
+               if (stat(lte->staging_file_name, &stbuf) != 0) {
+                       ERROR_WITH_ERRNO("Failed to stat `%s'", lte->staging_file_name);
+                       return WIMLIB_ERR_STAT;
+               }
+               if (stbuf.st_size == 0) {
+                       /* Zero-length stream.  No lookup table entry needed. */
+                       inode_update_lte_ptr(lte->lte_inode, lte, NULL);
+                       free_lookup_table_entry(lte);
+               } else {
+                       wimlib_assert(&lte->file_on_disk == &lte->staging_file_name);
+                       lte->resource_entry.original_size = stbuf.st_size;
+                       lte->resource_entry.size = stbuf.st_size;
+                       lte->resource_location = RESOURCE_IN_FILE_ON_DISK;
+                       lte->file_on_disk_fp = NULL;
+                       copy_hash(lte->hash, hash);
+                       lookup_table_insert(table, lte);
+               }
+       }
+       return 0;
+}
+
+static int inode_close_fds(struct inode *inode)
+{
+       u16 num_opened_fds = inode->num_opened_fds;
+       for (u16 i = 0, j = 0; j < num_opened_fds; i++) {
+               struct wimlib_fd *fd = inode->fds[i];
+               if (fd) {
+                       wimlib_assert(fd->f_inode == inode);
+                       int ret = close_wimlib_fd(fd);
+                       if (ret != 0)
+                               return ret;
+                       j++;
+               }
+       }
+       return 0;
+}
+
+/* Overwrites the WIM file, with changes saved. */
+static int rebuild_wim(struct wimfs_context *ctx, int write_flags)
+{
+       int ret;
+       struct lookup_table_entry *lte, *tmp;
+       WIMStruct *w = ctx->wim;
+
+       DEBUG("Closing all staging file descriptors.");
+       list_for_each_entry_safe(lte, tmp, &ctx->staging_list, staging_list) {
+               ret = inode_close_fds(lte->lte_inode);
+               if (ret != 0)
+                       return ret;
+       }
+
+       DEBUG("Calculating SHA1 checksums for all new staging files.");
+       list_for_each_entry(lte, &ctx->staging_list, staging_list) {
+               ret = update_lte_of_staging_file(lte, w->lookup_table);
+               if (ret != 0)
+                       return ret;
+       }
+
+       xml_update_image_info(w, w->current_image);
+       ret = wimlib_overwrite(w, write_flags, 0, NULL);
+       if (ret != 0)
+               ERROR("Failed to commit changes to mounted WIM image");
+       return ret;
+}
+
+
 
 /* Simple function that returns the concatenation of 2 strings. */
 static char *strcat_dup(const char *s1, const char *s2, size_t max_len)
@@ -764,8 +867,7 @@ static void free_message_queue_names(struct wimfs_context *ctx)
  * Opens two POSIX message queue: one for sending messages from the unmount
  * process to the daemon process, and one to go the other way.  The names of the
  * message queues, which must be system-wide unique, are be based on the mount
- * point.  (There of course is still a possibility of a collision if one were to
- * unmount two identically named directories simultaneously...)
+ * point.
  *
  * @daemon specifies whether the calling process is the filesystem daemon or the
  * unmount process.
@@ -866,6 +968,7 @@ static void unlink_message_queues(struct wimfs_context *ctx)
 /* Closes the message queues, which are allocated in static variables */
 static void close_message_queues(struct wimfs_context *ctx)
 {
+       DEBUG("Closing message queues");
        mq_close(ctx->unmount_to_daemon_mq);
        ctx->unmount_to_daemon_mq = (mqd_t)(-1);
        mq_close(ctx->daemon_to_unmount_mq);
@@ -873,277 +976,333 @@ static void close_message_queues(struct wimfs_context *ctx)
        unlink_message_queues(ctx);
 }
 
-struct unmount_message_header {
+
+struct unmount_msg_hdr {
        u32 min_version;
        u32 cur_version;
-       u32 data_size;
+       u32 msg_type;
+       u32 msg_size;
+} PACKED;
+
+struct msg_unmount_request {
+       struct unmount_msg_hdr hdr;
+       u32 unmount_flags;
+} PACKED;
+
+struct msg_daemon_info {
+       struct unmount_msg_hdr hdr;
+       pid_t daemon_pid;
+       u32 mount_flags;
+} PACKED;
+
+struct msg_unmount_finished {
+       struct unmount_msg_hdr hdr;
+       int32_t status;
+} PACKED;
+
+enum {
+       MSG_TYPE_UNMOUNT_REQUEST,
+       MSG_TYPE_DAEMON_INFO,
+       MSG_TYPE_UNMOUNT_FINISHED,
+       MSG_TYPE_MAX,
 };
 
-struct unmount_request {
-       struct unmount_message_header hdr;
-       struct {
-               u32 unmount_flags;
-       } data;
+struct msg_handler_context {
+       bool is_daemon;
+       int timeout_seconds;
+       union {
+               struct {
+                       /*bool unmount_complete;*/
+                       pid_t daemon_pid;
+                       int mount_flags;
+                       int status;
+               } unmount;
+               struct {
+                       /*int unmount_flags;*/
+                       struct wimfs_context *wimfs_ctx;
+               } daemon;
+       };
 };
 
-struct unmount_response {
-       struct unmount_message_header hdr;
-       struct {
-               int32_t status;
-               u32 mount_flags;
-       } data;
-};
+static int send_unmount_request_msg(mqd_t mq, int unmount_flags)
+{
+       DEBUG("Sending unmount request msg");
+       struct msg_unmount_request msg = {
+               .hdr = {
+                       .min_version = WIMLIB_MAKEVERSION(1, 2, 0),
+                       .cur_version = WIMLIB_VERSION_CODE,
+                       .msg_type    = MSG_TYPE_UNMOUNT_REQUEST,
+                       .msg_size    = sizeof(msg),
+               },
+               .unmount_flags = unmount_flags,
+       };
 
-static void inode_update_lte_ptr(struct inode *inode,
-                                struct lookup_table_entry *old_lte,
-                                struct lookup_table_entry *new_lte)
-{
-       if (inode->lte == old_lte) {
-               inode->lte = new_lte;
-       } else {
-               for (unsigned i = 0; i < inode->num_ads; i++) {
-                       if (inode->ads_entries[i].lte == old_lte) {
-                               inode->ads_entries[i].lte = new_lte;
-                               break;
-                       }
-               }
+       if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) {
+               ERROR_WITH_ERRNO("Failed to communicate with filesystem daemon");
+               return WIMLIB_ERR_MQUEUE;
        }
+       return 0;
 }
 
-static int update_lte_of_staging_file(struct lookup_table_entry *lte,
-                                     struct lookup_table *table)
+static int send_daemon_info_msg(mqd_t mq, pid_t pid, int mount_flags)
 {
-       struct lookup_table_entry *duplicate_lte;
-       int ret;
-       u8 hash[SHA1_HASH_SIZE];
-       struct stat stbuf;
+       DEBUG("Sending daemon info msg (pid = %d, mount_flags=%x)",
+             pid, mount_flags);
 
-       ret = sha1sum(lte->staging_file_name, hash);
-       if (ret != 0)
-               return ret;
-       lookup_table_unlink(table, lte);
-       duplicate_lte = __lookup_resource(table, hash);
-       if (duplicate_lte) {
-               /* Merge duplicate lookup table entries */
-               duplicate_lte->refcnt += lte->refcnt;
-               inode_update_lte_ptr(lte->lte_inode, lte, duplicate_lte);
-               free_lookup_table_entry(lte);
-       } else {
-               if (stat(lte->staging_file_name, &stbuf) != 0) {
-                       ERROR_WITH_ERRNO("Failed to stat `%s'", lte->staging_file_name);
-                       return WIMLIB_ERR_STAT;
-               }
-               if (stbuf.st_size == 0) {
-                       /* Zero-length stream.  No lookup table entry needed. */
-                       inode_update_lte_ptr(lte->lte_inode, lte, NULL);
-                       free_lookup_table_entry(lte);
-               } else {
-                       wimlib_assert(&lte->file_on_disk == &lte->staging_file_name);
-                       lte->resource_entry.original_size = stbuf.st_size;
-                       lte->resource_entry.size = stbuf.st_size;
-                       lte->resource_location = RESOURCE_IN_FILE_ON_DISK;
-                       lte->file_on_disk_fp = NULL;
-                       copy_hash(lte->hash, hash);
-                       lookup_table_insert(table, lte);
-               }
+       struct msg_daemon_info msg = {
+               .hdr = {
+                       .min_version = WIMLIB_MAKEVERSION(1, 2, 0),
+                       .cur_version = WIMLIB_VERSION_CODE,
+                       .msg_type = MSG_TYPE_DAEMON_INFO,
+                       .msg_size = sizeof(msg),
+               },
+               .daemon_pid = pid,
+               .mount_flags = mount_flags,
+       };
+       if (mq_send(mq, (void*)&msg, sizeof(msg), 1)) {
+               ERROR_WITH_ERRNO("Failed to send daemon info to unmount process");
+               return WIMLIB_ERR_MQUEUE;
        }
        return 0;
 }
 
-static int inode_close_fds(struct inode *inode)
+static void send_unmount_finished_msg(mqd_t mq, int status)
 {
-       u16 num_opened_fds = inode->num_opened_fds;
-       for (u16 i = 0, j = 0; j < num_opened_fds; i++) {
-               struct wimlib_fd *fd = inode->fds[i];
-               if (fd) {
-                       wimlib_assert(fd->f_inode == inode);
-                       int ret = close_wimlib_fd(fd);
-                       if (ret != 0)
-                               return ret;
-                       j++;
-               }
-       }
-       return 0;
+       DEBUG("Sending unmount finished msg");
+       struct msg_unmount_finished msg = {
+               .hdr = {
+                       .min_version = WIMLIB_MAKEVERSION(1, 2, 0),
+                       .cur_version = WIMLIB_VERSION_CODE,
+                       .msg_type = MSG_TYPE_UNMOUNT_FINISHED,
+                       .msg_size = sizeof(msg),
+               },
+               .status = status,
+       };
+       if (mq_send(mq, (void*)&msg, sizeof(msg), 1))
+               ERROR_WITH_ERRNO("Failed to send status to unmount process");
 }
 
-/* Overwrites the WIM file, with changes saved. */
-static int rebuild_wim(struct wimfs_context *ctx, int write_flags)
+static int msg_unmount_request_handler(const void *_msg,
+                                      struct msg_handler_context *handler_ctx)
 {
+       const struct msg_unmount_request *msg;
+       struct wimfs_context *wimfs_ctx;
+       int status = 0;
        int ret;
-       struct lookup_table_entry *lte, *tmp;
-       WIMStruct *w = ctx->wim;
+       int unmount_flags;
 
-       DEBUG("Closing all staging file descriptors.");
-       list_for_each_entry_safe(lte, tmp, &ctx->staging_list, staging_list) {
-               ret = inode_close_fds(lte->lte_inode);
-               if (ret != 0)
-                       return ret;
+       DEBUG("Handling unmount request msg");
+
+       msg = _msg;
+       wimfs_ctx = handler_ctx->daemon.wimfs_ctx;
+
+       if (msg->hdr.msg_size < sizeof(*msg)) {
+               status = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+               goto out;
        }
 
-       DEBUG("Calculating SHA1 checksums for all new staging files.");
-       list_for_each_entry(lte, &ctx->staging_list, staging_list) {
-               ret = update_lte_of_staging_file(lte, w->lookup_table);
-               if (ret != 0)
-                       return ret;
+       unmount_flags = msg->unmount_flags;
+
+       ret = send_daemon_info_msg(wimfs_ctx->daemon_to_unmount_mq, getpid(),
+                                  wimfs_ctx->mount_flags);
+       if (ret != 0) {
+               status = ret;
+               goto out;
        }
 
-       xml_update_image_info(w, w->current_image);
-       ret = wimlib_overwrite(w, write_flags, 0, NULL);
-       if (ret != 0)
-               ERROR("Failed to commit changes to mounted WIM image");
-       return ret;
+       if (wimfs_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(wimfs_ctx, write_flags);
+               }
+       } else {
+               DEBUG("Read-only mount");
+               status = 0;
+       }
+
+out:
+       if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
+               ret = delete_staging_dir(wimfs_ctx);
+               if (ret != 0) {
+                       ERROR("Failed to delete the staging directory");
+                       if (status == 0)
+                               status = ret;
+               }
+       }
+       send_unmount_finished_msg(wimfs_ctx->daemon_to_unmount_mq, status);
+       return MSG_BREAK_LOOP;
+}
+
+static int msg_daemon_info_handler(const void *_msg,
+                                  struct msg_handler_context *handler_ctx)
+{
+       const struct msg_daemon_info *msg = _msg;
+       DEBUG("Handling daemon info msg");
+       if (msg->hdr.msg_size < sizeof(*msg))
+               return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+       handler_ctx->unmount.daemon_pid = msg->daemon_pid;
+       handler_ctx->unmount.mount_flags = msg->mount_flags;
+       handler_ctx->timeout_seconds = 1;
+       DEBUG("pid of daemon is %d; mount flags were %#x",
+             handler_ctx->unmount.daemon_pid,
+             handler_ctx->unmount.mount_flags);
+       return 0;
 }
 
-/* Send a message to the filesystem daemon saying whether to commit or not. */
-static int send_unmount_request(struct wimfs_context *ctx, int unmount_flags)
+static int msg_unmount_finished_handler(const void *_msg,
+                                       struct msg_handler_context *handler_ctx)
 {
-       int ret;
-       struct unmount_request msg;
-
-       msg.hdr.min_version    = WIMLIB_MAKEVERSION(1, 2, 0);
-       msg.hdr.cur_version    = WIMLIB_VERSION_CODE;
-       msg.hdr.data_size      = sizeof(msg.data);
-       msg.data.unmount_flags = unmount_flags;
+       const struct msg_unmount_finished *msg = _msg;
+       DEBUG("Handling unmount finished message");
+       if (msg->hdr.msg_size < sizeof(*msg))
+               return WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
+       handler_ctx->unmount.status = msg->status;
+       DEBUG("status is %d", handler_ctx->unmount.status);
+       return MSG_BREAK_LOOP;
+}
 
-       ret = mq_send(ctx->unmount_to_daemon_mq, (void*)&msg,
-                     sizeof(msg), 1);
-       if (ret == -1) {
-               ERROR_WITH_ERRNO("Failed to notify filesystem daemon whether "
-                                "we want to commit changes or not");
-               return WIMLIB_ERR_MQUEUE;
+static int unmount_timed_out_cb(struct msg_handler_context *handler_ctx)
+{
+       if (handler_ctx->unmount.daemon_pid == 0) {
+               goto out_crashed;
+       } else {
+               errno = 0;
+               kill(handler_ctx->unmount.daemon_pid, 0);
+               if (errno == ESRCH) {
+                       goto out_crashed;
+               } else if (errno != 0) {
+                       ERROR_WITH_ERRNO("Error determining state of "
+                                        "filesystem daemon");
+                       return WIMLIB_ERR_MQUEUE;
+               } else {
+                       DEBUG("Filesystem daemon is still alive... "
+                             "Waiting another %d seconds",
+                             handler_ctx->timeout_seconds);
+                       return 0;
+               }
        }
-       return 0;
+out_crashed:
+       ERROR("The filesystem daemon has crashed!  Changes to the "
+             "WIM may not have been commited.");
+       return WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED;
 }
 
-static int receive_unmount_request(struct wimfs_context *ctx,
-                                  int *unmount_flags)
+static int daemon_timed_out_cb(struct msg_handler_context *handler_ctx)
+{
+       DEBUG("Timed out");
+       return WIMLIB_ERR_TIMEOUT;
+}
+
+typedef int (*msg_handler_t)(const void *_msg,
+                            struct msg_handler_context *handler_ctx);
+
+struct msg_handler_callbacks {
+       int (*timed_out)(struct msg_handler_context *);
+       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_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 *handler_ctx,
+                          const msg_handler_t msg_handlers[],
+                          long mailbox_size, void *mailbox)
 {
-       void *mailbox;
-       long msgsize;
        struct timeval now;
        struct timespec timeout;
        ssize_t bytes_received;
-       struct unmount_request *msg;
+       struct unmount_msg_hdr *hdr;
        int ret;
 
-       ret = get_mailbox(ctx->unmount_to_daemon_mq,
-                         sizeof(struct unmount_request),
-                         &msgsize, &mailbox);
-       if (ret != 0)
-               return ret;
-
-       /* Wait at most 3 seconds before giving up and discarding changes. */
        gettimeofday(&now, NULL);
-       timeout.tv_sec = now.tv_sec + 3;
+       timeout.tv_sec = now.tv_sec + handler_ctx->timeout_seconds;
        timeout.tv_nsec = now.tv_usec * 1000;
 
-       bytes_received = mq_timedreceive(ctx->unmount_to_daemon_mq, mailbox,
-                                        msgsize, NULL, &timeout);
-       msg = mailbox;
+       bytes_received = mq_timedreceive(mq, mailbox,
+                                        mailbox_size, NULL, &timeout);
+       hdr = mailbox;
        if (bytes_received == -1) {
                ERROR_WITH_ERRNO("mq_timedreceive()");
-               ERROR("Not committing.");
-               ret = WIMLIB_ERR_MQUEUE;
-       } else if (bytes_received < sizeof(struct unmount_message_header) ||
-                  bytes_received != sizeof(struct unmount_message_header) + msg->hdr.data_size) {
-               ERROR("Received invalid unmount request");
+               if (errno == ETIMEDOUT)
+                       ret = WIMLIB_ERR_TIMEOUT;
+               else
+                       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 < msg->hdr.min_version) {
-               ERROR("Cannot understand the unmount request. "
-                     "Please upgrade wimlib to at least v%d.%d.%d",
-                     WIMLIB_GET_MAJOR_VERSION(msg->hdr.min_version),
-                     WIMLIB_GET_MINOR_VERSION(msg->hdr.min_version),
-                     WIMLIB_GET_PATCH_VERSION(msg->hdr.min_version));
+       } 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 {
-               *unmount_flags = msg->data.unmount_flags;
-               ret = 0;
+               ret = msg_handlers[hdr->msg_type](mailbox, handler_ctx);
        }
-       FREE(mailbox);
        return ret;
 }
 
-static void send_unmount_response(struct wimfs_context *ctx, int status)
-{
-       struct unmount_response response;
-       response.hdr.min_version  = WIMLIB_MAKEVERSION(1, 2, 0);
-       response.hdr.cur_version  = WIMLIB_VERSION_CODE;
-       response.hdr.data_size    = sizeof(response.data);
-       response.data.status      = status;
-       response.data.mount_flags = ctx->mount_flags;
-       if (mq_send(ctx->daemon_to_unmount_mq, (void*)&response,
-                   sizeof(response), 1))
-               ERROR_WITH_ERRNO("Failed to send status to unmount process");
-}
-
-/* Wait for a message from the filesytem daemon indicating whether  the
- * filesystem was unmounted successfully.  This may
- * take a long time if a big WIM file needs to be rewritten.
- *
- * Wait at most 600??? seconds before giving up and returning an error.  Either
- * it's a really big WIM file, or (more likely) the filesystem daemon has
- * crashed or failed for some reason.
- *
- * XXX come up with some method to determine if the filesystem daemon has really
- * crashed or not.
- *
- * XXX Idea: have mount daemon write its PID into the WIM file header?  No, this
- * wouldn't work because we know the directory but not the WIM file...
- */
-static int receive_unmount_response(struct wimfs_context *ctx)
+static int message_loop(mqd_t mq,
+                       const struct msg_handler_callbacks *callbacks,
+                       struct msg_handler_context *handler_ctx)
 {
+       static const size_t MAX_MSG_SIZE = 512;
        long msgsize;
        void *mailbox;
-       struct timeval now;
-       struct timespec timeout;
-       ssize_t bytes_received;
        int ret;
-       struct unmount_response *msg;
 
-       ret = get_mailbox(ctx->daemon_to_unmount_mq,
-                         sizeof(struct unmount_response), &msgsize, &mailbox);
+       DEBUG("Entering message loop");
+
+       ret = get_mailbox(mq, MAX_MSG_SIZE, &msgsize, &mailbox);
        if (ret != 0)
                return ret;
-
-       DEBUG("Waiting for message telling us whether the unmount was "
-             "successful or not.");
-
-       gettimeofday(&now, NULL);
-       timeout.tv_sec = now.tv_sec + 600;
-       timeout.tv_nsec = now.tv_usec * 1000;
-       bytes_received = mq_timedreceive(ctx->daemon_to_unmount_mq,
-                                        mailbox, msgsize, NULL, &timeout);
-       msg = mailbox;
-       if (bytes_received == -1) {
-               if (errno == ETIMEDOUT) {
-                       ERROR("Timed out- probably the filesystem daemon "
-                             "crashed and the WIM was not written "
-                             "successfully.");
-                       ret = WIMLIB_ERR_TIMEOUT;
+       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("mq_receive()");
-                       ret = WIMLIB_ERR_MQUEUE;
-               }
-       } else if (bytes_received < sizeof(struct unmount_message_header) ||
-                  bytes_received != sizeof(struct unmount_message_header) + msg->hdr.data_size ||
-                  WIMLIB_VERSION_CODE < msg->hdr.min_version) {
-               ERROR("Received invalid unmount response");
-               ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       } else if (WIMLIB_VERSION_CODE < msg->hdr.min_version) {
-               ERROR("Cannot understand the unmount response. "
-                     "Please upgrade wimlib to at least v%d.%d.%d",
-                     WIMLIB_GET_MAJOR_VERSION(msg->hdr.min_version),
-                     WIMLIB_GET_MINOR_VERSION(msg->hdr.min_version),
-                     WIMLIB_GET_PATCH_VERSION(msg->hdr.min_version));
-               ret = WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE;
-       } else {
-               ret = msg->data.status;
-               if (ret) {
-                       ERROR("A failure status (%d) was returned "
-                             "by the filesystem daemon", ret);
+                       ERROR_WITH_ERRNO("Error communicating with "
+                                        "filesystem daemon");
+                       break;
                }
        }
        FREE(mailbox);
+       DEBUG("Exiting message loop");
        return ret;
 }
 
@@ -1249,47 +1408,26 @@ static int wimfs_chmod(const char *path, mode_t mask)
 /* 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. */
+       struct wimfs_context *wimfs_ctx;
 
-       int ret;
-       int unmount_flags;
-       struct wimfs_context *ctx;
-       int status;
+       wimfs_ctx = wimfs_get_context();
 
-       ctx = wimfs_get_context();
-       if (open_message_queues(ctx, true))
+       if (open_message_queues(wimfs_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);
-       close_message_queues(ctx);
+       struct msg_handler_context handler_ctx = {
+               .is_daemon = true,
+               .timeout_seconds = 5,
+               .daemon = {
+                       .wimfs_ctx = wimfs_ctx,
+               },
+       };
+
+       message_loop(wimfs_ctx->unmount_to_daemon_mq,
+                    &daemon_msg_handler_callbacks,
+                    &handler_ctx);
+
+       close_message_queues(wimfs_ctx);
 }
 
 #if 0
@@ -2314,30 +2452,44 @@ WIMLIBAPI int wimlib_unmount_image(const char *dir, int unmount_flags,
                                   wimlib_progress_func_t progress_func)
 {
        int ret;
-       struct wimfs_context ctx;
+       struct wimfs_context wimfs_ctx;
 
-       init_wimfs_context(&ctx);
+       init_wimfs_context(&wimfs_ctx);
 
-       ret = set_message_queue_names(&ctx, dir);
+       ret = set_message_queue_names(&wimfs_ctx, dir);
        if (ret != 0)
                goto out;
 
-       ret = open_message_queues(&ctx, false);
+       ret = open_message_queues(&wimfs_ctx, false);
        if (ret != 0)
                goto out_free_message_queue_names;
 
-       ret = send_unmount_request(&ctx, unmount_flags);
+       ret = send_unmount_request_msg(wimfs_ctx.unmount_to_daemon_mq,
+                                      unmount_flags);
        if (ret != 0)
                goto out_close_message_queues;
 
        ret = execute_fusermount(dir);
        if (ret != 0)
                goto out_close_message_queues;
-       ret = receive_unmount_response(&ctx);
+
+       struct msg_handler_context handler_ctx = {
+               .is_daemon = false,
+               .timeout_seconds = 5,
+               .unmount = {
+                       .daemon_pid = 0,
+               },
+       };
+
+       ret = message_loop(wimfs_ctx.daemon_to_unmount_mq,
+                          &unmount_msg_handler_callbacks,
+                          &handler_ctx);
+       if (ret == 0)
+               ret = handler_ctx.unmount.status;
 out_close_message_queues:
-       close_message_queues(&ctx);
+       close_message_queues(&wimfs_ctx);
 out_free_message_queue_names:
-       free_message_queue_names(&ctx);
+       free_message_queue_names(&wimfs_ctx);
 out:
        return ret;
 }
index ed47795..7858a1c 100644 (file)
@@ -120,6 +120,8 @@ static const char *error_strings[] = {
                = "Failed to decompress compressed data",
        [WIMLIB_ERR_DELETE_STAGING_DIR]
                = "Failed to delete staging directory",
+       [WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED]
+               = "The process servicing the mounted WIM has crashed",
        [WIMLIB_ERR_FORK]
                = "Failed to fork another process",
        [WIMLIB_ERR_FUSE]
@@ -203,8 +205,6 @@ static const char *error_strings[] = {
                = "The WIM is part of a split WIM, which is not supported for this operation",
        [WIMLIB_ERR_STAT]
                = "Could not read the metadata for a file or directory",
-       [WIMLIB_ERR_TIMEOUT]
-               = "Timed out",
        [WIMLIB_ERR_UNKNOWN_VERSION]
                = "The WIM file is marked with an unknown version number",
        [WIMLIB_ERR_UNSUPPORTED]
index 083ddb2..8a6dc4d 100644 (file)
 
 #ifdef __GNUC__
 #      define WIMLIBAPI __attribute__((visibility("default")))
-#      define NOINLINE __attribute__((noinline))
 #      define ALWAYS_INLINE inline __attribute__((always_inline))
+#      define PACKED __attribute__((packed))
 #      define FORMAT(type, format_str, args_start) \
                        __attribute__((format(type, format_str, args_start)))
 #      if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
 #              define COLD     __attribute__((cold))
-#              define HOT      __attribute__((hot))
 #      else
 #              define COLD
-#              define HOT
 #      endif
 #else
 #      define WIMLIBAPI
-#      define NOINLINE
 #      define ALWAYS_INLINE inline
 #      define FORMAT(type, format_str, args_start)
 #      define COLD
-#      define HOT
+#      define PACKED
 #endif /* __GNUC__ */
 
 
index 8b64915..4225633 100644 (file)
@@ -685,6 +685,7 @@ enum wimlib_error_code {
        WIMLIB_ERR_COMPRESSED_LOOKUP_TABLE,
        WIMLIB_ERR_DECOMPRESSION,
        WIMLIB_ERR_DELETE_STAGING_DIR,
+       WIMLIB_ERR_FILESYSTEM_DAEMON_CRASHED,
        WIMLIB_ERR_FORK,
        WIMLIB_ERR_FUSE,
        WIMLIB_ERR_FUSERMOUNT,