- /* Length including the null terminator and the padding */
- total_length = ((length_no_padding + 2) + 7) & ~7;
-
- wimlib_assert(total_length == ads_entry_total_length(cur_entry));
-
- if (remaining_size < length_no_padding) {
- ERROR("Stream entries go past end of metadata resource");
- ERROR("(remaining_size = %"PRIu64" bytes, "
- "length_no_padding = %"PRIu64" bytes)",
- remaining_size, length_no_padding);
- ret = WIMLIB_ERR_INVALID_DENTRY;
- goto out_free_ads_entries;
- }
-
- /* The @length field in the on-disk ADS entry is expected to be
- * equal to @total_length, which includes all of the entry and
- * the padding that follows it to align the next ADS entry to an
- * 8-byte boundary. However, to be safe, we'll accept the
- * length field as long as it's not less than the un-padded
- * total length and not more than the padded total length. */
- if (length < length_no_padding || length > total_length) {
- ERROR("Stream entry has unexpected length "
- "field (length field = %"PRIu64", "
- "unpadded total length = %"PRIu64", "
- "padded total length = %"PRIu64")",
- length, length_no_padding, total_length);
- ret = WIMLIB_ERR_INVALID_DENTRY;
- goto out_free_ads_entries;
+ /* Do we have at least the size of the fixed-length data we know
+ * need? */
+ if (nbytes_remaining < sizeof(struct wim_ads_entry_on_disk))
+ goto out_invalid;
+
+ /* Read the length field */
+ length = le64_to_cpu(disk_entry->length);
+
+ /* 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_ads_entry_on_disk) ||
+ length > nbytes_remaining)
+ goto out_invalid;
+
+ /* Read the rest of the fixed-length data. */
+
+ cur_entry->reserved = le64_to_cpu(disk_entry->reserved);
+ copy_hash(cur_entry->hash, disk_entry->hash);
+ cur_entry->stream_name_nbytes = le16_to_cpu(disk_entry->stream_name_nbytes);
+
+ /* If stream_name_nbytes != 0, this is a named stream.
+ * Otherwise this is an unnamed stream, or in some cases (bugs
+ * in Microsoft's software I guess) a meaningless entry
+ * distinguished from the real unnamed stream entry, if any, by
+ * the fact that the real unnamed stream entry has a nonzero
+ * hash field. */
+ if (cur_entry->stream_name_nbytes) {
+ /* 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 (cur_entry->stream_name_nbytes & 1)
+ goto out_invalid;
+
+ /* 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_ads_entry_on_disk) +
+ cur_entry->stream_name_nbytes > length)
+ goto out_invalid;
+
+ cur_entry->stream_name = MALLOC(cur_entry->stream_name_nbytes + 2);
+ if (!cur_entry->stream_name)
+ goto out_of_memory;
+
+ memcpy(cur_entry->stream_name,
+ disk_entry->stream_name,
+ cur_entry->stream_name_nbytes);
+ cur_entry->stream_name[cur_entry->stream_name_nbytes / 2] = cpu_to_le16(0);