From 81d03c562d98c865fb2c03978231c250f68cb0a7 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 3 Feb 2016 19:20:26 -0600 Subject: [PATCH] Image metadata updates - 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 | 3 - include/wimlib/metadata.h | 60 ++++++++++++--- src/add_image.c | 76 +++++-------------- src/blob_table.c | 12 ++- src/delete_image.c | 20 +++-- src/export_image.c | 4 +- src/extract.c | 96 +++++++++++++----------- src/mount_image.c | 141 +++++++++++++---------------------- src/wim.c | 144 +++++++++++++++++++++--------------- 9 files changed, 277 insertions(+), 279 deletions(-) diff --git a/include/wimlib/blob_table.h b/include/wimlib/blob_table.h index 4e70e574..ae4f9f4d 100644 --- a/include/wimlib/blob_table.h +++ b/include/wimlib/blob_table.h @@ -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; }; }; diff --git a/include/wimlib/metadata.h b/include/wimlib/metadata.h index f9307a67..030b0b12 100644 --- a/include/wimlib/metadata.h +++ b/include/wimlib/metadata.h @@ -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 */ diff --git a/src/add_image.c b/src/add_image.c index b4eb0a9e..740f11d6 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -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 @@ -30,60 +30,11 @@ #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 diff --git a/src/blob_table.c b/src/blob_table.c index 54eca2c9..ab8d0846 100644 --- a/src/blob_table.c +++ b/src/blob_table.c @@ -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; } diff --git a/src/delete_image.c b/src/delete_image.c index 42e1ae4e..9cbec0a2 100644 --- a/src/delete_image.c +++ b/src/delete_image.c @@ -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 #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; } diff --git a/src/export_image.c b/src/export_image.c index da638322..b8d19dc2 100644 --- a/src/export_image.c +++ b/src/export_image.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 @@ -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); diff --git a/src/extract.c b/src/extract.c index c1a5adb6..39da4bee 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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; diff --git a/src/mount_image.c b/src/mount_image.c index bca9e439..18c3a4ef 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -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; } diff --git a/src/wim.c b/src/wim.c index 43b25aca..79a9ba72 100644 --- 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; } -- 2.43.0