/* 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 wim_lookup_table_entry *metadata_lte; struct wim_security_data *sd; struct wim_image_metadata *imd; /* Create lookup table entry for this metadata resource (for now really * just a dummy entry). */ ret = WIMLIB_ERR_NOMEM; metadata_lte = new_lookup_table_entry(); if (!metadata_lte) goto out; metadata_lte->flags = WIM_RESHDR_FLAG_METADATA; metadata_lte->unhashed = 1; /* Create empty security data (no security descriptors). */ sd = new_wim_security_data(); if (!sd) goto out_free_metadata_lte; 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_lte = metadata_lte; imd->security_data = sd; imd->modified = 1; /* 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_lte: free_lookup_table_entry(metadata_lte); out: return ret; }
static void destroy_image_metadata(struct wim_image_metadata *imd, struct blob_table *table, bool free_metadata_blob_descriptor) { free_dentry_tree(imd->root_dentry, table); 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); }
/* * Reads and parses a metadata resource for an image in the WIM file. * * @imd: * Pointer to the image metadata structure for the image whose metadata * resource we are reading. Its `metadata_blob' member specifies the blob * table entry for the metadata resource. The rest of the image metadata * entry will be filled in by this function. * * 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(struct wim_image_metadata *imd) { const struct blob_descriptor *metadata_blob; void *buf; int ret; u8 hash[SHA1_HASH_SIZE]; struct wim_security_data *sd; struct wim_dentry *root; metadata_blob = imd->metadata_blob; /* Read the metadata resource into memory. (It may be compressed.) */ ret = read_blob_into_alloc_buf(metadata_blob, &buf); if (ret) return ret; /* Checksum the metadata resource. */ sha1_buffer(buf, metadata_blob->size, hash); if (!hashes_equal(metadata_blob->hash, hash)) { ERROR("Metadata resource is corrupted " "(invalid SHA-1 message digest)!"); ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto out_free_buf; } /* Parse the metadata resource. * * 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_blob->size, &sd); if (ret) goto out_free_buf; ret = read_dentry_tree(buf, metadata_blob->size, sd->total_length, &root); if (ret) goto out_free_security_data; /* We have everything we need from the buffer now. */ FREE(buf); buf = NULL; /* Calculate and validate inodes. */ ret = dentry_tree_fix_inodes(root, &imd->inode_list); if (ret) goto out_free_dentry_tree; fix_security_ids(imd, sd->num_entries); /* Success; fill in the image_metadata structure. */ imd->root_dentry = root; imd->security_data = sd; INIT_LIST_HEAD(&imd->unhashed_blobs); return 0; out_free_dentry_tree: free_dentry_tree(root, NULL); out_free_security_data: free_wim_security_data(sd); out_free_buf: FREE(buf); return ret; }
/* * Reads the security data from the metadata resource of a WIM image. * * @buf * Buffer containing an uncompressed WIM metadata resource. * @buf_len * Length of the uncompressed metadata resource, in bytes. * @sd_ret * On success, a pointer to the resulting security data structure will be * returned here. * * Note: There is no `offset' argument because the security data is located at * the beginning of the metadata resource. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_INVALID_METADATA_RESOURCE * WIMLIB_ERR_NOMEM */ int read_wim_security_data(const u8 *buf, size_t buf_len, struct wim_security_data **sd_ret) { struct wim_security_data *sd; int ret; u64 total_len; u64 sizes_size; u64 size_no_descriptors; const struct wim_security_data_disk *sd_disk; const u8 *p; if (buf_len < 8) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; sd = new_wim_security_data(); if (!sd) goto out_of_memory; sd_disk = (const struct wim_security_data_disk *)buf; sd->total_length = le32_to_cpu(sd_disk->total_length); sd->num_entries = le32_to_cpu(sd_disk->num_entries); DEBUG("Reading security data: num_entries=%u, total_length=%u", sd->num_entries, sd->total_length); /* Length field of 0 is a special case that really means length * of 8. */ if (sd->total_length == 0) sd->total_length = 8; /* The security_id field of each dentry is a signed 32-bit integer, so * the possible indices into the security descriptors table are 0 * through 0x7fffffff. Which means 0x80000000 security descriptors * maximum. Not like you should ever have anywhere close to that many * security descriptors! */ if (sd->num_entries > 0x80000000) goto out_invalid_sd; /* Verify the listed total length of the security data is big enough to * include the sizes array, verify that the file data is big enough to * include it as well, then allocate the array of sizes. * * Note: The total length of the security data must fit in a 32-bit * integer, even though each security descriptor size is a 64-bit * integer. This is stupid, and we need to be careful not to actually * let the security descriptor sizes be over 0xffffffff. */ if (sd->total_length > buf_len) goto out_invalid_sd; sizes_size = (u64)sd->num_entries * sizeof(u64); size_no_descriptors = 8 + sizes_size; if (size_no_descriptors > sd->total_length) goto out_invalid_sd; total_len = size_no_descriptors; /* Return immediately if no security descriptors. */ if (sd->num_entries == 0) goto out_align_total_length; /* Allocate a new buffer for the sizes array */ sd->sizes = MALLOC(sizes_size); if (!sd->sizes) goto out_of_memory; /* Copy the sizes array into the new buffer */ for (u32 i = 0; i < sd->num_entries; i++) { sd->sizes[i] = le64_to_cpu(sd_disk->sizes[i]); if (sd->sizes[i] > 0xffffffff) goto out_invalid_sd; } p = (const u8*)sd_disk + size_no_descriptors; /* Allocate the array of pointers to the security descriptors, then read * them into separate buffers. */ sd->descriptors = CALLOC(sd->num_entries, sizeof(sd->descriptors[0])); if (!sd->descriptors) goto out_of_memory; for (u32 i = 0; i < sd->num_entries; i++) { if (sd->sizes[i] == 0) continue; total_len += sd->sizes[i]; if (total_len > (u64)sd->total_length) goto out_invalid_sd; sd->descriptors[i] = memdup(p, sd->sizes[i]); if (!sd->descriptors[i]) goto out_of_memory; p += sd->sizes[i]; } out_align_total_length: total_len = (total_len + 7) & ~7; sd->total_length = (sd->total_length + 7) & ~7; if (total_len != sd->total_length) { WARNING("Expected WIM security data total length of " "%u bytes, but calculated %u bytes", sd->total_length, (unsigned)total_len); } *sd_ret = sd; ret = 0; goto out; out_invalid_sd: ERROR("WIM security data is invalid!"); ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto out_free_sd; out_of_memory: ERROR("Out of memory while reading WIM security data!"); ret = WIMLIB_ERR_NOMEM; out_free_sd: free_wim_security_data(sd); out: return ret; }