c423bdd7ada1d0d669fbca9e8e3dae8e64077828
[wimlib] / src / reference.c
1 /*
2  * reference.c
3  *
4  * Reference resources from external WIM file(s).
5  */
6
7 /*
8  * Copyright (C) 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
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)
15  * any later version.
16  *
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
20  * details.
21  *
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/.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "wimlib.h"
31 #include "wimlib/error.h"
32 #include "wimlib/glob.h"
33 #include "wimlib/lookup_table.h"
34 #include "wimlib/wim.h"
35
36 #define WIMLIB_REF_MASK_PUBLIC (WIMLIB_REF_FLAG_GLOB_ENABLE | \
37                                 WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH)
38
39 #define WIMLIB_REF_FLAG_GIFT 0x80000000
40
41 struct lookup_tables {
42         struct wim_lookup_table *src_table;
43         struct wim_lookup_table *dest_table;
44 };
45
46 static int
47 lte_gift(struct wim_lookup_table_entry *lte, void *_tables)
48 {
49         struct lookup_tables *tables = _tables;
50         struct wim_lookup_table *src_table = tables->src_table;
51         struct wim_lookup_table *dest_table = tables->dest_table;
52
53         lookup_table_unlink(src_table, lte);
54         if (lookup_stream(dest_table, lte->hash)) {
55                 free_lookup_table_entry(lte);
56         } else {
57                 lte->out_refcnt = 1;
58                 lookup_table_insert(dest_table, lte);
59         }
60         return 0;
61 }
62
63 static int
64 lte_clone_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
65 {
66         struct wim_lookup_table *lookup_table = _lookup_table;
67
68         if (lookup_stream(lookup_table, lte->hash))
69                 return 0;  /*  Resource already present.  */
70
71         lte = clone_lookup_table_entry(lte);
72         if (lte == NULL)
73                 return WIMLIB_ERR_NOMEM;
74         lte->out_refcnt = 1;
75         lookup_table_insert(lookup_table, lte);
76         return 0;
77 }
78
79 static int
80 lte_delete_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
81 {
82         struct wim_lookup_table *lookup_table = _lookup_table;
83
84         if (lte->out_refcnt) {
85                 lookup_table_unlink(lookup_table, lte);
86                 free_lookup_table_entry(lte);
87         }
88         return 0;
89 }
90
91 static int
92 do_wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims,
93                               unsigned num_resource_wims, int ref_flags)
94 {
95         unsigned i;
96         int ret;
97
98         if (ref_flags & WIMLIB_REF_FLAG_GIFT) {
99                 struct lookup_tables tables;
100
101                 tables.dest_table = wim->lookup_table;
102
103                 for (i = 0; i < num_resource_wims; i++) {
104
105                         tables.src_table = resource_wims[i]->lookup_table;
106
107                         ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
108                                                      lte_gift, &tables);
109                         if (ret)
110                                 goto out_rollback;
111                 }
112         } else {
113                 for (i = 0; i < num_resource_wims; i++) {
114                         ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
115                                                      lte_clone_if_new, wim->lookup_table);
116                         if (ret)
117                                 goto out_rollback;
118                 }
119         }
120         return 0;
121
122 out_rollback:
123         for_lookup_table_entry(wim->lookup_table, lte_delete_if_new,
124                                wim->lookup_table);
125         return ret;
126 }
127
128 /* API function documented in wimlib.h  */
129 WIMLIBAPI int
130 wimlib_reference_resources(WIMStruct *wim,
131                            WIMStruct **resource_wims, unsigned num_resource_wims,
132                            int ref_flags)
133 {
134         unsigned i;
135
136         if (wim == NULL)
137                 return WIMLIB_ERR_INVALID_PARAM;
138
139         if (num_resource_wims != 0 && resource_wims == NULL)
140                 return WIMLIB_ERR_INVALID_PARAM;
141
142         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
143                 return WIMLIB_ERR_INVALID_PARAM;
144
145         for (i = 0; i < num_resource_wims; i++)
146                 if (resource_wims[i] == NULL)
147                         return WIMLIB_ERR_INVALID_PARAM;
148
149         return do_wimlib_reference_resources(wim, resource_wims,
150                                              num_resource_wims, ref_flags);
151 }
152
153 static int
154 reference_resource_paths(WIMStruct *wim,
155                          const tchar * const *resource_wimfiles,
156                          unsigned num_resource_wimfiles,
157                          int ref_flags,
158                          int open_flags,
159                          wimlib_progress_func_t progress_func)
160 {
161         WIMStruct **resource_wims;
162         unsigned i;
163         int ret;
164
165         resource_wims = CALLOC(num_resource_wimfiles, sizeof(resource_wims[0]));
166         if (!resource_wims)
167                 return WIMLIB_ERR_NOMEM;
168
169         for (i = 0; i < num_resource_wimfiles; i++) {
170                 DEBUG("Referencing resources from path \"%"TS"\"",
171                       resource_wimfiles[i]);
172                 ret = wimlib_open_wim(resource_wimfiles[i], open_flags,
173                                       &resource_wims[i], progress_func);
174                 if (ret)
175                         goto out_free_resource_wims;
176         }
177
178         ret = do_wimlib_reference_resources(wim, resource_wims,
179                                             num_resource_wimfiles,
180                                             ref_flags | WIMLIB_REF_FLAG_GIFT);
181         if (ret)
182                 goto out_free_resource_wims;
183
184         for (i = 0; i < num_resource_wimfiles; i++)
185                 list_add_tail(&resource_wims[i]->subwim_node, &wim->subwims);
186
187         ret = 0;
188         goto out_free_array;
189
190 out_free_resource_wims:
191         for (i = 0; i < num_resource_wimfiles; i++)
192                 wimlib_free(resource_wims[i]);
193 out_free_array:
194         FREE(resource_wims);
195         return ret;
196 }
197
198 static int
199 reference_resource_glob(WIMStruct *wim, const tchar *refglob,
200                         int ref_flags, int open_flags,
201                         wimlib_progress_func_t progress_func)
202 {
203         glob_t globbuf;
204         int ret;
205
206         /* Note: glob() is replaced in Windows native builds.  */
207         ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
208         if (ret) {
209                 if (ret == GLOB_NOMATCH) {
210                         if (ref_flags & WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) {
211                                 ERROR("Found no files for glob \"%"TS"\"", refglob);
212                                 return WIMLIB_ERR_GLOB_HAD_NO_MATCHES;
213                         } else {
214                                 return reference_resource_paths(wim,
215                                                                 &refglob,
216                                                                 1,
217                                                                 ref_flags,
218                                                                 open_flags,
219                                                                 progress_func);
220                         }
221                 } else {
222                         ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob);
223                         if (ret == GLOB_NOSPACE)
224                                 return WIMLIB_ERR_NOMEM;
225                         else
226                                 return WIMLIB_ERR_READ;
227                 }
228         }
229
230         ret = reference_resource_paths(wim,
231                                        (const tchar * const *)globbuf.gl_pathv,
232                                        globbuf.gl_pathc,
233                                        ref_flags,
234                                        open_flags,
235                                        progress_func);
236         globfree(&globbuf);
237         return ret;
238 }
239
240 /* API function documented in wimlib.h  */
241 WIMLIBAPI int
242 wimlib_reference_resource_files(WIMStruct *wim,
243                                 const tchar * const * resource_wimfiles_or_globs,
244                                 unsigned count,
245                                 int ref_flags,
246                                 int open_flags,
247                                 wimlib_progress_func_t progress_func)
248 {
249         unsigned i;
250         int ret;
251
252         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
253                 return WIMLIB_ERR_INVALID_PARAM;
254
255         if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE) {
256                 for (i = 0; i < count; i++) {
257                         ret = reference_resource_glob(wim,
258                                                       resource_wimfiles_or_globs[i],
259                                                       ref_flags,
260                                                       open_flags,
261                                                       progress_func);
262                         if (ret)
263                                 return ret;
264                 }
265                 return 0;
266         } else {
267                 return reference_resource_paths(wim, resource_wimfiles_or_globs,
268                                                 count, ref_flags,
269                                                 open_flags, progress_func);
270         }
271 }