]> wimlib.net Git - wimlib/blob - src/reference.c
Use --enable-ssse3-sha1 for x86_64 Windows builds
[wimlib] / src / reference.c
1 /*
2  * reference.c
3  *
4  * Reference resources 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/error.h"
30 #include "wimlib/glob.h"
31 #include "wimlib/lookup_table.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_streams;
40         struct list_head new_subwims;
41         int ref_flags;
42         struct wim_lookup_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_streams);
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 wim_lookup_table_entry *lte;
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_streams)) {
75                 lte = list_first_entry(&info->new_streams,
76                                        struct wim_lookup_table_entry,
77                                        lookup_table_list);
78                 list_del(&lte->lookup_table_list);
79                 lookup_table_unlink(info->dest_wim->lookup_table, lte);
80                 free_lookup_table_entry(lte);
81         }
82 }
83
84 static int
85 commit_or_rollback_reference_info(struct reference_info *info, int ret)
86 {
87         if (unlikely(ret))
88                 rollback_reference_info(info);
89         else
90                 commit_reference_info(info);
91         return ret;
92 }
93
94 static bool
95 need_stream(const struct reference_info *info,
96             const struct wim_lookup_table_entry *lte)
97 {
98         return !lookup_stream(info->dest_wim->lookup_table, lte->hash);
99 }
100
101 static void
102 reference_stream(struct reference_info *info,
103                  struct wim_lookup_table_entry *lte)
104 {
105         lookup_table_insert(info->dest_wim->lookup_table, lte);
106         list_add(&lte->lookup_table_list, &info->new_streams);
107 }
108
109 static void
110 reference_subwim(struct reference_info *info, WIMStruct *subwim)
111 {
112         list_add(&subwim->subwim_node, &info->new_subwims);
113 }
114
115 static int
116 lte_clone_if_new(struct wim_lookup_table_entry *lte, void *_info)
117 {
118         struct reference_info *info = _info;
119
120         if (need_stream(info, lte)) {
121                 lte = clone_lookup_table_entry(lte);
122                 if (unlikely(!lte))
123                         return WIMLIB_ERR_NOMEM;
124                 reference_stream(info, lte);
125         }
126         return 0;
127 }
128
129 /* API function documented in wimlib.h  */
130 WIMLIBAPI int
131 wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims,
132                            unsigned num_resource_wims, int ref_flags)
133 {
134         unsigned i;
135         struct reference_info info;
136         int ret = 0;
137
138         if (wim == NULL)
139                 return WIMLIB_ERR_INVALID_PARAM;
140
141         if (num_resource_wims != 0 && resource_wims == NULL)
142                 return WIMLIB_ERR_INVALID_PARAM;
143
144         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
145                 return WIMLIB_ERR_INVALID_PARAM;
146
147         for (i = 0; i < num_resource_wims; i++)
148                 if (resource_wims[i] == NULL)
149                         return WIMLIB_ERR_INVALID_PARAM;
150
151         init_reference_info(&info, wim, ref_flags);
152
153         for (i = 0; i < num_resource_wims; i++) {
154                 ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
155                                              lte_clone_if_new, &info);
156                 if (ret)
157                         break;
158         }
159
160         return commit_or_rollback_reference_info(&info, ret);
161 }
162
163 static int
164 lte_gift(struct wim_lookup_table_entry *lte, void *_info)
165 {
166         struct reference_info *info = _info;
167
168         lookup_table_unlink(info->src_table, lte);
169         if (need_stream(info, lte))
170                 reference_stream(info, lte);
171         else
172                 free_lookup_table_entry(lte);
173         return 0;
174 }
175
176 static int
177 reference_resource_path(struct reference_info *info, const tchar *path,
178                         int open_flags)
179 {
180         int ret;
181         WIMStruct *src_wim;
182
183         ret = wimlib_open_wim_with_progress(path, open_flags, &src_wim,
184                                             info->dest_wim->progfunc,
185                                             info->dest_wim->progctx);
186         if (ret)
187                 return ret;
188
189         info->src_table = src_wim->lookup_table;
190         for_lookup_table_entry(src_wim->lookup_table, lte_gift, info);
191         reference_subwim(info, src_wim);
192         return 0;
193 }
194
195 static int
196 reference_resource_paths(struct reference_info *info,
197                          const tchar * const *paths, unsigned num_paths,
198                          int open_flags)
199 {
200         for (unsigned i = 0; i < num_paths; i++) {
201                 int ret = reference_resource_path(info, paths[i], open_flags);
202                 if (ret)
203                         return ret;
204         }
205         return 0;
206 }
207
208 static int
209 reference_resource_glob(struct reference_info *info,
210                         const tchar *refglob, int open_flags)
211 {
212         int ret;
213         glob_t globbuf;
214
215         /* Note: glob() is replaced in Windows native builds.  */
216         ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
217         if (unlikely(ret)) {
218                 if (ret == GLOB_NOMATCH) {
219                         if (info->ref_flags &
220                             WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH)
221                         {
222                                 ERROR("Found no files for glob \"%"TS"\"", refglob);
223                                 return WIMLIB_ERR_GLOB_HAD_NO_MATCHES;
224                         }
225                         return reference_resource_path(info,
226                                                        refglob,
227                                                        open_flags);
228                 }
229                 ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob);
230                 if (ret == GLOB_NOSPACE)
231                         return WIMLIB_ERR_NOMEM;
232                 return WIMLIB_ERR_READ;
233         }
234
235         ret = reference_resource_paths(info,
236                                        (const tchar * const *)globbuf.gl_pathv,
237                                        globbuf.gl_pathc,
238                                        open_flags);
239         globfree(&globbuf);
240         return ret;
241 }
242
243 static int
244 reference_resource_globs(struct reference_info *info,
245                          const tchar * const *globs, unsigned num_globs,
246                          int open_flags)
247 {
248         for (unsigned i = 0; i < num_globs; i++) {
249                 int ret = reference_resource_glob(info, globs[i], open_flags);
250                 if (ret)
251                         return ret;
252         }
253         return 0;
254 }
255
256 /* API function documented in wimlib.h  */
257 WIMLIBAPI int
258 wimlib_reference_resource_files(WIMStruct *wim,
259                                 const tchar * const *paths_or_globs,
260                                 unsigned count, int ref_flags, int open_flags)
261 {
262         struct reference_info info;
263         int ret;
264
265         if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC)
266                 return WIMLIB_ERR_INVALID_PARAM;
267
268         init_reference_info(&info, wim, ref_flags);
269
270         if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE)
271                 ret = reference_resource_globs(&info, paths_or_globs, count, open_flags);
272         else
273                 ret = reference_resource_paths(&info, paths_or_globs, count, open_flags);
274
275         return commit_or_rollback_reference_info(&info, ret);
276 }