ab0bab2bf6a804b1990a8e43bc3dc3ee892bcd17
[wimlib] / src / reference.c
1 /*
2  * reference.c
3  *
4  * Reference blobs from external WIM file(s).
5  */
6
7 /*
8  * Copyright (C) 2013, 2014 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/error.h"
31 #include "wimlib/glob.h"
32 #include "wimlib/wim.h"
33
34 #define WIMLIB_REF_MASK_PUBLIC (WIMLIB_REF_FLAG_GLOB_ENABLE | \
35                                 WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH)
36
37 struct reference_info {
38         WIMStruct *dest_wim;
39         struct list_head new_blobs;
40         struct list_head new_subwims;
41         int ref_flags;
42         struct blob_table *src_table;
43 };
44
45 static void
46 init_reference_info(struct reference_info *info, WIMStruct *dest_wim,
47                     int ref_flags)
48 {
49         info->dest_wim = dest_wim;
50         INIT_LIST_HEAD(&info->new_blobs);
51         INIT_LIST_HEAD(&info->new_subwims);
52         info->ref_flags = ref_flags;
53 }
54
55 static void
56 commit_reference_info(struct reference_info *info)
57 {
58         list_splice(&info->new_subwims, &info->dest_wim->subwims);
59 }
60
61 static void
62 rollback_reference_info(struct reference_info *info)
63 {
64         WIMStruct *subwim;
65         struct blob_descriptor *blob;
66
67         while (!list_empty(&info->new_subwims)) {
68                 subwim = list_first_entry(&info->new_subwims,
69                                           WIMStruct, subwim_node);
70                 list_del(&subwim->subwim_node);
71                 wimlib_free(subwim);
72         }
73
74         while (!list_empty(&info->new_blobs)) {
75                 blob = list_first_entry(&info->new_blobs,
76                                         struct blob_descriptor, blob_table_list);
77                 list_del(&blob->blob_table_list);
78                 blob_table_unlink(info->dest_wim->blob_table, blob);
79                 free_blob_descriptor(blob);
80         }
81 }
82
83 static int
84 commit_or_rollback_reference_info(struct reference_info *info, int ret)
85 {
86         if (unlikely(ret))
87                 rollback_reference_info(info);
88         else
89                 commit_reference_info(info);
90         return ret;
91 }
92
93 static bool
94 need_blob(const struct reference_info *info, const struct blob_descriptor *blob)
95 {
96         return !lookup_blob(info->dest_wim->blob_table, blob->hash);
97 }
98
99 static void
100 reference_blob(struct reference_info *info, struct blob_descriptor *blob)
101 {
102         blob_table_insert(info->dest_wim->blob_table, blob);
103         list_add(&blob->blob_table_list, &info->new_blobs);
104 }
105
106 static void
107 reference_subwim(struct reference_info *info, WIMStruct *subwim)
108 {
109         list_add(&subwim->subwim_node, &info->new_subwims);
110 }
111
112 static int
113 blob_clone_if_new(struct blob_descriptor *blob, void *_info)
114 {
115         struct reference_info *info = _info;
116
117         if (need_blob(info, blob)) {
118                 blob = clone_blob_descriptor(blob);
119                 if (unlikely(!blob))
120                         return WIMLIB_ERR_NOMEM;
121                 reference_blob(info, blob);
122         }
123         return 0;
124 }
125
126 /* API function documented in wimlib.h  */
127 WIMLIBAPI int
128 wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims,
129                            unsigned num_resource_wims, int ref_flags)
130 {
131         unsigned i;
132         struct reference_info info;
133         int ret = 0;
134
135         if (wim == NULL)
136                 return WIMLIB_ERR_INVALID_PARAM;
137
138         if (num_resource_wims != 0 && resource_wims == NULL)
139                 return WIMLIB_ERR_INVALID_PARAM;
140
141         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
142                 return WIMLIB_ERR_INVALID_PARAM;
143
144         for (i = 0; i < num_resource_wims; i++)
145                 if (resource_wims[i] == NULL)
146                         return WIMLIB_ERR_INVALID_PARAM;
147
148         init_reference_info(&info, wim, ref_flags);
149
150         for (i = 0; i < num_resource_wims; i++) {
151                 ret = for_blob_in_table(resource_wims[i]->blob_table,
152                                         blob_clone_if_new, &info);
153                 if (ret)
154                         break;
155         }
156
157         return commit_or_rollback_reference_info(&info, ret);
158 }
159
160 static int
161 blob_gift(struct blob_descriptor *blob, void *_info)
162 {
163         struct reference_info *info = _info;
164
165         blob_table_unlink(info->src_table, blob);
166         if (need_blob(info, blob))
167                 reference_blob(info, blob);
168         else
169                 free_blob_descriptor(blob);
170         return 0;
171 }
172
173 static int
174 reference_resource_path(struct reference_info *info, const tchar *path,
175                         int open_flags)
176 {
177         int ret;
178         WIMStruct *src_wim;
179
180         ret = wimlib_open_wim_with_progress(path, open_flags, &src_wim,
181                                             info->dest_wim->progfunc,
182                                             info->dest_wim->progctx);
183         if (ret)
184                 return ret;
185
186         info->src_table = src_wim->blob_table;
187         for_blob_in_table(src_wim->blob_table, blob_gift, info);
188         reference_subwim(info, src_wim);
189         return 0;
190 }
191
192 static int
193 reference_resource_paths(struct reference_info *info,
194                          const tchar * const *paths, unsigned num_paths,
195                          int open_flags)
196 {
197         for (unsigned i = 0; i < num_paths; i++) {
198                 int ret = reference_resource_path(info, paths[i], open_flags);
199                 if (ret)
200                         return ret;
201         }
202         return 0;
203 }
204
205 static int
206 reference_resource_glob(struct reference_info *info,
207                         const tchar *refglob, int open_flags)
208 {
209         int ret;
210         glob_t globbuf;
211
212         /* Note: glob() is replaced in Windows native builds.  */
213         ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
214         if (unlikely(ret)) {
215                 if (ret == GLOB_NOMATCH) {
216                         if (info->ref_flags &
217                             WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH)
218                         {
219                                 ERROR("Found no files for glob \"%"TS"\"", refglob);
220                                 return WIMLIB_ERR_GLOB_HAD_NO_MATCHES;
221                         }
222                         return reference_resource_path(info,
223                                                        refglob,
224                                                        open_flags);
225                 }
226                 ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob);
227                 if (ret == GLOB_NOSPACE)
228                         return WIMLIB_ERR_NOMEM;
229                 return WIMLIB_ERR_READ;
230         }
231
232         ret = reference_resource_paths(info,
233                                        (const tchar * const *)globbuf.gl_pathv,
234                                        globbuf.gl_pathc,
235                                        open_flags);
236         globfree(&globbuf);
237         return ret;
238 }
239
240 static int
241 reference_resource_globs(struct reference_info *info,
242                          const tchar * const *globs, unsigned num_globs,
243                          int open_flags)
244 {
245         for (unsigned i = 0; i < num_globs; i++) {
246                 int ret = reference_resource_glob(info, globs[i], open_flags);
247                 if (ret)
248                         return ret;
249         }
250         return 0;
251 }
252
253 /* API function documented in wimlib.h  */
254 WIMLIBAPI int
255 wimlib_reference_resource_files(WIMStruct *wim,
256                                 const tchar * const *paths_or_globs,
257                                 unsigned count, int ref_flags, int open_flags)
258 {
259         struct reference_info info;
260         int ret;
261
262         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
263                 return WIMLIB_ERR_INVALID_PARAM;
264
265         init_reference_info(&info, wim, ref_flags);
266
267         if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE)
268                 ret = reference_resource_globs(&info, paths_or_globs, count, open_flags);
269         else
270                 ret = reference_resource_paths(&info, paths_or_globs, count, open_flags);
271
272         return commit_or_rollback_reference_info(&info, ret);
273 }