]> wimlib.net Git - wimlib/blobdiff - src/dentry.c
Fixed bug when capturing NTFS file with multiple named data streams
[wimlib] / src / dentry.c
index 6676377859041bdf495692a4442f4507a803f40f..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>
@@ -85,6 +82,12 @@ static u64 __dentry_total_length(const struct dentry *dentry, u64 length)
        return (length + 7) & ~7;
 }
 
+u64 dentry_correct_total_length(const struct dentry *dentry)
+{
+       return __dentry_total_length(dentry,
+                                    dentry_correct_length_unaligned(dentry));
+}
+
 /* Real length of a dentry, including the alternate data stream entries, which
  * are not included in the dentry->length field... */
 u64 dentry_total_length(const struct dentry *dentry)
@@ -104,9 +107,9 @@ void stbuf_to_dentry(const struct stat *stbuf, struct dentry *dentry)
                dentry->attributes = FILE_ATTRIBUTE_NORMAL;
        }
        if (sizeof(ino_t) >= 8)
-               dentry->hard_link = (u64)stbuf->st_ino;
+               dentry->link_group_id = (u64)stbuf->st_ino;
        else
-               dentry->hard_link = (u64)stbuf->st_ino |
+               dentry->link_group_id = (u64)stbuf->st_ino |
                                   ((u64)stbuf->st_dev << (sizeof(ino_t) * 8));
        /* Set timestamps */
        dentry->creation_time = timespec_to_wim_timestamp(&stbuf->st_mtim);
@@ -164,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;
+                       }
                }
        }
 
@@ -341,8 +376,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                /* Advance the subdir offset by the amount of space the children
                 * of this dentry take up. */
                do {
-                       *subdir_offset_p += __dentry_total_length(child,
-                                                                 dentry_correct_length(child));
+                       *subdir_offset_p += dentry_correct_total_length(child);
                        child = child->next;
                } while (child != dentry->children);
 
@@ -474,6 +508,8 @@ int print_dentry(struct dentry *dentry, void *lookup_table)
 {
        const u8 *hash;
        struct lookup_table_entry *lte;
+       time_t time;
+       char *p;
 
        printf("[DENTRY]\n");
        printf("Length            = %"PRIu64"\n", dentry->length);
@@ -495,15 +531,23 @@ int print_dentry(struct dentry *dentry, void *lookup_table)
 #endif
 
        /* Translate the timestamps into something readable */
-       time_t creat_time = wim_timestamp_to_unix(dentry->creation_time);
-       time_t access_time = wim_timestamp_to_unix(dentry->last_access_time);
-       time_t mod_time = wim_timestamp_to_unix(dentry->last_write_time);
-       printf("Creation Time     = %s", asctime(gmtime(&creat_time)));
-       printf("Last Access Time  = %s", asctime(gmtime(&access_time)));
-       printf("Last Write Time   = %s", asctime(gmtime(&mod_time)));
+       time = wim_timestamp_to_unix(dentry->creation_time);
+       p = asctime(gmtime(&time));
+       *(strrchr(p, '\n')) = '\0';
+       printf("Creation Time     = %s UTC\n", p);
+
+       time = wim_timestamp_to_unix(dentry->last_access_time);
+       p = asctime(gmtime(&time));
+       *(strrchr(p, '\n')) = '\0';
+       printf("Last Access Time  = %s UTC\n", p);
+
+       time = wim_timestamp_to_unix(dentry->last_write_time);
+       p = asctime(gmtime(&time));
+       *(strrchr(p, '\n')) = '\0';
+       printf("Last Write Time   = %s UTC\n", p);
 
        printf("Reparse Tag       = 0x%"PRIx32"\n", dentry->reparse_tag);
-       printf("Hard Link Group   = 0x%"PRIx64"\n", dentry->hard_link);
+       printf("Hard Link Group   = 0x%"PRIx64"\n", dentry->link_group_id);
        printf("Number of Alternate Data Streams = %hu\n", dentry->num_ads);
        printf("Filename          = \"");
        print_string(dentry->file_name, dentry->file_name_len);
@@ -579,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);
@@ -601,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. */
@@ -632,7 +678,6 @@ void put_dentry(struct dentry *dentry)
                }
                dentry->ads_entries_status = ADS_ENTRIES_USER;
        }
-       struct list_head *next;
        list_del(&dentry->link_group_list);
        free_dentry(dentry);
 }
@@ -793,6 +838,8 @@ int change_dentry_name(struct dentry *dentry, const char *new_name)
        ret = get_names(&dentry->file_name, &dentry->file_name_utf8,
                        &dentry->file_name_len, &dentry->file_name_utf8_len,
                         new_name);
+       FREE(dentry->short_name);
+       dentry->short_name_len = 0;
        if (ret == 0)
                dentry->length = dentry_correct_length(dentry);
        return ret;
@@ -922,7 +969,7 @@ static int read_ads_entries(const u8 *p, struct dentry *dentry,
                u64 length_no_padding;
                u64 total_length;
                size_t utf8_len;
-               const char *p_save = p;
+               const u8 *p_save = p;
 
                /* Read the base stream entry, excluding the stream name. */
                if (remaining_size < WIM_ADS_ENTRY_DISK_SIZE) {
@@ -953,7 +1000,7 @@ static int read_ads_entries(const u8 *p, struct dentry *dentry,
                if (remaining_size < length_no_padding) {
                        ERROR("Stream entries go past end of metadata resource");
                        ERROR("(remaining_size = %"PRIu64" bytes, "
-                             "length_no_padding = %"PRIu16" bytes)",
+                             "length_no_padding = %"PRIu64" bytes)",
                              remaining_size, length_no_padding);
                        ret = WIMLIB_ERR_INVALID_DENTRY;
                        goto out_free_ads_entries;
@@ -1043,7 +1090,7 @@ int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
        char *short_name = NULL;
        u16 short_name_len;
        u16 file_name_len;
-       size_t file_name_utf8_len;
+       size_t file_name_utf8_len = 0;
        int ret;
 
        dentry_common_init(dentry);
@@ -1117,7 +1164,7 @@ int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
                p += 4;
        } else {
                p = get_u32(p, &dentry->reparse_tag);
-               p = get_u64(p, &dentry->hard_link);
+               p = get_u64(p, &dentry->link_group_id);
        }
 
        /* By the way, the reparse_reserved field does not actually exist (at
@@ -1200,9 +1247,9 @@ int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
                 *      u64 reserved1; (always 0)
                 *      u64 reserved2; (always 0)
                 * };*/
-               WARNING("Dentry for file or directory `%s' has %zu extra "
-                       "bytes of data",
-                       file_name_utf8, dentry->length - calculated_size);
+               DEBUG("Dentry for file or directory `%s' has %zu extra "
+                     "bytes of data",
+                     file_name_utf8, dentry->length - calculated_size);
        }
 
        /* Read the short filename if present.  Note: if there is no short
@@ -1305,14 +1352,11 @@ int verify_dentry(struct dentry *dentry, void *wim)
 
        /* Make sure there is only one un-named stream. */
        unsigned num_unnamed_streams = 0;
-       unsigned unnamed_stream_idx;
        for (unsigned i = 0; i <= dentry->num_ads; i++) {
                const u8 *hash;
                hash = dentry_stream_hash_unresolved(dentry, i);
-               if (!dentry_stream_name_len(dentry, i) && !is_zero_hash(hash)) {
+               if (!dentry_stream_name_len(dentry, i) && !is_zero_hash(hash))
                        num_unnamed_streams++;
-                       unnamed_stream_idx = i;
-               }
        }
        if (num_unnamed_streams > 1) {
                ERROR("Dentry `%s' has multiple (%u) un-named streams", 
@@ -1320,6 +1364,22 @@ int verify_dentry(struct dentry *dentry, void *wim)
                goto out;
        }
 
+       /* Cannot have a short name but no long name */
+       if (dentry->short_name_len && !dentry->file_name_len) {
+               ERROR("Dentry `%s' has a short name but no long name",
+                     dentry->full_path_utf8);
+               goto out;
+       }
+
+       /* Make sure root dentry is unnamed */
+       if (dentry_is_root(dentry)) {
+               if (dentry->file_name_len) {
+                       ERROR("The root dentry is named `%s', but it must "
+                             "be unnamed", dentry->file_name_utf8);
+                       goto out;
+               }
+       }
+
 #if 0
        /* Check timestamps */
        if (dentry->last_access_time < dentry->creation_time ||
@@ -1345,7 +1405,6 @@ out:
 static u8 *write_dentry(const struct dentry *dentry, u8 *p)
 {
        u8 *orig_p = p;
-       unsigned padding;
        const u8 *hash;
 
        /* We calculate the correct length of the dentry ourselves because the
@@ -1363,23 +1422,20 @@ 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);
                p = put_u32(p, dentry->reparse_tag);
                p = put_zeroes(p, 4);
        } else {
-               u64 hard_link;
+               u64 link_group_id;
                p = put_u32(p, 0);
                if (dentry->link_group_list.next == &dentry->link_group_list)
-                       hard_link = 0;
+                       link_group_id = 0;
                else
-                       hard_link = dentry->hard_link;
-               p = put_u64(p, hard_link);
+                       link_group_id = dentry->link_group_id;
+               p = put_u64(p, link_group_id);
        }
        p = put_u16(p, dentry->num_ads);
        p = put_u16(p, dentry->short_name_len);
@@ -1404,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) {