]> wimlib.net Git - wimlib/blobdiff - src/resource.c
WIM chunk size: Calculate with existing macros when possible
[wimlib] / src / resource.c
index 464ca711ea43a3dbb7dda4d87b3a4d9a8e0b20ec..29bfcd7fd0a962fb7c29d6f7621251876bc43e5d 100644 (file)
  * wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
-#include "wimlib_internal.h"
-#include "dentry.h"
-#include "lookup_table.h"
-#include "buffer_io.h"
-#include "sha1.h"
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib.h"
+#include "wimlib/dentry.h"
+#include "wimlib/endianness.h"
+#include "wimlib/error.h"
+#include "wimlib/file_io.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/resource.h"
+#include "wimlib/sha1.h"
 
 #ifdef __WIN32__
-#  include "win32.h"
+/* for read_win32_file_prefix(), read_win32_encrypted_file_prefix() */
+#  include "wimlib/win32.h"
 #endif
 
-#include <errno.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
+#ifdef WITH_NTFS_3G
+/* for read_ntfs_file_prefix() */
+#  include "wimlib/ntfs_3g.h"
+#endif
 
 #ifdef HAVE_ALLOCA_H
 #  include <alloca.h>
 #endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 /*
  * Reads all or part of a compressed WIM resource.
@@ -48,7 +60,7 @@
  * Returns zero on success, nonzero on failure.
  */
 static int
-read_compressed_resource(filedes_t in_fd,
+read_compressed_resource(int in_fd,
                         u64 resource_compressed_size,
                         u64 resource_uncompressed_size,
                         u64 resource_offset,
@@ -85,8 +97,8 @@ read_compressed_resource(filedes_t in_fd,
 
        /* Calculate how many chunks the resource consists of in its entirety.
         * */
-       u64 num_chunks = (resource_uncompressed_size + WIM_CHUNK_SIZE - 1) /
-                                                               WIM_CHUNK_SIZE;
+       u64 num_chunks = DIV_ROUND_UP(resource_uncompressed_size, WIM_CHUNK_SIZE);
+
        /* As mentioned, the first chunk has no entry in the chunk table. */
        u64 num_chunk_entries = num_chunks - 1;
 
@@ -111,11 +123,20 @@ read_compressed_resource(filedes_t in_fd,
        if (end_chunk != num_chunks - 1)
                num_needed_chunks++;
 
+       /* According to M$'s documentation, if the uncompressed size of
+        * the file is greater than 4 GB, the chunk entries are 8-byte
+        * integers.  Otherwise, they are 4-byte integers. */
+       u64 chunk_entry_size = (resource_uncompressed_size >
+                               (u64)1 << 32) ?  8 : 4;
+
+       /* Size of the full chunk table in the WIM file. */
+       u64 chunk_table_size = chunk_entry_size * num_chunk_entries;
+
        /* Allocate the chunk table.  It will only contain offsets for the
         * chunks that are actually needed for this read. */
        u64 *chunk_offsets;
        bool chunk_offsets_malloced;
-       if (num_needed_chunks < 1000) {
+       if (num_needed_chunks < 1024) {
                chunk_offsets = alloca(num_needed_chunks * sizeof(u64));
                chunk_offsets_malloced = false;
        } else {
@@ -139,14 +160,6 @@ read_compressed_resource(filedes_t in_fd,
        if (start_chunk == 0)
                chunk_offsets[0] = 0;
 
-       /* According to M$'s documentation, if the uncompressed size of
-        * the file is greater than 4 GB, the chunk entries are 8-byte
-        * integers.  Otherwise, they are 4-byte integers. */
-       u64 chunk_entry_size = (resource_uncompressed_size >= (u64)1 << 32) ?
-                                                                       8 : 4;
-
-       /* Size of the full chunk table in the WIM file. */
-       u64 chunk_table_size = chunk_entry_size * num_chunk_entries;
 
        /* Read the needed chunk offsets from the table in the WIM file. */
 
@@ -163,12 +176,30 @@ read_compressed_resource(filedes_t in_fd,
        u64 file_offset_of_needed_chunk_entries = resource_offset +
                                start_table_idx * chunk_entry_size;
 
+       /* Allocate a buffer into which to read the raw chunk entries. */
+       void *chunk_tab_buf;
+       bool chunk_tab_buf_malloced = false;
+
        /* Number of bytes we need to read from the chunk table. */
        size_t size = num_needed_chunk_entries * chunk_entry_size;
+       if ((u64)size != num_needed_chunk_entries * chunk_entry_size) {
+               ERROR("Compressed read request too large to fit into memory!");
+               ret = WIMLIB_ERR_NOMEM;
+               goto out;
+       }
 
-       /* Read the raw data into the end of the chunk_offsets array to
-        * avoid allocating another array. */
-       void *chunk_tab_buf = (void*)&chunk_offsets[num_needed_chunks] - size;
+       if (size < 4096) {
+               chunk_tab_buf = alloca(size);
+       } else {
+               chunk_tab_buf = malloc(size);
+               if (!chunk_tab_buf) {
+                       ERROR("Failed to allocate chunk table buffer of "
+                             "size %zu bytes", size);
+                       ret = WIMLIB_ERR_NOMEM;
+                       goto out;
+               }
+               chunk_tab_buf_malloced = true;
+       }
 
        if (full_pread(in_fd, chunk_tab_buf, size,
                       file_offset_of_needed_chunk_entries) != size)
@@ -182,17 +213,17 @@ read_compressed_resource(filedes_t in_fd,
                chunk_tab_p++;
 
        if (chunk_entry_size == 4) {
-               u32 *entries = (u32*)chunk_tab_buf;
+               le32 *entries = (le32*)chunk_tab_buf;
                while (num_needed_chunk_entries--)
                        *chunk_tab_p++ = le32_to_cpu(*entries++);
        } else {
-               u64 *entries = (u64*)chunk_tab_buf;
+               le64 *entries = (le64*)chunk_tab_buf;
                while (num_needed_chunk_entries--)
                        *chunk_tab_p++ = le64_to_cpu(*entries++);
        }
 
-       /* Done with the chunk table now.  We must now seek to the first chunk
-        * that is needed for the read. */
+       /* Done reading the chunk table now.  Now calculate the file offset for
+        * the first byte of compressed data we need to read. */
 
        u64 cur_read_offset = resource_offset + chunk_table_size + chunk_offsets[0];
 
@@ -336,6 +367,8 @@ read_compressed_resource(filedes_t in_fd,
 out:
        if (chunk_offsets_malloced)
                FREE(chunk_offsets);
+       if (chunk_tab_buf_malloced)
+               FREE(chunk_tab_buf);
        return ret;
 
 read_error:
@@ -344,73 +377,63 @@ read_error:
        goto out;
 }
 
-/*
- * Reads uncompressed data from an open file stream.
- */
-int
-read_uncompressed_resource(FILE *fp, u64 offset, u64 len, void *contents_ret)
-{
-       if (fseeko(fp, offset, SEEK_SET) != 0) {
-               ERROR("Failed to seek to byte %"PRIu64" of input file "
-                     "to read uncompressed resource (len = %"PRIu64")",
-                     offset, len);
-               return WIMLIB_ERR_READ;
-       }
-       if (fread(contents_ret, 1, len, fp) != len) {
-               if (feof(fp)) {
-                       ERROR("Unexpected EOF in uncompressed file resource");
-               } else {
-                       ERROR("Failed to read %"PRIu64" bytes from "
-                             "uncompressed resource at offset %"PRIu64,
-                             len, offset);
-               }
-               return WIMLIB_ERR_READ;
-       }
-       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. */
-const void *
-get_resource_entry(const void *p, struct resource_entry *entry)
+/* Translates a WIM resource entry from the on-disk format to an in-memory
+ * format. */
+void
+get_resource_entry(const struct resource_entry_disk *disk_entry,
+                  struct resource_entry *entry)
 {
-       u64 size;
-       u8 flags;
-
-       p = get_u56(p, &size);
-       p = get_u8(p, &flags);
-       entry->size = size;
-       entry->flags = flags;
+       /* Note: disk_entry may not be 8 byte aligned--- in that case, the
+        * offset and original_size members will be unaligned.  (This should be
+        * okay since `struct resource_entry_disk' is declared as packed.) */
+
+       /* Read the size and flags into a bitfield portably... */
+       entry->size = (((u64)disk_entry->size[0] <<  0) |
+                      ((u64)disk_entry->size[1] <<  8) |
+                      ((u64)disk_entry->size[2] << 16) |
+                      ((u64)disk_entry->size[3] << 24) |
+                      ((u64)disk_entry->size[4] << 32) |
+                      ((u64)disk_entry->size[5] << 40) |
+                      ((u64)disk_entry->size[6] << 48));
+       entry->flags = disk_entry->flags;
+       entry->offset = le64_to_cpu(disk_entry->offset);
+       entry->original_size = le64_to_cpu(disk_entry->original_size);
 
        /* offset and original_size are truncated to 62 bits to avoid possible
         * overflows, when converting to a signed 64-bit integer (off_t) or when
         * adding size or original_size.  This is okay since no one would ever
         * actually have a WIM bigger than 4611686018427387903 bytes... */
-       p = get_u64(p, &entry->offset);
        if (entry->offset & 0xc000000000000000ULL) {
                WARNING("Truncating offset in resource entry");
                entry->offset &= 0x3fffffffffffffffULL;
        }
-       p = get_u64(p, &entry->original_size);
        if (entry->original_size & 0xc000000000000000ULL) {
                WARNING("Truncating original_size in resource entry");
                entry->original_size &= 0x3fffffffffffffffULL;
        }
-       return p;
 }
 
-/* Copies the struct resource_entry @entry to the memory pointed to by @p in the
- * on-disk format.  A pointer to the byte after the memory written at @p is
- * returned. */
-void *
-put_resource_entry(void *p, const struct resource_entry *entry)
+/* Translates a WIM resource entry from an in-memory format into the on-disk
+ * format. */
+void
+put_resource_entry(const struct resource_entry *entry,
+                  struct resource_entry_disk *disk_entry)
 {
-       p = put_u56(p, entry->size);
-       p = put_u8(p, entry->flags);
-       p = put_u64(p, entry->offset);
-       p = put_u64(p, entry->original_size);
-       return p;
+       /* Note: disk_entry may not be 8 byte aligned--- in that case, the
+        * offset and original_size members will be unaligned.  (This should be
+        * okay since `struct resource_entry_disk' is declared as packed.) */
+       u64 size = entry->size;
+
+       disk_entry->size[0] = size >>  0;
+       disk_entry->size[1] = size >>  8;
+       disk_entry->size[2] = size >> 16;
+       disk_entry->size[3] = size >> 24;
+       disk_entry->size[4] = size >> 32;
+       disk_entry->size[5] = size >> 40;
+       disk_entry->size[6] = size >> 48;
+       disk_entry->flags = entry->flags;
+       disk_entry->offset = cpu_to_le64(entry->offset);
+       disk_entry->original_size = cpu_to_le64(entry->original_size);
 }
 
 static int
@@ -422,7 +445,7 @@ read_partial_wim_resource(const struct wim_lookup_table_entry *lte,
                          u64 offset)
 {
        WIMStruct *wim;
-       filedes_t in_fd;
+       int in_fd;
        int ret;
 
        wimlib_assert(lte->resource_location == RESOURCE_IN_WIM);
@@ -588,8 +611,6 @@ typedef int (*read_resource_prefix_handler_t)(const struct wim_lookup_table_entr
  * size until the resource is exhausted.
  *
  * If the resource is located in a WIM file, @flags can be:
- *   * WIMLIB_RESOURCE_FLAG_THREADSAFE_READ if it must be safe to access the resource
- *     concurrently by multiple threads.
  *   * WIMLIB_RESOURCE_FLAG_RAW if the raw compressed data is to be supplied
  *     instead of the uncompressed data.
  * Otherwise, the @flags are ignored.