+
+static int
+init_wimlib_dentry(struct wimlib_dir_entry *wdentry,
+ struct wim_dentry *dentry,
+ const WIMStruct *wim)
+{
+ int ret;
+ size_t dummy;
+ const struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+
+#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);
+
+ for (unsigned i = 0; i < inode->i_num_ads; i++) {
+ if (inode->i_ads_entries[i].stream_name == NULL)
+ 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);
+ }
+ #if TCHAR_IS_UTF16LE
+ wdentry->streams[wdentry->num_named_streams].stream_name =
+ inode->i_ads_entries[i].stream_name;
+ #else
+ size_t dummy;
+
+ ret = utf16le_to_tstr(inode->i_ads_entries[i].stream_name,
+ inode->i_ads_entries[i].stream_name_nbytes,
+ (tchar**)&wdentry->streams[
+ wdentry->num_named_streams].stream_name,
+ &dummy);
+ if (ret)
+ return ret;
+ #endif
+ }
+ return 0;
+}
+
+static void
+free_wimlib_dentry(struct wimlib_dir_entry *wdentry)
+{
+#if !TCHAR_IS_UTF16LE
+ FREE((tchar*)wdentry->filename);
+ FREE((tchar*)wdentry->dos_name);
+ for (unsigned i = 1; i <= wdentry->num_named_streams; i++)
+ FREE((tchar*)wdentry->streams[i].stream_name);
+#endif
+ FREE(wdentry);
+}
+
+struct iterate_dir_tree_ctx {
+ WIMStruct *wim;
+ int flags;
+ wimlib_iterate_dir_tree_callback_t cb;
+ void *user_ctx;
+};
+
+static int
+do_iterate_dir_tree(WIMStruct *wim,
+ struct wim_dentry *dentry, int flags,
+ wimlib_iterate_dir_tree_callback_t cb,
+ void *user_ctx);
+
+static int
+call_do_iterate_dir_tree(struct wim_dentry *dentry, void *_ctx)
+{
+ struct iterate_dir_tree_ctx *ctx = _ctx;
+ return do_iterate_dir_tree(ctx->wim, dentry, ctx->flags,
+ ctx->cb, ctx->user_ctx);
+}
+
+static int
+do_iterate_dir_tree(WIMStruct *wim,
+ struct wim_dentry *dentry, int flags,
+ wimlib_iterate_dir_tree_callback_t cb,
+ void *user_ctx)
+{
+ struct wimlib_dir_entry *wdentry;
+ int ret = WIMLIB_ERR_NOMEM;
+
+
+ wdentry = CALLOC(1, sizeof(struct wimlib_dir_entry) +
+ (1 + dentry->d_inode->i_num_ads) *
+ sizeof(struct wimlib_stream_entry));
+ if (!wdentry)
+ goto out;
+
+ ret = init_wimlib_dentry(wdentry, dentry, wim);
+ if (ret)
+ goto out_free_wimlib_dentry;
+
+ if (!(flags & WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) {
+ ret = (*cb)(wdentry, user_ctx);
+ if (ret)
+ goto out_free_wimlib_dentry;
+ }
+
+ if (flags & (WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE |
+ WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN))
+ {
+ struct iterate_dir_tree_ctx ctx = {
+ .wim = wim,
+ .flags = flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN,
+ .cb = cb,
+ .user_ctx = user_ctx,
+ };
+ ret = for_dentry_child(dentry, call_do_iterate_dir_tree, &ctx);
+ }
+out_free_wimlib_dentry:
+ free_wimlib_dentry(wdentry);
+out:
+ return ret;
+}
+
+struct image_iterate_dir_tree_ctx {
+ const tchar *path;
+ int flags;
+ wimlib_iterate_dir_tree_callback_t cb;
+ void *user_ctx;
+};
+
+
+static int
+image_do_iterate_dir_tree(WIMStruct *wim)
+{
+ struct image_iterate_dir_tree_ctx *ctx = wim->private;
+ struct wim_dentry *dentry;
+
+ dentry = get_dentry(wim, ctx->path);
+ if (!dentry)
+ return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+ return do_iterate_dir_tree(wim, dentry, ctx->flags, ctx->cb, ctx->user_ctx);
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_iterate_dir_tree(WIMStruct *wim, int image, const tchar *path,
+ int flags,
+ wimlib_iterate_dir_tree_callback_t cb, void *user_ctx)
+{
+ struct image_iterate_dir_tree_ctx ctx = {
+ .path = path,
+ .flags = flags,
+ .cb = cb,
+ .user_ctx = user_ctx,
+ };
+ wim->private = &ctx;
+ return for_image(wim, image, image_do_iterate_dir_tree);
+}
+
+static bool
+inode_stream_sizes_consistent(const struct wim_inode *inode_1,
+ const struct wim_inode *inode_2,
+ const struct wim_lookup_table *lookup_table)
+{
+ if (inode_1->i_num_ads != inode_2->i_num_ads)
+ return false;
+ for (unsigned i = 0; i <= inode_1->i_num_ads; i++) {
+ const struct wim_lookup_table_entry *lte_1, *lte_2;
+
+ lte_1 = inode_stream_lte(inode_1, i, lookup_table);
+ lte_2 = inode_stream_lte(inode_2, i, lookup_table);
+ if (lte_1 && lte_2) {
+ if (wim_resource_size(lte_1) != wim_resource_size(lte_2))
+ return false;
+ } else if (lte_1 && wim_resource_size(lte_1)) {
+ return false;
+ } else if (lte_2 && wim_resource_size(lte_2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void
+inode_replace_ltes(struct wim_inode *inode,
+ struct wim_inode *template_inode,
+ struct wim_lookup_table *lookup_table)
+{
+ for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+ struct wim_lookup_table_entry *lte, *lte_template;
+
+ lte = inode_stream_lte(inode, i, lookup_table);
+ if (lte) {
+ for (unsigned j = 0; j < inode->i_nlink; j++)
+ lte_decrement_refcnt(lte, lookup_table);
+ lte_template = inode_stream_lte(template_inode, i,
+ lookup_table);
+ if (i == 0)
+ inode->i_lte = lte_template;
+ else
+ inode->i_ads_entries[i - 1].lte = lte_template;
+ if (lte_template)
+ lte_template->refcnt += inode->i_nlink;
+ }
+ }
+ inode->i_resolved = 1;
+}
+
+static int
+dentry_reference_template(struct wim_dentry *dentry, void *_wim)
+{
+ int ret;
+ struct wim_dentry *template_dentry;
+ struct wim_inode *inode, *template_inode;
+ WIMStruct *wim = _wim;
+
+ if (dentry->d_inode->i_visited)
+ return 0;
+
+ ret = calculate_dentry_full_path(dentry);
+ if (ret)
+ return ret;
+
+ template_dentry = get_dentry(wim, dentry->_full_path);
+ if (!template_dentry) {
+ DEBUG("\"%"TS"\": newly added file", dentry->_full_path);
+ return 0;
+ }
+
+ inode = dentry->d_inode;
+ template_inode = template_dentry->d_inode;
+
+ if (inode->i_last_write_time == template_inode->i_last_write_time
+ && inode->i_creation_time == template_inode->i_creation_time
+ && inode->i_last_access_time >= template_inode->i_last_access_time
+ && inode_stream_sizes_consistent(inode, template_inode,
+ wim->lookup_table))
+ {
+ /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
+ inode_replace_ltes(inode, template_inode, wim->lookup_table);
+ inode->i_visited = 1;
+ } else {
+ DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
+ }
+ return 0;
+}
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_reference_template_image(WIMStruct *wim, int new_image, int template_image,
+ int flags, wimlib_progress_func_t progress_func)
+{
+ int ret;
+ struct wim_image_metadata *new_imd;
+
+ if (new_image < 1 || new_image > wim->hdr.image_count)
+ return WIMLIB_ERR_INVALID_IMAGE;
+
+ if (template_image < 1 || template_image > wim->hdr.image_count)
+ return WIMLIB_ERR_INVALID_IMAGE;
+
+ if (new_image == template_image)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ if (!wim_has_metadata(wim))
+ return WIMLIB_ERR_METADATA_NOT_FOUND;
+
+ new_imd = wim->image_metadata[new_image - 1];
+ if (!new_imd->modified)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ ret = select_wim_image(wim, template_image);
+ if (ret)
+ return ret;
+
+ ret = for_dentry_in_tree(new_imd->root_dentry,
+ dentry_reference_template, wim);
+ dentry_tree_clear_inode_visited(new_imd->root_dentry);
+ return ret;
+}