-
- 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);
-}
-
-/* Returns %true iff the metadata of @inode and @template_inode are reasonably
- * consistent with them being the same, unmodified file. */
-static bool
-inode_metadata_consistent(const struct wim_inode *inode,
- const struct wim_inode *template_inode,
- const struct wim_lookup_table *template_lookup_table)
-{
- /* Must have exact same creation time and last write time. */
- if (inode->i_creation_time != template_inode->i_creation_time ||
- inode->i_last_write_time != template_inode->i_last_write_time)
- return false;
-
- /* Last access time may have stayed the same or increased, but certainly
- * shouldn't have decreased. */
- if (inode->i_last_access_time < template_inode->i_last_access_time)
- return false;
-
- /* Must have same number of alternate data stream entries. */
- if (inode->i_num_ads != template_inode->i_num_ads)
- return false;
-
- /* If the stream entries for the inode are for some reason not resolved,
- * then the hashes are already available and the point of this function
- * is defeated. */
- if (!inode->i_resolved)
- return false;
-
- /* Iterate through each stream and do some more checks. */
- for (unsigned i = 0; i <= inode->i_num_ads; i++) {
- const struct wim_lookup_table_entry *lte, *template_lte;
-
- lte = inode_stream_lte_resolved(inode, i);
- template_lte = inode_stream_lte(template_inode, i,
- template_lookup_table);
-
- /* Compare stream sizes. */
- if (lte && template_lte) {
- if (wim_resource_size(lte) != wim_resource_size(template_lte))
- return false;
-
- /* If hash happens to be available, compare with template. */
- if (!lte->unhashed && !template_lte->unhashed &&
- !hashes_equal(lte->hash, template_lte->hash))
- return false;
-
- } else if (lte && wim_resource_size(lte)) {
- return false;
- } else if (template_lte && wim_resource_size(template_lte)) {
- return false;
- }
- }
-
- /* All right, barring a full checksum and given that the inodes share a
- * path and the user isn't trying to trick us, these inodes most likely
- * refer to the same file. */
- return true;
-}
-
-/**
- * Given an inode @inode that has been determined to be "the same" as another
- * inode @template_inode in either the same WIM or another WIM, retrieve some
- * useful stream information (e.g. checksums) from @template_inode.
- *
- * This assumes that the streams for @inode have been resolved (to point
- * directly to the appropriate `struct wim_lookup_table_entry's) but do not
- * necessarily have checksum information filled in.
- */
-static int
-inode_copy_checksums(struct wim_inode *inode,
- struct wim_inode *template_inode,
- WIMStruct *wim,
- WIMStruct *template_wim)
-{
- for (unsigned i = 0; i <= inode->i_num_ads; i++) {
- struct wim_lookup_table_entry *lte, *template_lte;
- struct wim_lookup_table_entry *replace_lte;
-
- lte = inode_stream_lte_resolved(inode, i);
- template_lte = inode_stream_lte(template_inode, i,
- template_wim->lookup_table);
-
- /* Only take action if both entries exist, the entry for @inode
- * has no checksum calculated, but the entry for @template_inode
- * does. */
- if (!lte || !template_lte ||
- !lte->unhashed || template_lte->unhashed)
- continue;
-
- wimlib_assert(lte->refcnt == inode->i_nlink);
-
- /* If the WIM of the template image is the same as the WIM of
- * the new image, then @template_lte can be used directly.
- *
- * Otherwise, look for a stream with the same hash in the WIM of
- * the new image. If found, use it; otherwise re-use the entry
- * being discarded, filling in the hash. */
-
- if (wim == template_wim)
- replace_lte = template_lte;
- else
- replace_lte = lookup_resource(wim->lookup_table,
- template_lte->hash);
-
- list_del(<e->unhashed_list);
- if (replace_lte) {
- free_lookup_table_entry(lte);
- } else {
- copy_hash(lte->hash, template_lte->hash);
- lte->unhashed = 0;
- lookup_table_insert(wim->lookup_table, lte);
- lte->refcnt = 0;
- replace_lte = lte;
- }
-
- if (i == 0)
- inode->i_lte = replace_lte;
- else
- inode->i_ads_entries[i - 1].lte = replace_lte;
-
- replace_lte->refcnt += inode->i_nlink;
- }
- return 0;
-}
-
-struct reference_template_args {
- WIMStruct *wim;
- WIMStruct *template_wim;
-};
-
-static int
-dentry_reference_template(struct wim_dentry *dentry, void *_args)
-{
- int ret;
- struct wim_dentry *template_dentry;
- struct wim_inode *inode, *template_inode;
- struct reference_template_args *args = _args;
- WIMStruct *wim = args->wim;
- WIMStruct *template_wim = args->template_wim;
-
- if (dentry->d_inode->i_visited)
- return 0;
-
- ret = calculate_dentry_full_path(dentry);
- if (ret)
- return ret;
-
- template_dentry = get_dentry(template_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_metadata_consistent(inode, template_inode,
- template_wim->lookup_table)) {
- /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
- ret = inode_copy_checksums(inode, template_inode,
- wim, template_wim);
- inode->i_visited = 1;
- } else {
- DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
- ret = 0;
- }
- return ret;
-}
-
-/* API function documented in wimlib.h */
-WIMLIBAPI int
-wimlib_reference_template_image(WIMStruct *wim, int new_image,
- WIMStruct *template_wim, int template_image,
- int flags, wimlib_progress_func_t progress_func)
-{
- int ret;
- struct wim_image_metadata *new_imd;
-
- if (wim == NULL || template_wim == NULL)
- return WIMLIB_ERR_INVALID_PARAM;
-
- if (wim == template_wim && new_image == template_image)
- return WIMLIB_ERR_INVALID_PARAM;
-
- if (new_image < 1 || new_image > wim->hdr.image_count)
- return WIMLIB_ERR_INVALID_IMAGE;
-
- 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(template_wim, template_image);
- if (ret)
- return ret;
-
- struct reference_template_args args = {
- .wim = wim,
- .template_wim = template_wim,
- };
-
- ret = for_dentry_in_tree(new_imd->root_dentry,
- dentry_reference_template, &args);
- dentry_tree_clear_inode_visited(new_imd->root_dentry);
- return ret;