]> wimlib.net Git - wimlib/blobdiff - src/metadata_resource.c
wincfg: Add swapfile.sys
[wimlib] / src / metadata_resource.c
index 2f7dfeb6e729ce0ae3dd4c0f7a59ca05f9f96e49..b80ee710bca6f81db4ac6b0c7bece9ef5f0bf344 100644 (file)
  * wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
-#include "wimlib_internal.h"
-#include "dentry.h"
-#include "lookup_table.h"
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib/dentry.h"
+#include "wimlib/error.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/resource.h"
+#include "wimlib/security.h"
+#include "wimlib/write.h"
 
 /*
- * Reads a metadata resource for an image in the WIM file.  The metadata
- * resource consists of the security data, followed by the directory entry for
- * the root directory, followed by all the other directory entries in the
- * filesystem.  The subdir_offset field of each directory entry gives the start
- * of its child entries from the beginning of the metadata resource.  An
- * end-of-directory is signaled by a directory entry of length '0', really of
- * length 8, because that's how long the 'length' field is.
+ * Reads and parses a metadata resource for an image in the WIM file.
  *
- * @w:         Pointer to the WIMStruct for the WIM file.
+ * @wim:
+ *     Pointer to the WIMStruct for the WIM file.
  *
- * @imd:       Pointer to the image metadata structure for the image whose
- *             metadata resource we are reading.  Its `metadata_lte' member
- *             specifies the lookup table entry for the metadata resource.  The
- *             rest of the image metadata entry will be filled in by this
- *             function.
+ * @imd:
+ *     Pointer to the image metadata structure for the image whose metadata
+ *     resource we are reading.  Its `metadata_lte' member specifies the lookup
+ *     table entry for the metadata resource.  The rest of the image metadata
+ *     entry will be filled in by this function.
  *
- * Returns:    Zero on success, nonzero on failure.
+ * Return values:
+ *     WIMLIB_ERR_SUCCESS (0)
+ *     WIMLIB_ERR_INVALID_METADATA_RESOURCE
+ *     WIMLIB_ERR_NOMEM
+ *     WIMLIB_ERR_READ
+ *     WIMLIB_ERR_UNEXPECTED_END_OF_FILE
+ *     WIMLIB_ERR_DECOMPRESSION
  */
 int
-read_metadata_resource(WIMStruct *w, struct wim_image_metadata *imd)
+read_metadata_resource(WIMStruct *wim, struct wim_image_metadata *imd)
 {
-       u8 *buf;
-       u32 dentry_offset;
-       int ret;
-       struct wim_dentry *dentry;
        const struct wim_lookup_table_entry *metadata_lte;
-       u64 metadata_len;
+       void *buf;
+       int ret;
+       struct wim_security_data *sd;
+       struct wim_dentry *root;
+       struct wim_inode *inode;
 
        metadata_lte = imd->metadata_lte;
-       metadata_len = wim_resource_size(metadata_lte);
-
-       DEBUG("Reading metadata resource: original_size = %"PRIu64", "
-             "size = %"PRIu64", offset = %"PRIu64"",
-             metadata_lte->resource_entry.original_size,
-             metadata_lte->resource_entry.size,
-             metadata_lte->resource_entry.offset);
-
-       /* There is no way the metadata resource could possibly be less than (8
-        * + WIM_DENTRY_DISK_SIZE) bytes, where the 8 is for security data (with
-        * no security descriptors) and WIM_DENTRY_DISK_SIZE is for the root
-        * dentry. */
-       if (metadata_len < 8 + WIM_DENTRY_DISK_SIZE) {
-               ERROR("Expected at least %u bytes for the metadata resource",
-                     8 + WIM_DENTRY_DISK_SIZE);
-               return WIMLIB_ERR_INVALID_RESOURCE_SIZE;
-       }
-
-       if (sizeof(size_t) < 8 && metadata_len > 0xffffffff) {
-               ERROR("Metadata resource is too large (%"PRIu64" bytes",
-                     metadata_len);
-               return WIMLIB_ERR_INVALID_RESOURCE_SIZE;
-       }
 
-       /* Allocate memory for the uncompressed metadata resource. */
-       buf = MALLOC(metadata_len);
+       DEBUG("Reading metadata resource (size=%"PRIu64").", metadata_lte->size);
 
-       if (!buf) {
-               ERROR("Failed to allocate %"PRIu64" bytes for uncompressed "
-                     "metadata resource", metadata_len);
-               return WIMLIB_ERR_NOMEM;
-       }
-
-       /* Read the metadata resource into memory.  (It may be compressed.) */
-       ret = read_full_resource_into_buf(metadata_lte, buf);
+       /* Read the metadata resource into memory.  (It may be compressed.)  */
+       ret = read_full_stream_into_alloc_buf(metadata_lte, &buf);
        if (ret)
-               goto out_free_buf;
-
-       DEBUG("Finished reading metadata resource into memory.");
+               return ret;
+
+       /* Checksum the metadata resource.  */
+       if (!metadata_lte->dont_check_metadata_hash) {
+               u8 hash[SHA1_HASH_SIZE];
+
+               sha1_buffer(buf, metadata_lte->size, hash);
+               if (!hashes_equal(metadata_lte->hash, hash)) {
+                       ERROR("Metadata resource is corrupted "
+                             "(invalid SHA-1 message digest)!");
+                       ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+                       goto out_free_buf;
+               }
+       }
 
-       /* The root directory entry starts after security data, aligned on an
-        * 8-byte boundary within the metadata resource.
-        *
-        * The security data starts with a 4-byte integer giving its total
-        * length, so if we round that up to an 8-byte boundary that gives us
-        * the offset of the root dentry.
+       /* Parse the metadata resource.
         *
-        * Here we read the security data into a wim_security_data structure,
-        * and if successful, go ahead and calculate the offset in the metadata
-        * resource of the root dentry. */
-
-       wimlib_assert(imd->security_data == NULL);
-       ret = read_security_data(buf, metadata_len, &imd->security_data);
+        * Notes: The metadata resource consists of the security data, followed
+        * by the directory entry for the root directory, followed by all the
+        * other directory entries in the filesystem.  The subdir_offset field
+        * of each directory entry gives the start of its child entries from the
+        * beginning of the metadata resource.  An end-of-directory is signaled
+        * by a directory entry of length '0', really of length 8, because
+        * that's how long the 'length' field is.  */
+
+       ret = read_wim_security_data(buf, metadata_lte->size, &sd);
        if (ret)
                goto out_free_buf;
 
-       dentry_offset = (imd->security_data->total_length + 7) & ~7;
-
-       if (dentry_offset == 0) {
-               ERROR("Integer overflow while reading metadata resource");
-               ret = WIMLIB_ERR_INVALID_SECURITY_DATA;
-               goto out_free_security_data;
-       }
-
-       DEBUG("Reading root dentry");
-
-       /* Allocate memory for the root dentry and read it into memory */
-       dentry = MALLOC(sizeof(struct wim_dentry));
-       if (!dentry) {
-               ERROR("Failed to allocate %zu bytes for root dentry",
-                     sizeof(struct wim_dentry));
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_free_security_data;
-       }
-
-       ret = read_dentry(buf, metadata_len, dentry_offset, dentry);
-
-       /* This is the root dentry, so set its parent to itself. */
-       dentry->parent = dentry;
-
-       if (ret == 0 && dentry->length == 0) {
-               ERROR("Metadata resource cannot begin with end-of-directory entry!");
-               ret = WIMLIB_ERR_INVALID_DENTRY;
-       }
-
-       if (ret != 0) {
-               FREE(dentry);
+       ret = read_dentry_tree(buf, metadata_lte->size, sd->total_length, &root);
+       if (ret)
                goto out_free_security_data;
-       }
 
-       inode_add_dentry(dentry, dentry->d_inode);
+       /* We have everything we need from the buffer now.  */
+       FREE(buf);
+       buf = NULL;
 
-       /* Now read the entire directory entry tree into memory. */
-       DEBUG("Reading dentry tree");
-       ret = read_dentry_tree(buf, metadata_len, dentry);
-       if (ret)
-               goto out_free_dentry_tree;
+       /* Calculate and validate inodes.  */
 
-       /* Build hash table that maps hard link group IDs to dentry sets */
-       ret = dentry_tree_fix_inodes(dentry, &imd->inode_list);
+       ret = dentry_tree_fix_inodes(root, &imd->inode_list);
        if (ret)
                goto out_free_dentry_tree;
 
-       if (!w->all_images_verified) {
-               DEBUG("Running miscellaneous verifications on the dentry tree");
-               for_lookup_table_entry(w->lookup_table, lte_zero_real_refcnt, NULL);
-               ret = for_dentry_in_tree(dentry, verify_dentry, w);
+       image_for_each_inode(inode, imd) {
+               ret = verify_inode(inode, sd);
                if (ret)
                        goto out_free_dentry_tree;
        }
 
-       DEBUG("Done reading image metadata");
-
-       imd->root_dentry = dentry;
+       /* Success; fill in the image_metadata structure.  */
+       imd->root_dentry = root;
+       imd->security_data = sd;
        INIT_LIST_HEAD(&imd->unhashed_streams);
-       goto out_free_buf;
+       DEBUG("Done parsing metadata resource.");
+       return 0;
+
 out_free_dentry_tree:
-       free_dentry_tree(dentry, NULL);
+       free_dentry_tree(root, NULL);
 out_free_security_data:
-       free_security_data(imd->security_data);
-       imd->security_data = NULL;
+       free_wim_security_data(sd);
 out_free_buf:
        FREE(buf);
        return ret;
@@ -186,60 +140,35 @@ recalculate_security_data_length(struct wim_security_data *sd)
        u32 total_length = sizeof(u64) * sd->num_entries + 2 * sizeof(u32);
        for (u32 i = 0; i < sd->num_entries; i++)
                total_length += sd->sizes[i];
-       sd->total_length = total_length;
+       sd->total_length = (total_length + 7) & ~7;
 }
 
-/* Like write_wim_resource(), but the resource is specified by a buffer of
- * uncompressed data rather a lookup table entry; also writes the SHA1 hash of
- * the buffer to @hash.  */
 static int
-write_wim_resource_from_buffer(const void *buf, size_t buf_size,
-                              int out_fd, int out_ctype,
-                              struct resource_entry *out_res_entry,
-                              u8 hash[SHA1_HASH_SIZE])
-{
-       /* Set up a temporary lookup table entry to provide to
-        * write_wim_resource(). */
-       struct wim_lookup_table_entry lte;
-       int ret;
-       lte.resource_location            = RESOURCE_IN_ATTACHED_BUFFER;
-       lte.attached_buffer              = (void*)buf;
-       lte.resource_entry.original_size = buf_size;
-       lte.resource_entry.flags         = 0;
-       lte.unhashed                     = 1;
-       ret = write_wim_resource(&lte, out_fd, out_ctype, out_res_entry, 0);
-       if (ret == 0)
-               copy_hash(hash, lte.hash);
-       return ret;
-}
-
-/* Write the metadata resource for the current WIM image. */
-int
-write_metadata_resource(WIMStruct *w)
+prepare_metadata_resource(WIMStruct *wim, int image,
+                         u8 **buf_ret, size_t *len_ret)
 {
        u8 *buf;
        u8 *p;
        int ret;
        u64 subdir_offset;
        struct wim_dentry *root;
-       struct wim_lookup_table_entry *lte;
-       u64 metadata_original_size;
+       u64 len;
        struct wim_security_data *sd;
        struct wim_image_metadata *imd;
 
-       wimlib_assert(w->out_fd != -1);
-       wimlib_assert(w->current_image != WIMLIB_NO_IMAGE);
+       DEBUG("Preparing metadata resource for image %d", image);
 
-       DEBUG("Writing metadata resource for image %d (offset = %"PRIu64")",
-             w->current_image, filedes_offset(w->out_fd));
+       ret = select_wim_image(wim, image);
+       if (ret)
+               return ret;
 
-       imd = w->image_metadata[w->current_image - 1];
+       imd = wim->image_metadata[image - 1];
 
        root = imd->root_dentry;
        sd = imd->security_data;
 
        if (!root) {
-               /* Empty image; create a dummy root. */
+               /* Empty image; create a dummy root.  */
                ret = new_filler_directory(T(""), &root);
                if (ret)
                        return ret;
@@ -255,51 +184,65 @@ write_metadata_resource(WIMStruct *w)
         */
        recalculate_security_data_length(sd);
        subdir_offset = (((u64)sd->total_length + 7) & ~7) +
-                       dentry_correct_total_length(root) + 8;
+                       dentry_out_total_length(root) + 8;
 
-       /* Calculate the subdirectory offsets for the entire dentry tree. */
+       /* Calculate the subdirectory offsets for the entire dentry tree.  */
        calculate_subdir_offsets(root, &subdir_offset);
 
-       /* Total length of the metadata resource (uncompressed) */
-       metadata_original_size = subdir_offset;
+       /* Total length of the metadata resource (uncompressed) */
+       len = subdir_offset;
 
-       /* Allocate a buffer to contain the uncompressed metadata resource */
-       buf = MALLOC(metadata_original_size);
+       /* Allocate a buffer to contain the uncompressed metadata resource */
+       buf = MALLOC(len);
        if (!buf) {
                ERROR("Failed to allocate %"PRIu64" bytes for "
-                     "metadata resource", metadata_original_size);
+                     "metadata resource", len);
                return WIMLIB_ERR_NOMEM;
        }
 
-       /* Write the security data into the resource buffer */
-       p = write_security_data(sd, buf);
+       /* Write the security data into the resource buffer */
+       p = write_wim_security_data(sd, buf);
 
-       /* Write the dentry tree into the resource buffer */
+       /* Write the dentry tree into the resource buffer */
        p = write_dentry_tree(root, p);
 
        /* We MUST have exactly filled the buffer; otherwise we calculated its
-        * size incorrectly or wrote the data incorrectly. */
-       wimlib_assert(p - buf == metadata_original_size);
+        * size incorrectly or wrote the data incorrectly.  */
+       wimlib_assert(p - buf == len);
 
-       /* Get the lookup table entry for the metadata resource so we can update
-        * it. */
-       lte = wim_get_current_image_metadata(w)->metadata_lte;
+       *buf_ret = buf;
+       *len_ret = len;
+       return 0;
+}
+
+int
+write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags)
+{
+       int ret;
+       u8 *buf;
+       size_t len;
+       struct wim_image_metadata *imd;
+
+       ret = prepare_metadata_resource(wim, image, &buf, &len);
+       if (ret)
+               return ret;
+
+       imd = wim->image_metadata[image - 1];
 
        /* Write the metadata resource to the output WIM using the proper
-        * compression type.  The lookup table entry for the metadata resource
-        * is updated. */
-       ret = write_wim_resource_from_buffer(buf, metadata_original_size,
-                                            w->out_fd,
-                                            wimlib_get_compression_type(w),
-                                            &lte->output_resource_entry,
-                                            lte->hash);
-       /* Note that although the SHA1 message digest of the metadata resource
-        * is very likely to have changed, the corresponding lookup table entry
-        * is not actually located in the hash table, so it need not be
-        * re-inserted in the hash table. */
-
-       /* All the data has been written to the new WIM; no need for the buffer
-        * anymore */
+        * compression type, in the process updating the lookup table entry for
+        * the metadata resource.  */
+       ret = write_wim_resource_from_buffer(buf, len, WIM_RESHDR_FLAG_METADATA,
+                                            &wim->out_fd,
+                                            wim->out_compression_type,
+                                            wim->out_chunk_size,
+                                            &imd->metadata_lte->out_reshdr,
+                                            imd->metadata_lte->hash,
+                                            write_resource_flags);
+
+       /* Original checksum was overridden; set a flag so it isn't used.  */
+       imd->metadata_lte->dont_check_metadata_hash = 1;
+
        FREE(buf);
        return ret;
 }