4 * API to reference a template image to optimize later writing of a WIM file.
8 * Copyright (C) 2013 Eric Biggers
10 * This file is part of wimlib, a library for working with WIM files.
12 * wimlib is free software; you can redistribute it and/or modify it under the
13 * terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your option)
17 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19 * A PARTICULAR PURPOSE. See the GNU General Public License for more
22 * You should have received a copy of the GNU General Public License
23 * along with wimlib; if not, see http://www.gnu.org/licenses/.
31 #include "wimlib/dentry.h"
32 #include "wimlib/error.h"
33 #include "wimlib/lookup_table.h"
34 #include "wimlib/metadata.h"
35 #include "wimlib/util.h"
37 /* Returns %true iff the metadata of @inode and @template_inode are reasonably
38 * consistent with them being the same, unmodified file. */
40 inode_metadata_consistent(const struct wim_inode *inode,
41 const struct wim_inode *template_inode,
42 const struct wim_lookup_table *template_lookup_table)
44 /* Must have exact same creation time and last write time. */
45 if (inode->i_creation_time != template_inode->i_creation_time ||
46 inode->i_last_write_time != template_inode->i_last_write_time)
49 /* Last access time may have stayed the same or increased, but certainly
50 * shouldn't have decreased. */
51 if (inode->i_last_access_time < template_inode->i_last_access_time)
54 /* Must have same number of alternate data stream entries. */
55 if (inode->i_num_ads != template_inode->i_num_ads)
58 /* If the stream entries for the inode are for some reason not resolved,
59 * then the hashes are already available and the point of this function
61 if (!inode->i_resolved)
64 /* Iterate through each stream and do some more checks. */
65 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
66 const struct wim_lookup_table_entry *lte, *template_lte;
68 lte = inode_stream_lte_resolved(inode, i);
69 template_lte = inode_stream_lte(template_inode, i,
70 template_lookup_table);
72 /* Compare stream sizes. */
73 if (lte && template_lte) {
74 if (lte->size != template_lte->size)
77 /* If hash happens to be available, compare with template. */
78 if (!lte->unhashed && !template_lte->unhashed &&
79 !hashes_equal(lte->hash, template_lte->hash))
82 } else if (lte && lte->size) {
84 } else if (template_lte && template_lte->size) {
89 /* All right, barring a full checksum and given that the inodes share a
90 * path and the user isn't trying to trick us, these inodes most likely
91 * refer to the same file. */
96 * Given an inode @inode that has been determined to be "the same" as another
97 * inode @template_inode in either the same WIM or another WIM, retrieve some
98 * useful stream information (e.g. checksums) from @template_inode.
100 * This assumes that the streams for @inode have been resolved (to point
101 * directly to the appropriate `struct wim_lookup_table_entry's) but do not
102 * necessarily have checksum information filled in.
105 inode_copy_checksums(struct wim_inode *inode,
106 struct wim_inode *template_inode,
108 WIMStruct *template_wim)
110 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
111 struct wim_lookup_table_entry *lte, *template_lte;
112 struct wim_lookup_table_entry *replace_lte;
114 lte = inode_stream_lte_resolved(inode, i);
115 template_lte = inode_stream_lte(template_inode, i,
116 template_wim->lookup_table);
118 /* Only take action if both entries exist, the entry for @inode
119 * has no checksum calculated, but the entry for @template_inode
121 if (lte == NULL || template_lte == NULL ||
122 !lte->unhashed || template_lte->unhashed)
125 wimlib_assert(lte->refcnt == inode->i_nlink);
127 /* If the WIM of the template image is the same as the WIM of
128 * the new image, then @template_lte can be used directly.
130 * Otherwise, look for a stream with the same hash in the WIM of
131 * the new image. If found, use it; otherwise re-use the entry
132 * being discarded, filling in the hash. */
134 if (wim == template_wim)
135 replace_lte = template_lte;
137 replace_lte = lookup_stream(wim->lookup_table,
140 list_del(<e->unhashed_list);
142 free_lookup_table_entry(lte);
144 copy_hash(lte->hash, template_lte->hash);
146 lookup_table_insert(wim->lookup_table, lte);
152 inode->i_lte = replace_lte;
154 inode->i_ads_entries[i - 1].lte = replace_lte;
156 replace_lte->refcnt += inode->i_nlink;
161 struct reference_template_args {
163 WIMStruct *template_wim;
167 dentry_reference_template(struct wim_dentry *dentry, void *_args)
170 struct wim_dentry *template_dentry;
171 struct wim_inode *inode, *template_inode;
172 struct reference_template_args *args = _args;
173 WIMStruct *wim = args->wim;
174 WIMStruct *template_wim = args->template_wim;
176 if (dentry->d_inode->i_visited)
179 ret = calculate_dentry_full_path(dentry);
183 template_dentry = get_dentry(template_wim, dentry->_full_path,
184 WIMLIB_CASE_SENSITIVE);
185 if (template_dentry == NULL) {
186 DEBUG("\"%"TS"\": newly added file", dentry->_full_path);
190 inode = dentry->d_inode;
191 template_inode = template_dentry->d_inode;
193 if (inode_metadata_consistent(inode, template_inode,
194 template_wim->lookup_table)) {
195 /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
196 ret = inode_copy_checksums(inode, template_inode,
198 inode->i_visited = 1;
200 DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
206 /* API function documented in wimlib.h */
208 wimlib_reference_template_image(WIMStruct *wim, int new_image,
209 WIMStruct *template_wim, int template_image,
213 struct wim_image_metadata *new_imd;
216 return WIMLIB_ERR_INVALID_PARAM;
218 if (wim == NULL || template_wim == NULL)
219 return WIMLIB_ERR_INVALID_PARAM;
221 if (wim == template_wim && new_image == template_image)
222 return WIMLIB_ERR_INVALID_PARAM;
224 if (new_image < 1 || new_image > wim->hdr.image_count)
225 return WIMLIB_ERR_INVALID_IMAGE;
227 if (!wim_has_metadata(wim))
228 return WIMLIB_ERR_METADATA_NOT_FOUND;
230 new_imd = wim->image_metadata[new_image - 1];
231 if (!new_imd->modified)
232 return WIMLIB_ERR_INVALID_PARAM;
234 ret = select_wim_image(template_wim, template_image);
238 struct reference_template_args args = {
240 .template_wim = template_wim,
243 ret = for_dentry_in_tree(new_imd->root_dentry,
244 dentry_reference_template, &args);
245 dentry_tree_clear_inode_visited(new_imd->root_dentry);