]> wimlib.net Git - wimlib/blobdiff - src/mount_image.c
Clean up inode alias tracking
[wimlib] / src / mount_image.c
index 1c31cae26792a7ff32469c06327f6efae77203b2..0672c9c223834bf56c3f58ae4716586091be6671 100644 (file)
@@ -1,29 +1,27 @@
 /*
  * 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.
  */
 
 /*
  * Copyright (C) 2012, 2013, 2014 Eric Biggers
  *
- * 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 General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
+ * This file 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 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 General Public License for more
+ * This file 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
  * details.
  *
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -31,6 +29,7 @@
 #endif
 
 #include "wimlib.h"
+#include "wimlib/error.h"
 
 #ifdef WITH_FUSE
 
@@ -40,6 +39,7 @@
 
 #include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
+#include "wimlib/lookup_table.h"
 #include "wimlib/metadata.h"
 #include "wimlib/paths.h"
 #include "wimlib/progress.h"
@@ -324,15 +324,7 @@ close_wimfs_fd(struct wimfs_fd *fd)
        if (fd->f_idx < inode->i_next_fd)
                inode->i_next_fd = fd->f_idx;
        FREE(fd);
-       if (--inode->i_num_opened_fds == 0) {
-               /* The last file descriptor to this inode was closed.  */
-               FREE(inode->i_fds);
-               inode->i_fds = NULL;
-               inode->i_num_allocated_fds = 0;
-               if (inode->i_nlink == 0)
-                       /* No links to this inode remain.  Get rid of it.  */
-                       free_inode(inode);
-       }
+       inode_dec_num_opened_fds(inode);
        return ret;
 }
 
@@ -372,37 +364,36 @@ 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)
+                      unsigned *stream_idx_ret)
 {
        WIMStruct *wim = ctx->wim;
        struct wim_dentry *dentry;
        struct wim_lookup_table_entry *lte;
-       u16 stream_idx;
-       const tchar *stream_name = NULL;
+       unsigned stream_idx;
+       const char *stream_name = NULL;
        struct wim_inode *inode;
-       tchar *p = NULL;
+       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 = (tchar*)stream_name - 1;
-                       *p = T('\0');
+                       p = (char *)stream_name - 1;
+                       *p = '\0';
                }
        }
 
        dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE);
        if (p)
-               *p = T(':');
+               *p = ':';
        if (!dentry)
                return -errno;
 
        inode = dentry->d_inode;
 
-       if (!inode->i_resolved)
-               if (inode_resolve_streams(inode, wim->lookup_table, false))
-                       return -EIO;
+       if (inode_resolve_streams(inode, wim->lookup_table, false))
+               return -EIO;
 
        if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
              && inode_is_directory(inode))
@@ -504,8 +495,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;
 }
 
@@ -593,11 +583,11 @@ inode_to_stbuf(const struct wim_inode *inode,
        stbuf->st_mtim = wim_timestamp_to_timespec(inode->i_last_write_time);
        stbuf->st_ctim = stbuf->st_mtim;
 #else
-       stbuf->st_atime = wim_timestamp_to_unix(inode->i_last_access_time);
-       stbuf->st_mtime = wim_timestamp_to_unix(inode->i_last_write_time);
+       stbuf->st_atime = wim_timestamp_to_time_t(inode->i_last_access_time);
+       stbuf->st_mtime = wim_timestamp_to_time_t(inode->i_last_write_time);
        stbuf->st_ctime = stbuf->st_mtime;
 #endif
-       stbuf->st_blocks = (stbuf->st_size + 511) / 512;
+       stbuf->st_blocks = DIV_ROUND_UP(stbuf->st_size, 512);
        return 0;
 }
 
@@ -605,7 +595,7 @@ inode_to_stbuf(const struct wim_inode *inode,
 static void
 touch_inode(struct wim_inode *inode)
 {
-       u64 now = get_wim_timestamp();
+       u64 now = now_as_wim_timestamp();
        inode->i_last_access_time = now;
        inode->i_last_write_time = now;
 }
@@ -682,7 +672,7 @@ retry:
  */
 static int
 extract_resource_to_staging_dir(struct wim_inode *inode,
-                               u16 stream_idx,
+                               unsigned stream_idx,
                                struct wim_lookup_table_entry **lte_ptr,
                                off_t size,
                                const struct wimfs_context *ctx)
@@ -943,7 +933,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)
@@ -965,6 +955,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)
 {
@@ -991,6 +982,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)
 {
@@ -1008,6 +1001,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)
 {
@@ -1020,6 +1016,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)
 {
@@ -1068,7 +1065,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;
 
@@ -1127,16 +1124,20 @@ 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
+/* In the case of an allow_other mount, only the mount owner and root are
  * allowed to unmount the filesystem.  */
 static bool
 may_unmount_wimfs(void)
@@ -1148,6 +1149,7 @@ may_unmount_wimfs(void)
                fuse_ctx->uid == 0);
 }
 
+/* Unmount the mounted image, called from the daemon process.  */
 static int
 unmount_wimfs(void)
 {
@@ -1171,6 +1173,11 @@ unmount_wimfs(void)
        }
 
        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)
@@ -1178,19 +1185,25 @@ unmount_wimfs(void)
                        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)
                ret = commit_image(wimfs_ctx, unmount_flags, mq);
        else
-               ret = 0;
+               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
+        * forced unmount.  The user can retry without COMMIT if they
         * want.  */
-       if (!ret || (unmount_flags & WIMLIB_UNMOUNT_FLAG_FORCE))
+       if (!ret || (unmount_flags & WIMLIB_UNMOUNT_FLAG_FORCE)) {
+               unlock_wim_for_append(wimfs_ctx->wim);
                fuse_exit(fuse_ctx->fuse);
+       }
        if (mq != (mqd_t)-1)
                mq_close(mq);
        return ret;
@@ -1295,11 +1308,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;
 }
 
@@ -1377,11 +1390,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;
 }
@@ -1418,12 +1431,10 @@ wimfs_link(const char *existing_path, const char *new_path)
        if (new_dentry(new_name, &new_alias))
                return -ENOMEM;
 
-       new_alias->d_inode = inode;
-       inode_add_dentry(new_alias, inode);
+       inode_ref_streams(inode);
+       d_associate(new_alias, inode);
        dentry_add_child(dir, new_alias);
        touch_inode(dir->d_inode);
-       inode->i_nlink++;
-       inode_ref_streams(inode);
        return 0;
 }
 
@@ -1564,7 +1575,7 @@ wimfs_open(const char *path, struct fuse_file_info *fi)
        struct wim_dentry *dentry;
        struct wim_inode *inode;
        struct wim_lookup_table_entry *lte;
-       u16 stream_idx;
+       unsigned stream_idx;
        struct wimfs_fd *fd;
        int ret;
 
@@ -1656,7 +1667,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;
@@ -1812,7 +1823,8 @@ 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_info")) {
                        if (!may_unmount_wimfs())
@@ -1891,7 +1903,7 @@ wimfs_truncate(const char *path, off_t size)
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_lookup_table_entry *lte;
-       u16 stream_idx;
+       unsigned stream_idx;
        int ret;
        int fd;
 
@@ -1925,7 +1937,7 @@ wimfs_unlink(const char *path)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
-       u16 stream_idx;
+       unsigned stream_idx;
        int ret;
 
        ret = wim_pathname_to_stream(ctx, path, 0, &dentry, NULL, &stream_idx);
@@ -1961,15 +1973,15 @@ wimfs_utimens(const char *path, const struct timespec tv[2])
 
        if (tv[0].tv_nsec != UTIME_OMIT) {
                if (tv[0].tv_nsec == UTIME_NOW)
-                       inode->i_last_access_time = get_wim_timestamp();
+                       inode->i_last_access_time = now_as_wim_timestamp();
                else
-                       inode->i_last_access_time = timespec_to_wim_timestamp(tv[0]);
+                       inode->i_last_access_time = timespec_to_wim_timestamp(&tv[0]);
        }
        if (tv[1].tv_nsec != UTIME_OMIT) {
                if (tv[1].tv_nsec == UTIME_NOW)
-                       inode->i_last_write_time = get_wim_timestamp();
+                       inode->i_last_write_time = now_as_wim_timestamp();
                else
-                       inode->i_last_write_time = timespec_to_wim_timestamp(tv[1]);
+                       inode->i_last_write_time = timespec_to_wim_timestamp(&tv[1]);
        }
        return 0;
 }
@@ -1984,8 +1996,8 @@ wimfs_utime(const char *path, struct utimbuf *times)
        if (!inode)
                return -errno;
 
-       inode->i_last_access_time = unix_timestamp_to_wim(times->actime);
-       inode->i_last_write_time = unix_timestamp_to_wim(times->modtime);
+       inode->i_last_access_time = time_t_to_wim_timestamp(times->actime);
+       inode->i_last_write_time = time_t_to_wim_timestamp(times->modtime);
        return 0;
 }
 #endif /* !HAVE_UTIMENSAT */
@@ -2077,7 +2089,9 @@ 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) {
-               ret = can_delete_from_wim(wim);
+               if (!wim->filename)
+                       return WIMLIB_ERR_NO_FILENAME;
+               ret = can_modify_wim(wim);
                if (ret)
                        return ret;
        }
@@ -2098,9 +2112,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).  */
@@ -2229,7 +2245,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"
@@ -2255,7 +2270,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;
 }
 
@@ -2296,27 +2311,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)
@@ -2387,7 +2402,7 @@ do_unmount_commit(const char *dir, int unmount_flags,
        if (progfunc) {
                generate_message_queue_name(unmount_info.mq_name);
 
-               mq = create_message_queue(unmount_info.mq_name, progfunc != NULL);
+               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;
@@ -2411,7 +2426,8 @@ do_unmount_commit(const char *dir, int unmount_flags,
                ret = do_unmount(dir);
        if (progfunc) {
                /* Terminate the progress thread.  */
-               mq_send(mq, NULL, 0, 1);
+               char empty[1];
+               mq_send(mq, empty, 0, 1);
                pthread_join(commit_progress_tid, NULL);
        }
 out_delete_mq:
@@ -2472,7 +2488,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 |