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 #############################################################################*/
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>
39 struct pakfire_filelist
{
40 struct pakfire
* pakfire
;
43 struct pakfire_file
** elements
;
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
));
55 list
->elements
= elements
;
56 list
->elements_size
+= size
;
61 PAKFIRE_EXPORT
int pakfire_filelist_create(struct pakfire_filelist
** list
, struct pakfire
* pakfire
) {
62 struct pakfire_filelist
* l
= calloc(1, sizeof(*l
));
66 l
->pakfire
= pakfire_ref(pakfire
);
73 static void pakfire_filelist_free(struct pakfire_filelist
* list
) {
74 pakfire_filelist_clear(list
);
75 pakfire_unref(list
->pakfire
);
79 PAKFIRE_EXPORT
struct pakfire_filelist
* pakfire_filelist_ref(struct pakfire_filelist
* list
) {
85 PAKFIRE_EXPORT
struct pakfire_filelist
* pakfire_filelist_unref(struct pakfire_filelist
* list
) {
86 if (--list
->nrefs
> 0)
89 pakfire_filelist_free(list
);
93 PAKFIRE_EXPORT
size_t pakfire_filelist_size(struct pakfire_filelist
* list
) {
97 size_t pakfire_filelist_total_size(struct pakfire_filelist
* list
) {
98 struct pakfire_file
* file
= NULL
;
101 for (unsigned int i
= 0; i
< list
->size
; i
++) {
102 file
= list
->elements
[i
];
104 size
+= pakfire_file_get_size(file
);
110 PAKFIRE_EXPORT
int pakfire_filelist_is_empty(struct pakfire_filelist
* list
) {
111 return list
->size
== 0;
114 PAKFIRE_EXPORT
void pakfire_filelist_clear(struct pakfire_filelist
* list
) {
118 for (unsigned int i
= 0; i
< list
->size
; i
++)
119 pakfire_file_unref(list
->elements
[i
]);
121 free(list
->elements
);
122 list
->elements
= NULL
;
123 list
->elements_size
= 0;
128 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_filelist_get(struct pakfire_filelist
* list
, size_t index
) {
129 if (index
>= list
->size
)
132 return pakfire_file_ref(list
->elements
[index
]);
135 PAKFIRE_EXPORT
int pakfire_filelist_append(struct pakfire_filelist
* list
, struct pakfire_file
* file
) {
139 // Check if we have any space left
140 if (list
->size
>= list
->elements_size
) {
141 int r
= pakfire_filelist_grow(list
, 64);
146 list
->elements
[list
->size
++] = pakfire_file_ref(file
);
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
;
155 return pakfire_file_cmp(file1
, file2
);
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
))
163 qsort(list
->elements
, list
->size
, sizeof(*list
->elements
), __sort
);
166 // Returns true if s contains globbing characters
167 static int is_glob(const char* s
) {
177 static int pakfire_filelist_is_included(const char* path
, const char** includes
) {
178 // If the includes list is empty, everything is included
184 for (const char** include
= includes
; *include
; include
++) {
185 // "/" includes everything
186 if (strcmp(*include
, "/") == 0)
189 // Match any subdirectories
190 if (pakfire_string_startswith(path
, *include
))
193 // Skip fnmatch if the pattern doesn't have any globbing characters
194 if (!is_glob(*include
))
197 r
= fnmatch(*include
, path
, 0);
204 else if (r
== FNM_NOMATCH
)
216 static int pakfire_filelist_is_excluded(const char* path
, const char** excludes
) {
217 // If the exclude list is empty, nothing can be excluded
221 for (const char** exclude
= excludes
; *exclude
; exclude
++) {
222 if (pakfire_string_startswith(path
, *exclude
))
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
;
236 DEBUG(list
->pakfire
, "Scanning %s...\n", root
);
239 DEBUG(list
->pakfire
, " Includes:\n");
241 for (const char** include
= includes
; *include
; include
++)
242 DEBUG(list
->pakfire
, " %s\n", *include
);
246 DEBUG(list
->pakfire
, " Excludes:\n");
248 for (const char** exclude
= excludes
; *exclude
; exclude
++)
249 DEBUG(list
->pakfire
, " %s\n", *exclude
);
252 struct archive
* reader
= pakfire_make_archive_disk_reader(list
->pakfire
, 1);
256 // Allocate a new file entry
257 entry
= archive_entry_new();
265 // Walk through the whole file system tree and find all matching files
266 FTS
* tree
= fts_open(paths
, FTS_NOCHDIR
, 0);
271 while ((node
= fts_read(tree
))) {
272 // Ignore any directories in post order
273 if (node
->fts_info
== FTS_DP
)
276 // Compute the relative path
277 const char* path
= pakfire_path_relpath(root
, node
->fts_path
);
281 // Skip what is not included
282 if (!pakfire_filelist_is_included(path
, includes
)) {
283 DEBUG(list
->pakfire
, "Skipping %s...\n", path
);
285 // We do not mark the whole tree as to skip because some matches might
286 // look for file extensions, etc.
291 if (pakfire_filelist_is_excluded(path
, excludes
)) {
292 DEBUG(list
->pakfire
, "Skipping %s...\n", path
);
294 r
= fts_set(tree
, node
, FTS_SKIP
);
301 DEBUG(list
->pakfire
, "Processing %s...\n", path
);
303 // Reset the file entry
304 entry
= archive_entry_clear(entry
);
307 archive_entry_set_pathname(entry
, path
);
310 archive_entry_copy_sourcepath(entry
, node
->fts_path
);
312 // Read all file attributes from disk
313 r
= archive_read_disk_entry_from_file(reader
, entry
, -1, node
->fts_statp
);
315 ERROR(list
->pakfire
, "Could not read from %s: %m\n", node
->fts_path
);
320 r
= pakfire_file_create_from_archive_entry(&file
, list
->pakfire
, entry
);
324 // Append it to the list
325 r
= pakfire_filelist_append(list
, file
);
327 pakfire_file_unref(file
);
331 pakfire_file_unref(file
);
339 archive_entry_free(entry
);
340 archive_read_free(reader
);
345 int pakfire_filelist_contains(struct pakfire_filelist
* list
, const char* pattern
) {
351 for (unsigned int i
= 0; i
< list
->size
; i
++) {
352 struct pakfire_file
* file
= list
->elements
[i
];
354 const char* path
= pakfire_file_get_path(file
);
358 int r
= fnmatch(pattern
, path
, 0);
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
];
370 const char* path
= pakfire_file_get_path(file
);
374 int r
= fprintf(f
, "%s\n", path
);
382 int pakfire_filelist_walk(struct pakfire_filelist
* list
,
383 pakfire_filelist_walk_callback callback
, void* data
) {
384 struct pakfire_file
* file
= NULL
;
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
];
392 r
= callback(list
->pakfire
, file
, data
);
401 Verifies all files on the filelist
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
;
409 DEBUG(list
->pakfire
, "Verifying filelist (%zu file(s))...\n", list
->size
);
412 r
= pakfire_progressbar_create(&progressbar
, NULL
);
417 r
= pakfire_progressbar_add_string(progressbar
, _("Verifying Files..."));
422 r
= pakfire_progressbar_add_bar(progressbar
);
427 r
= pakfire_progressbar_add_percentage(progressbar
);
432 r
= pakfire_progressbar_add_eta(progressbar
);
436 // Start the progressbar
437 r
= pakfire_progressbar_start(progressbar
, list
->size
);
441 // Iterate over the entire list
442 for (unsigned int i
= 0; i
< list
->size
; i
++) {
443 file
= list
->elements
[i
];
446 r
= pakfire_file_verify(file
, &status
);
450 // If the verification failed, we append it to the errors list
452 // Append the file to the error list
453 r
= pakfire_filelist_append(errors
, file
);
458 // Increment progressbar
459 r
= pakfire_progressbar_increment(progressbar
, 1);
465 r
= pakfire_progressbar_finish(progressbar
);
471 pakfire_progressbar_unref(progressbar
);
476 int pakfire_filelist_cleanup(struct pakfire_filelist
* list
) {
479 // Walk through the list backwards
480 for (unsigned int i
= list
->size
; i
> 0; i
--) {
481 r
= pakfire_file_cleanup(list
->elements
[i
]);