1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2014 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
26 #include <sys/queue.h>
30 #include <pakfire/file.h>
31 #include <pakfire/filelist.h>
32 #include <pakfire/i18n.h>
33 #include <pakfire/logging.h>
34 #include <pakfire/pakfire.h>
35 #include <pakfire/private.h>
36 #include <pakfire/progressbar.h>
37 #include <pakfire/string.h>
38 #include <pakfire/util.h>
40 struct pakfire_filelist_element
{
41 TAILQ_ENTRY(pakfire_filelist_element
) nodes
;
43 struct pakfire_file
* file
;
46 struct pakfire_filelist
{
47 struct pakfire
* pakfire
;
50 TAILQ_HEAD(entries
, pakfire_filelist_element
) files
;
53 PAKFIRE_EXPORT
int pakfire_filelist_create(struct pakfire_filelist
** list
, struct pakfire
* pakfire
) {
54 struct pakfire_filelist
* l
= calloc(1, sizeof(*l
));
58 l
->pakfire
= pakfire_ref(pakfire
);
62 TAILQ_INIT(&l
->files
);
68 static void pakfire_filelist_free(struct pakfire_filelist
* list
) {
69 pakfire_filelist_clear(list
);
70 pakfire_unref(list
->pakfire
);
74 PAKFIRE_EXPORT
struct pakfire_filelist
* pakfire_filelist_ref(struct pakfire_filelist
* list
) {
80 PAKFIRE_EXPORT
struct pakfire_filelist
* pakfire_filelist_unref(struct pakfire_filelist
* list
) {
81 if (--list
->nrefs
> 0)
84 pakfire_filelist_free(list
);
88 PAKFIRE_EXPORT
size_t pakfire_filelist_length(struct pakfire_filelist
* list
) {
89 struct pakfire_filelist_element
* element
= NULL
;
92 TAILQ_FOREACH(element
, &list
->files
, nodes
)
98 size_t pakfire_filelist_total_size(struct pakfire_filelist
* list
) {
99 struct pakfire_filelist_element
* element
= NULL
;
102 TAILQ_FOREACH(element
, &list
->files
, nodes
)
103 size
+= pakfire_file_get_size(element
->file
);
108 PAKFIRE_EXPORT
int pakfire_filelist_is_empty(struct pakfire_filelist
* list
) {
109 return TAILQ_EMPTY(&list
->files
);
112 PAKFIRE_EXPORT
void pakfire_filelist_clear(struct pakfire_filelist
* list
) {
113 struct pakfire_filelist_element
* element
= NULL
;
115 while (!TAILQ_EMPTY(&list
->files
)) {
116 // Fetch the first element
117 element
= TAILQ_FIRST(&list
->files
);
119 // Remove it from the list
120 TAILQ_REMOVE(&list
->files
, element
, nodes
);
122 // Dereference the file
123 pakfire_file_unref(element
->file
);
130 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_filelist_get(struct pakfire_filelist
* list
, size_t index
) {
131 struct pakfire_filelist_element
* element
= NULL
;
133 // Fetch the first element
134 element
= TAILQ_FIRST(&list
->files
);
136 while (element
&& index
--)
137 element
= TAILQ_NEXT(element
, nodes
);
139 return pakfire_file_ref(element
->file
);
142 PAKFIRE_EXPORT
int pakfire_filelist_add(struct pakfire_filelist
* list
, struct pakfire_file
* file
) {
143 struct pakfire_filelist_element
* element
= NULL
;
144 struct pakfire_filelist_element
* e
= NULL
;
146 // Allocate a new element
147 element
= calloc(1, sizeof *element
);
149 ERROR(list
->pakfire
, "Could not allocate a new filelist element: %m\n");
153 // Reference the file
154 element
->file
= pakfire_file_ref(file
);
156 // Fetch the last element
157 e
= TAILQ_LAST(&list
->files
, entries
);
159 // Skip all elements that are "greater than" the element we want to add
161 if (pakfire_file_cmp(e
->file
, file
) <= 0)
164 e
= TAILQ_PREV(e
, entries
, nodes
);
167 // If we found an element on the list, we will append after it,
168 // otherwise we add right at the start.
170 TAILQ_INSERT_AFTER(&list
->files
, e
, element
, nodes
);
172 TAILQ_INSERT_HEAD(&list
->files
, element
, nodes
);
177 static int pakfire_filelist_remove(struct pakfire_filelist
* list
, struct pakfire_file
* file
) {
178 struct pakfire_filelist_element
* element
= NULL
;
180 TAILQ_FOREACH(element
, &list
->files
, nodes
) {
181 if (element
->file
== file
) {
182 // Remove the element from the list
183 TAILQ_REMOVE(&list
->files
, element
, nodes
);
186 pakfire_file_unref(element
->file
);
196 static int __pakfire_filelist_remove_one(
197 struct pakfire
* pakfire
, struct pakfire_file
* file
, void* data
) {
198 struct pakfire_filelist
* list
= (struct pakfire_filelist
*)data
;
200 // Remove the file from the given filelist
201 return pakfire_filelist_remove(list
, file
);
204 int pakfire_filelist_remove_all(
205 struct pakfire_filelist
* list
, struct pakfire_filelist
* removees
) {
206 return pakfire_filelist_walk(removees
, __pakfire_filelist_remove_one
, list
, 0);
209 // Returns true if s contains globbing characters
210 static int is_glob(const char* s
) {
220 static int pakfire_filelist_match_patterns(const char* path
, const char** patterns
) {
224 for (const char** pattern
= patterns
; *pattern
; pattern
++) {
225 // Match any subdirectories
226 if (pakfire_string_startswith(path
, *pattern
))
229 // Skip fnmatch if the pattern doesn't have any globbing characters
230 if (!is_glob(*pattern
))
237 fnmatch is way too eager for patterns line /usr/lib/lib*.so which will also match
238 things like /usr/lib/python3.x/blah/libblubb.so.
239 To prevent this for absolute file paths, we set the FNM_FILE_NAME flag so that
240 asterisk (*) won't match any slashes (/).
242 if (**pattern
== '/')
243 flags
|= FNM_FILE_NAME
;
246 r
= fnmatch(*pattern
, path
, flags
);
253 else if (r
== FNM_NOMATCH
)
265 int pakfire_filelist_scan(struct pakfire_filelist
* list
, const char* root
,
266 const char** includes
, const char** excludes
) {
267 struct pakfire_file
* file
= NULL
;
268 struct archive_entry
* entry
= NULL
;
271 // Root must be absolute
272 if (!pakfire_path_is_absolute(root
)) {
277 DEBUG(list
->pakfire
, "Scanning %s...\n", root
);
280 DEBUG(list
->pakfire
, " Includes:\n");
282 for (const char** include
= includes
; *include
; include
++)
283 DEBUG(list
->pakfire
, " %s\n", *include
);
287 DEBUG(list
->pakfire
, " Excludes:\n");
289 for (const char** exclude
= excludes
; *exclude
; exclude
++)
290 DEBUG(list
->pakfire
, " %s\n", *exclude
);
293 struct archive
* reader
= pakfire_make_archive_disk_reader(list
->pakfire
, 1);
297 // Allocate a new file entry
298 entry
= archive_entry_new();
306 // Walk through the whole file system tree and find all matching files
307 FTS
* tree
= fts_open(paths
, FTS_NOCHDIR
, 0);
312 const char* path
= NULL
;
314 while ((node
= fts_read(tree
))) {
315 // Ignore any directories in post order
316 if (node
->fts_info
== FTS_DP
)
319 // Compute the relative path
320 path
= pakfire_path_relpath(root
, node
->fts_path
);
325 if (excludes
&& pakfire_filelist_match_patterns(path
, excludes
)) {
326 DEBUG(list
->pakfire
, "Skipping %s...\n", path
);
328 r
= fts_set(tree
, node
, FTS_SKIP
);
335 // Skip what is not included
336 if (includes
&& !pakfire_filelist_match_patterns(path
, includes
)) {
337 DEBUG(list
->pakfire
, "Skipping %s...\n", path
);
339 // We do not mark the whole tree as to skip because some matches might
340 // look for file extensions, etc.
344 DEBUG(list
->pakfire
, "Processing %s...\n", path
);
346 // Reset the file entry
347 entry
= archive_entry_clear(entry
);
350 archive_entry_set_pathname(entry
, path
);
353 archive_entry_copy_sourcepath(entry
, node
->fts_path
);
355 // Read all file attributes from disk
356 r
= archive_read_disk_entry_from_file(reader
, entry
, -1, node
->fts_statp
);
358 ERROR(list
->pakfire
, "Could not read from %s: %m\n", node
->fts_path
);
363 r
= pakfire_file_create_from_archive_entry(&file
, list
->pakfire
, entry
);
367 // Append it to the list
368 r
= pakfire_filelist_add(list
, file
);
370 pakfire_file_unref(file
);
374 pakfire_file_unref(file
);
382 archive_entry_free(entry
);
383 archive_read_free(reader
);
388 int pakfire_filelist_contains(struct pakfire_filelist
* list
, const char* pattern
) {
389 struct pakfire_filelist_element
* element
= NULL
;
396 TAILQ_FOREACH(element
, &list
->files
, nodes
) {
397 const char* path
= pakfire_file_get_path(element
->file
);
401 int r
= fnmatch(pattern
, path
, 0);
409 int pakfire_filelist_walk(struct pakfire_filelist
* list
,
410 pakfire_filelist_walk_callback callback
, void* data
, int flags
) {
411 struct pakfire_filelist_element
* element
= NULL
;
414 // Call the callback once for every element on the list
415 TAILQ_FOREACH(element
, &list
->files
, nodes
) {
417 r
= callback(list
->pakfire
, element
->file
, data
);
425 static int __pakfire_filelist_dump(
426 struct pakfire
* pakfire
, struct pakfire_file
* file
, void* data
) {
427 int* flags
= (int*)data
;
429 char* s
= pakfire_file_dump(file
, *flags
);
431 INFO(pakfire
, "%s\n", s
);
438 int pakfire_filelist_dump(struct pakfire_filelist
* list
, int flags
) {
439 return pakfire_filelist_walk(list
, __pakfire_filelist_dump
, &flags
, 0);
443 Verifies all files on the filelist
445 int pakfire_filelist_verify(struct pakfire_filelist
* list
, struct pakfire_filelist
* errors
) {
446 struct pakfire_filelist_element
* element
= NULL
;
447 struct pakfire_progressbar
* progressbar
= NULL
;
451 const size_t length
= pakfire_filelist_length(list
);
453 DEBUG(list
->pakfire
, "Verifying filelist (%zu file(s))...\n", length
);
456 r
= pakfire_progressbar_create(&progressbar
, NULL
);
461 r
= pakfire_progressbar_add_string(progressbar
, _("Verifying Files..."));
466 r
= pakfire_progressbar_add_bar(progressbar
);
471 r
= pakfire_progressbar_add_percentage(progressbar
);
476 r
= pakfire_progressbar_add_eta(progressbar
);
480 // Start the progressbar
481 r
= pakfire_progressbar_start(progressbar
, length
);
485 // Iterate over the entire list
486 TAILQ_FOREACH(element
, &list
->files
, nodes
) {
488 r
= pakfire_file_verify(element
->file
, &status
);
492 // If the verification failed, we append it to the errors list
494 // Append the file to the error list
495 r
= pakfire_filelist_add(errors
, element
->file
);
500 // Increment progressbar
501 r
= pakfire_progressbar_increment(progressbar
, 1);
507 r
= pakfire_progressbar_finish(progressbar
);
513 pakfire_progressbar_unref(progressbar
);
518 int pakfire_filelist_cleanup(struct pakfire_filelist
* list
) {
519 struct pakfire_filelist_element
* element
= NULL
;
522 // Nothing to do if the filelist is empty
523 if (pakfire_filelist_is_empty(list
))
526 // Walk through the list backwards
527 TAILQ_FOREACH_REVERSE(element
, &list
->files
, entries
, nodes
) {
528 r
= pakfire_file_cleanup(element
->file
);
536 static int __pakfire_filelist_matches_class(struct pakfire
* pakfire
,
537 struct pakfire_file
* file
, void* p
) {
538 int* class = (int*)p
;
540 return pakfire_file_matches_class(file
, *class);
544 Returns true if any file on the list matches class
546 int pakfire_filelist_matches_class(struct pakfire_filelist
* list
, int class) {
547 return pakfire_filelist_walk(list
, __pakfire_filelist_matches_class
, &class, 0);