]> wimlib.net Git - wimlib/blobdiff - src/resource.c
Update progress functions
[wimlib] / src / resource.c
index 6e47dff71da9c932f73d1cc89c14412d314f530c..7631dd492309283bb7d8fa72a569004f64ae619d 100644 (file)
 #endif
 
 #include "wimlib.h"
+#include "wimlib/assert.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"
+#include "wimlib/wim.h"
 
 #ifdef __WIN32__
 /* for read_win32_file_prefix(), read_win32_encrypted_file_prefix() */
@@ -175,37 +177,21 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec,
 
        /* Get the maximum size of uncompressed chunks in this resource, which
         * we require be a power of 2.  */
-       u32 chunk_size = 0;
        u64 cur_read_offset = rspec->offset_in_wim;
-       int ctype = WIMLIB_COMPRESSION_TYPE_NONE;
+       int ctype = rspec->compression_type;
+       u32 chunk_size = rspec->chunk_size;
        if (alt_chunk_table) {
                /* Alternate chunk table format.  Its header specifies the chunk
-                * size and compression format.  */
-               struct alt_chunk_table_header_disk hdr;
-
-               ret = full_pread(in_fd, &hdr, sizeof(hdr), cur_read_offset);
-               if (ret)
-                       goto read_error;
-               cur_read_offset += sizeof(hdr);
-
-               chunk_size = le32_to_cpu(hdr.chunk_size);
-               ctype = le32_to_cpu(hdr.compression_format);
-
-               /* Format numbers must be the same as in WIMGAPI to be
-                * compatible.  */
-               BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_NONE != 0);
-               BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 1);
-               BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 2);
-               BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZMS != 3);
-       } else {
-               /* "Normal" format: the maximum uncompressed chunk size and the
-                * compression format default to those of the WIM itself.  */
-               chunk_size = rspec->wim->chunk_size;
-               ctype = rspec->wim->compression_type;
+                * size and compression format.  Note: it could be read here;
+                * however, the relevant data was already loaded into @rspec by
+                * read_wim_lookup_table().  */
+               cur_read_offset += sizeof(struct alt_chunk_table_header_disk);
        }
+
        if (!is_power_of_2(chunk_size)) {
                ERROR("Invalid compressed resource: "
-                     "expected power-of-2 chunk size (got %u)", chunk_size);
+                     "expected power-of-2 chunk size (got %"PRIu32")",
+                     chunk_size);
                ret = WIMLIB_ERR_INVALID_CHUNK_SIZE;
                goto out_free_memory;
        }
@@ -576,6 +562,28 @@ read_error:
        goto out_free_memory;
 }
 
+static int
+fill_zeroes(u64 size, consume_data_callback_t cb, void *cb_ctx)
+{
+       if (unlikely(size)) {
+               u8 buf[min(size, BUFFER_SIZE)];
+
+               memset(buf, 0, sizeof(buf));
+
+               do {
+                       size_t len;
+                       int ret;
+
+                       len = min(size, BUFFER_SIZE);
+                       ret = cb(buf, len, cb_ctx);
+                       if (ret)
+                               return ret;
+                       size -= len;
+               } while (size);
+       }
+       return 0;
+}
+
 /* Read raw data from a file descriptor at the specified offset, feeding the
  * data it in chunks into the specified callback function.  */
 static int
@@ -668,11 +676,37 @@ read_partial_wim_resource(const struct wim_resource_spec *rspec,
                return read_compressed_wim_resource(rspec, &range, 1,
                                                    cb, cb_ctx);
        } else {
-               return read_raw_file_data(&rspec->wim->in_fd,
-                                         rspec->offset_in_wim + offset,
-                                         size,
-                                         cb,
-                                         cb_ctx);
+               /* Reading uncompressed resource.  For completeness, handle the
+                * weird case where size_in_wim < uncompressed_size.  */
+
+               u64 read_size;
+               u64 zeroes_size;
+               int ret;
+
+               if (likely(offset + size <= rspec->size_in_wim) ||
+                   rspec->is_pipable)
+               {
+                       read_size = size;
+                       zeroes_size = 0;
+               } else {
+                       if (offset >= rspec->size_in_wim) {
+                               read_size = 0;
+                               zeroes_size = size;
+                       } else {
+                               read_size = rspec->size_in_wim - offset;
+                               zeroes_size = offset + size - rspec->size_in_wim;
+                       }
+               }
+
+               ret = read_raw_file_data(&rspec->wim->in_fd,
+                                        rspec->offset_in_wim + offset,
+                                        read_size,
+                                        cb,
+                                        cb_ctx);
+               if (ret)
+                       return ret;
+
+               return fill_zeroes(zeroes_size, cb, cb_ctx);
        }
 }
 
@@ -724,7 +758,6 @@ read_wim_stream_prefix(const struct wim_lookup_table_entry *lte, u64 size,
                                         cb, cb_ctx);
 }
 
-#ifndef __WIN32__
 /* This function handles reading stream data that is located in an external
  * file,  such as a file that has been added to the WIM image through execution
  * of a wimlib_add_command.
@@ -745,7 +778,7 @@ read_file_on_disk_prefix(const struct wim_lookup_table_entry *lte, u64 size,
 
        DEBUG("Reading %"PRIu64" bytes from \"%"TS"\"", size, lte->file_on_disk);
 
-       raw_fd = open(lte->file_on_disk, O_BINARY | O_RDONLY);
+       raw_fd = topen(lte->file_on_disk, O_BINARY | O_RDONLY);
        if (raw_fd < 0) {
                ERROR_WITH_ERRNO("Can't open \"%"TS"\"", lte->file_on_disk);
                return WIMLIB_ERR_OPEN;
@@ -755,7 +788,6 @@ read_file_on_disk_prefix(const struct wim_lookup_table_entry *lte, u64 size,
        filedes_close(&fd);
        return ret;
 }
-#endif /* !__WIN32__ */
 
 /* This function handles the trivial case of reading stream data that is, in
  * fact, already located in an in-memory buffer.  */
@@ -793,11 +825,7 @@ read_stream_prefix(const struct wim_lookup_table_entry *lte, u64 size,
 {
        static const read_stream_prefix_handler_t handlers[] = {
                [RESOURCE_IN_WIM]             = read_wim_stream_prefix,
-       #ifdef __WIN32__
-               [RESOURCE_IN_FILE_ON_DISK]    = read_win32_file_prefix,
-       #else
                [RESOURCE_IN_FILE_ON_DISK]    = read_file_on_disk_prefix,
-       #endif
                [RESOURCE_IN_ATTACHED_BUFFER] = read_buffer_prefix,
        #ifdef WITH_FUSE
                [RESOURCE_IN_STAGING_FILE]    = read_file_on_disk_prefix,
@@ -806,6 +834,7 @@ read_stream_prefix(const struct wim_lookup_table_entry *lte, u64 size,
                [RESOURCE_IN_NTFS_VOLUME]     = read_ntfs_file_prefix,
        #endif
        #ifdef __WIN32__
+               [RESOURCE_IN_WINNT_FILE_ON_DISK] = read_winnt_file_prefix,
                [RESOURCE_WIN32_ENCRYPTED]    = read_win32_encrypted_file_prefix,
        #endif
        };
@@ -892,6 +921,34 @@ wim_reshdr_to_data(const struct wim_reshdr *reshdr, WIMStruct *wim, void **buf_r
        return wim_resource_spec_to_data(&rspec, buf_ret);
 }
 
+int
+wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim,
+                  u8 hash[SHA1_HASH_SIZE])
+{
+       struct wim_resource_spec rspec;
+       int ret;
+       struct wim_lookup_table_entry *lte;
+
+       wim_res_hdr_to_spec(reshdr, wim, &rspec);
+
+       lte = new_lookup_table_entry();
+       if (lte == NULL)
+               return WIMLIB_ERR_NOMEM;
+
+       lte_bind_wim_resource_spec(lte, &rspec);
+       lte->flags = rspec.flags;
+       lte->size = rspec.uncompressed_size;
+       lte->offset_in_res = 0;
+       lte->unhashed = 1;
+
+       ret = sha1_stream(lte);
+
+       lte_unbind_wim_resource_spec(lte);
+       copy_hash(hash, lte->hash);
+       free_lookup_table_entry(lte);
+       return ret;
+}
+
 struct streamifier_context {
        struct read_stream_list_callbacks cbs;
        struct wim_lookup_table_entry *cur_stream;
@@ -926,10 +983,17 @@ streamifier_cb(const void *chunk, size_t size, void *_ctx)
        wimlib_assert(size <= ctx->cur_stream->size - ctx->cur_stream_offset);
 
        if (ctx->cur_stream_offset == 0) {
+               u32 flags;
+
                /* Starting a new stream.  */
                DEBUG("Begin new stream (size=%"PRIu64").",
                      ctx->cur_stream->size);
-               ret = (*ctx->cbs.begin_stream)(ctx->cur_stream, true,
+
+               flags = BEGIN_STREAM_FLAG_PARTIAL_RESOURCE;
+               if (size == ctx->cur_stream->size)
+                       flags |= BEGIN_STREAM_FLAG_WHOLE_STREAM;
+               ret = (*ctx->cbs.begin_stream)(ctx->cur_stream,
+                                              flags,
                                               ctx->cbs.begin_stream_ctx);
                if (ret)
                        return ret;
@@ -938,9 +1002,9 @@ streamifier_cb(const void *chunk, size_t size, void *_ctx)
        /* Consume the chunk.  */
        ret = (*ctx->cbs.consume_chunk)(chunk, size,
                                        ctx->cbs.consume_chunk_ctx);
+       ctx->cur_stream_offset += size;
        if (ret)
                return ret;
-       ctx->cur_stream_offset += size;
 
        if (ctx->cur_stream_offset == ctx->cur_stream->size) {
                /* Finished reading all the data for a stream.  */
@@ -975,7 +1039,7 @@ struct hasher_context {
 /* Callback for starting to read a stream while calculating its SHA1 message
  * digest.  */
 static int
-hasher_begin_stream(struct wim_lookup_table_entry *lte, bool is_partial_res,
+hasher_begin_stream(struct wim_lookup_table_entry *lte, u32 flags,
                    void *_ctx)
 {
        struct hasher_context *ctx = _ctx;
@@ -985,7 +1049,7 @@ hasher_begin_stream(struct wim_lookup_table_entry *lte, bool is_partial_res,
        if (ctx->cbs.begin_stream == NULL)
                return 0;
        else
-               return (*ctx->cbs.begin_stream)(lte, is_partial_res,
+               return (*ctx->cbs.begin_stream)(lte, flags,
                                                ctx->cbs.begin_stream_ctx);
 }
 
@@ -1005,6 +1069,13 @@ hasher_consume_chunk(const void *chunk, size_t size, void *_ctx)
                return (*ctx->cbs.consume_chunk)(chunk, size, ctx->cbs.consume_chunk_ctx);
 }
 
+static void
+get_sha1_string(const u8 md[SHA1_HASH_SIZE], tchar *str)
+{
+       for (size_t i = 0; i < SHA1_HASH_SIZE; i++)
+               str += tsprintf(str, T("%02x"), md[i]);
+}
+
 /* Callback for finishing reading a stream while calculating its SHA1 message
  * digest.  */
 static int
@@ -1037,9 +1108,14 @@ hasher_end_stream(struct wim_lookup_table_entry *lte, int status, void *_ctx)
                         * that it is the same as the calculated value.  */
                        if (!hashes_equal(hash, lte->hash)) {
                                if (wimlib_print_errors) {
-                                       ERROR("Invalid SHA1 message digest "
-                                             "on the following WIM stream:");
-                                       print_lookup_table_entry(lte, stderr);
+                                       tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1];
+                                       tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1];
+                                       get_sha1_string(lte->hash, expected_hashstr);
+                                       get_sha1_string(hash, actual_hashstr);
+                                       ERROR("The stream is corrupted!\n"
+                                             "        (Expected SHA1=%"TS",\n"
+                                             "              got SHA1=%"TS")",
+                                             expected_hashstr, actual_hashstr);
                                }
                                ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
                                errno = EINVAL;
@@ -1063,7 +1139,7 @@ read_full_stream_with_cbs(struct wim_lookup_table_entry *lte,
 {
        int ret;
 
-       ret = (*cbs->begin_stream)(lte, false, cbs->begin_stream_ctx);
+       ret = (*cbs->begin_stream)(lte, 0, cbs->begin_stream_ctx);
        if (ret)
                return ret;
 
@@ -1091,7 +1167,6 @@ read_full_stream_with_sha1(struct wim_lookup_table_entry *lte,
                .consume_chunk_ctx      = &hasher_ctx,
                .end_stream             = hasher_end_stream,
                .end_stream_ctx         = &hasher_ctx,
-
        };
        return read_full_stream_with_cbs(lte, &hasher_cbs);
 }
@@ -1377,7 +1452,10 @@ sha1_stream(struct wim_lookup_table_entry *lte)
 }
 
 /* Convert a short WIM resource header to a stand-alone WIM resource
- * specification.  */
+ * specification.
+ *
+ * Note: for packed resources some fields still need to be overridden.
+ */
 void
 wim_res_hdr_to_spec(const struct wim_reshdr *reshdr, WIMStruct *wim,
                    struct wim_resource_spec *rspec)
@@ -1389,6 +1467,13 @@ wim_res_hdr_to_spec(const struct wim_reshdr *reshdr, WIMStruct *wim,
        INIT_LIST_HEAD(&rspec->stream_list);
        rspec->flags = reshdr->flags;
        rspec->is_pipable = wim_is_pipable(wim);
+       if (rspec->flags & WIM_RESHDR_FLAG_COMPRESSED) {
+               rspec->compression_type = wim->compression_type;
+               rspec->chunk_size = wim->chunk_size;
+       } else {
+               rspec->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
+               rspec->chunk_size = 0;
+       }
 }
 
 /* Convert a stand-alone resource specification to a WIM resource header.  */