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