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, 2015 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/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"
35 
36 static u64
stream_size(const struct wim_inode_stream * strm,const struct blob_table * blob_table)37 stream_size(const struct wim_inode_stream *strm,
38 	    const struct blob_table *blob_table)
39 {
40 	const struct blob_descriptor *blob;
41 
42 	blob = stream_blob(strm, blob_table);
43 	if (!blob)
44 		return 0;
45 	return blob->size;
46 }
47 
48 /* Returns %true iff the metadata of @inode and @template_inode are reasonably
49  * consistent with them being the same, unmodified file.  */
50 static bool
inode_metadata_consistent(const struct wim_inode * inode,const struct wim_inode * template_inode,const struct blob_table * blob_table,const struct blob_table * template_blob_table)51 inode_metadata_consistent(const struct wim_inode *inode,
52 			  const struct wim_inode *template_inode,
53 			  const struct blob_table *blob_table,
54 			  const struct blob_table *template_blob_table)
55 {
56 	/* Must have exact same creation time and last write time.  */
57 	if (inode->i_creation_time != template_inode->i_creation_time ||
58 	    inode->i_last_write_time != template_inode->i_last_write_time)
59 		return false;
60 
61 	/* Last access time may have stayed the same or increased, but certainly
62 	 * shouldn't have decreased.  */
63 	if (inode->i_last_access_time < template_inode->i_last_access_time)
64 		return false;
65 
66 	/* All stream sizes must match.  */
67 	for (unsigned i = 0; i < inode->i_num_streams; i++) {
68 		const struct wim_inode_stream *strm, *template_strm;
69 
70 		strm = &inode->i_streams[i];
71 		template_strm = inode_get_stream(template_inode,
72 						 strm->stream_type,
73 						 strm->stream_name);
74 		if (!template_strm)
75 			return false;
76 
77 		if (stream_size(strm, blob_table) !=
78 		    stream_size(template_strm, template_blob_table))
79 			return false;
80 	}
81 
82 	return true;
83 }
84 
85 /**
86  * Given an inode @inode that has been determined to be "the same" as another
87  * inode @template_inode in either the same WIM or another WIM, copy stream
88  * checksums from @template_inode to @inode.
89  */
90 static void
inode_copy_checksums(struct wim_inode * inode,struct wim_inode * template_inode,struct blob_table * blob_table,struct blob_table * template_blob_table)91 inode_copy_checksums(struct wim_inode *inode,
92 		     struct wim_inode *template_inode,
93 		     struct blob_table *blob_table,
94 		     struct blob_table *template_blob_table)
95 {
96 	for (unsigned i = 0; i < inode->i_num_streams; i++) {
97 		const struct wim_inode_stream *strm, *template_strm;
98 		struct blob_descriptor *blob, *template_blob, **back_ptr;
99 
100 		strm = &inode->i_streams[i];
101 		template_strm = inode_get_stream(template_inode,
102 						 strm->stream_type,
103 						 strm->stream_name);
104 
105 		blob = stream_blob(strm, blob_table);
106 		template_blob = stream_blob(template_strm, template_blob_table);
107 
108 		/* To copy hashes: both blobs must exist, the blob for @inode
109 		 * must be unhashed, and the blob for @template_inode must be
110 		 * hashed.  */
111 		if (!blob || !template_blob ||
112 		    !blob->unhashed || template_blob->unhashed)
113 			continue;
114 
115 		back_ptr = retrieve_pointer_to_unhashed_blob(blob);
116 		copy_hash(blob->hash, template_blob->hash);
117 		if (after_blob_hashed(blob, back_ptr, blob_table) != blob)
118 			free_blob_descriptor(blob);
119 	}
120 }
121 
122 static int
reference_template_file(struct wim_inode * inode,WIMStruct * wim,WIMStruct * template_wim)123 reference_template_file(struct wim_inode *inode, WIMStruct *wim,
124 			WIMStruct *template_wim)
125 {
126 	struct wim_dentry *dentry = inode_any_dentry(inode);
127 	struct wim_dentry *template_dentry;
128 	int ret;
129 
130 	ret = calculate_dentry_full_path(dentry);
131 	if (ret)
132 		return ret;
133 
134 	template_dentry = get_dentry(template_wim, dentry->d_full_path,
135 				     WIMLIB_CASE_SENSITIVE);
136 	if (template_dentry != NULL &&
137 	    inode_metadata_consistent(inode, template_dentry->d_inode,
138 				      wim->blob_table, template_wim->blob_table))
139 	{
140 		inode_copy_checksums(inode, template_dentry->d_inode,
141 				     wim->blob_table, template_wim->blob_table);
142 	}
143 
144 	FREE(dentry->d_full_path);
145 	dentry->d_full_path = NULL;
146 	return 0;
147 }
148 
149 /* API function documented in wimlib.h  */
150 WIMLIBAPI int
wimlib_reference_template_image(WIMStruct * wim,int new_image,WIMStruct * template_wim,int template_image,int flags)151 wimlib_reference_template_image(WIMStruct *wim, int new_image,
152 				WIMStruct *template_wim, int template_image,
153 				int flags)
154 {
155 	int ret;
156 	struct wim_image_metadata *new_imd;
157 	struct wim_inode *inode;
158 
159 	if (flags != 0)
160 		return WIMLIB_ERR_INVALID_PARAM;
161 
162 	if (wim == NULL || template_wim == NULL)
163 		return WIMLIB_ERR_INVALID_PARAM;
164 
165 	if (wim == template_wim && new_image == template_image)
166 		return WIMLIB_ERR_INVALID_PARAM;
167 
168 	if (new_image < 1 || new_image > wim->hdr.image_count)
169 		return WIMLIB_ERR_INVALID_IMAGE;
170 
171 	if (!wim_has_metadata(wim))
172 		return WIMLIB_ERR_METADATA_NOT_FOUND;
173 
174 	new_imd = wim->image_metadata[new_image - 1];
175 	if (!is_image_dirty(new_imd))
176 		return WIMLIB_ERR_INVALID_PARAM;
177 
178 	ret = select_wim_image(template_wim, template_image);
179 	if (ret)
180 		return ret;
181 
182 	image_for_each_inode(inode, new_imd) {
183 		ret = reference_template_file(inode, wim, template_wim);
184 		if (ret)
185 			return ret;
186 	}
187 
188 	return 0;
189 }
190