+ if (likely(dentry_is_directory(child))) {
+ ret = read_dentry_tree(metadata_resource,
+ metadata_resource_len,
+ child);
+ if (ret)
+ break;
+ } else {
+ WARNING("Ignoring children of non-directory \"%"TS"\"",
+ dentry_full_path(child));
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Writes a WIM alternate data stream (ADS) entry to an output buffer.
+ *
+ * @ads_entry: The ADS entry structure.
+ * @hash: The hash field to use (instead of the one in the ADS entry).
+ * @p: The memory location to write the data to.
+ *
+ * Returns a pointer to the byte after the last byte written.
+ */
+static u8 *
+write_ads_entry(const struct wim_ads_entry *ads_entry,
+ const u8 *hash, u8 * restrict p)
+{
+ struct wim_ads_entry_on_disk *disk_ads_entry =
+ (struct wim_ads_entry_on_disk*)p;
+ u8 *orig_p = p;
+
+ disk_ads_entry->reserved = cpu_to_le64(ads_entry->reserved);
+ copy_hash(disk_ads_entry->hash, hash);
+ disk_ads_entry->stream_name_nbytes = cpu_to_le16(ads_entry->stream_name_nbytes);
+ p += sizeof(struct wim_ads_entry_on_disk);
+ if (ads_entry->stream_name_nbytes) {
+ p = mempcpy(p, ads_entry->stream_name,
+ ads_entry->stream_name_nbytes + 2);
+ }
+ /* Align to 8-byte boundary */
+ while ((uintptr_t)p & 7)
+ *p++ = 0;
+ disk_ads_entry->length = cpu_to_le64(p - orig_p);
+ return p;
+}
+
+/*
+ * Writes a WIM dentry to an output buffer.
+ *
+ * @dentry: The dentry structure.
+ * @p: The memory location to write the data to.
+ *
+ * Returns the pointer to the byte after the last byte we wrote as part of the
+ * dentry, including any alternate data stream entries.
+ */
+static u8 *
+write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
+{
+ const struct wim_inode *inode;
+ struct wim_dentry_on_disk *disk_dentry;
+ const u8 *orig_p;
+ const u8 *hash;
+ bool use_dummy_stream;
+ u16 num_ads;
+
+ wimlib_assert(((uintptr_t)p & 7) == 0); /* 8 byte aligned */
+ orig_p = p;
+
+ inode = dentry->d_inode;
+ use_dummy_stream = inode_needs_dummy_stream(inode);
+ disk_dentry = (struct wim_dentry_on_disk*)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->unused_1 = cpu_to_le64(dentry->d_unused_1);
+ disk_dentry->unused_2 = cpu_to_le64(dentry->d_unused_2);
+ 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);
+ if (use_dummy_stream)
+ hash = zero_hash;
+ else
+ hash = inode_stream_hash(inode, 0);
+ copy_hash(disk_dentry->unnamed_stream_hash, hash);
+ 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);
+ } 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);
+ }
+ num_ads = inode->i_num_ads;
+ if (use_dummy_stream)
+ num_ads++;
+ disk_dentry->num_alternate_data_streams = cpu_to_le16(num_ads);
+ disk_dentry->short_name_nbytes = cpu_to_le16(dentry->short_name_nbytes);
+ disk_dentry->file_name_nbytes = cpu_to_le16(dentry->file_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, dentry->file_name_nbytes + 2);
+
+ if (dentry_has_short_name(dentry))
+ p = mempcpy(p, dentry->short_name, dentry->short_name_nbytes + 2);
+
+ /* Align to 8-byte boundary */
+ while ((uintptr_t)p & 7)
+ *p++ = 0;
+
+ /* We calculate the correct length of the dentry ourselves because the
+ * dentry->length field may been set to an unexpected value from when we
+ * read the dentry in (for example, there may have been unknown data
+ * appended to the end of the dentry...). Furthermore, the dentry may
+ * have been renamed, thus changing its needed length. */
+ disk_dentry->length = cpu_to_le64(p - orig_p);
+
+ if (use_dummy_stream) {
+ hash = inode_unnamed_stream_hash(inode);
+ p = write_ads_entry(&(struct wim_ads_entry){}, hash, p);
+ }
+
+ /* Write the alternate data streams entries, if any. */
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ hash = inode_stream_hash(inode, i + 1);
+ p = write_ads_entry(&inode->i_ads_entries[i], hash, p);
+ }
+
+ return p;
+}
+
+static int
+write_dentry_cb(struct wim_dentry *dentry, void *_p)
+{
+ u8 **p = _p;
+ *p = write_dentry(dentry, *p);
+ return 0;
+}
+
+static u8 *
+write_dentry_tree_recursive(const struct wim_dentry *parent, u8 *p);
+
+static int
+write_dentry_tree_recursive_cb(struct wim_dentry *dentry, void *_p)
+{
+ u8 **p = _p;
+ *p = write_dentry_tree_recursive(dentry, *p);
+ return 0;
+}
+
+/* Recursive function that writes a dentry tree rooted at @parent, not including
+ * @parent itself, which has already been written. */
+static u8 *
+write_dentry_tree_recursive(const struct wim_dentry *parent, u8 *p)
+{
+ /* Nothing to do if this dentry has no children. */
+ if (parent->subdir_offset == 0)
+ return p;
+
+ /* Write child dentries and end-of-directory entry.
+ *
+ * Note: we need to write all of this dentry's children before
+ * 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! */
+ for_dentry_child(parent, write_dentry_cb, &p);
+
+ /* write end of directory entry */
+ *(le64*)p = cpu_to_le64(0);
+ p += 8;
+
+ /* Recurse on children. */
+ for_dentry_child(parent, write_dentry_tree_recursive_cb, &p);
+ return p;
+}
+
+/* Writes a directory tree to the metadata resource.
+ *
+ * @root: Root of the dentry tree.
+ * @p: Pointer to a buffer with enough space for the dentry tree.
+ *
+ * Returns pointer to the byte after the last byte we wrote.
+ */
+u8 *
+write_dentry_tree(const struct wim_dentry * restrict root, u8 * restrict p)
+{
+ DEBUG("Writing dentry tree.");
+ wimlib_assert(dentry_is_root(root));
+
+ /* If we're the root dentry, we have no parent that already
+ * wrote us, so we need to write ourselves. */
+ p = write_dentry(root, p);
+
+ /* Write end of directory entry after the root dentry just to be safe;
+ * however the root dentry obviously cannot have any siblings. */
+ *(le64*)p = cpu_to_le64(0);
+ p += 8;
+
+ /* Recursively write the rest of the dentry tree. */
+ return write_dentry_tree_recursive(root, p);
+}
+
+
+static int
+init_wimlib_dentry(struct wimlib_dir_entry *wdentry,
+ struct wim_dentry *dentry,
+ const WIMStruct *wim,
+ int flags)
+{
+ int ret;
+ size_t dummy;
+ const struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ const u8 *hash;
+
+#if TCHAR_IS_UTF16LE
+ wdentry->filename = dentry->file_name;
+ wdentry->dos_name = dentry->short_name;
+#else
+ if (dentry_has_long_name(dentry)) {
+ ret = utf16le_to_tstr(dentry->file_name,
+ dentry->file_name_nbytes,
+ (tchar**)&wdentry->filename,
+ &dummy);
+ if (ret)
+ return ret;
+ }
+ if (dentry_has_short_name(dentry)) {
+ ret = utf16le_to_tstr(dentry->short_name,
+ dentry->short_name_nbytes,
+ (tchar**)&wdentry->dos_name,
+ &dummy);
+ if (ret)
+ return ret;
+ }
+#endif
+ ret = calculate_dentry_full_path(dentry);
+ if (ret)
+ return ret;
+ wdentry->full_path = dentry->_full_path;
+
+ for (struct wim_dentry *d = dentry; !dentry_is_root(d); d = d->parent)
+ wdentry->depth++;
+
+ if (inode->i_security_id >= 0) {
+ const struct wim_security_data *sd = wim_const_security_data(wim);
+ wdentry->security_descriptor = sd->descriptors[inode->i_security_id];
+ wdentry->security_descriptor_size = sd->sizes[inode->i_security_id];
+ }
+ wdentry->reparse_tag = inode->i_reparse_tag;
+ wdentry->num_links = inode->i_nlink;
+ wdentry->attributes = inode->i_attributes;
+ wdentry->hard_link_group_id = inode->i_ino;
+ wdentry->creation_time = wim_timestamp_to_timespec(inode->i_creation_time);
+ wdentry->last_write_time = wim_timestamp_to_timespec(inode->i_last_write_time);
+ wdentry->last_access_time = wim_timestamp_to_timespec(inode->i_last_access_time);
+
+ lte = inode_unnamed_lte(inode, wim->lookup_table);
+ if (lte) {
+ lte_to_wimlib_resource_entry(lte, &wdentry->streams[0].resource);
+ } else if (!is_zero_hash(hash = inode_unnamed_stream_hash(inode))) {
+ if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)
+ return resource_not_found_error(inode, hash);
+ copy_hash(wdentry->streams[0].resource.sha1_hash, hash);
+ wdentry->streams[0].resource.is_missing = 1;
+ }
+
+ for (unsigned i = 0; i < inode->i_num_ads; i++) {
+ if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
+ continue;
+ lte = inode_stream_lte(inode, i + 1, wim->lookup_table);
+ wdentry->num_named_streams++;
+ if (lte) {
+ lte_to_wimlib_resource_entry(lte, &wdentry->streams[
+ wdentry->num_named_streams].resource);
+ } else if (!is_zero_hash(hash = inode_stream_hash(inode, i + 1))) {
+ if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)
+ return resource_not_found_error(inode, hash);
+ copy_hash(wdentry->streams[
+ wdentry->num_named_streams].resource.sha1_hash, hash);
+ wdentry->streams[
+ wdentry->num_named_streams].resource.is_missing = 1;