]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/filelist.c
packager: Remove files after they have been packaged
[people/ms/pakfire.git] / src / libpakfire / filelist.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2014 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <errno.h>
22 #include <fnmatch.h>
23 #include <fts.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <archive.h>
28
29 #include <pakfire/file.h>
30 #include <pakfire/filelist.h>
31 #include <pakfire/i18n.h>
32 #include <pakfire/logging.h>
33 #include <pakfire/pakfire.h>
34 #include <pakfire/private.h>
35 #include <pakfire/progressbar.h>
36 #include <pakfire/string.h>
37 #include <pakfire/util.h>
38
39 struct pakfire_filelist {
40 struct pakfire* pakfire;
41 int nrefs;
42
43 struct pakfire_file** elements;
44 size_t elements_size;
45
46 size_t size;
47 };
48
49 static int pakfire_filelist_grow(struct pakfire_filelist* list, size_t size) {
50 struct pakfire_file** elements = reallocarray(list->elements,
51 list->elements_size + size, sizeof(*list->elements));
52 if (!elements)
53 return -errno;
54
55 list->elements = elements;
56 list->elements_size += size;
57
58 return 0;
59 }
60
61 PAKFIRE_EXPORT int pakfire_filelist_create(struct pakfire_filelist** list, struct pakfire* pakfire) {
62 struct pakfire_filelist* l = calloc(1, sizeof(*l));
63 if (!l)
64 return -ENOMEM;
65
66 l->pakfire = pakfire_ref(pakfire);
67 l->nrefs = 1;
68
69 *list = l;
70 return 0;
71 }
72
73 static void pakfire_filelist_free(struct pakfire_filelist* list) {
74 pakfire_filelist_clear(list);
75 pakfire_unref(list->pakfire);
76 free(list);
77 }
78
79 PAKFIRE_EXPORT struct pakfire_filelist* pakfire_filelist_ref(struct pakfire_filelist* list) {
80 list->nrefs++;
81
82 return list;
83 }
84
85 PAKFIRE_EXPORT struct pakfire_filelist* pakfire_filelist_unref(struct pakfire_filelist* list) {
86 if (--list->nrefs > 0)
87 return list;
88
89 pakfire_filelist_free(list);
90 return NULL;
91 }
92
93 PAKFIRE_EXPORT size_t pakfire_filelist_size(struct pakfire_filelist* list) {
94 return list->size;
95 }
96
97 size_t pakfire_filelist_total_size(struct pakfire_filelist* list) {
98 struct pakfire_file* file = NULL;
99 size_t size = 0;
100
101 for (unsigned int i = 0; i < list->size; i++) {
102 file = list->elements[i];
103
104 size += pakfire_file_get_size(file);
105 }
106
107 return size;
108 }
109
110 PAKFIRE_EXPORT int pakfire_filelist_is_empty(struct pakfire_filelist* list) {
111 return list->size == 0;
112 }
113
114 PAKFIRE_EXPORT void pakfire_filelist_clear(struct pakfire_filelist* list) {
115 if (!list->elements)
116 return;
117
118 for (unsigned int i = 0; i < list->size; i++)
119 pakfire_file_unref(list->elements[i]);
120
121 free(list->elements);
122 list->elements = NULL;
123 list->elements_size = 0;
124
125 list->size = 0;
126 }
127
128 PAKFIRE_EXPORT struct pakfire_file* pakfire_filelist_get(struct pakfire_filelist* list, size_t index) {
129 if (index >= list->size)
130 return NULL;
131
132 return pakfire_file_ref(list->elements[index]);
133 }
134
135 PAKFIRE_EXPORT int pakfire_filelist_append(struct pakfire_filelist* list, struct pakfire_file* file) {
136 if (!file)
137 return EINVAL;
138
139 // Check if we have any space left
140 if (list->size >= list->elements_size) {
141 int r = pakfire_filelist_grow(list, 64);
142 if (r)
143 return r;
144 }
145
146 list->elements[list->size++] = pakfire_file_ref(file);
147
148 return 0;
149 }
150
151 static int __sort(const void* ptr1, const void* ptr2) {
152 struct pakfire_file* file1 = (struct pakfire_file*)ptr1;
153 struct pakfire_file* file2 = (struct pakfire_file*)ptr2;
154
155 return pakfire_file_cmp(file1, file2);
156 }
157
158 PAKFIRE_EXPORT void pakfire_filelist_sort(struct pakfire_filelist* list) {
159 // Nothing to do on empty list
160 if (pakfire_filelist_is_empty(list))
161 return;
162
163 qsort(list->elements, list->size, sizeof(*list->elements), __sort);
164 }
165
166 // Returns true if s contains globbing characters
167 static int is_glob(const char* s) {
168 if (strchr(s, '*'))
169 return 1;
170
171 if (strchr(s, '?'))
172 return 1;
173
174 return 0;
175 }
176
177 static int pakfire_filelist_is_included(const char* path, const char** includes) {
178 // If the includes list is empty, everything is included
179 if (!includes)
180 return 1;
181
182 int r;
183
184 for (const char** include = includes; *include; include++) {
185 // "/" includes everything
186 if (strcmp(*include, "/") == 0)
187 return 1;
188
189 // Match any subdirectories
190 if (pakfire_string_startswith(path, *include))
191 return 1;
192
193 // Skip fnmatch if the pattern doesn't have any globbing characters
194 if (!is_glob(*include))
195 continue;
196
197 r = fnmatch(*include, path, 0);
198
199 // Found a match
200 if (r == 0)
201 return 1;
202
203 // No match found
204 else if (r == FNM_NOMATCH)
205 continue;
206
207 // Any other error
208 else
209 return r;
210 }
211
212 // No match
213 return 0;
214 }
215
216 static int pakfire_filelist_is_excluded(const char* path, const char** excludes) {
217 // If the exclude list is empty, nothing can be excluded
218 if (!excludes)
219 return 0;
220
221 for (const char** exclude = excludes; *exclude; exclude++) {
222 if (pakfire_string_startswith(path, *exclude))
223 return 1;
224 }
225
226 // No match
227 return 0;
228 }
229
230 int pakfire_filelist_scan(struct pakfire_filelist* list, const char* root,
231 const char** includes, const char** excludes) {
232 struct pakfire_file* file = NULL;
233 struct archive_entry* entry = NULL;
234 int r = 1;
235
236 DEBUG(list->pakfire, "Scanning %s...\n", root);
237
238 if (includes) {
239 DEBUG(list->pakfire, " Includes:\n");
240
241 for (const char** include = includes; *include; include++)
242 DEBUG(list->pakfire, " %s\n", *include);
243 }
244
245 if (excludes) {
246 DEBUG(list->pakfire, " Excludes:\n");
247
248 for (const char** exclude = excludes; *exclude; exclude++)
249 DEBUG(list->pakfire, " %s\n", *exclude);
250 }
251
252 struct archive* reader = pakfire_make_archive_disk_reader(list->pakfire, 1);
253 if (!reader)
254 return 1;
255
256 // Allocate a new file entry
257 entry = archive_entry_new();
258 if (!entry)
259 goto ERROR;
260
261 char* paths[] = {
262 (char*)root, NULL,
263 };
264
265 // Walk through the whole file system tree and find all matching files
266 FTS* tree = fts_open(paths, FTS_NOCHDIR, 0);
267 if (!tree)
268 goto ERROR;
269
270 FTSENT* node;
271 while ((node = fts_read(tree))) {
272 // Ignore any directories in post order
273 if (node->fts_info == FTS_DP)
274 continue;
275
276 // Compute the relative path
277 const char* path = pakfire_path_relpath(root, node->fts_path);
278 if (!path || !*path)
279 continue;
280
281 // Skip what is not included
282 if (!pakfire_filelist_is_included(path, includes)) {
283 DEBUG(list->pakfire, "Skipping %s...\n", path);
284
285 // We do not mark the whole tree as to skip because some matches might
286 // look for file extensions, etc.
287 continue;
288 }
289
290 // Skip excludes
291 if (pakfire_filelist_is_excluded(path, excludes)) {
292 DEBUG(list->pakfire, "Skipping %s...\n", path);
293
294 r = fts_set(tree, node, FTS_SKIP);
295 if (r)
296 goto ERROR;
297
298 continue;
299 }
300
301 DEBUG(list->pakfire, "Processing %s...\n", path);
302
303 // Reset the file entry
304 entry = archive_entry_clear(entry);
305
306 // Set path
307 archive_entry_set_pathname(entry, path);
308
309 // Set source path
310 archive_entry_copy_sourcepath(entry, node->fts_path);
311
312 // Read all file attributes from disk
313 r = archive_read_disk_entry_from_file(reader, entry, -1, node->fts_statp);
314 if (r) {
315 ERROR(list->pakfire, "Could not read from %s: %m\n", node->fts_path);
316 goto ERROR;
317 }
318
319 // Create file
320 r = pakfire_file_create_from_archive_entry(&file, list->pakfire, entry);
321 if (r)
322 goto ERROR;
323
324 // Append it to the list
325 r = pakfire_filelist_append(list, file);
326 if (r) {
327 pakfire_file_unref(file);
328 goto ERROR;
329 }
330
331 pakfire_file_unref(file);
332 }
333
334 // Success
335 r = 0;
336
337 ERROR:
338 if (entry)
339 archive_entry_free(entry);
340 archive_read_free(reader);
341
342 return r;
343 }
344
345 int pakfire_filelist_contains(struct pakfire_filelist* list, const char* pattern) {
346 if (!pattern) {
347 errno = EINVAL;
348 return -1;
349 }
350
351 for (unsigned int i = 0; i < list->size; i++) {
352 struct pakfire_file* file = list->elements[i];
353
354 const char* path = pakfire_file_get_path(file);
355 if (!path)
356 return -1;
357
358 int r = fnmatch(pattern, path, 0);
359 if (r == 0)
360 return 1;
361 }
362
363 return 0;
364 }
365
366 int pakfire_filelist_export(struct pakfire_filelist* list, FILE* f) {
367 for (unsigned int i = 0; i < list->size; i++) {
368 struct pakfire_file* file = list->elements[i];
369
370 const char* path = pakfire_file_get_path(file);
371 if (!path)
372 return 1;
373
374 int r = fprintf(f, "%s\n", path);
375 if (r < 0)
376 return r;
377 }
378
379 return 0;
380 }
381
382 int pakfire_filelist_walk(struct pakfire_filelist* list,
383 pakfire_filelist_walk_callback callback, void* data) {
384 struct pakfire_file* file = NULL;
385 int r = 0;
386
387 // Call the callback once for every element on the list
388 for (unsigned int i = 0; i < list->size; i++) {
389 file = list->elements[i];
390
391 // Call the callback
392 r = callback(list->pakfire, file, data);
393 if (r)
394 break;
395 }
396
397 return r;
398 }
399
400 /*
401 Verifies all files on the filelist
402 */
403 int pakfire_filelist_verify(struct pakfire_filelist* list, struct pakfire_filelist* errors) {
404 struct pakfire_progressbar* progressbar = NULL;
405 struct pakfire_file* file = NULL;
406 int status;
407 int r;
408
409 DEBUG(list->pakfire, "Verifying filelist (%zu file(s))...\n", list->size);
410
411 // Setup progressbar
412 r = pakfire_progressbar_create(&progressbar, NULL);
413 if (r)
414 goto ERROR;
415
416 // Add status
417 r = pakfire_progressbar_add_string(progressbar, _("Verifying Files..."));
418 if (r)
419 goto ERROR;
420
421 // Add bar
422 r = pakfire_progressbar_add_bar(progressbar);
423 if (r)
424 goto ERROR;
425
426 // Add percentage
427 r = pakfire_progressbar_add_percentage(progressbar);
428 if (r)
429 goto ERROR;
430
431 // Add ETA
432 r = pakfire_progressbar_add_eta(progressbar);
433 if (r)
434 goto ERROR;
435
436 // Start the progressbar
437 r = pakfire_progressbar_start(progressbar, list->size);
438 if (r)
439 goto ERROR;
440
441 // Iterate over the entire list
442 for (unsigned int i = 0; i < list->size; i++) {
443 file = list->elements[i];
444
445 // Verify the file
446 r = pakfire_file_verify(file, &status);
447 if (r)
448 goto ERROR;
449
450 // If the verification failed, we append it to the errors list
451 if (status) {
452 // Append the file to the error list
453 r = pakfire_filelist_append(errors, file);
454 if (r)
455 goto ERROR;
456 }
457
458 // Increment progressbar
459 r = pakfire_progressbar_increment(progressbar, 1);
460 if (r)
461 goto ERROR;
462 }
463
464 // Finish
465 r = pakfire_progressbar_finish(progressbar);
466 if (r)
467 goto ERROR;
468
469 ERROR:
470 if (progressbar)
471 pakfire_progressbar_unref(progressbar);
472
473 return r;
474 }
475
476 int pakfire_filelist_cleanup(struct pakfire_filelist* list) {
477 int r;
478
479 // Walk through the list backwards
480 for (unsigned int i = list->size; i > 0; i--) {
481 r = pakfire_file_cleanup(list->elements[i]);
482 if (r)
483 return r;
484 }
485
486 return 0;
487 }