+out_free_inode:
+ free_inode(inode);
+out:
+ return ret;
+}
+
+static const tchar *
+dentry_get_file_type_string(const struct wim_dentry *dentry)
+{
+ const struct wim_inode *inode = dentry->d_inode;
+ if (inode_is_directory(inode))
+ return T("directory");
+ else if (inode_is_symlink(inode))
+ return T("symbolic link");
+ else
+ return T("file");
+}
+
+/* Reads the children of a dentry, and all their children, ..., etc. from the
+ * metadata resource and into the dentry tree.
+ *
+ * @metadata_resource: An array that contains the uncompressed metadata
+ * resource for the WIM file.
+ *
+ * @metadata_resource_len: The length of the uncompressed metadata resource, in
+ * bytes.
+ *
+ * @dentry: A pointer to a `struct wim_dentry' that is the root of the directory
+ * tree and has already been read from the metadata resource. It
+ * does not need to be the real root because this procedure is
+ * called recursively.
+ *
+ * Returns zero on success; nonzero on failure.
+ */
+int
+read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len,
+ struct wim_dentry *dentry)
+{
+ u64 cur_offset = dentry->subdir_offset;
+ struct wim_dentry *child;
+ struct wim_dentry *duplicate;
+ struct wim_dentry cur_child;
+ int ret;
+
+ /*
+ * If @dentry has no child dentries, nothing more needs to be done for
+ * this branch. This is the case for regular files, symbolic links, and
+ * *possibly* empty directories (although an empty directory may also
+ * have one child dentry that is the special end-of-directory dentry)
+ */
+ if (cur_offset == 0)
+ return 0;
+
+ /* Find and read all the children of @dentry. */
+ for (;;) {
+
+ /* Read next child of @dentry into @cur_child. */
+ ret = read_dentry(metadata_resource, metadata_resource_len,
+ cur_offset, &cur_child);
+ if (ret)
+ break;
+
+ /* Check for end of directory. */
+ if (cur_child.length == 0)
+ break;
+
+ /* Not end of directory. Allocate this child permanently and
+ * link it to the parent and previous child. */
+ child = memdup(&cur_child, sizeof(struct wim_dentry));
+ if (!child) {
+ ERROR("Failed to allocate new dentry!");
+ ret = WIMLIB_ERR_NOMEM;
+ break;
+ }
+
+ /* Advance to the offset of the next child. Note: We need to
+ * advance by the TOTAL length of the dentry, not by the length
+ * cur_child.length, which although it does take into account
+ * the padding, it DOES NOT take into account alternate stream
+ * entries. */
+ cur_offset += dentry_total_length(child);
+
+ duplicate = dentry_add_child(dentry, child);
+ if (duplicate) {
+ const tchar *child_type, *duplicate_type;
+ child_type = dentry_get_file_type_string(child);
+ duplicate_type = dentry_get_file_type_string(duplicate);
+ WARNING("Ignoring duplicate %"TS" \"%"TS"\" "
+ "(the WIM image already contains a %"TS" "
+ "at that path with the exact same name)",
+ child_type, dentry_full_path(duplicate),
+ duplicate_type);
+ } else {
+ inode_add_dentry(child, child->d_inode);
+ /* If there are children of this child, call this
+ * procedure recursively. */
+ if (child->subdir_offset != 0) {
+ if (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));
+ }
+ }
+
+ }
+ }