Image metadata updates
authorEric Biggers <ebiggers3@gmail.com>
Thu, 4 Feb 2016 01:20:26 +0000 (19:20 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 9 Feb 2016 01:43:11 +0000 (19:43 -0600)
- Maintain selected_count of each WIM image
- Unload each WIM image when it is no longer selected by any WIMStruct
- Enforce that every 'wim_image_metadata' is created in a valid state
- Simplify NEW_IMAGE support for read-write mounts
- Improve comments

include/wimlib/blob_table.h
include/wimlib/metadata.h
src/add_image.c
src/blob_table.c
src/delete_image.c
src/export_image.c
src/extract.c
src/mount_image.c
src/wim.c

index 4e70e57..ae4f9f4 100644 (file)
@@ -259,9 +259,6 @@ struct blob_descriptor {
 
                /* Links blobs being exported.  */
                struct list_head export_blob_list;
-
-               /* Links original list of blobs in the read-write mounted image.  */
-               struct list_head orig_blob_list;
        };
 };
 
index f9307a6..030b0b1 100644 (file)
@@ -6,17 +6,37 @@
 #include "wimlib/types.h"
 #include "wimlib/wim.h"
 
-/* Metadata for a WIM image  */
+/*
+ * This structure holds the directory tree that comprises a WIM image, along
+ * with other information maintained at the image level.  It is populated either
+ * by reading and parsing a metadata resource or by scanning new files.
+ *
+ * An image that hasn't been modified from its on-disk copy is considered
+ * "clean" and is loaded from its metadata resource on demand by
+ * select_wim_image().  Such an image may be unloaded later to save memory when
+ * a different image is selected.  An image that has been modified or has been
+ * created from scratch, on the other hand, is considered "dirty" and is never
+ * automatically unloaded.
+ *
+ * To implement exports, it's allowed that multiple WIMStructs reference the
+ * same wim_image_metadata.
+ */
 struct wim_image_metadata {
 
-       /* Number of WIMStruct's that are sharing this image metadata (from
-        * calls to wimlib_export_image().) */
-       unsigned long refcnt;
+       /* Number of WIMStructs that reference this image.  This will always be
+        * >= 1.  It may be > 1 if this image has been exported.  */
+       u32 refcnt;
 
-       /* Pointer to the root dentry of the image. */
+       /* Number of WIMStructs that have this image selected as their
+        * current_image.  This will always be <= 'refcnt' and may be 0.  */
+       u32 selected_refcnt;
+
+       /* Pointer to the root dentry of this image, or NULL if this image is
+        * completely empty or is not currently loaded.  */
        struct wim_dentry *root_dentry;
 
-       /* Pointer to the security data of the image. */
+       /* Pointer to the security data of this image, or NULL if this image is
+        * not currently loaded.  */
        struct wim_security_data *security_data;
 
        /* Pointer to the blob descriptor for this image's metadata resource.
@@ -27,7 +47,8 @@ struct wim_image_metadata {
         * with blob_location==BLOB_NONEXISTENT.  */
        struct blob_descriptor *metadata_blob;
 
-       /* Linked list of 'struct wim_inode's for this image. */
+       /* Linked list of 'struct wim_inode's for this image, or an empty list
+        * if this image is completely empty or is not currently loaded.  */
        struct hlist_head inode_list;
 
        /* Linked list of 'struct blob_descriptor's for blobs that are
@@ -94,6 +115,24 @@ mark_image_dirty(struct wim_image_metadata *imd)
        imd->stats_outdated = true;
 }
 
+/* Return true iff the specified image is currently loaded into memory.  */
+static inline bool
+is_image_loaded(const struct wim_image_metadata *imd)
+{
+       /* Check security_data rather than root_dentry, since root_dentry will
+        * be NULL for a completely empty image whereas security_data will still
+        * be non-NULL in that case.  */
+       return imd->security_data != NULL;
+}
+
+/* Return true iff it is okay to unload the specified image.  The image can be
+ * unloaded if no WIMStructs have it selected and it is not dirty.  */
+static inline bool
+can_unload_image(const struct wim_image_metadata *imd)
+{
+       return imd->selected_refcnt == 0 && !is_image_dirty(imd);
+}
+
 /* Iterate over each inode in a WIM image  */
 #define image_for_each_inode(inode, imd) \
        hlist_for_each_entry(inode, &(imd)->inode_list, i_hlist_node)
@@ -112,12 +151,15 @@ mark_image_dirty(struct wim_image_metadata *imd)
        list_for_each_entry_safe(blob, tmp, &(imd)->unhashed_blobs, unhashed_list)
 
 extern void
-put_image_metadata(struct wim_image_metadata *imd, struct blob_table *table);
+put_image_metadata(struct wim_image_metadata *imd);
 
 extern int
 append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd);
 
 extern struct wim_image_metadata *
-new_image_metadata(void) _malloc_attribute;
+new_empty_image_metadata(void);
+
+extern struct wim_image_metadata *
+new_unloaded_image_metadata(struct blob_descriptor *metadata_blob);
 
 #endif /* _WIMLIB_METADATA_H */
index b4eb0a9..740f11d 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * add_image.c - Add an image to a WIM file.
+ * add_image.c - Add an image to a WIMStruct.
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
 #include "wimlib/security.h"
 #include "wimlib/xml.h"
 
-/* Creates and appends a 'struct wim_image_metadata' for an empty image.
- *
- * The resulting image will be the last in the WIM, so its index will be
- * the new value of wim->hdr.image_count.  */
-static int
-add_empty_image_metadata(WIMStruct *wim)
-{
-       int ret;
-       struct blob_descriptor *metadata_blob;
-       struct wim_security_data *sd;
-       struct wim_image_metadata *imd;
-
-       /* Create a blob descriptor for the new metadata resource.  */
-       ret = WIMLIB_ERR_NOMEM;
-       metadata_blob = new_blob_descriptor();
-       if (!metadata_blob)
-               goto out;
-
-       metadata_blob->refcnt = 1;
-       metadata_blob->is_metadata = 1;
-
-       /* Create empty security data (no security descriptors).  */
-       sd = new_wim_security_data();
-       if (!sd)
-               goto out_free_metadata_blob;
-
-       imd = new_image_metadata();
-       if (!imd)
-               goto out_free_security_data;
-
-       /* A NULL root_dentry indicates a completely empty image, without even a
-        * root directory.  */
-       imd->root_dentry = NULL;
-       imd->metadata_blob = metadata_blob;
-       imd->security_data = sd;
-
-       /* Append as next image index.  */
-       ret = append_image_metadata(wim, imd);
-       if (ret)
-               put_image_metadata(imd, NULL);
-       goto out;
-
-out_free_security_data:
-       free_wim_security_data(sd);
-out_free_metadata_blob:
-       free_blob_descriptor(metadata_blob);
-out:
-       return ret;
-}
-
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
 wimlib_add_empty_image(WIMStruct *wim, const tchar *name, int *new_idx_ret)
 {
+       struct wim_image_metadata *imd;
        int ret;
 
        if (wimlib_image_name_in_use(wim, name)) {
@@ -92,20 +43,27 @@ wimlib_add_empty_image(WIMStruct *wim, const tchar *name, int *new_idx_ret)
                return WIMLIB_ERR_IMAGE_NAME_COLLISION;
        }
 
-       ret = add_empty_image_metadata(wim);
+       imd = new_empty_image_metadata();
+       if (!imd)
+               return WIMLIB_ERR_NOMEM;
+
+       ret = append_image_metadata(wim, imd);
        if (ret)
-               return ret;
+               goto err_put_imd;
 
        ret = xml_add_image(wim->xml_info, name);
-       if (ret) {
-               put_image_metadata(wim->image_metadata[--wim->hdr.image_count],
-                                  NULL);
-               return ret;
-       }
+       if (ret)
+               goto err_undo_append;
 
        if (new_idx_ret)
                *new_idx_ret = wim->hdr.image_count;
        return 0;
+
+err_undo_append:
+       wim->hdr.image_count--;
+err_put_imd:
+       put_image_metadata(imd);
+       return ret;
 }
 
 /* Translate the 'struct wimlib_capture_source's passed to
index 54eca2c..ab8d084 100644 (file)
@@ -9,7 +9,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
@@ -1007,9 +1007,6 @@ read_blob_table(WIMStruct *wim)
                }
 
                if (reshdr.flags & WIM_RESHDR_FLAG_METADATA) {
-
-                       cur_blob->is_metadata = 1;
-
                        /* Blob table entry for a metadata resource.  */
 
                        /* Metadata entries with no references must be ignored.
@@ -1056,7 +1053,10 @@ read_blob_table(WIMStruct *wim)
                         * this overrides the actual locations of the metadata
                         * resources themselves in the WIM file as well as any
                         * information written in the XML data.  */
-                       wim->image_metadata[image_index++]->metadata_blob = cur_blob;
+                       wim->image_metadata[image_index] = new_unloaded_image_metadata(cur_blob);
+                       if (!wim->image_metadata[image_index])
+                               goto oom;
+                       image_index++;
                } else {
                        /* Blob table entry for a non-metadata blob.  */
 
@@ -1091,8 +1091,6 @@ read_blob_table(WIMStruct *wim)
 
        if (wim->hdr.part_number == 1 && image_index != wim->hdr.image_count) {
                WARNING("Could not find metadata resources for all images");
-               for (u32 i = image_index; i < wim->hdr.image_count; i++)
-                       put_image_metadata(wim->image_metadata[i], NULL);
                wim->hdr.image_count = image_index;
        }
 
index 42e1ae4..9cbec0a 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
@@ -26,6 +26,7 @@
 #include <string.h>
 
 #include "wimlib.h"
+#include "wimlib/dentry.h"
 #include "wimlib/metadata.h"
 #include "wimlib/wim.h"
 #include "wimlib/xml.h"
@@ -36,6 +37,7 @@ int
 delete_wim_image(WIMStruct *wim, int image)
 {
        int ret;
+       struct wim_image_metadata *imd;
 
        /* Load the metadata for the image to be deleted.  This is necessary
         * because blobs referenced by files in the image need to have their
@@ -44,9 +46,15 @@ delete_wim_image(WIMStruct *wim, int image)
        if (ret)
                return ret;
 
-       /* Release the reference to the image metadata and decrement reference
-        * counts on the blobs referenced by files in the image.  */
-       put_image_metadata(wim->image_metadata[image - 1], wim->blob_table);
+       /* Release the files and decrement the reference counts of the blobs
+        * they reference.  */
+       imd = wim->image_metadata[image - 1];
+       free_dentry_tree(imd->root_dentry, wim->blob_table);
+       imd->root_dentry = NULL;
+
+       /* Deselect the image and release its metadata.  */
+       deselect_current_wim_image(wim);
+       put_image_metadata(imd);
 
        /* Remove the empty slot from the image metadata array.  */
        memmove(&wim->image_metadata[image - 1], &wim->image_metadata[image],
@@ -54,7 +62,7 @@ delete_wim_image(WIMStruct *wim, int image)
                        sizeof(wim->image_metadata[0]));
 
        /* Decrement the image count. */
-       --wim->hdr.image_count;
+       wim->hdr.image_count--;
 
        /* Remove the image from the XML information. */
        xml_delete_image(wim->xml_info, image);
@@ -65,8 +73,6 @@ delete_wim_image(WIMStruct *wim, int image)
        else if (wim->hdr.boot_idx > image)
                wim->hdr.boot_idx--;
 
-       /* The image is no longer valid.  */
-       wim->current_image = WIMLIB_NO_IMAGE;
        return 0;
 }
 
index da63832..b8d19dc 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
@@ -275,7 +275,7 @@ out_rollback:
        while (dest_wim->hdr.image_count > orig_dest_image_count)
        {
                put_image_metadata(dest_wim->image_metadata[
-                                       --dest_wim->hdr.image_count], NULL);
+                                       --dest_wim->hdr.image_count]);
        }
        for_blob_in_table(dest_wim->blob_table, blob_rollback_export,
                          dest_wim->blob_table);
index c1a5adb..39da4be 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
@@ -259,6 +259,55 @@ read_blobs_from_pipe(struct apply_ctx *ctx, const struct read_blob_callbacks *cb
        return 0;
 }
 
+static int
+handle_pwm_metadata_resource(WIMStruct *pwm, int image, bool is_needed)
+{
+       struct blob_descriptor *blob;
+       struct wim_reshdr reshdr;
+       struct wim_resource_descriptor *rdesc;
+       int ret;
+
+       ret = WIMLIB_ERR_NOMEM;
+       blob = new_blob_descriptor();
+       if (!blob)
+               goto out;
+
+       ret = read_pwm_blob_header(pwm, blob->hash, &reshdr, NULL);
+       if (ret)
+               goto out;
+
+       ret = WIMLIB_ERR_INVALID_PIPABLE_WIM;
+       if (!(reshdr.flags & WIM_RESHDR_FLAG_METADATA)) {
+               ERROR("Expected metadata resource, but found non-metadata "
+                     "resource");
+               goto out;
+       }
+
+       ret = WIMLIB_ERR_NOMEM;
+       rdesc = MALLOC(sizeof(*rdesc));
+       if (!rdesc)
+               goto out;
+
+       wim_reshdr_to_desc_and_blob(&reshdr, pwm, rdesc, blob);
+       pwm->refcnt++;
+
+       ret = WIMLIB_ERR_NOMEM;
+       pwm->image_metadata[image - 1] = new_unloaded_image_metadata(blob);
+       if (!pwm->image_metadata[image - 1])
+               goto out;
+       blob = NULL;
+
+       /* If the metadata resource is for the image being extracted, then parse
+        * it and save the metadata in memory.  Otherwise, skip over it.  */
+       if (is_needed)
+               ret = select_wim_image(pwm, image);
+       else
+               ret = skip_wim_resource(rdesc);
+out:
+       free_blob_descriptor(blob);
+       return ret;
+}
+
 /* Creates a temporary file opened for writing.  The open file descriptor is
  * returned in @fd_ret and its name is returned in @name_ret (dynamically
  * allocated).  */
@@ -1906,52 +1955,9 @@ wimlib_extract_image_from_pipe_with_progress(int pipe_fd,
 
        /* Load the needed metadata resource.  */
        for (i = 1; i <= pwm->hdr.image_count; i++) {
-               struct wim_image_metadata *imd;
-               struct wim_reshdr reshdr;
-               struct wim_resource_descriptor *metadata_rdesc;
-
-               imd = pwm->image_metadata[i - 1];
-
-               ret = WIMLIB_ERR_NOMEM;
-               imd->metadata_blob = new_blob_descriptor();
-               if (!imd->metadata_blob)
-                       goto out_wimlib_free;
-
-               imd->metadata_blob->is_metadata = 1;
-
-               ret = read_pwm_blob_header(pwm, imd->metadata_blob->hash,
-                                          &reshdr, NULL);
+               ret = handle_pwm_metadata_resource(pwm, i, i == image);
                if (ret)
                        goto out_wimlib_free;
-
-               if (!(reshdr.flags & WIM_RESHDR_FLAG_METADATA)) {
-                       ERROR("Expected metadata resource, but found "
-                             "non-metadata resource");
-                       ret = WIMLIB_ERR_INVALID_PIPABLE_WIM;
-                       goto out_wimlib_free;
-               }
-
-               ret = WIMLIB_ERR_NOMEM;
-               metadata_rdesc = MALLOC(sizeof(struct wim_resource_descriptor));
-               if (!metadata_rdesc)
-                       goto out_wimlib_free;
-               wim_reshdr_to_desc_and_blob(&reshdr, pwm, metadata_rdesc,
-                                           imd->metadata_blob);
-               pwm->refcnt++;
-
-               if (i == image) {
-                       /* Metadata resource is for the image being extracted.
-                        * Parse it and save the metadata in memory.  */
-                       ret = read_metadata_resource(imd);
-                       if (ret)
-                               goto out_wimlib_free;
-               } else {
-                       /* Metadata resource is not for the image being
-                        * extracted.  Skip over it.  */
-                       ret = skip_wim_resource(metadata_rdesc);
-                       if (ret)
-                               goto out_wimlib_free;
-               }
        }
        /* Extract the image.  */
        extract_flags |= WIMLIB_EXTRACT_FLAG_FROM_PIPE;
index bca9e43..18c3a4e 100644 (file)
@@ -8,7 +8,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
@@ -164,9 +164,9 @@ struct wimfs_context {
        /* Number of file descriptors open to the mounted WIM image.  */
        unsigned long num_open_fds;
 
-       /* Original list of blobs in the mounted image, linked by
-        * 'struct blob_descriptor'.orig_blob_list.  */
-       struct list_head orig_blob_list;
+       /* For read-write mounts, the original metadata resource of the mounted
+        * image.  */
+       struct blob_descriptor *metadata_resource;
 
        /* Parameters for unmounting the image (can be set via extended
         * attribute "wimfs.unmount_info").  */
@@ -964,17 +964,6 @@ prepare_inodes(struct wimfs_context *ctx)
        }
 }
 
-static void
-release_extra_refcnts(struct wimfs_context *ctx)
-{
-       struct list_head *list = &ctx->orig_blob_list;
-       struct blob_table *blob_table = ctx->wim->blob_table;
-       struct blob_descriptor *blob, *tmp;
-
-       list_for_each_entry_safe(blob, tmp, list, orig_blob_list)
-               blob_subtract_refcnt(blob, blob_table, blob->out_refcnt);
-}
-
 /* Delete the 'struct blob_descriptor' for any stream that was modified
  * or created in the read-write mounted image and had a final size of 0.  */
 static void
@@ -1030,52 +1019,56 @@ static int
 renew_current_image(struct wimfs_context *ctx)
 {
        WIMStruct *wim = ctx->wim;
-       int idx = wim->current_image - 1;
-       struct wim_image_metadata *imd = wim->image_metadata[idx];
-       struct wim_image_metadata *replace_imd;
-       struct blob_descriptor *new_blob;
+       int image = wim->current_image;
+       struct wim_image_metadata *imd;
+       struct wim_inode *inode;
        int ret;
 
-       /* Create 'replace_imd' structure to use for the reset original,
-        * unmodified image.  */
        ret = WIMLIB_ERR_NOMEM;
-       replace_imd = new_image_metadata();
-       if (!replace_imd)
+       imd = new_unloaded_image_metadata(ctx->metadata_resource);
+       if (!imd)
                goto err;
 
-       /* Create new blob descriptor for the modified image's metadata
-        * resource, which doesn't exist yet.  */
-       ret = WIMLIB_ERR_NOMEM;
-       new_blob = new_blob_descriptor();
-       if (!new_blob)
-               goto err_put_replace_imd;
-
-       new_blob->refcnt = 1;
-       new_blob->is_metadata = 1;
-
-       /* Make the image being moved available at a new index.  Increments the
-        * WIM's image count, but does not increment the reference count of the
-        * 'struct image_metadata'.  */
-       ret = append_image_metadata(wim, imd);
+       ret = append_image_metadata(wim, wim->image_metadata[image - 1]);
        if (ret)
-               goto err_free_new_blob;
+               goto err_put_imd;
 
-       ret = xml_add_image(wim->xml_info, NULL);
+       ret = xml_export_image(wim->xml_info, image,
+                              wim->xml_info, NULL, NULL, false);
        if (ret)
                goto err_undo_append;
 
-       replace_imd->metadata_blob = imd->metadata_blob;
-       imd->metadata_blob = new_blob;
-       wim->image_metadata[idx] = replace_imd;
+       wim->image_metadata[image - 1] = imd;
        wim->current_image = wim->hdr.image_count;
+
+       ret = select_wim_image(wim, image);
+       if (ret)
+               goto err_undo_export;
+
+       image_for_each_inode(inode, imd) {
+               for (unsigned i = 0; i < inode->i_num_streams; i++) {
+                       struct blob_descriptor *blob;
+
+                       blob = stream_blob(&inode->i_streams[i],
+                                          wim->blob_table);
+                       if (blob)
+                               blob->refcnt += inode->i_nlink;
+               }
+       }
+
+       select_wim_image(wim, wim->hdr.image_count);
+       ctx->metadata_resource = NULL;
        return 0;
 
+err_undo_export:
+       xml_delete_image(wim->xml_info, wim->hdr.image_count);
+       wim->image_metadata[image - 1] = wim->image_metadata[wim->hdr.image_count - 1];
+       wim->current_image = image;
 err_undo_append:
        wim->hdr.image_count--;
-err_free_new_blob:
-       free_blob_descriptor(new_blob);
-err_put_replace_imd:
-       put_image_metadata(replace_imd, NULL);
+err_put_imd:
+       imd->metadata_blob = NULL;
+       put_image_metadata(imd);
 err:
        return ret;
 }
@@ -1111,12 +1104,8 @@ commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq)
                int ret = renew_current_image(ctx);
                if (ret)
                        return ret;
-       } else {
-               release_extra_refcnts(ctx);
        }
-       INIT_LIST_HEAD(&ctx->orig_blob_list);
        delete_empty_blobs(ctx);
-       mark_image_dirty(wim_get_current_image_metadata(ctx->wim));
 
        write_flags = 0;
 
@@ -2161,49 +2150,23 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        ctx.mount_flags = mount_flags;
        if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
                ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK;
-       /* For read-write mount, create the staging directory.  */
+
+       /* For read-write mounts, create the staging directory, save a reference
+        * to the image's metadata resource, and mark the image dirty.  */
        if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
                ret = make_staging_dir(&ctx, staging_dir);
                if (ret)
-                       goto out_unlock;
+                       goto out;
+               ret = WIMLIB_ERR_NOMEM;
+               ctx.metadata_resource = clone_blob_descriptor(
+                                                       imd->metadata_blob);
+               if (!ctx.metadata_resource)
+                       goto out;
+               mark_image_dirty(imd);
        }
        ctx.owner_uid = getuid();
        ctx.owner_gid = getgid();
 
-       /* Add each blob referenced by files in the image to a list and
-        * preemptively double the number of references to each.  This is done
-        * to allow implementing the WIMLIB_UNMOUNT_FLAG_NEW_IMAGE semantics.
-        */
-       INIT_LIST_HEAD(&ctx.orig_blob_list);
-       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
-               unsigned i;
-               struct wim_inode *inode;
-               struct blob_descriptor *blob;
-
-               image_for_each_inode(inode, imd) {
-                       for (i = 0; i < inode->i_num_streams; i++) {
-                               blob = stream_blob(&inode->i_streams[i],
-                                                  wim->blob_table);
-                               if (blob)
-                                       blob->out_refcnt = 0;
-                       }
-               }
-
-               image_for_each_inode(inode, imd) {
-                       for (i = 0; i < inode->i_num_streams; i++) {
-                               blob = stream_blob(&inode->i_streams[i],
-                                                  wim->blob_table);
-                               if (blob) {
-                                       if (blob->out_refcnt == 0)
-                                               list_add(&blob->orig_blob_list,
-                                                        &ctx.orig_blob_list);
-                                       blob->out_refcnt += inode->i_nlink;
-                                       blob->refcnt += inode->i_nlink;
-                               }
-                       }
-               }
-       }
-
        /* Number the inodes in the mounted image sequentially and initialize
         * the file descriptor arrays  */
        prepare_inodes(&ctx);
@@ -2298,11 +2261,11 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        /* Cleanup and return.  */
        if (ret)
                ret = WIMLIB_ERR_FUSE;
+out:
        FREE(ctx.mountpoint_abspath);
-       release_extra_refcnts(&ctx);
-       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
+       free_blob_descriptor(ctx.metadata_resource);
+       if (ctx.staging_dir_name)
                delete_staging_dir(&ctx);
-out_unlock:
        unlock_wim_for_append(wim);
        return ret;
 }
index 43b25ac..79a9ba7 100644 (file)
--- a/src/wim.c
+++ b/src/wim.c
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
+ * Copyright (C) 2012-2016 Eric Biggers
  *
  * 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
@@ -206,35 +206,33 @@ wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret)
 }
 
 static void
-destroy_image_metadata(struct wim_image_metadata *imd,
-                      struct blob_table *table,
-                      bool free_metadata_blob_descriptor)
+unload_image_metadata(struct wim_image_metadata *imd)
 {
-       free_dentry_tree(imd->root_dentry, table);
+       free_dentry_tree(imd->root_dentry, NULL);
        imd->root_dentry = NULL;
        free_wim_security_data(imd->security_data);
        imd->security_data = NULL;
-
-       if (free_metadata_blob_descriptor) {
-               free_blob_descriptor(imd->metadata_blob);
-               imd->metadata_blob = NULL;
-       }
-       if (!table) {
-               struct blob_descriptor *blob, *tmp;
-               list_for_each_entry_safe(blob, tmp, &imd->unhashed_blobs, unhashed_list)
-                       free_blob_descriptor(blob);
-       }
-       INIT_LIST_HEAD(&imd->unhashed_blobs);
        INIT_HLIST_HEAD(&imd->inode_list);
 }
 
+/* Release a reference to the specified image metadata.  This assumes that no
+ * WIMStruct has the image selected.  */
 void
-put_image_metadata(struct wim_image_metadata *imd, struct blob_table *table)
+put_image_metadata(struct wim_image_metadata *imd)
 {
-       if (imd && --imd->refcnt == 0) {
-               destroy_image_metadata(imd, table, true);
-               FREE(imd);
-       }
+       struct blob_descriptor *blob, *tmp;
+
+       if (!imd)
+               return;
+       wimlib_assert(imd->refcnt > 0);
+       if (--imd->refcnt != 0)
+               return;
+       wimlib_assert(imd->selected_refcnt == 0);
+       unload_image_metadata(imd);
+       list_for_each_entry_safe(blob, tmp, &imd->unhashed_blobs, unhashed_list)
+               free_blob_descriptor(blob);
+       free_blob_descriptor(imd->metadata_blob);
+       FREE(imd);
 }
 
 /* Appends the specified image metadata structure to the array of image metadata
@@ -244,6 +242,12 @@ append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd)
 {
        struct wim_image_metadata **imd_array;
 
+       if (!wim_has_metadata(wim))
+               return WIMLIB_ERR_METADATA_NOT_FOUND;
+
+       if (wim->hdr.image_count >= MAX_IMAGES)
+               return WIMLIB_ERR_IMAGE_COUNT;
+
        imd_array = REALLOC(wim->image_metadata,
                            sizeof(wim->image_metadata[0]) * (wim->hdr.image_count + 1));
 
@@ -254,41 +258,57 @@ append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd)
        return 0;
 }
 
-struct wim_image_metadata *
-new_image_metadata(void)
+static struct wim_image_metadata *
+new_image_metadata(struct blob_descriptor *metadata_blob,
+                  struct wim_security_data *security_data)
 {
        struct wim_image_metadata *imd;
 
        imd = CALLOC(1, sizeof(*imd));
-       if (imd) {
-               imd->refcnt = 1;
-               INIT_HLIST_HEAD(&imd->inode_list);
-               INIT_LIST_HEAD(&imd->unhashed_blobs);
-       }
+       if (!imd)
+               return NULL;
+
+       metadata_blob->is_metadata = 1;
+       imd->refcnt = 1;
+       imd->selected_refcnt = 0;
+       imd->root_dentry = NULL;
+       imd->security_data = security_data;
+       imd->metadata_blob = metadata_blob;
+       INIT_HLIST_HEAD(&imd->inode_list);
+       INIT_LIST_HEAD(&imd->unhashed_blobs);
+       imd->stats_outdated = false;
        return imd;
 }
 
-static struct wim_image_metadata **
-new_image_metadata_array(unsigned num_images)
+/* Create an image metadata structure for a new empty image.  */
+struct wim_image_metadata *
+new_empty_image_metadata(void)
 {
-       struct wim_image_metadata **imd_array;
-
-       imd_array = CALLOC(num_images, sizeof(imd_array[0]));
+       struct blob_descriptor *metadata_blob;
+       struct wim_security_data *security_data;
+       struct wim_image_metadata *imd;
 
-       if (!imd_array)
-               return NULL;
-       for (unsigned i = 0; i < num_images; i++) {
-               imd_array[i] = new_image_metadata();
-               if (unlikely(!imd_array[i])) {
-                       for (unsigned j = 0; j < i; j++)
-                               put_image_metadata(imd_array[j], NULL);
-                       FREE(imd_array);
-                       return NULL;
-               }
+       metadata_blob = new_blob_descriptor();
+       security_data = new_wim_security_data();
+       if (metadata_blob && security_data) {
+               metadata_blob->refcnt = 1;
+               imd = new_image_metadata(metadata_blob, security_data);
+               if (imd)
+                       return imd;
        }
-       return imd_array;
+       free_blob_descriptor(metadata_blob);
+       FREE(security_data);
+       return NULL;
 }
 
+/* Create an image metadata structure that refers to the specified metadata
+ * resource and is initially not loaded.  */
+struct wim_image_metadata *
+new_unloaded_image_metadata(struct blob_descriptor *metadata_blob)
+{
+       wimlib_assert(metadata_blob->blob_location == BLOB_IN_WIM);
+       return new_image_metadata(metadata_blob, NULL);
+}
 
 /*
  * Load the metadata for the specified WIM image into memory and set it
@@ -324,33 +344,39 @@ select_wim_image(WIMStruct *wim, int image)
        if (!wim_has_metadata(wim))
                return WIMLIB_ERR_METADATA_NOT_FOUND;
 
-       /* If a valid image is currently selected, its metadata can be freed if
-        * it is not dirty and no other WIMStructs may have it selected.  */
        deselect_current_wim_image(wim);
-       wim->current_image = image;
-       imd = wim_get_current_image_metadata(wim);
-       if (imd->root_dentry || is_image_dirty(imd)) {
-               ret = 0;
-       } else {
+
+       imd = wim->image_metadata[image - 1];
+       if (!is_image_loaded(imd)) {
                ret = read_metadata_resource(imd);
                if (ret)
-                       wim->current_image = WIMLIB_NO_IMAGE;
+                       return ret;
        }
-       return ret;
+       wim->current_image = image;
+       imd->selected_refcnt++;
+       return 0;
 }
 
+/*
+ * Deselect the WIMStruct's currently selected image, if any.  To reduce memory
+ * usage, possibly unload the newly deselected image's metadata from memory.
+ */
 void
 deselect_current_wim_image(WIMStruct *wim)
 {
        struct wim_image_metadata *imd;
+
        if (wim->current_image == WIMLIB_NO_IMAGE)
                return;
        imd = wim_get_current_image_metadata(wim);
-       if (!is_image_dirty(imd) && imd->refcnt == 1) {
+       wimlib_assert(imd->selected_refcnt > 0);
+       imd->selected_refcnt--;
+       wim->current_image = WIMLIB_NO_IMAGE;
+
+       if (can_unload_image(imd)) {
                wimlib_assert(list_empty(&imd->unhashed_blobs));
-               destroy_image_metadata(imd, NULL, false);
+               unload_image_metadata(imd);
        }
-       wim->current_image = WIMLIB_NO_IMAGE;
 }
 
 /*
@@ -723,7 +749,8 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags)
        }
 
        if (wim->hdr.image_count != 0 && wim->hdr.part_number == 1) {
-               wim->image_metadata = new_image_metadata_array(wim->hdr.image_count);
+               wim->image_metadata = CALLOC(wim->hdr.image_count,
+                                            sizeof(wim->image_metadata[0]));
                if (!wim->image_metadata)
                        return WIMLIB_ERR_NOMEM;
        }
@@ -899,8 +926,9 @@ wimlib_free(WIMStruct *wim)
        free_blob_table(wim->blob_table);
        wim->blob_table = NULL;
        if (wim->image_metadata != NULL) {
+               deselect_current_wim_image(wim);
                for (int i = 0; i < wim->hdr.image_count; i++)
-                       put_image_metadata(wim->image_metadata[i], NULL);
+                       put_image_metadata(wim->image_metadata[i]);
                FREE(wim->image_metadata);
                wim->image_metadata = NULL;
        }