]> wimlib.net Git - wimlib/blobdiff - src/dentry.c
Fix verify_dentry()
[wimlib] / src / dentry.c
index bca9f35e2afc0ca9fa5fdbb38e3536e5d183361b..4a7b1f363db071c85050d862194d249ab5bc52ac 100644 (file)
@@ -40,6 +40,9 @@
 #include "wimlib_internal.h"
 
 
+/* Calculates the unaligned length, in bytes, of an on-disk WIM dentry that has
+ * a file name and short name that take the specified numbers of bytes.  This
+ * excludes any alternate data stream entries that may follow the dentry. */
 static u64 __dentry_correct_length_unaligned(u16 file_name_len,
                                             u16 short_name_len)
 {
@@ -51,23 +54,25 @@ static u64 __dentry_correct_length_unaligned(u16 file_name_len,
        return length;
 }
 
+/* Calculates the unaligned length, in bytes, of an on-disk WIM dentry, based on
+ * the file name length and short name length.  Note that dentry->length is
+ * ignored; also, this excludes any alternate data stream entries that may
+ * follow the dentry. */
 static u64 dentry_correct_length_unaligned(const struct dentry *dentry)
 {
        return __dentry_correct_length_unaligned(dentry->file_name_len,
                                                 dentry->short_name_len);
 }
 
-/* Return the "correct" value to write in the length field of the dentry, based
- * on the file name length and short name length */
+/* Return the "correct" value to write in the length field of a WIM dentry,
+ * based on the file name length and short name length. */
 static u64 dentry_correct_length(const struct dentry *dentry)
 {
        return (dentry_correct_length_unaligned(dentry) + 7) & ~7;
 }
 
-/*
- * Returns true if @dentry has the UTF-8 file name @name that has length
- * @name_len.
- */
+/* Return %true iff @dentry has the UTF-8 file name @name that has length
+ * @name_len bytes. */
 static bool dentry_has_name(const struct dentry *dentry, const char *name,
                            size_t name_len)
 {
@@ -76,6 +81,8 @@ static bool dentry_has_name(const struct dentry *dentry, const char *name,
        return memcmp(dentry->file_name_utf8, name, name_len) == 0;
 }
 
+/* Return %true iff the alternate data stream entry @entry has the UTF-8 stream
+ * name @name that has length @name_len bytes. */
 static inline bool ads_entry_has_name(const struct ads_entry *entry,
                                      const char *name, size_t name_len)
 {
@@ -163,14 +170,16 @@ static u64 __dentry_total_length(const struct dentry *dentry, u64 length)
        return (length + 7) & ~7;
 }
 
+/* Calculate the aligned *total* length of an on-disk WIM dentry.  This includes
+ * all alternate data streams. */
 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... */
+/* Like dentry_correct_total_length(), but use the existing dentry->length field
+ * instead of calculating its "correct" value. */
 static u64 dentry_total_length(const struct dentry *dentry)
 {
        return __dentry_total_length(dentry, dentry->length);
@@ -352,13 +361,14 @@ oom:
  *
  * @dentry:  The root of the directory tree.
  * @subdir_offset_p:  The current subdirectory offset; i.e., the subdirectory
- *     offset for @dentry.
+ *                   offset for @dentry.
  */
 void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
 {
-       struct dentry *child;
+       struct dentry *child, *children;
 
-       child = dentry->d_inode->children;
+       children = dentry->d_inode->children;
+       child = children;
        dentry->subdir_offset = *subdir_offset_p;
 
        if (child) {
@@ -367,7 +377,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                do {
                        *subdir_offset_p += dentry_correct_total_length(child);
                        child = child->next;
-               } while (child != dentry->d_inode->children);
+               } while (child != children);
 
                /* End-of-directory dentry on disk. */
                *subdir_offset_p += 8;
@@ -377,7 +387,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                do {
                        calculate_subdir_offsets(child, subdir_offset_p);
                        child = child->next;
-               } while (child != dentry->d_inode->children);
+               } while (child != children);
        } else {
                /* On disk, childless directories have a valid subdir_offset
                 * that points to an 8-byte end-of-directory dentry.  Regular
@@ -943,19 +953,19 @@ int verify_dentry(struct dentry *dentry, void *wim)
 {
        const WIMStruct *w = wim;
        const struct inode *inode = dentry->d_inode;
-       int ret = WIMLIB_ERR_INVALID_DENTRY;
+       int ret;
 
        if (!dentry->d_inode->verified) {
                ret = verify_inode(dentry->d_inode, w);
                if (ret != 0)
-                       goto out;
+                       return ret;
        }
 
        /* 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;
+               return WIMLIB_ERR_INVALID_DENTRY;
        }
 
        /* Make sure root dentry is unnamed */
@@ -963,7 +973,7 @@ int verify_dentry(struct dentry *dentry, void *wim)
                if (dentry->file_name_len) {
                        ERROR("The root dentry is named `%s', but it must "
                              "be unnamed", dentry->file_name_utf8);
-                       goto out;
+                       return WIMLIB_ERR_INVALID_DENTRY;
                }
        }
 
@@ -976,9 +986,7 @@ int verify_dentry(struct dentry *dentry, void *wim)
        }
 #endif
 
-       ret = 0;
-out:
-       return ret;
+       return 0;
 }
 
 
@@ -1445,7 +1453,7 @@ int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
 
                p = get_bytes(p, short_name_len, short_name);
                if (*(u16*)p)
-                       WARNING("Expected two zero bytes following the file name "
+                       WARNING("Expected two zero bytes following the short name of "
                                "`%s', but found non-zero bytes", file_name_utf8);
                p += 2;
        }
@@ -1460,18 +1468,35 @@ int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
         * included in the dentry->length field for some reason.
         */
        if (inode->num_ads != 0) {
-               if (calculated_size > metadata_resource_len - offset) {
-                       ERROR("Not enough space in metadata resource for "
-                             "alternate stream entries");
-                       ret = WIMLIB_ERR_INVALID_DENTRY;
-                       goto out_free_short_name;
+
+               /* Trying different lengths is just a hack to make sure we have
+                * a chance of reading the ADS entries correctly despite the
+                * poor documentation. */
+
+               if (calculated_size != dentry->length) {
+                       WARNING("Trying calculated dentry length (%"PRIu64") "
+                               "instead of dentry->length field (%"PRIu64") "
+                               "to read ADS entries",
+                               calculated_size, dentry->length);
                }
-               ret = read_ads_entries(&metadata_resource[offset + calculated_size],
-                                      inode,
-                                      metadata_resource_len - offset - calculated_size);
-               if (ret != 0)
-                       goto out_free_short_name;
+               u64 lengths_to_try[3] = {calculated_size,
+                                        dentry->length + 7 & ~7,
+                                        dentry->length};
+               ret = WIMLIB_ERR_INVALID_DENTRY;
+               for (size_t i = 0; i < ARRAY_LEN(lengths_to_try); i++) {
+                       if (lengths_to_try[i] > metadata_resource_len - offset)
+                               continue;
+                       ret = read_ads_entries(&metadata_resource[offset + lengths_to_try[i]],
+                                              inode,
+                                              metadata_resource_len - offset - lengths_to_try[i]);
+                       if (ret == 0)
+                               goto out;
+               }
+               ERROR("Failed to read alternate data stream "
+                     "entries of `%s'", dentry->file_name_utf8);
+               goto out_free_short_name;
        }
+out:
 
        /* We've read all the data for this dentry.  Set the names and their
         * lengths, and we've done. */
@@ -1675,7 +1700,7 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
  * @parent itself, which has already been written. */
 static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
 {
-       const struct dentry *child;
+       const struct dentry *child, *children;
 
        /* Nothing to do if this dentry has no children. */
        if (parent->subdir_offset == 0)
@@ -1687,12 +1712,13 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
         * recursively writing the directory trees rooted at each of the child
         * dentries, since the on-disk dentries for a dentry's children are
         * always located at consecutive positions in the metadata resource! */
-       child = parent->d_inode->children;
+       children = parent->d_inode->children;
+       child = children;
        if (child) {
                do {
                        p = write_dentry(child, p);
                        child = child->next;
-               } while (child != parent->d_inode->children);
+               } while (child != children);
        }
 
        /* write end of directory entry */
@@ -1703,7 +1729,7 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
                do {
                        p = write_dentry_tree_recursive(child, p);
                        child = child->next;
-               } while (child != parent->d_inode->children);
+               } while (child != children);
        }
        return p;
 }
@@ -1717,6 +1743,7 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
  */
 u8 *write_dentry_tree(const struct dentry *root, u8 *p)
 {
+       DEBUG("Writing dentry tree.");
        wimlib_assert(dentry_is_root(root));
 
        /* If we're the root dentry, we have no parent that already