4 * API to reference a template image to optimize later writing of a WIM file.
8 * Copyright (C) 2013, 2015 Eric Biggers
10 * This file is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU Lesser General Public License as published by the Free
12 * Software Foundation; either version 3 of the License, or (at your option) any
15 * This file is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this file; if not, see http://www.gnu.org/licenses/.
29 #include "wimlib/blob_table.h"
30 #include "wimlib/assert.h"
31 #include "wimlib/dentry.h"
32 #include "wimlib/error.h"
33 #include "wimlib/metadata.h"
34 #include "wimlib/util.h"
36 /* Returns %true iff the metadata of @inode and @template_inode are reasonably
37 * consistent with them being the same, unmodified file. */
39 inode_metadata_consistent(const struct wim_inode *inode,
40 const struct wim_inode *template_inode,
41 const struct blob_table *template_blob_table)
43 /* Must have exact same creation time and last write time. */
44 if (inode->i_creation_time != template_inode->i_creation_time ||
45 inode->i_last_write_time != template_inode->i_last_write_time)
48 /* Last access time may have stayed the same or increased, but certainly
49 * shouldn't have decreased. */
50 if (inode->i_last_access_time < template_inode->i_last_access_time)
53 /* Must have same number of streams. */
54 if (inode->i_num_streams != template_inode->i_num_streams)
57 for (unsigned i = 0; i < inode->i_num_streams; i++) {
58 const struct blob_descriptor *blob, *template_blob;
60 /* If the streams for the inode are for some reason not
61 * resolved, then the hashes are already available and the point
62 * of this function is defeated. */
63 if (!inode->i_streams[i].stream_resolved)
66 blob = stream_blob_resolved(&inode->i_streams[i]);
67 template_blob = stream_blob(&template_inode->i_streams[i],
70 /* Compare blob sizes. */
71 if (blob && template_blob) {
72 if (blob->size != template_blob->size)
75 /* If hash happens to be available, compare with template. */
76 if (!blob->unhashed && !template_blob->unhashed &&
77 !hashes_equal(blob->hash, template_blob->hash))
80 } else if (blob && blob->size) {
82 } else if (template_blob && template_blob->size) {
87 /* All right, barring a full checksum and given that the inodes share a
88 * path and the user isn't trying to trick us, these inodes most likely
89 * refer to the same file. */
94 * Given an inode @inode that has been determined to be "the same" as another
95 * inode @template_inode in either the same WIM or another WIM, retrieve some
96 * useful information (e.g. checksums) from @template_inode.
98 * This assumes that the streams for @inode have been resolved (to point
99 * directly to the appropriate `struct blob_descriptor's) but do not necessarily
100 * have checksum information filled in.
103 inode_copy_checksums(struct wim_inode *inode,
104 struct wim_inode *template_inode,
106 WIMStruct *template_wim)
108 for (unsigned i = 0; i < inode->i_num_streams; i++) {
109 struct blob_descriptor *blob, *template_blob;
110 struct blob_descriptor *replace_blob;
112 blob = stream_blob_resolved(&inode->i_streams[i]);
113 template_blob = stream_blob(&template_inode->i_streams[i],
114 template_wim->blob_table);
116 /* Only take action if both entries exist, the entry for @inode
117 * has no checksum calculated, but the entry for @template_inode
119 if (blob == NULL || template_blob == NULL ||
120 !blob->unhashed || template_blob->unhashed)
123 wimlib_assert(blob->refcnt == inode->i_nlink);
125 /* If the WIM of the template image is the same as the WIM of
126 * the new image, then @template_blob can be used directly.
128 * Otherwise, look for a blob with the same hash in the WIM of
129 * the new image. If found, use it; otherwise re-use the
130 * blob descriptor being discarded, filling in the hash. */
132 if (wim == template_wim)
133 replace_blob = template_blob;
135 replace_blob = lookup_blob(wim->blob_table,
136 template_blob->hash);
138 list_del(&blob->unhashed_list);
140 free_blob_descriptor(blob);
142 copy_hash(blob->hash, template_blob->hash);
144 blob_table_insert(wim->blob_table, blob);
149 stream_set_blob(&inode->i_streams[i], replace_blob);
150 replace_blob->refcnt += inode->i_nlink;
155 struct reference_template_args {
157 WIMStruct *template_wim;
161 dentry_reference_template(struct wim_dentry *dentry, void *_args)
164 struct wim_dentry *template_dentry;
165 struct wim_inode *inode, *template_inode;
166 struct reference_template_args *args = _args;
167 WIMStruct *wim = args->wim;
168 WIMStruct *template_wim = args->template_wim;
170 if (dentry->d_inode->i_visited)
173 ret = calculate_dentry_full_path(dentry);
177 template_dentry = get_dentry(template_wim, dentry->_full_path,
178 WIMLIB_CASE_SENSITIVE);
179 if (template_dentry == NULL) {
180 DEBUG("\"%"TS"\": newly added file", dentry->_full_path);
184 inode = dentry->d_inode;
185 template_inode = template_dentry->d_inode;
187 if (inode_metadata_consistent(inode, template_inode,
188 template_wim->blob_table)) {
189 /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
190 ret = inode_copy_checksums(inode, template_inode,
192 inode->i_visited = 1;
194 DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
200 /* API function documented in wimlib.h */
202 wimlib_reference_template_image(WIMStruct *wim, int new_image,
203 WIMStruct *template_wim, int template_image,
207 struct wim_image_metadata *new_imd;
210 return WIMLIB_ERR_INVALID_PARAM;
212 if (wim == NULL || template_wim == NULL)
213 return WIMLIB_ERR_INVALID_PARAM;
215 if (wim == template_wim && new_image == template_image)
216 return WIMLIB_ERR_INVALID_PARAM;
218 if (new_image < 1 || new_image > wim->hdr.image_count)
219 return WIMLIB_ERR_INVALID_IMAGE;
221 if (!wim_has_metadata(wim))
222 return WIMLIB_ERR_METADATA_NOT_FOUND;
224 new_imd = wim->image_metadata[new_image - 1];
225 if (!new_imd->modified)
226 return WIMLIB_ERR_INVALID_PARAM;
228 ret = select_wim_image(template_wim, template_image);
232 struct reference_template_args args = {
234 .template_wim = template_wim,
237 ret = for_dentry_in_tree(new_imd->root_dentry,
238 dentry_reference_template, &args);
239 dentry_tree_clear_inode_visited(new_imd->root_dentry);