+/* This function handles the trivial case of reading blob data that is, in fact,
+ * already located in an in-memory buffer. */
+static int
+read_buffer_prefix(const struct blob_descriptor *blob,
+ u64 size, const struct read_blob_callbacks *cbs)
+{
+ if (unlikely(!size))
+ return 0;
+ return call_consume_chunk(blob->attached_buffer, size, cbs);
+}
+
+typedef int (*read_blob_prefix_handler_t)(const struct blob_descriptor *blob,
+ u64 size,
+ const struct read_blob_callbacks *cbs);
+
+/*
+ * Read the first @size bytes from a generic "blob", which may be located in any
+ * one of several locations, such as in a WIM resource (possibly compressed), in
+ * an external file, or directly in an in-memory buffer. The blob data will be
+ * fed to the cbs->consume_chunk() callback function in chunks that are nonempty
+ * but otherwise are of unspecified size.
+ *
+ * Returns 0 on success; nonzero on error. A nonzero value will be returned if
+ * the blob data cannot be successfully read (for a number of different reasons,
+ * depending on the blob location), or if cbs->consume_chunk() returned nonzero
+ * in which case that error code will be returned.
+ */
+static int
+read_blob_prefix(const struct blob_descriptor *blob, u64 size,
+ const struct read_blob_callbacks *cbs)
+{
+ static const read_blob_prefix_handler_t handlers[] = {
+ [BLOB_IN_WIM] = read_wim_blob_prefix,
+ [BLOB_IN_FILE_ON_DISK] = read_file_on_disk_prefix,
+ [BLOB_IN_ATTACHED_BUFFER] = read_buffer_prefix,
+ #ifdef WITH_FUSE
+ [BLOB_IN_STAGING_FILE] = read_staging_file_prefix,
+ #endif
+ #ifdef WITH_NTFS_3G
+ [BLOB_IN_NTFS_VOLUME] = read_ntfs_attribute_prefix,
+ #endif
+ #ifdef __WIN32__
+ [BLOB_IN_WINNT_FILE_ON_DISK] = read_winnt_stream_prefix,
+ [BLOB_WIN32_ENCRYPTED] = read_win32_encrypted_file_prefix,
+ #endif
+ };
+ wimlib_assert(blob->blob_location < ARRAY_LEN(handlers)
+ && handlers[blob->blob_location] != NULL);
+ wimlib_assert(size <= blob->size);
+ return handlers[blob->blob_location](blob, size, cbs);
+}
+
+/* Read the full data of the specified blob, passing the data into the specified
+ * callbacks (all of which are optional). */
+int
+read_blob_with_cbs(struct blob_descriptor *blob,
+ const struct read_blob_callbacks *cbs)
+{
+ int ret;
+
+ ret = call_begin_blob(blob, cbs);
+ if (unlikely(ret))
+ return ret;
+
+ ret = read_blob_prefix(blob, blob->size, cbs);
+
+ return call_end_blob(blob, ret, cbs);
+}
+
+/* Read the full uncompressed data of the specified blob into the specified
+ * buffer, which must have space for at least blob->size bytes. The SHA-1
+ * message digest is *not* checked. */
+int
+read_blob_into_buf(const struct blob_descriptor *blob, void *buf)
+{
+ struct read_blob_callbacks cbs = {
+ .consume_chunk = bufferer_cb,
+ .ctx = &buf,
+ };
+ return read_blob_prefix(blob, blob->size, &cbs);
+}
+
+/* Retrieve the full uncompressed data of the specified blob. A buffer large
+ * enough hold the data is allocated and returned in @buf_ret. The SHA-1
+ * message digest is *not* checked. */
+int
+read_blob_into_alloc_buf(const struct blob_descriptor *blob, void **buf_ret)
+{
+ int ret;
+ void *buf;
+
+ if (unlikely((size_t)blob->size != blob->size)) {
+ ERROR("Can't read %"PRIu64" byte blob into memory", blob->size);
+ return WIMLIB_ERR_NOMEM;