+ u16 num_ads;
+ struct wim_ads_entry *ads_entries;
+ int ret;
+
+ num_ads = inode->i_num_ads;
+ ads_entries = CALLOC(num_ads, sizeof(inode->i_ads_entries[0]));
+ if (!ads_entries) {
+ ERROR("Could not allocate memory for %"PRIu16" "
+ "alternate data stream entries", num_ads);
+ return WIMLIB_ERR_NOMEM;
+ }
+
+ for (u16 i = 0; i < num_ads; i++) {
+ struct wim_ads_entry *cur_entry;
+ u64 length;
+ u64 length_no_padding;
+ u64 total_length;
+ size_t utf8_len;
+ const u8 *p_save = p;
+
+ cur_entry = &ads_entries[i];
+
+ #ifdef WITH_FUSE
+ ads_entries[i].stream_id = i + 1;
+ #endif
+
+ /* Read the base stream entry, excluding the stream name. */
+ if (remaining_size < WIM_ADS_ENTRY_DISK_SIZE) {
+ ERROR("Stream entries go past end of metadata resource");
+ ERROR("(remaining_size = %"PRIu64")", remaining_size);
+ ret = WIMLIB_ERR_INVALID_DENTRY;
+ goto out_free_ads_entries;
+ }
+
+ p = get_u64(p, &length);
+ p += 8; /* Skip the reserved field */
+ p = get_bytes(p, SHA1_HASH_SIZE, (u8*)cur_entry->hash);
+ p = get_u16(p, &cur_entry->stream_name_len);
+
+ cur_entry->stream_name = NULL;
+ cur_entry->stream_name_utf8 = NULL;
+
+ /* Length including neither the null terminator nor the padding
+ * */
+ length_no_padding = WIM_ADS_ENTRY_DISK_SIZE +
+ cur_entry->stream_name_len;
+
+ /* 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;
+ }
+
+ if (cur_entry->stream_name_len) {
+ cur_entry->stream_name = MALLOC(cur_entry->stream_name_len);
+ if (!cur_entry->stream_name) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_ads_entries;
+ }
+ get_bytes(p, cur_entry->stream_name_len,
+ (u8*)cur_entry->stream_name);
+
+ ret = utf16_to_utf8(cur_entry->stream_name,
+ cur_entry->stream_name_len,
+ &cur_entry->stream_name_utf8,
+ &utf8_len);
+ if (ret != 0)
+ goto out_free_ads_entries;
+ cur_entry->stream_name_utf8_len = utf8_len;
+ }
+ /* It's expected that the size of every ADS entry is a multiple
+ * of 8. However, to be safe, I'm allowing the possibility of
+ * an ADS entry at the very end of the metadata resource ending
+ * un-aligned. So although we still need to increment the input
+ * pointer by @total_length to reach the next ADS entry, it's
+ * possible that less than @total_length is actually remaining
+ * in the metadata resource. We should set the remaining size to
+ * 0 bytes if this happens. */
+ p = p_save + total_length;
+ if (remaining_size < total_length)
+ remaining_size = 0;
+ else
+ remaining_size -= total_length;
+ }
+ inode->i_ads_entries = ads_entries;
+#ifdef WITH_FUSE
+ inode->i_next_stream_id = inode->i_num_ads + 1;
+#endif
+ return 0;
+out_free_ads_entries:
+ for (u16 i = 0; i < num_ads; i++)
+ destroy_ads_entry(&ads_entries[i]);
+ FREE(ads_entries);
+ return ret;