]> wimlib.net Git - wimlib/blobdiff - src/dentry.c
Fixed bug when capturing NTFS file with multiple named data streams
[wimlib] / src / dentry.c
index fbc62b869295af7fcccb61eeb4864d5e1a01968f..1f0fd86c7e047a4121757ed12c84318aa21e99c6 100644 (file)
  */
 
 /*
- *
- * Copyright (C) 2010 Carl Thijssen
  * Copyright (C) 2012 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
  * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option)
- * any later version.
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3 of the License, or (at your option) any later
+ * version.
  *
  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU General Public License along with
+ * wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
 #include <errno.h>
@@ -170,19 +167,51 @@ struct ads_entry *dentry_add_ads(struct dentry *dentry, const char *stream_name)
        DEBUG("Add alternate data stream %s:%s",
               dentry->file_name_utf8, stream_name);
 
-       if (dentry->num_ads == 0xffff)
+       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(struct ads_entry));
-       if (!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;
-                       cur->prev->next = cur;
-                       cur->next->prev = cur;
+                       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;
+                       }
                }
        }
 
@@ -594,6 +623,7 @@ struct dentry *new_dentry(const char *name)
        dentry->prev   = dentry;
        dentry->parent = dentry;
        INIT_LIST_HEAD(&dentry->link_group_list);
+       INIT_LIST_HEAD(&dentry->lte_group_list.list);
        return dentry;
 err:
        FREE(dentry);
@@ -616,7 +646,8 @@ static void __destroy_dentry(struct dentry *dentry)
        FREE(dentry->file_name_utf8);
        FREE(dentry->short_name);
        FREE(dentry->full_path_utf8);
-       FREE(dentry->extracted_file);
+       if (dentry->extracted_file != dentry->full_path_utf8)
+               FREE(dentry->extracted_file);
 }
 
 /* Frees a WIM dentry. */
@@ -1391,10 +1422,7 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
        p = put_u64(p, dentry->creation_time);
        p = put_u64(p, dentry->last_access_time);
        p = put_u64(p, dentry->last_write_time);
-       if (dentry->resolved && dentry->lte)
-               hash = dentry->lte->hash;
-       else
-               hash = dentry->hash;
+       hash = dentry_stream_hash(dentry, 0);
        p = put_bytes(p, SHA1_HASH_SIZE, hash);
        if (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                p = put_zeroes(p, 4);
@@ -1432,10 +1460,7 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
        for (u16 i = 0; i < dentry->num_ads; i++) {
                p = put_u64(p, ads_entry_total_length(&dentry->ads_entries[i]));
                p = put_u64(p, 0); /* Unused */
-               if (dentry->resolved && dentry->ads_entries[i].lte)
-                       hash = dentry->ads_entries[i].lte->hash;
-               else
-                       hash = dentry->ads_entries[i].hash;
+               hash = dentry_stream_hash(dentry, i + 1);
                p = put_bytes(p, SHA1_HASH_SIZE, hash);
                p = put_u16(p, dentry->ads_entries[i].stream_name_len);
                if (dentry->ads_entries[i].stream_name_len) {