X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fresource.c;h=eb79c7498c98a2e0b7d78129dad067c5df3d9643;hp=f8f484484ce0a9137ba9973d7d206687e7eef109;hb=a2984b07d10ef6b49a509cf0289bd9dc824e42e7;hpb=fea0ee9f10fbcb85ff56ca06510e96186a7611ae diff --git a/src/resource.c b/src/resource.c index f8f48448..eb79c749 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1,11 +1,11 @@ /* * resource.c * - * Read uncompressed and compressed metadata and file resources. + * Read uncompressed and compressed metadata and file resources from a WIM file. */ /* - * Copyright (C) 2012 Eric Biggers + * Copyright (C) 2012, 2013 Eric Biggers * * This file is part of wimlib, a library for working with WIM files. * @@ -22,21 +22,18 @@ * wimlib; if not, see http://www.gnu.org/licenses/. */ -#include "config.h" - -#include -#include - -#include "dentry.h" - #include "wimlib_internal.h" +#include "dentry.h" #include "lookup_table.h" #include "buffer_io.h" #include "lzx.h" #include "xpress.h" #include "sha1.h" -#include + #include +#include +#include +#include #ifdef WITH_NTFS_3G #include @@ -45,7 +42,6 @@ #include #endif - /* * Reads all or part of a compressed resource into an in-memory buffer. * @@ -375,9 +371,6 @@ int read_uncompressed_resource(FILE *fp, u64 offset, u64 len, return 0; } - - - /* Reads the contents of a struct resource_entry, as represented in the on-disk * format, from the memory pointed to by @p, and fills in the fields of @entry. * A pointer to the byte after the memory read at @p is returned. */ @@ -483,7 +476,7 @@ out: * * Returns zero on success, nonzero on failure. */ -int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[], +int read_wim_resource(const struct wim_lookup_table_entry *lte, u8 buf[], size_t size, u64 offset, int flags) { int ctype; @@ -546,8 +539,8 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[], case RESOURCE_IN_FILE_ON_DISK: /* The resource is in some file on the external filesystem and * needs to be read uncompressed */ - wimlib_assert(lte->file_on_disk); - wimlib_assert(<e->file_on_disk == <e->staging_file_name); + wimlib_assert(lte->file_on_disk != NULL); + BUILD_BUG_ON(<e->file_on_disk != <e->staging_file_name); /* Use existing file pointer if available; otherwise open one * temporarily */ if (lte->file_on_disk_fp) { @@ -565,6 +558,13 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[], if (fp != lte->file_on_disk_fp) fclose(fp); break; +#if defined(__CYGWIN__) || defined(__WIN32__) + case RESOURCE_WIN32: + wimlib_assert(lte->file_on_disk_fp != NULL); + ret = win32_read_file(lte->file_on_disk, lte->file_on_disk_fp, + offset, size, buf); + break; +#endif case RESOURCE_IN_ATTACHED_BUFFER: /* The resource is directly attached uncompressed in an * in-memory buffer. */ @@ -603,89 +603,96 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[], * * Returns 0 on success; nonzero on failure. */ -int read_full_wim_resource(const struct lookup_table_entry *lte, u8 buf[], +int read_full_wim_resource(const struct wim_lookup_table_entry *lte, u8 buf[], int flags) { return read_wim_resource(lte, buf, wim_resource_size(lte), 0, flags); } -/* 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]) -{ - /* Set up a temporary lookup table entry to provide to - * write_wim_resource(). */ - struct 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(<e, out_fp, out_ctype, out_res_entry, 0); - if (ret != 0) - return ret; - copy_hash(hash, lte.hash); - return 0; -} - -/* - * Extracts the first @size bytes of the WIM resource specified by @lte to the - * open file descriptor @fd. +/* Extracts the first @size bytes of a WIM resource to somewhere. In the + * process, the SHA1 message digest of the resource is checked if the full + * resource is being extracted. * - * Returns 0 on success; nonzero on failure. - */ -int extract_wim_resource_to_fd(const struct lookup_table_entry *lte, int fd, - u64 size) + * @extract_chunk is a function that is called to extract each chunk of the + * resource. */ +int extract_wim_resource(const struct wim_lookup_table_entry *lte, + u64 size, + extract_chunk_func_t extract_chunk, + void *extract_chunk_arg) { u64 bytes_remaining = size; u8 buf[min(WIM_CHUNK_SIZE, bytes_remaining)]; u64 offset = 0; int ret = 0; u8 hash[SHA1_HASH_SIZE]; - + bool check_hash = (size == wim_resource_size(lte)); SHA_CTX ctx; - sha1_init(&ctx); + + if (check_hash) + sha1_init(&ctx); while (bytes_remaining) { - u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE); + u64 to_read = min(bytes_remaining, sizeof(buf)); ret = read_wim_resource(lte, buf, to_read, offset, 0); if (ret != 0) - break; - sha1_update(&ctx, buf, to_read); - if (full_write(fd, buf, to_read) < to_read) { + return ret; + if (check_hash) + sha1_update(&ctx, buf, to_read); + ret = extract_chunk(buf, to_read, offset, extract_chunk_arg); + if (ret != 0) { ERROR_WITH_ERRNO("Error extracting WIM resource"); - return WIMLIB_ERR_WRITE; + return ret; } bytes_remaining -= to_read; offset += to_read; } - sha1_final(hash, &ctx); - if (!hashes_equal(hash, lte->hash)) { - ERROR("Invalid checksum on a WIM resource " - "(detected when extracting to external file)"); - ERROR("The following WIM resource is invalid:"); - print_lookup_table_entry(lte); - return WIMLIB_ERR_INVALID_RESOURCE_HASH; + if (check_hash) { + sha1_final(hash, &ctx); + if (!hashes_equal(hash, lte->hash)) { + #ifdef ENABLE_ERROR_MESSAGES + ERROR("Invalid checksum on the following WIM resource:"); + print_lookup_table_entry(lte, stderr); + #endif + return WIMLIB_ERR_INVALID_RESOURCE_HASH; + } } return 0; } -/* - * Extracts the WIM resource specified by @lte to the open file descriptor @fd. +/* Write @n bytes from @buf to the file descriptor @fd, retrying on internupt + * and on short writes. * - * Returns 0 on success; nonzero on failure. - */ -int extract_full_wim_resource_to_fd(const struct lookup_table_entry *lte, int fd) + * Returns short count and set errno on failure. */ +static ssize_t full_write(int fd, const void *buf, size_t n) { - return extract_wim_resource_to_fd(lte, fd, wim_resource_size(lte)); + const char *p = buf; + ssize_t ret; + ssize_t total = 0; + + while (total != n) { + ret = write(fd, p, n); + if (ret < 0) { + if (errno == EINTR) + continue; + else + break; + } + total += ret; + p += ret; + } + return total; +} + +int extract_wim_chunk_to_fd(const u8 *buf, size_t len, u64 offset, void *arg) +{ + int fd = *(int*)arg; + ssize_t ret = full_write(fd, buf, len); + if (ret < len) { + ERROR_WITH_ERRNO("Error writing to file descriptor"); + return WIMLIB_ERR_WRITE; + } else { + return 0; + } } /* @@ -695,8 +702,10 @@ int extract_full_wim_resource_to_fd(const struct lookup_table_entry *lte, int fd * * The output_resource_entry, out_refcnt, and part_number fields of @lte are * updated. + * + * (This function is confusing and should be refactored somehow.) */ -int copy_resource(struct lookup_table_entry *lte, void *wim) +int copy_resource(struct wim_lookup_table_entry *lte, void *wim) { WIMStruct *w = wim; int ret; @@ -714,272 +723,3 @@ int copy_resource(struct lookup_table_entry *lte, void *wim) lte->part_number = w->hdr.part_number; return 0; } - -/* - * Reads the metadata metadata resource from 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. - * - * @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. - * - * @return: Zero on success, nonzero on failure. - */ -int read_metadata_resource(WIMStruct *w, struct image_metadata *imd) -{ - u8 *buf; - u32 dentry_offset; - int ret; - struct dentry *dentry; - struct inode_table inode_tab; - const struct lookup_table_entry *metadata_lte; - u64 metadata_len; - struct hlist_head inode_list; - - metadata_lte = imd->metadata_lte; - metadata_len = wim_resource_size(metadata_lte); - - DEBUG("Reading metadata resource: length = %"PRIu64", " - "offset = %"PRIu64"", metadata_len, - 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); - - 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_wim_resource(metadata_lte, buf, 0); - if (ret != 0) - goto out_free_buf; - - DEBUG("Finished reading metadata resource into memory."); - - /* 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. - * - * 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); - if (ret != 0) - 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)); - 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); - goto out_free_security_data; - } - - inode_add_dentry(dentry, dentry->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) - goto out_free_dentry_tree; - - /* Build hash table that maps hard link group IDs to dentry sets */ - DEBUG("Building link group table"); - ret = init_inode_table(&inode_tab, 9001); - if (ret != 0) - goto out_free_dentry_tree; - - for_dentry_in_tree(dentry, inode_table_insert, &inode_tab); - - DEBUG("Fixing inconsistencies in the hard link groups"); - ret = fix_inodes(&inode_tab, &inode_list); - destroy_inode_table(&inode_tab); - if (ret != 0) - 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); - if (ret != 0) - goto out_free_dentry_tree; - } - - DEBUG("Done reading image metadata"); - - imd->root_dentry = dentry; - imd->inode_list = inode_list; - goto out_free_buf; -out_free_dentry_tree: - free_dentry_tree(dentry, NULL); -out_free_security_data: - free_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) -{ - 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; -} - -/* Write the metadata resource for the current WIM image. */ -int write_metadata_resource(WIMStruct *w) -{ - u8 *buf; - u8 *p; - int ret; - u64 subdir_offset; - struct dentry *root; - struct lookup_table_entry *lte; - u64 metadata_original_size; - struct wim_security_data *sd; - - DEBUG("Writing metadata resource for image %d (offset = %"PRIu64")", - w->current_image, ftello(w->out_fp)); - - root = wim_root_dentry(w); - sd = wim_security_data(w); - - /* 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 - * boundary, - * - plus the total length of the root dentry, - * - plus 8 bytes for an end-of-directory entry following the root - * dentry (shouldn't really be needed, but just in case...) - */ - recalculate_security_data_length(sd); - subdir_offset = (((u64)sd->total_length + 7) & ~7) + - dentry_correct_total_length(root) + 8; - - /* 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; - - /* Allocate a buffer to contain the uncompressed metadata resource */ - buf = MALLOC(metadata_original_size); - if (!buf) { - ERROR("Failed to allocate %"PRIu64" bytes for " - "metadata resource", metadata_original_size); - return WIMLIB_ERR_NOMEM; - } - - /* Write the security data into the resource buffer */ - p = write_security_data(sd, buf); - - /* 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); - - /* Get the lookup table entry for the metadata resource so we can update - * it. */ - lte = w->image_metadata[w->current_image - 1].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, - wimlib_get_compression_type(w), - <e->output_resource_entry, - lte->hash); - if (ret != 0) - goto out; - - /* 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); - return ret; -}