+/* Returns the alternate data stream entry belonging to @dentry that has the
+ * stream name @stream_name. */
+struct ads_entry *dentry_get_ads_entry(struct dentry *dentry,
+ const char *stream_name)
+{
+ size_t stream_name_len;
+ if (!stream_name)
+ return NULL;
+ if (dentry->num_ads) {
+ u16 i = 0;
+ stream_name_len = strlen(stream_name);
+ do {
+ if (ads_entry_has_name(&dentry->ads_entries[i],
+ stream_name, stream_name_len))
+ return &dentry->ads_entries[i];
+ } while (++i != dentry->num_ads);
+ }
+ return NULL;
+}
+
+static void ads_entry_init(struct ads_entry *ads_entry)
+{
+ memset(ads_entry, 0, sizeof(struct ads_entry));
+ INIT_LIST_HEAD(&ads_entry->lte_group_list.list);
+ ads_entry->lte_group_list.type = STREAM_TYPE_ADS;
+}
+
+/*
+ * Add an alternate stream entry to a dentry and return a pointer to it, or NULL
+ * if memory could not be allocated.
+ */
+struct ads_entry *dentry_add_ads(struct dentry *dentry, const char *stream_name)
+{
+ u16 num_ads;
+ struct ads_entry *ads_entries;
+ struct ads_entry *new_entry;
+
+ DEBUG("Add alternate data stream %s:%s",
+ dentry->file_name_utf8, stream_name);
+
+ if (dentry->num_ads == 0xffff) {
+ ERROR("Too many alternate data streams in one dentry!");
+ return NULL;
+ }
+ num_ads = dentry->num_ads + 1;
+ ads_entries = REALLOC(dentry->ads_entries,
+ num_ads * sizeof(dentry->ads_entries[0]));
+ if (!ads_entries) {
+ ERROR("Failed to allocate memory for new alternate data stream");
+ return NULL;
+ }
+ wimlib_assert(ads_entries == dentry->ads_entries ||
+ ads_entries < dentry->ads_entries ||
+ ads_entries > dentry->ads_entries + dentry->num_ads);
+ if (ads_entries != dentry->ads_entries) {
+ /* We moved the ADS entries. Adjust the stream lists. */
+ for (u16 i = 0; i < dentry->num_ads; i++) {
+ struct list_head *cur = &ads_entries[i].lte_group_list.list;
+ struct list_head *prev = cur->prev;
+ struct list_head *next = cur->next;
+ if ((u8*)prev >= (u8*)dentry->ads_entries
+ && (u8*)prev < (u8*)(dentry->ads_entries + dentry->num_ads)) {
+ /* Previous entry was located in the same ads_entries
+ * array! Adjust our own prev pointer. */
+ u16 idx = (struct ads_entry*)prev -
+ (struct ads_entry*)&dentry->ads_entries[0].lte_group_list.list;
+ cur->prev = &ads_entries[idx].lte_group_list.list;
+ } else {
+ /* Previous entry is located in a different ads_entries
+ * array. Adjust its next pointer. */
+ prev->next = cur;
+ }
+ next = cur->next;
+ if ((u8*)next >= (u8*)dentry->ads_entries
+ && (u8*)next < (u8*)(dentry->ads_entries + dentry->num_ads)) {
+ /* Next entry was located in the same ads_entries array!
+ * Adjust our own next pointer. */
+ u16 idx = (struct ads_entry*)next -
+ (struct ads_entry*)&dentry->ads_entries[0].lte_group_list.list;
+ cur->next = &ads_entries[idx].lte_group_list.list;
+ } else {
+ /* Next entry is located in a different ads_entries
+ * array. Adjust its prev pointer. */
+ next->prev = cur;
+ }
+ }
+ }
+
+ new_entry = &ads_entries[num_ads - 1];
+ ads_entry_init(new_entry);
+ if (change_ads_name(new_entry, stream_name) != 0)
+ return NULL;
+ dentry->ads_entries = ads_entries;
+ dentry->num_ads = num_ads;
+ return new_entry;
+}
+
+/* Remove an alternate data stream from a dentry.
+ *
+ * The corresponding lookup table entry for the stream is NOT changed.
+ *
+ * @dentry: The dentry
+ * @ads_entry: The alternate data stream entry (it MUST be one of the
+ * ads_entry's in the array dentry->ads_entries).
+ */
+void dentry_remove_ads(struct dentry *dentry, struct ads_entry *ads_entry)
+{
+ u16 idx;
+ u16 following;
+
+ wimlib_assert(dentry->num_ads);
+ idx = ads_entry - dentry->ads_entries;
+ wimlib_assert(idx < dentry->num_ads);
+ following = dentry->num_ads - idx - 1;
+
+ destroy_ads_entry(ads_entry);
+ memcpy(ads_entry, ads_entry + 1, following * sizeof(struct ads_entry));
+
+ /* We moved the ADS entries. Adjust the stream lists. */
+ for (u16 i = 0; i < following; i++) {
+ struct list_head *cur = &ads_entry[i].lte_group_list.list;
+ cur->prev->next = cur;
+ cur->next->prev = cur;
+ }
+
+ dentry->num_ads--;
+}
+