]> wimlib.net Git - wimlib/blobdiff - src/metadata_resource.c
Win32: Fix drive root detection with \\?\-style paths
[wimlib] / src / metadata_resource.c
index 726deb54ecd91ee208c126f9cb36fe0239eeae78..041d1f78007bb843631e84f578268fa5f8f88a87 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012 Eric Biggers
+ * Copyright (C) 2012, 2013 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
  * 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/file_io.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/resource.h"
+#include "wimlib/security.h"
 
 /*
- * Reads the metadata metadata resource from the WIM file.  The metadata
+ * 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
  * 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.
  *
- * @fp:                The FILE* for the input WIM file.
- * @wim_ctype: The compression type of the WIM file.
- * @imd:       Pointer to the image metadata structure.  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.
+ * @w:         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.
  *
- * @return:    Zero on success, nonzero on failure.
+ * Returns:    Zero on success, nonzero on failure.
  */
-int read_metadata_resource(WIMStruct *w, struct image_metadata *imd)
+int
+read_metadata_resource(WIMStruct *wim, struct wim_image_metadata *imd)
 {
        u8 *buf;
-       u32 dentry_offset;
        int ret;
-       struct dentry *dentry;
-       const struct lookup_table_entry *metadata_lte;
+       struct wim_dentry *root;
+       const struct wim_lookup_table_entry *metadata_lte;
        u64 metadata_len;
-       struct hlist_head inode_list;
+       u8 hash[SHA1_HASH_SIZE];
 
        metadata_lte = imd->metadata_lte;
        metadata_len = wim_resource_size(metadata_lte);
 
-       DEBUG("Reading metadata resource: length = %"PRIu64", "
-             "offset = %"PRIu64"", metadata_len,
+       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. */
+        * entry. */
        if (metadata_len < 8 + WIM_DENTRY_DISK_SIZE) {
                ERROR("Expected at least %u bytes for the metadata resource",
                      8 + WIM_DENTRY_DISK_SIZE);
@@ -85,10 +96,17 @@ int read_metadata_resource(WIMStruct *w, struct image_metadata *imd)
        }
 
        /* Read the metadata resource into memory.  (It may be compressed.) */
-       ret = read_full_wim_resource(metadata_lte, buf, 0);
-       if (ret != 0)
+       ret = read_full_resource_into_buf(metadata_lte, buf);
+       if (ret)
                goto out_free_buf;
 
+       sha1_buffer(buf, metadata_len, hash);
+       if (!hashes_equal(metadata_lte->hash, hash)) {
+               ERROR("Metadata resource is corrupted (invalid SHA-1 message digest)!");
+               ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
+               goto out_free_buf;
+       }
+
        DEBUG("Finished reading metadata resource into memory.");
 
        /* The root directory entry starts after security data, aligned on an
@@ -99,144 +117,142 @@ int read_metadata_resource(WIMStruct *w, struct image_metadata *imd)
         * the offset of the root dentry.
         *
         * 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. */
+        * which takes case of rouding total_length.  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);
-       if (ret != 0)
+       ret = read_wim_security_data(buf, metadata_len, &imd->security_data);
+       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 dentry));
-       if (!dentry) {
-               ERROR("Failed to allocate %zu bytes for root dentry",
-                     sizeof(struct dentry));
+       root = MALLOC(sizeof(struct wim_dentry));
+       if (!root) {
                ret = WIMLIB_ERR_NOMEM;
                goto out_free_security_data;
        }
 
-       ret = read_dentry(buf, metadata_len, dentry_offset, dentry);
+       ret = read_dentry(buf, metadata_len,
+                         imd->security_data->total_length, root);
 
-       /* This is the root dentry, so set its parent to itself. */
-       dentry->parent = dentry;
-
-       if (ret == 0 && dentry->length == 0) {
+       if (ret == 0 && root->length == 0) {
                ERROR("Metadata resource cannot begin with end-of-directory entry!");
                ret = WIMLIB_ERR_INVALID_DENTRY;
        }
 
-       if (ret != 0) {
-               FREE(dentry);
+       if (ret) {
+               FREE(root);
                goto out_free_security_data;
        }
 
-       inode_add_dentry(dentry, dentry->d_inode);
+       /* This is the root dentry, so set its parent to itself. */
+       root->parent = root;
+
+       inode_add_dentry(root, root->d_inode);
 
        /* Now read the entire directory entry tree into memory. */
        DEBUG("Reading dentry tree");
-       ret = read_dentry_tree(buf, metadata_len, dentry);
-       if (ret != 0)
-               goto out_free_dentry_tree;
-
-       /* Calculate the full paths in the dentry tree. */
-       DEBUG("Calculating dentry full paths");
-       ret = for_dentry_in_tree(dentry, calculate_dentry_full_path, NULL);
-       if (ret != 0)
+       ret = read_dentry_tree(buf, metadata_len, root);
+       if (ret)
                goto out_free_dentry_tree;
 
        /* Build hash table that maps hard link group IDs to dentry sets */
-       ret = dentry_tree_fix_inodes(dentry, &inode_list);
-       if (ret != 0)
+       ret = dentry_tree_fix_inodes(root, &imd->inode_list);
+       if (ret)
                goto out_free_dentry_tree;
 
-       if (!w->all_images_verified) {
+       if (!wim->all_images_verified) {
+               /* Note: verify_dentry() expects to access imd->security_data,
+                * so it needs to be set before here. */
                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);
-               if (ret != 0)
+               for_lookup_table_entry(wim->lookup_table, lte_zero_real_refcnt, NULL);
+               ret = for_dentry_in_tree(root, verify_dentry, wim);
+               if (ret)
                        goto out_free_dentry_tree;
        }
 
        DEBUG("Done reading image metadata");
 
-       imd->root_dentry = dentry;
-       imd->inode_list  = inode_list;
-       if (imd->inode_list.first)
-               imd->inode_list.first->pprev = &imd->inode_list.first;
+       imd->root_dentry = root;
+       INIT_LIST_HEAD(&imd->unhashed_streams);
        goto out_free_buf;
 out_free_dentry_tree:
-       free_dentry_tree(dentry, NULL);
+       free_dentry_tree(root, wim->lookup_table);
 out_free_security_data:
-       free_security_data(imd->security_data);
+       free_wim_security_data(imd->security_data);
        imd->security_data = NULL;
 out_free_buf:
        FREE(buf);
        return ret;
 }
 
-static void recalculate_security_data_length(struct wim_security_data *sd)
+static void
+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 u8 *buf, u64 buf_size,
-                                         FILE *out_fp, int out_ctype,
-                                         struct resource_entry *out_res_entry,
-                                         u8 hash[SHA1_HASH_SIZE])
+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 lookup_table_entry lte;
+       struct wim_lookup_table_entry lte;
        int ret;
-       lte.resource_entry.flags         = 0;
-       lte.resource_entry.original_size = buf_size;
-       lte.resource_entry.size          = buf_size;
-       lte.resource_entry.offset        = 0;
        lte.resource_location            = RESOURCE_IN_ATTACHED_BUFFER;
-       lte.attached_buffer              = (u8*)buf;
-
-       zero_out_hash(lte.hash);
-       ret = write_wim_resource(&lte, out_fp, out_ctype, out_res_entry, 0);
-       if (ret != 0)
-               return ret;
-       copy_hash(hash, lte.hash);
-       return 0;
+       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)
+int
+write_metadata_resource(WIMStruct *w)
 {
        u8 *buf;
        u8 *p;
        int ret;
        u64 subdir_offset;
-       struct dentry *root;
-       struct lookup_table_entry *lte;
+       struct wim_dentry *root;
+       struct wim_lookup_table_entry *lte;
        u64 metadata_original_size;
        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("Writing metadata resource for image %d (offset = %"PRIu64")",
-             w->current_image, ftello(w->out_fp));
+             w->current_image, filedes_offset(w->out_fd));
+
+       imd = w->image_metadata[w->current_image - 1];
+
+       root = imd->root_dentry;
+       sd = imd->security_data;
 
-       root = wim_root_dentry(w);
-       sd = wim_security_data(w);
+       if (!root) {
+               /* Empty image; create a dummy root. */
+               ret = new_filler_directory(T(""), &root);
+               if (ret)
+                       return ret;
+               imd->root_dentry = root;
+       }
 
        /* Offset of first child of the root dentry.  It's equal to:
         * - The total length of the security data, rounded to the next 8-byte
@@ -264,7 +280,7 @@ int write_metadata_resource(WIMStruct *w)
        }
 
        /* Write the security data into the resource buffer */
-       p = write_security_data(sd, buf);
+       p = write_wim_security_data(sd, buf);
 
        /* Write the dentry tree into the resource buffer */
        p = write_dentry_tree(root, p);
@@ -275,37 +291,21 @@ int write_metadata_resource(WIMStruct *w)
 
        /* Get the lookup table entry for the metadata resource so we can update
         * it. */
-       lte = w->image_metadata[w->current_image - 1].metadata_lte;
+       lte = wim_get_current_image_metadata(w)->metadata_lte;
 
        /* 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_fp,
+                                            w->out_fd,
                                             wimlib_get_compression_type(w),
                                             &lte->output_resource_entry,
                                             lte->hash);
-       if (ret != 0)
-               goto out;
+       /* 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. */
 
-       /* It's very likely the SHA1 message digest of the metadata resource
-        * changed, so re-insert the lookup table entry into the lookup table.
-        *
-        * We do not check for other lookup table entries having the same SHA1
-        * message digest.  It's possible for 2 absolutely identical images to
-        * be added, therefore causing 2 identical metadata resources to be in
-        * the WIM.  However, in this case, it's expected for 2 separate lookup
-        * table entries to be created, even though this doesn't make a whole
-        * lot of sense since they will share the same SHA1 message digest.
-        * */
-       lookup_table_unlink(w->lookup_table, lte);
-       lookup_table_insert(w->lookup_table, lte);
-       lte->out_refcnt = 1;
-
-       /* Make sure that the lookup table entry for this metadata resource is
-        * marked with the metadata flag. */
-       lte->output_resource_entry.flags |= WIM_RESHDR_FLAG_METADATA;
-out:
        /* All the data has been written to the new WIM; no need for the buffer
         * anymore */
        FREE(buf);