]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/filelist.c
logging: Make the legacy logger configurable
[people/ms/pakfire.git] / src / libpakfire / filelist.c
CommitLineData
221cc3ce
MT
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
d03fa9a3
MT
21#include <errno.h>
22#include <stdlib.h>
d5d7737f 23#include <sys/queue.h>
d03fa9a3 24
3a9677db
MT
25#include <archive.h>
26
517708c8
MT
27// Enable legacy logging
28#define PAKFIRE_LEGACY_LOGGING
29
221cc3ce
MT
30#include <pakfire/file.h>
31#include <pakfire/filelist.h>
bd261c01 32#include <pakfire/i18n.h>
3a9677db 33#include <pakfire/logging.h>
883b3be9 34#include <pakfire/pakfire.h>
9f953e68 35#include <pakfire/private.h>
76c59d9f 36#include <pakfire/progress.h>
d973a13d 37#include <pakfire/string.h>
221cc3ce
MT
38#include <pakfire/util.h>
39
d5d7737f
MT
40struct pakfire_filelist_element {
41 TAILQ_ENTRY(pakfire_filelist_element) nodes;
42
43 struct pakfire_file* file;
44};
45
1bbbfb9e 46struct pakfire_filelist {
88b4d102 47 struct pakfire_ctx* ctx;
ac4c607b 48 struct pakfire* pakfire;
d03fa9a3
MT
49 int nrefs;
50
d5d7737f 51 TAILQ_HEAD(entries, pakfire_filelist_element) files;
78d8a62a
MT
52};
53
ac4c607b 54PAKFIRE_EXPORT int pakfire_filelist_create(struct pakfire_filelist** list, struct pakfire* pakfire) {
1bbbfb9e 55 struct pakfire_filelist* l = calloc(1, sizeof(*l));
d03fa9a3
MT
56 if (!l)
57 return -ENOMEM;
58
88b4d102
MT
59 // Store a reference to the context
60 l->ctx = pakfire_ctx(pakfire);
61
883b3be9 62 l->pakfire = pakfire_ref(pakfire);
d03fa9a3
MT
63 l->nrefs = 1;
64
d5d7737f
MT
65 // Initialise files
66 TAILQ_INIT(&l->files);
67
d03fa9a3
MT
68 *list = l;
69 return 0;
70}
71
1bbbfb9e 72static void pakfire_filelist_free(struct pakfire_filelist* list) {
d03fa9a3 73 pakfire_filelist_clear(list);
883b3be9 74 pakfire_unref(list->pakfire);
88b4d102
MT
75 if (list->ctx)
76 pakfire_ctx_unref(list->ctx);
f0d6233d 77 free(list);
221cc3ce
MT
78}
79
1bbbfb9e 80PAKFIRE_EXPORT struct pakfire_filelist* pakfire_filelist_ref(struct pakfire_filelist* list) {
d03fa9a3
MT
81 list->nrefs++;
82
83 return list;
84}
85
1bbbfb9e 86PAKFIRE_EXPORT struct pakfire_filelist* pakfire_filelist_unref(struct pakfire_filelist* list) {
d03fa9a3
MT
87 if (--list->nrefs > 0)
88 return list;
89
90 pakfire_filelist_free(list);
91 return NULL;
92}
93
94ff3014 94PAKFIRE_EXPORT size_t pakfire_filelist_length(struct pakfire_filelist* list) {
d5d7737f
MT
95 struct pakfire_filelist_element* element = NULL;
96 size_t size = 0;
97
98 TAILQ_FOREACH(element, &list->files, nodes)
99 size++;
100
101 return size;
d03fa9a3
MT
102}
103
c258ca9b 104size_t pakfire_filelist_total_size(struct pakfire_filelist* list) {
d5d7737f 105 struct pakfire_filelist_element* element = NULL;
c258ca9b
MT
106 size_t size = 0;
107
d5d7737f
MT
108 TAILQ_FOREACH(element, &list->files, nodes)
109 size += pakfire_file_get_size(element->file);
c258ca9b
MT
110
111 return size;
112}
113
1bbbfb9e 114PAKFIRE_EXPORT int pakfire_filelist_is_empty(struct pakfire_filelist* list) {
d5d7737f 115 return TAILQ_EMPTY(&list->files);
5e9463ec
MT
116}
117
1bbbfb9e 118PAKFIRE_EXPORT void pakfire_filelist_clear(struct pakfire_filelist* list) {
d5d7737f
MT
119 struct pakfire_filelist_element* element = NULL;
120
121 while (!TAILQ_EMPTY(&list->files)) {
122 // Fetch the first element
123 element = TAILQ_FIRST(&list->files);
221cc3ce 124
d5d7737f
MT
125 // Remove it from the list
126 TAILQ_REMOVE(&list->files, element, nodes);
221cc3ce 127
d5d7737f
MT
128 // Dereference the file
129 pakfire_file_unref(element->file);
221cc3ce 130
d5d7737f
MT
131 // Free it all
132 free(element);
133 }
221cc3ce
MT
134}
135
1bbbfb9e 136PAKFIRE_EXPORT struct pakfire_file* pakfire_filelist_get(struct pakfire_filelist* list, size_t index) {
d5d7737f
MT
137 struct pakfire_filelist_element* element = NULL;
138
139 // Fetch the first element
140 element = TAILQ_FIRST(&list->files);
141
142 while (element && index--)
143 element = TAILQ_NEXT(element, nodes);
d03fa9a3 144
420db400
MT
145 if (!element)
146 return NULL;
147
d5d7737f 148 return pakfire_file_ref(element->file);
221cc3ce
MT
149}
150
2f88682d 151PAKFIRE_EXPORT int pakfire_filelist_add(struct pakfire_filelist* list, struct pakfire_file* file) {
d5d7737f
MT
152 struct pakfire_filelist_element* element = NULL;
153 struct pakfire_filelist_element* e = NULL;
9a6e3e2d 154
d5d7737f
MT
155 // Allocate a new element
156 element = calloc(1, sizeof *element);
157 if (!element) {
158 ERROR(list->pakfire, "Could not allocate a new filelist element: %m\n");
159 return 1;
221cc3ce 160 }
d03fa9a3 161
d5d7737f
MT
162 // Reference the file
163 element->file = pakfire_file_ref(file);
d03fa9a3 164
d5d7737f
MT
165 // Fetch the last element
166 e = TAILQ_LAST(&list->files, entries);
5e9463ec 167
d5d7737f
MT
168 // Skip all elements that are "greater than" the element we want to add
169 while (e) {
f0bd0a4b 170 if (pakfire_file_cmp(e->file, file) <= 0)
d5d7737f
MT
171 break;
172
173 e = TAILQ_PREV(e, entries, nodes);
174 }
2b04f43c 175
d5d7737f
MT
176 // If we found an element on the list, we will append after it,
177 // otherwise we add right at the start.
178 if (e)
179 TAILQ_INSERT_AFTER(&list->files, e, element, nodes);
180 else
181 TAILQ_INSERT_HEAD(&list->files, element, nodes);
182
183 return 0;
2b04f43c
MT
184}
185
79fdf091
MT
186static int pakfire_filelist_remove(struct pakfire_filelist* list, struct pakfire_file* file) {
187 struct pakfire_filelist_element* element = NULL;
188
189 TAILQ_FOREACH(element, &list->files, nodes) {
190 if (element->file == file) {
191 // Remove the element from the list
192 TAILQ_REMOVE(&list->files, element, nodes);
193
194 // Free the element
195 pakfire_file_unref(element->file);
196 free(element);
197
198 return 0;
199 }
200 }
201
202 return 0;
203}
204
205static int __pakfire_filelist_remove_one(
206 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
207 struct pakfire_filelist* list = (struct pakfire_filelist*)data;
208
209 // Remove the file from the given filelist
210 return pakfire_filelist_remove(list, file);
211}
212
213int pakfire_filelist_remove_all(
214 struct pakfire_filelist* list, struct pakfire_filelist* removees) {
a3f3c077 215 return pakfire_filelist_walk(removees, __pakfire_filelist_remove_one, list, 0);
5e9463ec
MT
216}
217
677f2a67
MT
218static int pakfire_filelist_match_patterns(const char* path,
219 const char** patterns, const int flags) {
220 char buffer[PATH_MAX];
221 int r;
f0456138 222
677f2a67
MT
223 // Check inputs
224 if (!path || !patterns)
225 return -EINVAL;
226
227 for (const char** pattern = patterns; *pattern; pattern++) {
228 // Try to find a simple match
229 if (pakfire_path_match(*pattern, path))
230 return 1;
231
232 // If requested perform extended matching
233 if (flags & PAKFIRE_FILELIST_EXTENDED_MATCHING) {
234 // / matches everything
235 if (strcmp(*pattern, "/") == 0)
236 return 1;
237
238 // Try to match any sub-directories and files
239 r = pakfire_string_format(buffer, "%s/**", *pattern);
240 if (r)
241 return 1;
242
243 if (pakfire_path_match(buffer, path))
244 return 1;
245 }
246 }
f0456138 247
677f2a67 248 // No match
f0456138
MT
249 return 0;
250}
251
66450e63
MT
252struct pakfire_filelist_matches {
253 const char* root;
254 const char** includes;
255 const char** excludes;
677f2a67 256 const int flags;
66450e63 257};
70aabbfd 258
66450e63
MT
259static int pakfire_filelist_scan_filter(struct archive* archive, void* p,
260 struct archive_entry* entry) {
261 const struct pakfire_filelist_matches* matches = p;
f0456138 262
66450e63
MT
263 // Descend if possible
264 if (archive_read_disk_can_descend(archive))
265 archive_read_disk_descend(archive);
f0456138 266
66450e63
MT
267 // Fetch the path
268 const char* path = archive_entry_pathname(entry);
04fbda35 269
66450e63
MT
270 // Make the path relative to the root
271 path = pakfire_path_relpath(matches->root, path);
04fbda35 272
66450e63
MT
273 // Skip the root
274 if (!path || !*path)
275 return 0;
70aabbfd 276
6f1c578b
MT
277 // Should we skip any directories?
278 if (matches->flags & PAKFIRE_FILELIST_NO_DIRECTORIES) {
279 mode_t filetype = archive_entry_filetype(entry);
280
281 if (filetype == S_IFDIR)
282 return 0;
283 }
284
66450e63 285 // Skip excludes
677f2a67
MT
286 if (matches->excludes &&
287 pakfire_filelist_match_patterns(path, matches->excludes, matches->flags))
66450e63 288 return 0;
70aabbfd 289
66450e63 290 // Skip what is not included
677f2a67
MT
291 if (matches->includes &&
292 !pakfire_filelist_match_patterns(path, matches->includes, matches->flags))
66450e63 293 return 0;
70aabbfd 294
677f2a67
MT
295 // Store the new path
296 archive_entry_set_pathname(entry, path);
297
66450e63 298 return 1;
70aabbfd
MT
299}
300
1bbbfb9e 301int pakfire_filelist_scan(struct pakfire_filelist* list, const char* root,
677f2a67 302 const char** includes, const char** excludes, int flags) {
31a1df67 303 struct archive* reader = NULL;
7b57bb4b
MT
304 struct pakfire_file* file = NULL;
305 struct archive_entry* entry = NULL;
66450e63
MT
306 struct pakfire_filelist_matches matches = {
307 .root = root,
308 .includes = includes,
309 .excludes = excludes,
677f2a67 310 .flags = flags,
66450e63 311 };
7b57bb4b
MT
312 int r = 1;
313
86f7f0ce
MT
314 // Root must be absolute
315 if (!pakfire_path_is_absolute(root)) {
316 errno = EINVAL;
317 return 1;
318 }
319
70aabbfd
MT
320 DEBUG(list->pakfire, "Scanning %s...\n", root);
321
48d07bbd
MT
322 if (includes) {
323 DEBUG(list->pakfire, " Includes:\n");
324
325 for (const char** include = includes; *include; include++)
326 DEBUG(list->pakfire, " %s\n", *include);
327 }
328
329 if (excludes) {
330 DEBUG(list->pakfire, " Excludes:\n");
331
332 for (const char** exclude = excludes; *exclude; exclude++)
333 DEBUG(list->pakfire, " %s\n", *exclude);
334 }
335
31a1df67
MT
336 // Check if the path exists
337 if (!pakfire_path_exists(root)) {
338 DEBUG(list->pakfire, "Path to scan (%s) does not exist\n", root);
339 r = 0;
340 goto ERROR;
341 }
342
66450e63 343 // Create a new disk reader
31a1df67 344 reader = pakfire_make_archive_disk_reader(list->pakfire, 1);
3a9677db 345 if (!reader)
7b57bb4b 346 goto ERROR;
3a9677db 347
66450e63
MT
348 // Start reading from here
349 r = archive_read_disk_open(reader, root);
350 if (r) {
351 ERROR(list->pakfire, "Could not open %s: %s\n", root,
352 archive_error_string(reader));
3a9677db 353 goto ERROR;
66450e63 354 }
3a9677db 355
66450e63
MT
356 // Configure filter function
357 r = archive_read_disk_set_metadata_filter_callback(reader,
358 pakfire_filelist_scan_filter, &matches);
359 if (r) {
360 ERROR(list->pakfire, "Could not set filter callback: %s\n",
361 archive_error_string(reader));
362 goto ERROR;
363 }
3a9677db 364
66450e63
MT
365 // Walk through all files
366 for (;;) {
367 r = archive_read_next_header(reader, &entry);
3a9677db 368
66450e63
MT
369 // Handle the return code
370 switch (r) {
371 // Fall through if everything is okay
372 case ARCHIVE_OK:
373 break;
3a9677db 374
66450e63
MT
375 // Return OK when we reached the end of the archive
376 case ARCHIVE_EOF:
377 r = 0;
3a9677db
MT
378 goto ERROR;
379
66450e63
MT
380 // Raise any other errors
381 default:
382 ERROR(list->pakfire, "Could not read next file: %s\n",
383 archive_error_string(reader));
384 goto ERROR;
3a9677db
MT
385 }
386
387 // Create file
7b57bb4b 388 r = pakfire_file_create_from_archive_entry(&file, list->pakfire, entry);
3a9677db
MT
389 if (r)
390 goto ERROR;
391
3a9677db 392 // Append it to the list
2f88682d 393 r = pakfire_filelist_add(list, file);
3a9677db
MT
394 if (r) {
395 pakfire_file_unref(file);
396 goto ERROR;
397 }
06066cb1
MT
398
399 pakfire_file_unref(file);
3a9677db
MT
400 }
401
3a9677db 402ERROR:
31a1df67
MT
403 if (reader)
404 archive_read_free(reader);
3a9677db
MT
405
406 return r;
407}
4140a923 408
1bbbfb9e 409int pakfire_filelist_contains(struct pakfire_filelist* list, const char* pattern) {
d5d7737f
MT
410 struct pakfire_filelist_element* element = NULL;
411
4140a923
MT
412 if (!pattern) {
413 errno = EINVAL;
414 return -1;
415 }
416
d5d7737f
MT
417 TAILQ_FOREACH(element, &list->files, nodes) {
418 const char* path = pakfire_file_get_path(element->file);
4140a923
MT
419 if (!path)
420 return -1;
421
b76073b9 422 if (pakfire_path_match(pattern, path))
4140a923
MT
423 return 1;
424 }
425
426 return 0;
427}
5b0b3dc2 428
c258ca9b 429int pakfire_filelist_walk(struct pakfire_filelist* list,
a3f3c077 430 pakfire_filelist_walk_callback callback, void* data, int flags) {
76c59d9f 431 struct pakfire_progress* progress = NULL;
d5d7737f 432 struct pakfire_filelist_element* element = NULL;
c258ca9b
MT
433 int r = 0;
434
76c59d9f 435 // Show progress when iterating over the filelist
73c6c3da 436 if (flags & PAKFIRE_FILELIST_SHOW_PROGRESS) {
88b4d102 437 r = pakfire_progress_create(&progress, list->ctx,
15d74456 438 PAKFIRE_PROGRESS_SHOW_PERCENTAGE|PAKFIRE_PROGRESS_SHOW_ETA, NULL);
76c59d9f
MT
439 if (r)
440 goto ERROR;
441
442 // Set title
443 r = pakfire_progress_set_title(progress, "%s", _("Processing Files..."));
73c6c3da
MT
444 if (r)
445 goto ERROR;
446
447 const size_t length = pakfire_filelist_length(list);
448
76c59d9f
MT
449 // Start progress
450 r = pakfire_progress_start(progress, length);
451 if (r)
452 goto ERROR;
73c6c3da
MT
453 }
454
c258ca9b 455 // Call the callback once for every element on the list
d5d7737f 456 TAILQ_FOREACH(element, &list->files, nodes) {
76c59d9f
MT
457 // Increment the progress
458 if (progress)
459 pakfire_progress_increment(progress, 1);
73c6c3da 460
c258ca9b 461 // Call the callback
d5d7737f 462 r = callback(list->pakfire, element->file, data);
c258ca9b
MT
463 if (r)
464 break;
465 }
466
73c6c3da 467 // Done!
76c59d9f
MT
468 if (progress) {
469 r = pakfire_progress_finish(progress);
470 if (r)
471 goto ERROR;
472 }
73c6c3da
MT
473
474ERROR:
475 // Finish progress
76c59d9f
MT
476 if (progress)
477 pakfire_progress_unref(progress);
73c6c3da 478
c258ca9b
MT
479 return r;
480}
481
9cfb8bb8 482static int __pakfire_filelist_dump(
f7f44921
MT
483 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
484 int* flags = (int*)data;
9cfb8bb8 485
f7f44921 486 char* s = pakfire_file_dump(file, *flags);
9cfb8bb8 487 if (s) {
f7f44921 488 INFO(pakfire, "%s\n", s);
9cfb8bb8
MT
489 free(s);
490 }
491
492 return 0;
493}
494
f7f44921 495int pakfire_filelist_dump(struct pakfire_filelist* list, int flags) {
a3f3c077 496 return pakfire_filelist_walk(list, __pakfire_filelist_dump, &flags, 0);
9cfb8bb8
MT
497}
498
bd261c01
MT
499/*
500 Verifies all files on the filelist
501*/
502int pakfire_filelist_verify(struct pakfire_filelist* list, struct pakfire_filelist* errors) {
d5d7737f 503 struct pakfire_filelist_element* element = NULL;
76c59d9f 504 struct pakfire_progress* progress = NULL;
c0b051bb 505 int status;
bd261c01
MT
506 int r;
507
94ff3014 508 const size_t length = pakfire_filelist_length(list);
d5d7737f
MT
509
510 DEBUG(list->pakfire, "Verifying filelist (%zu file(s))...\n", length);
bd261c01 511
76c59d9f 512 // Setup progress
88b4d102 513 r = pakfire_progress_create(&progress, list->ctx,
15d74456 514 PAKFIRE_PROGRESS_SHOW_PERCENTAGE|PAKFIRE_PROGRESS_SHOW_ETA, NULL);
bd261c01
MT
515 if (r)
516 goto ERROR;
517
76c59d9f
MT
518 // Add title
519 r = pakfire_progress_set_title(progress, "%s", _("Verifying Files..."));
bd261c01
MT
520 if (r)
521 goto ERROR;
522
76c59d9f
MT
523 // Start the progress indicator
524 r = pakfire_progress_start(progress, length);
bd261c01
MT
525 if (r)
526 goto ERROR;
527
528 // Iterate over the entire list
d5d7737f 529 TAILQ_FOREACH(element, &list->files, nodes) {
bd261c01 530 // Verify the file
d5d7737f 531 r = pakfire_file_verify(element->file, &status);
c0b051bb
MT
532 if (r)
533 goto ERROR;
534
535 // If the verification failed, we append it to the errors list
536 if (status) {
bd261c01 537 // Append the file to the error list
2f88682d 538 r = pakfire_filelist_add(errors, element->file);
bd261c01
MT
539 if (r)
540 goto ERROR;
541 }
542
76c59d9f
MT
543 // Increment progress
544 r = pakfire_progress_increment(progress, 1);
bd261c01
MT
545 if (r)
546 goto ERROR;
547 }
548
549 // Finish
76c59d9f 550 r = pakfire_progress_finish(progress);
bd261c01
MT
551 if (r)
552 goto ERROR;
553
554ERROR:
76c59d9f
MT
555 if (progress)
556 pakfire_progress_unref(progress);
bd261c01
MT
557
558 return r;
559}
2a838122 560
9274236e 561int pakfire_filelist_cleanup(struct pakfire_filelist* list, int flags) {
d5d7737f 562 struct pakfire_filelist_element* element = NULL;
2a838122
MT
563 int r;
564
90a9a801
MT
565 // Nothing to do if the filelist is empty
566 if (pakfire_filelist_is_empty(list))
567 return 0;
568
2a838122 569 // Walk through the list backwards
d5d7737f 570 TAILQ_FOREACH_REVERSE(element, &list->files, entries, nodes) {
9274236e 571 r = pakfire_file_cleanup(element->file, flags);
2a838122
MT
572 if (r)
573 return r;
574 }
575
576 return 0;
577}
34dd7fbf
MT
578
579static int __pakfire_filelist_matches_class(struct pakfire* pakfire,
580 struct pakfire_file* file, void* p) {
581 int* class = (int*)p;
582
583 return pakfire_file_matches_class(file, *class);
584}
585
586/*
587 Returns true if any file on the list matches class
588*/
589int pakfire_filelist_matches_class(struct pakfire_filelist* list, int class) {
a3f3c077 590 return pakfire_filelist_walk(list, __pakfire_filelist_matches_class, &class, 0);
34dd7fbf 591}