]> wimlib.net Git - wimlib/blobdiff - src/dentry.c
template.c: iterate through inodes instead of dentries
[wimlib] / src / dentry.c
index 302409f9fe56034a1c29c191ebc1995af7fbab6b..01945b2a33062b80b8a53f1c830ab8441d30795e 100644 (file)
@@ -129,41 +129,33 @@ struct wim_dentry_on_disk {
         */
        u8 default_hash[SHA1_HASH_SIZE];
 
-       /* The format of the following data is not yet completely known and they
-        * do not correspond to Microsoft's documentation.
+       /* Unknown field (maybe accidental padding)  */
+       le32 unknown_0x54;
+
+       /*
+        * The following 8-byte union contains either information about the
+        * reparse point (for files with FILE_ATTRIBUTE_REPARSE_POINT set), or
+        * the "hard link group ID" (for other files).
         *
-        * If this directory entry is for a reparse point (has
-        * FILE_ATTRIBUTE_REPARSE_POINT set in the 'attributes' field), then the
-        * version of the following fields containing the reparse tag is valid.
-        * Furthermore, the field notated as not_rpfixed, as far as I can tell,
-        * is supposed to be set to 1 if reparse point fixups (a.k.a. fixing the
-        * targets of absolute symbolic links) were *not* done, and otherwise 0.
+        * The reparse point information contains ReparseTag and ReparseReserved
+        * from the header of the reparse point buffer.  It also contains a flag
+        * that indicates whether a reparse point fixup (for the target of an
+        * absolute symbolic link or junction) was done or not.
         *
-        * If this directory entry is not for a reparse point, then the version
-        * of the following fields containing the hard_link_group_id is valid.
-        * All MS says about this field is that "If this file is part of a hard
-        * link set, all the directory entries in the set will share the same
-        * value in this field.".  However, more specifically I have observed
-        * the following:
-        *    - If the file is part of a hard link set of size 1, then the
-        *    hard_link_group_id should be set to either 0, which is treated
-        *    specially as indicating "not hardlinked", or any unique value.
-        *    - The specific nonzero values used to identity hard link sets do
-        *    not matter, as long as they are unique.
-        *    - However, due to bugs in Microsoft's software, it is actually NOT
-        *    guaranteed that directory entries that share the same hard link
-        *    group ID are actually hard linked to each either.  See
-        *    inode_fixup.c for the code that handles this.
+        * The "hard link group ID" is like an inode number; all dentries for
+        * the same inode share the same value.  See inode_fixup.c for more
+        * information.
+        *
+        * Note that this union creates the limitation that reparse point files
+        * cannot have multiple names (hard links).
         */
        union {
                struct {
-                       le32 rp_unknown_1;
                        le32 reparse_tag;
-                       le16 rp_unknown_2;
-                       le16 not_rpfixed;
+                       le16 rp_reserved;
+                       le16 rp_flags;
                } _packed_attribute reparse;
                struct {
-                       le32 rp_unknown_1;
                        le64 hard_link_group_id;
                } _packed_attribute nonreparse;
        };
@@ -183,14 +175,14 @@ struct wim_dentry_on_disk {
         * encoded "long" name, excluding the null terminator.  If zero, then
         * this file has no long name.  The root dentry should not have a long
         * name, but all other dentries in the image should have long names.  */
-       le16 file_name_nbytes;
+       le16 name_nbytes;
 
        /* Beginning of optional, variable-length fields  */
 
-       /* If file_name_nbytes != 0, the next field will be the UTF-16LE encoded
-        * long file name.  This will be null-terminated, so the size of this
-        * field will really be file_name_nbytes + 2.  */
-       /*utf16lechar file_name[];*/
+       /* If name_nbytes != 0, the next field will be the UTF-16LE encoded long
+        * name.  This will be null-terminated, so the size of this field will
+        * really be name_nbytes + 2.  */
+       /*utf16lechar name[];*/
 
        /* If short_name_nbytes != 0, the next field will be the UTF-16LE
         * encoded short name.  This will be null-terminated, so the size of
@@ -236,17 +228,17 @@ struct wim_extra_stream_entry_on_disk {
 } _packed_attribute;
 
 static void
-do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *file_name,
-                  size_t file_name_nbytes)
+do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *name,
+                  size_t name_nbytes)
 {
-       FREE(dentry->file_name);
-       dentry->file_name = file_name;
-       dentry->file_name_nbytes = file_name_nbytes;
+       FREE(dentry->d_name);
+       dentry->d_name = name;
+       dentry->d_name_nbytes = name_nbytes;
 
        if (dentry_has_short_name(dentry)) {
-               FREE(dentry->short_name);
-               dentry->short_name = NULL;
-               dentry->short_name_nbytes = 0;
+               FREE(dentry->d_short_name);
+               dentry->d_short_name = NULL;
+               dentry->d_short_name_nbytes = 0;
        }
 }
 
@@ -323,11 +315,11 @@ dentry_set_name(struct wim_dentry *dentry, const tchar *name)
  * tagged metadata items as well as any extra stream entries that may need to
  * follow the dentry.  */
 static size_t
-dentry_min_len_with_names(u16 file_name_nbytes, u16 short_name_nbytes)
+dentry_min_len_with_names(u16 name_nbytes, u16 short_name_nbytes)
 {
        size_t length = sizeof(struct wim_dentry_on_disk);
-       if (file_name_nbytes)
-               length += (u32)file_name_nbytes + 2;
+       if (name_nbytes)
+               length += (u32)name_nbytes + 2;
        if (short_name_nbytes)
                length += (u32)short_name_nbytes + 2;
        return length;
@@ -347,7 +339,7 @@ stream_out_total_length(const struct wim_inode_stream *strm)
                len += utf16le_len_bytes(strm->stream_name) + 2;
 
        /* Account for any necessary padding to the next 8-byte boundary.  */
-       return (len + 7) & ~7;
+       return ALIGN(len, 8);
 }
 
 /*
@@ -362,14 +354,11 @@ dentry_out_total_length(const struct wim_dentry *dentry)
        const struct wim_inode *inode = dentry->d_inode;
        size_t len;
 
-       len = dentry_min_len_with_names(dentry->file_name_nbytes,
-                                       dentry->short_name_nbytes);
-       len = (len + 7) & ~7;
+       len = dentry_min_len_with_names(dentry->d_name_nbytes,
+                                       dentry->d_short_name_nbytes);
+       len = ALIGN(len, 8);
 
-       if (inode->i_extra_size) {
-               len += inode->i_extra_size;
-               len = (len + 7) & ~7;
-       }
+       len += ALIGN(inode->i_extra_size, 8);
 
        if (!(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
                /*
@@ -396,8 +385,8 @@ dentry_out_total_length(const struct wim_dentry *dentry)
 
                if (have_named_data_stream || have_reparse_point_stream) {
                        if (have_reparse_point_stream)
-                               len += (sizeof(struct wim_extra_stream_entry_on_disk) + 7) & ~7;
-                       len += (sizeof(struct wim_extra_stream_entry_on_disk) + 7) & ~7;
+                               len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8);
+                       len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8);
                }
        }
 
@@ -483,10 +472,10 @@ for_dentry_in_tree_depth(struct wim_dentry *root,
 /*
  * Calculate the full path to @dentry within the WIM image, if not already done.
  *
- * The full name will be saved in the cached value 'dentry->_full_path'.
+ * The full name will be saved in the cached value 'dentry->d_full_path'.
  *
  * Whenever possible, use dentry_full_path() instead of calling this and
- * accessing _full_path directly.
+ * accessing d_full_path directly.
  *
  * Returns 0 or an error code resulting from a failed string conversion.
  */
@@ -497,13 +486,13 @@ calculate_dentry_full_path(struct wim_dentry *dentry)
        size_t dummy;
        const struct wim_dentry *d;
 
-       if (dentry->_full_path)
+       if (dentry->d_full_path)
                return 0;
 
        ulen = 0;
        d = dentry;
        do {
-               ulen += d->file_name_nbytes / sizeof(utf16lechar);
+               ulen += d->d_name_nbytes / sizeof(utf16lechar);
                ulen++;
                d = d->d_parent;  /* assumes d == d->d_parent for root  */
        } while (!dentry_is_root(d));
@@ -513,8 +502,8 @@ calculate_dentry_full_path(struct wim_dentry *dentry)
 
        d = dentry;
        do {
-               p -= d->file_name_nbytes / sizeof(utf16lechar);
-               memcpy(p, d->file_name, d->file_name_nbytes);
+               p -= d->d_name_nbytes / sizeof(utf16lechar);
+               memcpy(p, d->d_name, d->d_name_nbytes);
                *--p = cpu_to_le16(WIM_PATH_SEPARATOR);
                d = d->d_parent;  /* assumes d == d->d_parent for root  */
        } while (!dentry_is_root(d));
@@ -522,7 +511,7 @@ calculate_dentry_full_path(struct wim_dentry *dentry)
        wimlib_assert(p == ubuf);
 
        return utf16le_to_tstr(ubuf, ulen * sizeof(utf16lechar),
-                              &dentry->_full_path, &dummy);
+                              &dentry->d_full_path, &dummy);
 }
 
 /*
@@ -536,7 +525,7 @@ tchar *
 dentry_full_path(struct wim_dentry *dentry)
 {
        calculate_dentry_full_path(dentry);
-       return dentry->_full_path;
+       return dentry->d_full_path;
 }
 
 static int
@@ -547,7 +536,7 @@ dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p
                struct wim_dentry *child;
 
                /* Set offset of directory's child dentries  */
-               dentry->subdir_offset = *subdir_offset_p;
+               dentry->d_subdir_offset = *subdir_offset_p;
 
                /* Account for child dentries  */
                for_dentry_child(child, dentry)
@@ -556,8 +545,8 @@ dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p
                /* Account for end-of-directory entry  */
                *subdir_offset_p += 8;
        } else {
-               /* Not a directory; set subdir_offset to 0  */
-               dentry->subdir_offset = 0;
+               /* Not a directory; set the subdir offset to 0  */
+               dentry->d_subdir_offset = 0;
        }
        return 0;
 }
@@ -587,10 +576,10 @@ static int
 dentry_compare_names_case_insensitive(const struct wim_dentry *d1,
                                      const struct wim_dentry *d2)
 {
-       return cmp_utf16le_strings(d1->file_name,
-                                  d1->file_name_nbytes / 2,
-                                  d2->file_name,
-                                  d2->file_name_nbytes / 2,
+       return cmp_utf16le_strings(d1->d_name,
+                                  d1->d_name_nbytes / 2,
+                                  d2->d_name,
+                                  d2->d_name_nbytes / 2,
                                   true);
 }
 
@@ -599,10 +588,10 @@ static int
 dentry_compare_names_case_sensitive(const struct wim_dentry *d1,
                                    const struct wim_dentry *d2)
 {
-       return cmp_utf16le_strings(d1->file_name,
-                                  d1->file_name_nbytes / 2,
-                                  d2->file_name,
-                                  d2->file_name_nbytes / 2,
+       return cmp_utf16le_strings(d1->d_name,
+                                  d1->d_name_nbytes / 2,
+                                  d2->d_name,
+                                  d2->d_name_nbytes / 2,
                                   false);
 }
 
@@ -640,8 +629,8 @@ bool default_ignore_case =
 #endif
 ;
 
-/* Case-sensitive dentry lookup.  Only @file_name and @file_name_nbytes of
- * @dummy must be valid.  */
+/* Case-sensitive dentry lookup.  Only @d_name and @d_name_nbytes of @dummy must
+ * be valid.  */
 static struct wim_dentry *
 dir_lookup(const struct wim_inode *dir, const struct wim_dentry *dummy)
 {
@@ -655,8 +644,8 @@ dir_lookup(const struct wim_inode *dir, const struct wim_dentry *dummy)
        return avl_tree_entry(node, struct wim_dentry, d_index_node);
 }
 
-/* Case-insensitive dentry lookup.  Only @file_name and @file_name_nbytes of
- * @dummy must be valid.  */
+/* Case-insensitive dentry lookup.  Only @d_name and @d_name_nbytes of @dummy
+ * must be valid.  */
 static struct wim_dentry *
 dir_lookup_ci(const struct wim_inode *dir, const struct wim_dentry *dummy)
 {
@@ -677,15 +666,15 @@ struct wim_dentry *
 get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry,
                                   const utf16lechar *name,
                                   size_t name_nbytes,
-                                  CASE_SENSITIVITY_TYPE case_ctype)
+                                  CASE_SENSITIVITY_TYPE case_type)
 {
        const struct wim_inode *dir = dentry->d_inode;
-       bool ignore_case = will_ignore_case(case_ctype);
+       bool ignore_case = will_ignore_case(case_type);
        struct wim_dentry dummy;
        struct wim_dentry *child;
 
-       dummy.file_name = (utf16lechar*)name;
-       dummy.file_name_nbytes = name_nbytes;
+       dummy.d_name = (utf16lechar*)name;
+       dummy.d_name_nbytes = name_nbytes;
 
        if (!ignore_case)
                /* Case-sensitive lookup.  */
@@ -993,19 +982,6 @@ new_filler_directory(struct wim_dentry **dentry_ret)
        return 0;
 }
 
-static int
-dentry_clear_inode_visited(struct wim_dentry *dentry, void *_ignore)
-{
-       dentry->d_inode->i_visited = 0;
-       return 0;
-}
-
-void
-dentry_tree_clear_inode_visited(struct wim_dentry *root)
-{
-       for_dentry_in_tree(root, dentry_clear_inode_visited, NULL);
-}
-
 /*
  * Free a WIM dentry.
  *
@@ -1017,9 +993,9 @@ free_dentry(struct wim_dentry *dentry)
 {
        if (dentry) {
                d_disassociate(dentry);
-               FREE(dentry->file_name);
-               FREE(dentry->short_name);
-               FREE(dentry->_full_path);
+               FREE(dentry->d_name);
+               FREE(dentry->d_short_name);
+               FREE(dentry->d_full_path);
                FREE(dentry);
        }
 }
@@ -1299,9 +1275,7 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
 
        inode->i_num_streams = 1 + num_extra_streams;
 
-       if (likely(inode->i_num_streams <= ARRAY_LEN(inode->i_embedded_streams))) {
-               inode->i_streams = inode->i_embedded_streams;
-       } else {
+       if (unlikely(inode->i_num_streams > ARRAY_LEN(inode->i_embedded_streams))) {
                inode->i_streams = CALLOC(inode->i_num_streams,
                                          sizeof(inode->i_streams[0]));
                if (!inode->i_streams)
@@ -1333,10 +1307,7 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
                disk_strm = (const struct wim_extra_stream_entry_on_disk *)p;
 
                /* Read the length field  */
-               length = le64_to_cpu(disk_strm->length);
-
-               /* 8-byte align the length  */
-               length = (length + 7) & ~7;
+               length = ALIGN(le64_to_cpu(disk_strm->length), 8);
 
                /* Make sure the length field is neither so small it doesn't
                 * include all the fixed-length data nor so large it overflows
@@ -1406,7 +1377,7 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
        struct wim_dentry *dentry;
        struct wim_inode *inode;
        u16 short_name_nbytes;
-       u16 file_name_nbytes;
+       u16 name_nbytes;
        u64 calculated_size;
        int ret;
 
@@ -1426,7 +1397,7 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
        disk_dentry = (const struct wim_dentry_on_disk*)p;
 
        /* Get dentry length.  */
-       length = (le64_to_cpu(disk_dentry->length) + 7) & ~7;
+       length = ALIGN(le64_to_cpu(disk_dentry->length), 8);
 
        /* Check for end-of-directory.  */
        if (length <= 8) {
@@ -1453,24 +1424,19 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
        /* Read more fields: some into the dentry, and some into the inode.  */
        inode->i_attributes = le32_to_cpu(disk_dentry->attributes);
        inode->i_security_id = le32_to_cpu(disk_dentry->security_id);
-       dentry->subdir_offset = le64_to_cpu(disk_dentry->subdir_offset);
+       dentry->d_subdir_offset = le64_to_cpu(disk_dentry->subdir_offset);
        inode->i_creation_time = le64_to_cpu(disk_dentry->creation_time);
        inode->i_last_access_time = le64_to_cpu(disk_dentry->last_access_time);
        inode->i_last_write_time = le64_to_cpu(disk_dentry->last_write_time);
+       inode->i_unknown_0x54 = le32_to_cpu(disk_dentry->unknown_0x54);
 
-       /* I don't know what's going on here.  It seems like M$ screwed up the
-        * reparse points, then put the fields in the same place and didn't
-        * document it.  So we have some fields we read for reparse points, and
-        * some fields in the same place for non-reparse-points.  */
        if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               inode->i_rp_unknown_1 = le32_to_cpu(disk_dentry->reparse.rp_unknown_1);
                inode->i_reparse_tag = le32_to_cpu(disk_dentry->reparse.reparse_tag);
-               inode->i_rp_unknown_2 = le16_to_cpu(disk_dentry->reparse.rp_unknown_2);
-               inode->i_not_rpfixed = le16_to_cpu(disk_dentry->reparse.not_rpfixed);
+               inode->i_rp_reserved = le16_to_cpu(disk_dentry->reparse.rp_reserved);
+               inode->i_rp_flags = le16_to_cpu(disk_dentry->reparse.rp_flags);
                /* Leave inode->i_ino at 0.  Note: this means that WIM cannot
                 * represent multiple hard links to a reparse point file.  */
        } else {
-               inode->i_rp_unknown_1 = le32_to_cpu(disk_dentry->nonreparse.rp_unknown_1);
                inode->i_ino = le64_to_cpu(disk_dentry->nonreparse.hard_link_group_id);
        }
 
@@ -1478,16 +1444,16 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
         * name, and the short name.  */
 
        short_name_nbytes = le16_to_cpu(disk_dentry->short_name_nbytes);
-       file_name_nbytes = le16_to_cpu(disk_dentry->file_name_nbytes);
+       name_nbytes = le16_to_cpu(disk_dentry->name_nbytes);
 
-       if (unlikely((short_name_nbytes & 1) | (file_name_nbytes & 1))) {
+       if (unlikely((short_name_nbytes & 1) | (name_nbytes & 1))) {
                ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
                goto err_free_dentry;
        }
 
        /* We now know the length of the file name and short name.  Make sure
         * the length of the dentry is large enough to actually hold them.  */
-       calculated_size = dentry_min_len_with_names(file_name_nbytes,
+       calculated_size = dentry_min_len_with_names(name_nbytes,
                                                    short_name_nbytes);
 
        if (unlikely(length < calculated_size)) {
@@ -1500,25 +1466,25 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
 
        /* Read the filename if present.  Note: if the filename is empty, there
         * is no null terminator following it.  */
-       if (file_name_nbytes) {
-               dentry->file_name = utf16le_dupz(p, file_name_nbytes);
-               if (dentry->file_name == NULL) {
+       if (name_nbytes) {
+               dentry->d_name = utf16le_dupz(p, name_nbytes);
+               if (unlikely(!dentry->d_name)) {
                        ret = WIMLIB_ERR_NOMEM;
                        goto err_free_dentry;
                }
-               dentry->file_name_nbytes = file_name_nbytes;
-               p += (u32)file_name_nbytes + 2;
+               dentry->d_name_nbytes = name_nbytes;
+               p += (u32)name_nbytes + 2;
        }
 
        /* Read the short filename if present.  Note: if there is no short
         * filename, there is no null terminator following it. */
        if (short_name_nbytes) {
-               dentry->short_name = utf16le_dupz(p, short_name_nbytes);
-               if (dentry->short_name == NULL) {
+               dentry->d_short_name = utf16le_dupz(p, short_name_nbytes);
+               if (unlikely(!dentry->d_short_name)) {
                        ret = WIMLIB_ERR_NOMEM;
                        goto err_free_dentry;
                }
-               dentry->short_name_nbytes = short_name_nbytes;
+               dentry->d_short_name_nbytes = short_name_nbytes;
                p += (u32)short_name_nbytes + 2;
        }
 
@@ -1553,13 +1519,13 @@ err_free_dentry:
 static bool
 dentry_is_dot_or_dotdot(const struct wim_dentry *dentry)
 {
-       if (dentry->file_name_nbytes <= 4) {
-               if (dentry->file_name_nbytes == 4) {
-                       if (dentry->file_name[0] == cpu_to_le16('.') &&
-                           dentry->file_name[1] == cpu_to_le16('.'))
+       if (dentry->d_name_nbytes <= 4) {
+               if (dentry->d_name_nbytes == 4) {
+                       if (dentry->d_name[0] == cpu_to_le16('.') &&
+                           dentry->d_name[1] == cpu_to_le16('.'))
                                return true;
-               } else if (dentry->file_name_nbytes == 2) {
-                       if (dentry->file_name[0] == cpu_to_le16('.'))
+               } else if (dentry->d_name_nbytes == 2) {
+                       if (dentry->d_name[0] == cpu_to_le16('.'))
                                return true;
                }
        }
@@ -1570,14 +1536,14 @@ static int
 read_dentry_tree_recursive(const u8 * restrict buf, size_t buf_len,
                           struct wim_dentry * restrict dir)
 {
-       u64 cur_offset = dir->subdir_offset;
+       u64 cur_offset = dir->d_subdir_offset;
 
        /* Check for cyclic directory structure, which would cause infinite
         * recursion if not handled.  */
        for (struct wim_dentry *d = dir->d_parent;
             !dentry_is_root(d); d = d->d_parent)
        {
-               if (unlikely(d->subdir_offset == cur_offset)) {
+               if (unlikely(d->d_subdir_offset == cur_offset)) {
                        ERROR("Cyclic directory structure detected: children "
                              "of \"%"TS"\" coincide with children of \"%"TS"\"",
                              dentry_full_path(dir), dentry_full_path(d));
@@ -1631,7 +1597,7 @@ read_dentry_tree_recursive(const u8 * restrict buf, size_t buf_len,
 
                /* If this child is a directory that itself has children, call
                 * this procedure recursively.  */
-               if (child->subdir_offset != 0) {
+               if (child->d_subdir_offset != 0) {
                        if (likely(dentry_is_directory(child))) {
                                ret = read_dentry_tree_recursive(buf,
                                                                 buf_len,
@@ -1697,7 +1663,7 @@ read_dentry_tree(const u8 *buf, size_t buf_len,
                        goto err_free_dentry_tree;
                }
 
-               if (likely(root->subdir_offset != 0)) {
+               if (likely(root->d_subdir_offset != 0)) {
                        ret = read_dentry_tree_recursive(buf, buf_len, root);
                        if (ret)
                                goto err_free_dentry_tree;
@@ -1769,7 +1735,7 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
 
        disk_dentry->attributes = cpu_to_le32(inode->i_attributes);
        disk_dentry->security_id = cpu_to_le32(inode->i_security_id);
-       disk_dentry->subdir_offset = cpu_to_le64(dentry->subdir_offset);
+       disk_dentry->subdir_offset = cpu_to_le64(dentry->d_subdir_offset);
 
        disk_dentry->unused_1 = cpu_to_le64(0);
        disk_dentry->unused_2 = cpu_to_le64(0);
@@ -1777,28 +1743,27 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
        disk_dentry->creation_time = cpu_to_le64(inode->i_creation_time);
        disk_dentry->last_access_time = cpu_to_le64(inode->i_last_access_time);
        disk_dentry->last_write_time = cpu_to_le64(inode->i_last_write_time);
+       disk_dentry->unknown_0x54 = cpu_to_le32(inode->i_unknown_0x54);
        if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               disk_dentry->reparse.rp_unknown_1 = cpu_to_le32(inode->i_rp_unknown_1);
                disk_dentry->reparse.reparse_tag = cpu_to_le32(inode->i_reparse_tag);
-               disk_dentry->reparse.rp_unknown_2 = cpu_to_le16(inode->i_rp_unknown_2);
-               disk_dentry->reparse.not_rpfixed = cpu_to_le16(inode->i_not_rpfixed);
+               disk_dentry->reparse.rp_reserved = cpu_to_le16(inode->i_rp_reserved);
+               disk_dentry->reparse.rp_flags = cpu_to_le16(inode->i_rp_flags);
        } else {
-               disk_dentry->nonreparse.rp_unknown_1 = cpu_to_le32(inode->i_rp_unknown_1);
                disk_dentry->nonreparse.hard_link_group_id =
                        cpu_to_le64((inode->i_nlink == 1) ? 0 : inode->i_ino);
        }
 
-       disk_dentry->short_name_nbytes = cpu_to_le16(dentry->short_name_nbytes);
-       disk_dentry->file_name_nbytes = cpu_to_le16(dentry->file_name_nbytes);
+       disk_dentry->short_name_nbytes = cpu_to_le16(dentry->d_short_name_nbytes);
+       disk_dentry->name_nbytes = cpu_to_le16(dentry->d_name_nbytes);
        p += sizeof(struct wim_dentry_on_disk);
 
        wimlib_assert(dentry_is_root(dentry) != dentry_has_long_name(dentry));
 
        if (dentry_has_long_name(dentry))
-               p = mempcpy(p, dentry->file_name, (u32)dentry->file_name_nbytes + 2);
+               p = mempcpy(p, dentry->d_name, (u32)dentry->d_name_nbytes + 2);
 
        if (dentry_has_short_name(dentry))
-               p = mempcpy(p, dentry->short_name, (u32)dentry->short_name_nbytes + 2);
+               p = mempcpy(p, dentry->d_short_name, (u32)dentry->d_short_name_nbytes + 2);
 
        /* Align to 8-byte boundary */
        while ((uintptr_t)p & 7)
@@ -1821,8 +1786,7 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
                const struct wim_inode_stream *efs_strm;
                const u8 *efs_hash;
 
-               efs_strm = inode_get_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA,
-                                           NO_STREAM_NAME);
+               efs_strm = inode_get_unnamed_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA);
                efs_hash = efs_strm ? stream_hash(efs_strm) : zero_hash;
                copy_hash(disk_dentry->default_hash, efs_hash);
                disk_dentry->num_extra_streams = cpu_to_le16(0);
@@ -1838,7 +1802,6 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
                 */
                bool have_named_data_stream = false;
                bool have_reparse_point_stream = false;
-               u16 num_extra_streams = 0;
                const u8 *unnamed_data_stream_hash = zero_hash;
                const u8 *reparse_point_hash;
                for (unsigned i = 0; i < inode->i_num_streams; i++) {
@@ -1854,7 +1817,9 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
                        }
                }
 
-               if (have_reparse_point_stream || have_named_data_stream) {
+               if (unlikely(have_reparse_point_stream || have_named_data_stream)) {
+
+                       unsigned num_extra_streams = 0;
 
                        copy_hash(disk_dentry->default_hash, zero_hash);
 
@@ -1867,19 +1832,22 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
                        p = write_extra_stream_entry(p, NO_STREAM_NAME,
                                                     unnamed_data_stream_hash);
                        num_extra_streams++;
-               } else {
-                       copy_hash(disk_dentry->default_hash, unnamed_data_stream_hash);
-               }
 
-               for (unsigned i = 0; i < inode->i_num_streams; i++) {
-                       const struct wim_inode_stream *strm = &inode->i_streams[i];
-                       if (stream_is_named_data_stream(strm)) {
-                               p = write_extra_stream_entry(p, strm->stream_name,
-                                                            stream_hash(strm));
-                               num_extra_streams++;
+                       for (unsigned i = 0; i < inode->i_num_streams; i++) {
+                               const struct wim_inode_stream *strm = &inode->i_streams[i];
+                               if (stream_is_named_data_stream(strm)) {
+                                       p = write_extra_stream_entry(p, strm->stream_name,
+                                                                    stream_hash(strm));
+                                       num_extra_streams++;
+                               }
                        }
+                       wimlib_assert(num_extra_streams <= 0xFFFF);
+
+                       disk_dentry->num_extra_streams = cpu_to_le16(num_extra_streams);
+               } else {
+                       copy_hash(disk_dentry->default_hash, unnamed_data_stream_hash);
+                       disk_dentry->num_extra_streams = cpu_to_le16(0);
                }
-               disk_dentry->num_extra_streams = cpu_to_le16(num_extra_streams);
        }
 
        return p;
@@ -1888,7 +1856,7 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
 static int
 write_dir_dentries(struct wim_dentry *dir, void *_pp)
 {
-       if (dir->subdir_offset != 0) {
+       if (dir->d_subdir_offset != 0) {
                u8 **pp = _pp;
                u8 *p = *pp;
                struct wim_dentry *child;