]> wimlib.net Git - wimlib/blob - src/template.c
Use --enable-ssse3-sha1 for x86_64 Windows builds
[wimlib] / src / template.c
1 /*
2  * template.c
3  *
4  * API to reference a template image to optimize later writing of a WIM file.
5  */
6
7 /*
8  * Copyright (C) 2013 Eric Biggers
9  *
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
13  * later version.
14  *
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
18  * details.
19  *
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/.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib.h"
29 #include "wimlib/assert.h"
30 #include "wimlib/dentry.h"
31 #include "wimlib/error.h"
32 #include "wimlib/lookup_table.h"
33 #include "wimlib/metadata.h"
34 #include "wimlib/util.h"
35
36 /* Returns %true iff the metadata of @inode and @template_inode are reasonably
37  * consistent with them being the same, unmodified file.  */
38 static bool
39 inode_metadata_consistent(const struct wim_inode *inode,
40                           const struct wim_inode *template_inode,
41                           const struct wim_lookup_table *template_lookup_table)
42 {
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)
46                 return false;
47
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)
51                 return false;
52
53         /* Must have same number of alternate data stream entries.  */
54         if (inode->i_num_ads != template_inode->i_num_ads)
55                 return false;
56
57         /* If the stream entries for the inode are for some reason not resolved,
58          * then the hashes are already available and the point of this function
59          * is defeated.  */
60         if (!inode->i_resolved)
61                 return false;
62
63         /* Iterate through each stream and do some more checks.  */
64         for (unsigned i = 0; i <= inode->i_num_ads; i++) {
65                 const struct wim_lookup_table_entry *lte, *template_lte;
66
67                 lte = inode_stream_lte_resolved(inode, i);
68                 template_lte = inode_stream_lte(template_inode, i,
69                                                 template_lookup_table);
70
71                 /* Compare stream sizes.  */
72                 if (lte && template_lte) {
73                         if (lte->size != template_lte->size)
74                                 return false;
75
76                         /* If hash happens to be available, compare with template.  */
77                         if (!lte->unhashed && !template_lte->unhashed &&
78                             !hashes_equal(lte->hash, template_lte->hash))
79                                 return false;
80
81                 } else if (lte && lte->size) {
82                         return false;
83                 } else if (template_lte && template_lte->size) {
84                         return false;
85                 }
86         }
87
88         /* All right, barring a full checksum and given that the inodes share a
89          * path and the user isn't trying to trick us, these inodes most likely
90          * refer to the same file.  */
91         return true;
92 }
93
94 /**
95  * Given an inode @inode that has been determined to be "the same" as another
96  * inode @template_inode in either the same WIM or another WIM, retrieve some
97  * useful stream information (e.g. checksums) from @template_inode.
98  *
99  * This assumes that the streams for @inode have been resolved (to point
100  * directly to the appropriate `struct wim_lookup_table_entry's)  but do not
101  * necessarily have checksum information filled in.
102  */
103 static int
104 inode_copy_checksums(struct wim_inode *inode,
105                      struct wim_inode *template_inode,
106                      WIMStruct *wim,
107                      WIMStruct *template_wim)
108 {
109         for (unsigned i = 0; i <= inode->i_num_ads; i++) {
110                 struct wim_lookup_table_entry *lte, *template_lte;
111                 struct wim_lookup_table_entry *replace_lte;
112
113                 lte = inode_stream_lte_resolved(inode, i);
114                 template_lte = inode_stream_lte(template_inode, i,
115                                                 template_wim->lookup_table);
116
117                 /* Only take action if both entries exist, the entry for @inode
118                  * has no checksum calculated, but the entry for @template_inode
119                  * does.  */
120                 if (lte == NULL || template_lte == NULL ||
121                     !lte->unhashed || template_lte->unhashed)
122                         continue;
123
124                 wimlib_assert(lte->refcnt == inode->i_nlink);
125
126                 /* If the WIM of the template image is the same as the WIM of
127                  * the new image, then @template_lte can be used directly.
128                  *
129                  * Otherwise, look for a stream with the same hash in the WIM of
130                  * the new image.  If found, use it; otherwise re-use the entry
131                  * being discarded, filling in the hash.  */
132
133                 if (wim == template_wim)
134                         replace_lte = template_lte;
135                 else
136                         replace_lte = lookup_stream(wim->lookup_table,
137                                                     template_lte->hash);
138
139                 list_del(&lte->unhashed_list);
140                 if (replace_lte) {
141                         free_lookup_table_entry(lte);
142                 } else {
143                         copy_hash(lte->hash, template_lte->hash);
144                         lte->unhashed = 0;
145                         lookup_table_insert(wim->lookup_table, lte);
146                         lte->refcnt = 0;
147                         replace_lte = lte;
148                 }
149
150                 if (i == 0)
151                         inode->i_lte = replace_lte;
152                 else
153                         inode->i_ads_entries[i - 1].lte = replace_lte;
154
155                 replace_lte->refcnt += inode->i_nlink;
156         }
157         return 0;
158 }
159
160 struct reference_template_args {
161         WIMStruct *wim;
162         WIMStruct *template_wim;
163 };
164
165 static int
166 dentry_reference_template(struct wim_dentry *dentry, void *_args)
167 {
168         int ret;
169         struct wim_dentry *template_dentry;
170         struct wim_inode *inode, *template_inode;
171         struct reference_template_args *args = _args;
172         WIMStruct *wim = args->wim;
173         WIMStruct *template_wim = args->template_wim;
174
175         if (dentry->d_inode->i_visited)
176                 return 0;
177
178         ret = calculate_dentry_full_path(dentry);
179         if (ret)
180                 return ret;
181
182         template_dentry = get_dentry(template_wim, dentry->_full_path,
183                                      WIMLIB_CASE_SENSITIVE);
184         if (template_dentry == NULL) {
185                 DEBUG("\"%"TS"\": newly added file", dentry->_full_path);
186                 return 0;
187         }
188
189         inode = dentry->d_inode;
190         template_inode = template_dentry->d_inode;
191
192         if (inode_metadata_consistent(inode, template_inode,
193                                       template_wim->lookup_table)) {
194                 /*DEBUG("\"%"TS"\": No change detected", dentry->_full_path);*/
195                 ret = inode_copy_checksums(inode, template_inode,
196                                            wim, template_wim);
197                 inode->i_visited = 1;
198         } else {
199                 DEBUG("\"%"TS"\": change detected!", dentry->_full_path);
200                 ret = 0;
201         }
202         return ret;
203 }
204
205 /* API function documented in wimlib.h  */
206 WIMLIBAPI int
207 wimlib_reference_template_image(WIMStruct *wim, int new_image,
208                                 WIMStruct *template_wim, int template_image,
209                                 int flags)
210 {
211         int ret;
212         struct wim_image_metadata *new_imd;
213
214         if (flags != 0)
215                 return WIMLIB_ERR_INVALID_PARAM;
216
217         if (wim == NULL || template_wim == NULL)
218                 return WIMLIB_ERR_INVALID_PARAM;
219
220         if (wim == template_wim && new_image == template_image)
221                 return WIMLIB_ERR_INVALID_PARAM;
222
223         if (new_image < 1 || new_image > wim->hdr.image_count)
224                 return WIMLIB_ERR_INVALID_IMAGE;
225
226         if (!wim_has_metadata(wim))
227                 return WIMLIB_ERR_METADATA_NOT_FOUND;
228
229         new_imd = wim->image_metadata[new_image - 1];
230         if (!new_imd->modified)
231                 return WIMLIB_ERR_INVALID_PARAM;
232
233         ret = select_wim_image(template_wim, template_image);
234         if (ret)
235                 return ret;
236
237         struct reference_template_args args = {
238                 .wim = wim,
239                 .template_wim = template_wim,
240         };
241
242         ret = for_dentry_in_tree(new_imd->root_dentry,
243                                  dentry_reference_template, &args);
244         dentry_tree_clear_inode_visited(new_imd->root_dentry);
245         return ret;
246 }