wimlib: strict checks for unassigned flags
[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 static int
40 lte_clone_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
41 {
42         struct wim_lookup_table *lookup_table = _lookup_table;
43
44         if (lookup_stream(lookup_table, lte->hash))
45                 return 0;  /*  Resource already present.  */
46
47         lte = clone_lookup_table_entry(lte);
48         if (lte == NULL)
49                 return WIMLIB_ERR_NOMEM;
50         lte->out_refcnt = 1;
51         lookup_table_insert(lookup_table, lte);
52         return 0;
53 }
54
55 static int
56 lte_delete_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
57 {
58         struct wim_lookup_table *lookup_table = _lookup_table;
59
60         if (lte->out_refcnt) {
61                 lookup_table_unlink(lookup_table, lte);
62                 free_lookup_table_entry(lte);
63         }
64         return 0;
65 }
66
67 /* API function documented in wimlib.h  */
68 WIMLIBAPI int
69 wimlib_reference_resources(WIMStruct *wim,
70                            WIMStruct **resource_wims, unsigned num_resource_wims,
71                            int ref_flags)
72 {
73         int ret;
74         unsigned i;
75
76         if (wim == NULL)
77                 return WIMLIB_ERR_INVALID_PARAM;
78
79         if (num_resource_wims != 0 && resource_wims == NULL)
80                 return WIMLIB_ERR_INVALID_PARAM;
81
82         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
83                 return WIMLIB_ERR_INVALID_PARAM;
84
85         for (i = 0; i < num_resource_wims; i++)
86                 if (resource_wims[i] == NULL)
87                         return WIMLIB_ERR_INVALID_PARAM;
88
89         for_lookup_table_entry(wim->lookup_table, lte_zero_out_refcnt, NULL);
90
91         for (i = 0; i < num_resource_wims; i++) {
92                 ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
93                                              lte_clone_if_new,
94                                              wim->lookup_table);
95                 if (ret)
96                         goto out_rollback;
97         }
98         return 0;
99
100 out_rollback:
101         for_lookup_table_entry(wim->lookup_table, lte_delete_if_new,
102                                wim->lookup_table);
103         return ret;
104 }
105
106 static int
107 reference_resource_paths(WIMStruct *wim,
108                          const tchar * const *resource_wimfiles,
109                          unsigned num_resource_wimfiles,
110                          int ref_flags,
111                          int open_flags,
112                          wimlib_progress_func_t progress_func)
113 {
114         WIMStruct **resource_wims;
115         unsigned i;
116         int ret;
117
118         resource_wims = CALLOC(num_resource_wimfiles, sizeof(resource_wims[0]));
119         if (!resource_wims)
120                 return WIMLIB_ERR_NOMEM;
121
122         for (i = 0; i < num_resource_wimfiles; i++) {
123                 DEBUG("Referencing resources from path \"%"TS"\"",
124                       resource_wimfiles[i]);
125                 ret = wimlib_open_wim(resource_wimfiles[i], open_flags,
126                                       &resource_wims[i], progress_func);
127                 if (ret)
128                         goto out_free_resource_wims;
129         }
130
131         ret = wimlib_reference_resources(wim, resource_wims,
132                                          num_resource_wimfiles, ref_flags);
133         if (ret)
134                 goto out_free_resource_wims;
135
136         for (i = 0; i < num_resource_wimfiles; i++)
137                 list_add_tail(&resource_wims[i]->subwim_node, &wim->subwims);
138
139         ret = 0;
140         goto out_free_array;
141
142 out_free_resource_wims:
143         for (i = 0; i < num_resource_wimfiles; i++)
144                 wimlib_free(resource_wims[i]);
145 out_free_array:
146         FREE(resource_wims);
147         return ret;
148 }
149
150 static int
151 reference_resource_glob(WIMStruct *wim, const tchar *refglob,
152                         int ref_flags, int open_flags,
153                         wimlib_progress_func_t progress_func)
154 {
155         glob_t globbuf;
156         int ret;
157
158         /* Note: glob() is replaced in Windows native builds.  */
159         ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
160         if (ret) {
161                 if (ret == GLOB_NOMATCH) {
162                         if (ref_flags & WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) {
163                                 ERROR("Found no files for glob \"%"TS"\"", refglob);
164                                 return WIMLIB_ERR_GLOB_HAD_NO_MATCHES;
165                         } else {
166                                 return reference_resource_paths(wim,
167                                                                 &refglob,
168                                                                 1,
169                                                                 ref_flags,
170                                                                 open_flags,
171                                                                 progress_func);
172                         }
173                 } else {
174                         ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob);
175                         if (ret == GLOB_NOSPACE)
176                                 return WIMLIB_ERR_NOMEM;
177                         else
178                                 return WIMLIB_ERR_READ;
179                 }
180         }
181
182         ret = reference_resource_paths(wim,
183                                        (const tchar * const *)globbuf.gl_pathv,
184                                        globbuf.gl_pathc,
185                                        ref_flags,
186                                        open_flags,
187                                        progress_func);
188         globfree(&globbuf);
189         return ret;
190 }
191
192 /* API function documented in wimlib.h  */
193 WIMLIBAPI int
194 wimlib_reference_resource_files(WIMStruct *wim,
195                                 const tchar * const * resource_wimfiles_or_globs,
196                                 unsigned count,
197                                 int ref_flags,
198                                 int open_flags,
199                                 wimlib_progress_func_t progress_func)
200 {
201         unsigned i;
202         int ret;
203
204         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
205                 return WIMLIB_ERR_INVALID_PARAM;
206
207         if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE) {
208                 for (i = 0; i < count; i++) {
209                         ret = reference_resource_glob(wim,
210                                                       resource_wimfiles_or_globs[i],
211                                                       ref_flags,
212                                                       open_flags,
213                                                       progress_func);
214                         if (ret)
215                                 return ret;
216                 }
217                 return 0;
218         } else {
219                 return reference_resource_paths(wim, resource_wimfiles_or_globs,
220                                                 count, ref_flags,
221                                                 open_flags, progress_func);
222         }
223 }