]> wimlib.net Git - wimlib/blobdiff - src/mount.c
Make WIM mounting work on FreeBSD
[wimlib] / src / mount.c
index 82abe7d90b4640e29742facfcd6aac0539bb9ba2..6efe84c6a375468654f246b3313b055d5782f682 100644 (file)
  * This file is part of wimlib, a library for working with WIM files.
  *
  * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option)
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
  * any later version.
  *
  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more
  * details.
  *
- * You should have received a copy of the GNU Lesser General Public License
+ * You should have received a copy of the GNU General Public License
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
 #include "wimlib_internal.h"
 
 #ifdef WITH_FUSE
+
 #include "sha1.h"
 #include "lookup_table.h"
 #include "xml.h"
 #include <fuse.h>
 #include <ftw.h>
 #include <mqueue.h>
+#include <utime.h>
+
+#ifdef ENABLE_XATTR
 #include <attr/xattr.h>
+#endif
 
 struct wimlib_fd {
        u16 idx;
@@ -589,8 +594,8 @@ static inline int delete_staging_dir()
  * commands */
 static char *unmount_to_daemon_mq_name;
 static char *daemon_to_unmount_mq_name;
-static int unmount_to_daemon_mq;
-static int daemon_to_unmount_mq;
+static mqd_t unmount_to_daemon_mq;
+static mqd_t daemon_to_unmount_mq;
 
 /* Simple function that returns the concatenation of 4 strings. */
 static char *strcat_dup(const char *s1, const char *s2, const char *s3, 
@@ -678,7 +683,7 @@ static int open_message_queues(bool daemon)
        unmount_to_daemon_mq = mq_open(unmount_to_daemon_mq_name, flags, 
                                       0700, NULL);
 
-       if (unmount_to_daemon_mq == -1) {
+       if (unmount_to_daemon_mq == (mqd_t)-1) {
                ERROR_WITH_ERRNO("mq_open()");
                ret = WIMLIB_ERR_MQUEUE;
                goto err2;
@@ -692,7 +697,7 @@ static int open_message_queues(bool daemon)
        daemon_to_unmount_mq = mq_open(daemon_to_unmount_mq_name, flags, 
                                       0700, NULL);
 
-       if (daemon_to_unmount_mq == -1) {
+       if (daemon_to_unmount_mq == (mqd_t)-1) {
                ERROR_WITH_ERRNO("mq_open()");
                ret = WIMLIB_ERR_MQUEUE;
                goto err3;
@@ -950,10 +955,11 @@ done:
        close_message_queues();
 }
 
+#if 0
 static int wimfs_fallocate(const char *path, int mode,
                           off_t offset, off_t len, struct fuse_file_info *fi)
 {
-       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
        wimlib_assert(fd->staging_fd != -1);
        return fallocate(fd->staging_fd, mode, offset, len);
 }
@@ -961,14 +967,15 @@ static int wimfs_fallocate(const char *path, int mode,
 static int wimfs_fgetattr(const char *path, struct stat *stbuf,
                          struct fuse_file_info *fi)
 {
-       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
        return dentry_to_stbuf(fd->dentry, stbuf);
 }
+#endif
 
 static int wimfs_ftruncate(const char *path, off_t size,
                           struct fuse_file_info *fi)
 {
-       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
        int ret = ftruncate(fd->staging_fd, size);
        if (ret != 0)
                return ret;
@@ -992,6 +999,7 @@ static int wimfs_getattr(const char *path, struct stat *stbuf)
        return dentry_to_stbuf(dentry, stbuf);
 }
 
+#ifdef ENABLE_XATTR
 /* Read an alternate data stream through the XATTR interface, or get its size */
 static int wimfs_getxattr(const char *path, const char *name, char *value,
                          size_t size)
@@ -1028,6 +1036,7 @@ static int wimfs_getxattr(const char *path, const char *name, char *value,
                return -EIO;
        return res_size;
 }
+#endif
 
 /* Create a hard link */
 static int wimfs_link(const char *to, const char *from)
@@ -1081,6 +1090,7 @@ static int wimfs_link(const char *to, const char *from)
        return 0;
 }
 
+#ifdef ENABLE_XATTR
 static int wimfs_listxattr(const char *path, char *list, size_t size)
 {
        struct dentry *dentry;
@@ -1113,6 +1123,7 @@ static int wimfs_listxattr(const char *path, char *list, size_t size)
                return p - list;
        }
 }
+#endif
 
 /* 
  * Create a directory in the WIM.  
@@ -1255,7 +1266,7 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi)
                        return -errno;
                }
        }
-       fi->fh = (uint64_t)fd;
+       fi->fh = (uintptr_t)fd;
        return 0;
 }
 
@@ -1270,7 +1281,7 @@ static int wimfs_opendir(const char *path, struct fuse_file_info *fi)
        if (!dentry_is_directory(dentry))
                return -ENOTDIR;
        dentry->num_times_opened++;
-       fi->fh = (uint64_t)dentry;
+       fi->fh = (uintptr_t)dentry;
        return 0;
 }
 
@@ -1281,7 +1292,7 @@ static int wimfs_opendir(const char *path, struct fuse_file_info *fi)
 static int wimfs_read(const char *path, char *buf, size_t size, 
                      off_t offset, struct fuse_file_info *fi)
 {
-       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
 
        if (!fd) {
                /* Empty file with no lookup table entry on read-only mounted
@@ -1333,7 +1344,7 @@ static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 {
        struct dentry *parent, *child;
        
-       parent = (struct dentry*)fi->fh;
+       parent = (struct dentry*)(uintptr_t)fi->fh;
        wimlib_assert(parent);
        child = parent->children;
 
@@ -1367,10 +1378,35 @@ static int wimfs_readlink(const char *path, char *buf, size_t buf_len)
        return ret;
 }
 
+/* Creation time, write time, access time */
+static void
+dentry_link_group_set_times(struct dentry *dentry, u64 times[3])
+{
+       struct dentry *cur = dentry;
+       do {
+               if (times[0])
+                       cur->creation_time = times[0];
+               if (times[1])
+                       cur->last_write_time = times[1];
+               if (times[2])
+                       cur->last_access_time = times[2];
+       } while ((cur = container_of(dentry->link_group_list.next,
+                                    struct dentry,
+                                    link_group_list)) != dentry);
+}
+
+static void
+dentry_link_group_update_times(struct dentry *dentry)
+{
+       u64 now = get_wim_timestamp();
+       u64 times[3] = {now, now, now};
+       dentry_link_group_set_times(dentry, times);
+}
+
 /* Close a file. */
 static int wimfs_release(const char *path, struct fuse_file_info *fi)
 {
-       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
 
        if (!fd) {
                /* Empty file with no lookup table entry on read-only mounted
@@ -1379,18 +1415,12 @@ static int wimfs_release(const char *path, struct fuse_file_info *fi)
                return 0;
        }
 
-       if (flags_writable(fi->flags) && fd->dentry) {
-               u64 now = get_wim_timestamp();
-               fd->dentry->last_access_time = now;
-               fd->dentry->last_write_time = now;
-       }
-
        return close_wimlib_fd(fd);
 }
 
 static int wimfs_releasedir(const char *path, struct fuse_file_info *fi)
 {
-       struct dentry *dentry = (struct dentry *)fi->fh;
+       struct dentry *dentry = (struct dentry *)(uintptr_t)fi->fh;
 
        wimlib_assert(dentry);
        wimlib_assert(dentry->num_times_opened);
@@ -1399,6 +1429,7 @@ static int wimfs_releasedir(const char *path, struct fuse_file_info *fi)
        return 0;
 }
 
+#ifdef ENABLE_XATTR
 /* Remove an alternate data stream through the XATTR interface */
 static int wimfs_removexattr(const char *path, const char *name)
 {
@@ -1421,6 +1452,7 @@ static int wimfs_removexattr(const char *path, const char *name)
        remove_ads(dentry, ads_entry, w->lookup_table);
        return 0;
 }
+#endif
 
 /* Renames a file or directory.  See rename (3) */
 static int wimfs_rename(const char *from, const char *to)
@@ -1508,6 +1540,7 @@ static int wimfs_rmdir(const char *path)
        return 0;
 }
 
+#ifdef ENABLE_XATTR
 /* Write an alternate data stream through the XATTR interface */
 static int wimfs_setxattr(const char *path, const char *name,
                          const char *value, size_t size, int flags)
@@ -1571,6 +1604,7 @@ static int wimfs_setxattr(const char *path, const char *name,
        new_ads_entry->lte = lte;
        return 0;
 }
+#endif
 
 static int wimfs_symlink(const char *to, const char *from)
 {
@@ -1642,7 +1676,7 @@ static int wimfs_truncate(const char *path, off_t size)
                ret = extract_resource_to_staging_dir(dentry, stream_idx,
                                                      &lte, size);
        }
-       dentry_update_all_timestamps(dentry);
+       dentry_link_group_update_times(dentry);
        return ret;
 }
 
@@ -1675,6 +1709,7 @@ static int wimfs_unlink(const char *path)
        return 0;
 }
 
+#ifdef HAVE_UTIMENSAT
 /* 
  * Change the timestamp on a file dentry. 
  *
@@ -1685,20 +1720,36 @@ static int wimfs_utimens(const char *path, const struct timespec tv[2])
        struct dentry *dentry = get_dentry(w, path);
        if (!dentry)
                return -ENOENT;
+       u64 times[3] = {0, 0, 0};
        if (tv[0].tv_nsec != UTIME_OMIT) {
                if (tv[0].tv_nsec == UTIME_NOW)
-                       dentry->last_access_time = get_wim_timestamp();
+                       times[2] = get_wim_timestamp();
                else
-                       dentry->last_access_time = timespec_to_wim_timestamp(&tv[0]);
+                       times[2] = timespec_to_wim_timestamp(&tv[0]);
        }
        if (tv[1].tv_nsec != UTIME_OMIT) {
                if (tv[1].tv_nsec == UTIME_NOW)
-                       dentry->last_write_time = get_wim_timestamp();
+                       times[1] = get_wim_timestamp();
                else
-                       dentry->last_write_time = timespec_to_wim_timestamp(&tv[1]);
+                       times[1] = timespec_to_wim_timestamp(&tv[1]);
        }
+       dentry_link_group_set_times(dentry, times);
+       return 0;
+}
+#else
+static int wimfs_utime(const char *path, struct utimbuf *times)
+{
+       struct dentry *dentry = get_dentry(w, path);
+       if (!dentry)
+               return -ENOENT;
+       u64 wim_times[3];
+       wim_times[0] = 0;
+       wim_times[1] = unix_timestamp_to_wim(times->modtime);
+       wim_times[2] = unix_timestamp_to_wim(times->actime);
+       dentry_link_group_set_times(dentry, wim_times);
        return 0;
 }
+#endif
 
 /* Writes to a file in the WIM filesystem. 
  * It may be an alternate data stream, but here we don't even notice because we
@@ -1706,7 +1757,7 @@ static int wimfs_utimens(const char *path, const struct timespec tv[2])
 static int wimfs_write(const char *path, const char *buf, size_t size, 
                       off_t offset, struct fuse_file_info *fi)
 {
-       struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
        int ret;
 
        wimlib_assert(fd);
@@ -1723,19 +1774,31 @@ static int wimfs_write(const char *path, const char *buf, size_t size,
        if (ret == -1)
                return -errno;
 
+       if (fd->dentry) {
+               u64 now = get_wim_timestamp();
+               u64 times[3] = {0, now, now};
+               dentry_link_group_set_times(fd->dentry, times);
+       }
+
        return ret;
 }
 
 static struct fuse_operations wimfs_operations = {
        .access      = wimfs_access,
        .destroy     = wimfs_destroy,
+#if 0
        .fallocate   = wimfs_fallocate,
        .fgetattr    = wimfs_fgetattr,
+#endif
        .ftruncate   = wimfs_ftruncate,
        .getattr     = wimfs_getattr,
+#ifdef ENABLE_XATTR
        .getxattr    = wimfs_getxattr,
+#endif
        .link        = wimfs_link,
+#ifdef ENABLE_XATTR
        .listxattr   = wimfs_listxattr,
+#endif
        .mkdir       = wimfs_mkdir,
        .mknod       = wimfs_mknod,
        .open        = wimfs_open,
@@ -1745,14 +1808,22 @@ static struct fuse_operations wimfs_operations = {
        .readlink    = wimfs_readlink,
        .release     = wimfs_release,
        .releasedir  = wimfs_releasedir,
+#ifdef ENABLE_XATTR
        .removexattr = wimfs_removexattr,
+#endif
        .rename      = wimfs_rename,
        .rmdir       = wimfs_rmdir,
+#ifdef ENABLE_XATTR
        .setxattr    = wimfs_setxattr,
+#endif
        .symlink     = wimfs_symlink,
        .truncate    = wimfs_truncate,
        .unlink      = wimfs_unlink,
+#ifdef HAVE_UTIMENSAT
        .utimens     = wimfs_utimens,
+#else
+       .utime       = wimfs_utime,
+#endif
        .write       = wimfs_write,
 };
 
@@ -1799,35 +1870,54 @@ static int check_lte_refcnt(struct lookup_table_entry *lte, void *ignore)
 
 /* Mounts a WIM file. */
 WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, 
-                          int flags)
+                          int flags, WIMStruct **additional_swms,
+                          unsigned num_additional_swms)
 {
        int argc = 0;
        char *argv[16];
        int ret;
        char *p;
+       struct lookup_table *joined_tab, *wim_tab_save;
 
        DEBUG("Mount: wim = %p, image = %d, dir = %s, flags = %d, ",
                        wim, image, dir, flags);
 
-       if (!dir)
+       if (!wim || !dir)
                return WIMLIB_ERR_INVALID_PARAM;
 
+       ret = verify_swm_set(wim, additional_swms, num_additional_swms);
+       if (ret != 0)
+               return ret;
+
+       if (num_additional_swms) {
+               ret = new_joined_lookup_table(wim, additional_swms,
+                                             num_additional_swms,
+                                             &joined_tab);
+               if (ret != 0)
+                       return ret;
+               wim_tab_save = wim->lookup_table;
+               wim->lookup_table = joined_tab;
+       }
+
        ret = wimlib_select_image(wim, image);
 
        if (ret != 0)
-               return ret;
+               goto out;
 
        DEBUG("Selected image %d", image);
 
        next_link_group_id = assign_link_group_ids(wim->image_metadata[image - 1].lgt);
 
+       DEBUG("Resolving lookup table entries");
        /* Resolve all the lookup table entries of the dentry tree */
        for_dentry_in_tree(wim_root_dentry(wim), dentry_resolve_ltes,
                           wim->lookup_table);
 
+       DEBUG("Checking lookup table entry reference counts");
+
        ret = for_lookup_table_entry(wim->lookup_table, check_lte_refcnt, NULL);
        if (ret != 0)
-               return ret;
+               goto out;
 
        if (flags & WIMLIB_MOUNT_FLAG_READWRITE)
                wim_get_current_image_metadata(wim)->modified = true;
@@ -1837,16 +1927,33 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir,
                       WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)))
                flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
 
+       DEBUG("Getting current directory");
+
        mount_dir = dir;
        working_directory = getcwd(NULL, 0);
        if (!working_directory) {
                ERROR_WITH_ERRNO("Could not determine current directory");
-               return WIMLIB_ERR_NOTDIR;
+               ret = WIMLIB_ERR_NOTDIR;
+               goto out;
        }
 
+       DEBUG("Closing POSIX message queues");
+       /* XXX hack to get rid of the message queues if they already exist for
+        * some reason (maybe left over from a previous mount that wasn't
+        * unmounted correctly) */
+       ret = open_message_queues(true);
+       if (ret != 0)
+               goto out;
+       close_message_queues();
+
+       DEBUG("Preparing arguments to fuse_main()");
+
+
        p = STRDUP(dir);
-       if (!p)
-               return WIMLIB_ERR_NOMEM;
+       if (!p) {
+               ret = WIMLIB_ERR_NOMEM;
+               goto out;
+       }
 
        argv[argc++] = "imagex";
        argv[argc++] = p;
@@ -1870,12 +1977,14 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir,
                make_staging_dir();
                if (!staging_dir_name) {
                        FREE(p);
-                       return WIMLIB_ERR_MKDIR;
+                       ret = WIMLIB_ERR_MKDIR;
+                       goto out;
                }
        } else {
                /* Read-only mount */
                strcat(optstring, ",ro");
        }
+       strcat(optstring, ",subtype=wimfs,attr_timeout=0");
        argv[argc] = NULL;
 
 #ifdef ENABLE_DEBUG
@@ -1896,8 +2005,14 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir,
        mount_flags = flags;
 
        ret = fuse_main(argc, argv, &wimfs_operations, NULL);
-
-       return (ret == 0) ? 0 : WIMLIB_ERR_FUSE;
+       if (ret)
+               ret = WIMLIB_ERR_FUSE;
+out:
+       if (num_additional_swms) {
+               free_lookup_table(wim->lookup_table);
+               wim->lookup_table = wim_tab_save;
+       }
+       return ret;
 }
 
 
@@ -1916,6 +2031,30 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags)
        int msgsize;
        int errno_save;
 
+       mount_dir = dir;
+
+       /* Open message queues between the unmount process and the
+        * filesystem daemon. */
+       ret = open_message_queues(false);
+       if (ret != 0)
+               return ret;
+
+       /* Send a message to the filesystem saying whether to commit or
+        * not. */
+       msg[0] = (flags & WIMLIB_UNMOUNT_FLAG_COMMIT) ? 1 : 0;
+       msg[1] = (flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) ? 1 : 0;
+
+       DEBUG("Sending message: %s, %s", 
+                       (msg[0] == 0) ? "don't commit" : "commit",
+                       (msg[1] == 0) ? "don't check"  : "check");
+       ret = mq_send(unmount_to_daemon_mq, msg, 2, 1);
+       if (ret == -1) {
+               ERROR("Failed to notify filesystem daemon whether we want to "
+                     "commit changes or not");
+               close_message_queues();
+               return WIMLIB_ERR_MQUEUE;
+       }
+
        /* Execute `fusermount -u', which is installed setuid root, to unmount
         * the WIM.
         *
@@ -1928,7 +2067,7 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags)
         * the WIM file. 
         */
 
-       mount_dir = dir;
+
        pid = fork();
        if (pid == -1) {
                ERROR_WITH_ERRNO("Failed to fork()");
@@ -1937,10 +2076,10 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags)
        if (pid == 0) {
                execlp("fusermount", "fusermount", "-u", dir, NULL);
                ERROR_WITH_ERRNO("Failed to execute `fusermount'");
-               return WIMLIB_ERR_FUSERMOUNT;
+               exit(WIMLIB_ERR_FUSERMOUNT);
        }
 
-       ret = waitpid(pid, &status, 0);
+       ret = wait(&status);
        if (ret == -1) {
                ERROR_WITH_ERRNO("Failed to wait for fusermount process to "
                                 "terminate");
@@ -1949,31 +2088,35 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags)
 
        if (status != 0) {
                ERROR("fusermount exited with status %d", status);
-               return WIMLIB_ERR_FUSERMOUNT;
-       }
 
-       /* Open message queues between the unmount process and the
-        * filesystem daemon. */
-       ret = open_message_queues(false);
-       if (ret != 0)
-               return ret;
+               /* 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. */
 
-       /* Send a message to the filesystem saying whether to commit or
-        * not. */
-       msg[0] = (flags & WIMLIB_UNMOUNT_FLAG_COMMIT) ? 1 : 0;
-       msg[1] = (flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) ? 1 : 0;
+               pid = fork();
+               if (pid == -1) {
+                       ERROR_WITH_ERRNO("Failed to fork()");
+                       return WIMLIB_ERR_FORK;
+               }
+               if (pid == 0) {
+                       execlp("umount", "umount", dir, NULL);
+                       ERROR_WITH_ERRNO("Failed to execute `umount'");
+                       exit(WIMLIB_ERR_FUSERMOUNT);
+               }
 
-       DEBUG("Sending message: %s, %s", 
-                       (msg[0] == 0) ? "don't commit" : "commit",
-                       (msg[1] == 0) ? "don't check"  : "check");
-       ret = mq_send(unmount_to_daemon_mq, msg, 2, 1);
-       if (ret == -1) {
-               ERROR("Failed to notify filesystem daemon whether we want to "
-                     "commit changes or not");
-               close_message_queues();
-               return WIMLIB_ERR_MQUEUE;
+               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;
+               }
        }
 
+
        /* Wait for a message from the filesytem daemon indicating whether  the
         * filesystem was unmounted successfully (0) or an error occurred (1).
         * This may take a long time if a big WIM file needs to be rewritten. */
@@ -2037,7 +2180,8 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags)
 }
 
 WIMLIBAPI int wimlib_mount(WIMStruct *wim_p, int image, const char *dir, 
-                          int flags)
+                          int flags, WIMStruct **additional_swms,
+                          unsigned num_additional_swms)
 {
        return mount_unsupported_error();
 }