+/*
+ * Read and interpret the collection of streams for the specified inode.
+ */
+static int
+setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
+ unsigned num_extra_streams, const u8 *default_hash,
+ u64 *offset_p)
+{
+ const u8 *orig_p = p;
+
+ inode->i_num_streams = 1 + num_extra_streams;
+
+ if (unlikely(inode->i_num_streams > ARRAY_LEN(inode->i_embedded_streams))) {
+ inode->i_streams = CALLOC(inode->i_num_streams,
+ sizeof(inode->i_streams[0]));
+ if (!inode->i_streams)
+ return WIMLIB_ERR_NOMEM;
+ }
+
+ /* Use the default hash field for the first stream */
+ inode->i_streams[0].stream_name = (utf16lechar *)NO_STREAM_NAME;
+ copy_hash(inode->i_streams[0]._stream_hash, default_hash);
+ inode->i_streams[0].stream_type = STREAM_TYPE_UNKNOWN;
+ inode->i_streams[0].stream_id = 0;
+
+ /* Read the extra stream entries */
+ for (unsigned i = 1; i < inode->i_num_streams; i++) {
+ struct wim_inode_stream *strm;
+ const struct wim_extra_stream_entry_on_disk *disk_strm;
+ u64 length;
+ u16 name_nbytes;
+
+ strm = &inode->i_streams[i];
+
+ strm->stream_id = i;
+
+ /* Do we have at least the size of the fixed-length data we know
+ * need? */
+ if ((end - p) < sizeof(struct wim_extra_stream_entry_on_disk))
+ return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+
+ disk_strm = (const struct wim_extra_stream_entry_on_disk *)p;
+
+ /* Read the length field */
+ length = le64_to_cpu(disk_strm->length);
+
+ /* 8-byte align the length */
+ length = (length + 7) & ~7;
+
+ /* Make sure the length field is neither so small it doesn't
+ * include all the fixed-length data nor so large it overflows
+ * the metadata resource buffer. */
+ if (length < sizeof(struct wim_extra_stream_entry_on_disk) ||
+ length > (end - p))
+ return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+
+ /* Read the rest of the fixed-length data. */
+
+ copy_hash(strm->_stream_hash, disk_strm->hash);
+ name_nbytes = le16_to_cpu(disk_strm->name_nbytes);
+
+ /* If stream_name_nbytes != 0, the stream is named. */
+ if (name_nbytes != 0) {
+ /* The name is encoded in UTF16-LE, which uses 2-byte
+ * coding units, so the length of the name had better be
+ * an even number of bytes. */
+ if (name_nbytes & 1)
+ return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+
+ /* Add the length of the stream name to get the length
+ * we actually need to read. Make sure this isn't more
+ * than the specified length of the entry. */
+ if (sizeof(struct wim_extra_stream_entry_on_disk) +
+ name_nbytes > length)
+ return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+
+ strm->stream_name = utf16le_dupz(disk_strm->name,
+ name_nbytes);
+ if (!strm->stream_name)
+ return WIMLIB_ERR_NOMEM;
+ } else {
+ strm->stream_name = (utf16lechar *)NO_STREAM_NAME;
+ }
+
+ strm->stream_type = STREAM_TYPE_UNKNOWN;
+
+ p += length;
+ }
+
+ inode->i_next_stream_id = inode->i_num_streams;
+
+ /* Now, assign a type to each stream. Unfortunately this requires
+ * various hacks because stream types aren't explicitly provided in the
+ * WIM on-disk format. */
+
+ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED))
+ assign_stream_types_encrypted(inode);
+ else
+ assign_stream_types_unencrypted(inode);
+
+ *offset_p += p - orig_p;
+ return 0;
+}
+
+/* Read a dentry, including all extra stream entries that follow it, from an
+ * uncompressed metadata resource buffer. */