]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/compress.c
compress: Show how large a package is
[people/ms/pakfire.git] / src / libpakfire / compress.c
CommitLineData
c352b776
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 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
64f977a3 21#include <errno.h>
c352b776 22#include <stdio.h>
99018421 23#include <stdlib.h>
c352b776
MT
24#include <string.h>
25
79824416 26#include <archive.h>
c352b776 27#include <lzma.h>
9476d502 28#include <zstd.h>
c352b776
MT
29
30#include <pakfire/compress.h>
bb7f09cd
MT
31#include <pakfire/file.h>
32#include <pakfire/filelist.h>
79824416
MT
33#include <pakfire/logging.h>
34#include <pakfire/progressbar.h>
35#include <pakfire/util.h>
c352b776
MT
36
37// Read up to N bytes for analyze the magic
38#define MAX_MAGIC_LENGTH 6
39
5cd454df
MT
40// Compression/Decompression buffer size
41#define BUFFER_SIZE 64 * 1024
42
0bed1e1d
MT
43// Settings for XZ
44#define XZ_COMPRESSION_LEVEL 6
45
9476d502
MT
46// Settings for ZSTD
47#define ZSTD_COMPRESSION_LEVEL 7
48
c352b776
MT
49const struct compressor {
50 char magic[MAX_MAGIC_LENGTH];
51 size_t magic_length;
52 FILE* (*open)(FILE* f, const char* mode);
53} compressors[] = {
54 // XZ
55 { { 0xFD, '7', 'z', 'X', 'Z', 0x00 }, 6, pakfire_xzfopen, },
60732b6e
MT
56 // ZSTD
57 { { 0x28, 0xb5, 0x2f, 0xfd }, 4, pakfire_zstdfopen, },
58 // End
c352b776
MT
59 { "", 0, NULL, },
60};
61
c352b776
MT
62// Try to guess the compression
63FILE* pakfire_xfopen(FILE* f, const char* mode) {
64 char buffer[MAX_MAGIC_LENGTH];
65
64f977a3
MT
66 if (!f) {
67 errno = EBADFD;
c352b776 68 return NULL;
64f977a3
MT
69 }
70
71 if (!mode) {
72 errno = EINVAL;
73 return NULL;
74 }
75
76 // This only works for reading files
77 if (*mode != 'r') {
78 errno = ENOTSUP;
79 return NULL;
80 }
c352b776
MT
81
82 fpos_t pos;
83
84 // Store the position
85 int r = fgetpos(f, &pos);
86 if (r < 0)
87 return NULL;
88
89 // Read a couple of bytes
90 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
91
92 // Reset position
93 r = fsetpos(f, &pos);
94 if (r < 0)
95 return NULL;
96
97 // Check if we could read anything
98 if (!bytes_read || bytes_read < sizeof(buffer))
99 return f;
100
101 // Analyze magic
102 for (const struct compressor* c = compressors; c->open; c++) {
103 // Check if we have read enough data
104 if (bytes_read < c->magic_length)
105 continue;
106
107 // Compare the magic value
108 r = memcmp(c->magic, buffer, c->magic_length);
109 if (r)
110 continue;
111
112 // We found a match!
113 return c->open(f, mode);
114 }
115
116 // Nothing seems to match
49492077 117 errno = ENOTSUP;
c352b776
MT
118 return f;
119}
120
5cd454df
MT
121struct xz_cookie {
122 FILE* f;
123 char mode;
124 lzma_stream stream;
125 int done;
126
127 uint8_t buffer[BUFFER_SIZE];
128};
129
c352b776
MT
130static ssize_t xz_read(void* data, char* buffer, size_t size) {
131 struct xz_cookie* cookie = (struct xz_cookie*)data;
132 if (!cookie)
133 return -1;
134
48187ff3
MT
135 // Do not read when mode is "w"
136 if (cookie->mode == 'w')
6923552a 137 return -1;
48187ff3 138
c352b776
MT
139 lzma_action action = LZMA_RUN;
140
141 // Set output to allocated buffer
142 cookie->stream.next_out = (uint8_t *)buffer;
143 cookie->stream.avail_out = size;
144
145 while (1) {
146 // Read something when the input buffer is empty
147 if (cookie->stream.avail_in == 0) {
148 cookie->stream.next_in = cookie->buffer;
149 cookie->stream.avail_in = fread(cookie->buffer,
5cd454df 150 1, sizeof(cookie->buffer), cookie->f);
c352b776
MT
151
152 // Break if the input file could not be read
153 if (ferror(cookie->f))
154 return -1;
155
156 // Finish after we have reached the end of the input file
5e12758c 157 if (feof(cookie->f))
c352b776 158 cookie->done = 1;
c352b776
MT
159 }
160
161 lzma_ret ret = lzma_code(&cookie->stream, action);
162
163 // If the stream has ended, we just send the
164 // remaining output and mark that we are done.
165 if (ret == LZMA_STREAM_END) {
166 cookie->done = 1;
167 return size - cookie->stream.avail_out;
168 }
169
170 // Break on all other unexpected errors
171 if (ret != LZMA_OK)
172 return -1;
173
174 // When we have read enough to fill the entire output buffer, we return
175 if (cookie->stream.avail_out == 0)
176 return size;
177
178 if (cookie->done)
179 return -1;
180 }
181}
182
0bed1e1d
MT
183static ssize_t xz_write(void* data, const char* buffer, size_t size) {
184 struct xz_cookie* cookie = (struct xz_cookie*)data;
185 if (!cookie)
186 return -1;
187
188 // Do not write when mode is "r"
189 if (cookie->mode == 'r')
6923552a 190 return -1;
0bed1e1d
MT
191
192 // Return nothing when there is no input
193 if (size == 0)
194 return 0;
195
196 // Set input to allocated buffer
9d816df3
MT
197 cookie->stream.next_in = (uint8_t *)buffer;
198 cookie->stream.avail_in = size;
0bed1e1d
MT
199
200 while (1) {
201 cookie->stream.next_out = cookie->buffer;
202 cookie->stream.avail_out = sizeof(cookie->buffer);
203
204 lzma_ret ret = lzma_code(&cookie->stream, LZMA_RUN);
205 if (ret != LZMA_OK)
206 return -1;
207
208 size_t bytes_to_write = sizeof(cookie->buffer) - cookie->stream.avail_out;
209 if (bytes_to_write) {
210 size_t bytes_written = fwrite(cookie->buffer, 1, bytes_to_write, cookie->f);
211
212 if (bytes_written != bytes_to_write)
213 return -1;
214 }
215
216 // Report that all data has been written
217 if (cookie->stream.avail_in == 0)
218 return size;
219 }
220}
221
c352b776
MT
222static int xz_close(void* data) {
223 struct xz_cookie* cookie = (struct xz_cookie*)data;
99018421
MT
224 if (!cookie)
225 return -1;
226
227 if (cookie->mode == 'w') {
228 while (1) {
229 cookie->stream.next_out = cookie->buffer;
230 cookie->stream.avail_out = sizeof(cookie->buffer);
231
232 lzma_ret ret = lzma_code(&cookie->stream, LZMA_FINISH);
233 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
234 return -1;
235
236 size_t bytes_to_write = sizeof(cookie->buffer) - cookie->stream.avail_out;
237 if (bytes_to_write) {
238 size_t bytes_written = fwrite(cookie->buffer, 1, bytes_to_write, cookie->f);
239
240 if (bytes_written != bytes_to_write)
241 return -1;
242 }
243
244 if (ret == LZMA_STREAM_END)
245 break;
246 }
247 }
c352b776 248
c352b776
MT
249 lzma_end(&cookie->stream);
250
251 // Close input file
99018421
MT
252 int r = fclose(cookie->f);
253 free(cookie);
c352b776 254
99018421 255 return r;
c352b776
MT
256}
257
99018421
MT
258static cookie_io_functions_t xz_functions = {
259 .read = xz_read,
260 .write = xz_write,
261 .seek = NULL,
262 .close = xz_close,
263};
264
c352b776 265FILE* pakfire_xzfopen(FILE* f, const char* mode) {
99018421
MT
266 lzma_ret ret;
267
48187ff3
MT
268 if (!f) {
269 errno = EBADFD;
270 return NULL;
271 }
272
273 if (!mode) {
274 errno = EINVAL;
275 return NULL;
276 }
277
99018421
MT
278 struct xz_cookie* cookie = calloc(1, sizeof(*cookie));
279 if (!cookie)
280 return NULL;
c352b776 281
99018421
MT
282 cookie->f = f;
283 cookie->mode = *mode;
0bed1e1d 284
99018421 285 switch (cookie->mode) {
0bed1e1d 286 case 'r':
99018421 287 ret = lzma_stream_decoder(&cookie->stream, UINT64_MAX, 0);
0bed1e1d
MT
288 break;
289
290 case 'w':
99018421 291 ret = lzma_easy_encoder(&cookie->stream, XZ_COMPRESSION_LEVEL, LZMA_CHECK_SHA256);
0bed1e1d
MT
292 break;
293
294 default:
295 errno = ENOTSUP;
2d4af759 296 goto ERROR;
0bed1e1d
MT
297 }
298
c352b776 299 if (ret != LZMA_OK)
2d4af759 300 goto ERROR;
c352b776 301
99018421 302 return fopencookie(cookie, mode, xz_functions);
2d4af759
MT
303
304ERROR:
305 free(cookie);
306 return NULL;
c352b776 307}
9476d502
MT
308
309// ZSTD
310
311struct zstd_cookie {
312 FILE* f;
313 char mode;
314 int done;
315
316 // Encoder
317 ZSTD_CStream* cstream;
318 ZSTD_inBuffer in;
319
320 // Decoder
321 ZSTD_DStream* dstream;
322 ZSTD_outBuffer out;
323
324 uint8_t buffer[BUFFER_SIZE];
325};
326
327static ssize_t zstd_read(void* data, char* buffer, size_t size) {
328 struct zstd_cookie* cookie = (struct zstd_cookie*)data;
329 if (!cookie)
330 return -1;
331
332 // Do not read when mode is "w"
333 if (cookie->mode == 'w')
334 return -1;
335
336 if (cookie->done)
337 return 0;
338
339 size_t r = 0;
340
341 // Configure output buffer
342 cookie->out.dst = buffer;
343 cookie->out.pos = 0;
344 cookie->out.size = size;
345
346 while (1) {
347 if (!feof(cookie->f) && (cookie->in.pos == cookie->in.size)) {
348 cookie->in.pos = 0;
349 cookie->in.size = fread(cookie->buffer, 1, sizeof(cookie->buffer), cookie->f);
350 }
351
352 if (r || cookie->in.size)
353 r = ZSTD_decompressStream(cookie->dstream, &cookie->out, &cookie->in);
354
355 if (r == 0 && feof(cookie->f)) {
356 cookie->done = 1;
357 return cookie->out.pos;
358 }
359
360 if (ZSTD_isError(r))
361 return -1;
362
363 // Buffer full
364 if (cookie->out.pos == size)
365 return size;
366 }
367}
368
99a1527a
MT
369static ssize_t zstd_write(void* data, const char* buffer, size_t size) {
370 struct zstd_cookie* cookie = (struct zstd_cookie*)data;
371 if (!cookie)
372 return -1;
373
374 // Do not write when mode is "r"
375 if (cookie->mode == 'r')
376 return -1;
377
378 // Return nothing when there is no input
379 if (size == 0)
380 return 0;
381
382 // Configure input buffer
383 cookie->in.src = buffer;
384 cookie->in.pos = 0;
385 cookie->in.size = size;
386
387 while (1) {
388 cookie->out.pos = 0;
389
390 size_t r = ZSTD_compressStream(cookie->cstream, &cookie->out, &cookie->in);
391 if (ZSTD_isError(r))
392 return -1;
393
394 if (cookie->out.pos > 0) {
395 size_t bytes_written = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f);
396
397 if (bytes_written != cookie->out.pos)
398 return -1;
399 }
400
401 // Return when all input has been written
402 if (cookie->in.pos == size)
403 return size;
404 }
405}
406
9476d502
MT
407static int zstd_close(void* data) {
408 struct zstd_cookie* cookie = (struct zstd_cookie*)data;
409 if (!cookie)
410 return -1;
411
99a1527a
MT
412 if (cookie->mode == 'w') {
413 while (1) {
414 // Reset output buffer
415 cookie->out.pos = 0;
416
417 size_t r = ZSTD_endStream(cookie->cstream, &cookie->out);
418 if (ZSTD_isError(r))
419 return -1;
420
421 if (cookie->out.pos > 0) {
422 size_t bytes_written = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f);
423
424 if (bytes_written != cookie->out.pos)
425 return -1;
426 }
427
428 if (r == 0)
429 break;
430 }
431 }
9476d502
MT
432
433 int r = fclose(cookie->f);
434
435 // Free everything
436 if (cookie->cstream)
437 ZSTD_freeCStream(cookie->cstream);
438 if (cookie->dstream)
439 ZSTD_freeDStream(cookie->dstream);
440 free(cookie);
441
442 return r;
443}
444
445static cookie_io_functions_t zstd_functions = {
446 .read = zstd_read,
99a1527a 447 .write = zstd_write,
9476d502
MT
448 .seek = NULL,
449 .close = zstd_close,
450};
451
452FILE* pakfire_zstdfopen(FILE* f, const char* mode) {
453 if (!f) {
454 errno = EBADFD;
455 return NULL;
456 }
457
458 if (!mode) {
459 errno = EINVAL;
460 return NULL;
461 }
462
463 struct zstd_cookie* cookie = calloc(1, sizeof(*cookie));
464 if (!cookie)
465 return NULL;
466
467 cookie->f = f;
468 cookie->mode = *mode;
469
470 size_t r;
471 switch (cookie->mode) {
472 case 'r':
473 // Allocate stream
474 cookie->dstream = ZSTD_createDStream();
475 if (!cookie->dstream)
476 goto ERROR;
477
478 // Initialize stream
479 r = ZSTD_initDStream(cookie->dstream);
480 if (ZSTD_isError(r))
481 goto ERROR;
482
483 cookie->in.src = cookie->buffer;
484 cookie->in.pos = 0;
485 cookie->in.size = 0;
486 break;
487
488 case 'w':
489 // Allocate stream
490 cookie->cstream = ZSTD_createCStream();
491 if (!cookie->cstream)
492 goto ERROR;
493
494 // Initialize stream
495 r = ZSTD_initCStream(cookie->cstream, ZSTD_COMPRESSION_LEVEL);
496 if (ZSTD_isError(r))
497 goto ERROR;
498
499 cookie->out.dst = cookie->buffer;
500 cookie->out.pos = 0;
501 cookie->out.size = sizeof(cookie->buffer);
502 break;
503
504 default:
505 errno = ENOTSUP;
506 goto ERROR;
507 }
508
509 return fopencookie(cookie, mode, zstd_functions);
510
511ERROR:
512 if (cookie->cstream)
513 ZSTD_freeCStream(cookie->cstream);
514 if (cookie->dstream)
515 ZSTD_freeDStream(cookie->dstream);
516 free(cookie);
517
518 return NULL;
519}
79824416 520
d9302cc3
MT
521/*
522 Helper function to conditionally walk through an archive
523 and perform actions based on the callback.
524*/
525int pakfire_walk(struct pakfire* pakfire, struct archive* archive,
526 pakfire_walk_callback callback, pakfire_walk_filter_callback filter_callback,
527 void* p) {
528 struct archive_entry* entry = NULL;
662ff8e8 529 const char* path = NULL;
d9302cc3
MT
530 int r;
531
532 // Walk through the archive
533 for (;;) {
534 r = archive_read_next_header(archive, &entry);
535
536 // Handle the return code
537 switch (r) {
0e25bebb
MT
538 // Fall through if everything is okay
539 case ARCHIVE_OK:
540 break;
541
d9302cc3
MT
542 // Return OK when we reached the end of the archive
543 case ARCHIVE_EOF:
544 return 0;
545
546 // Raise any other errors
547 default:
548 return r;
549 }
550
662ff8e8
MT
551 path = archive_entry_pathname(entry);
552
553 DEBUG(pakfire, "Walking through %s...\n", path);
554
d9302cc3
MT
555 // Call the filter callback before we call the actual callback
556 if (filter_callback) {
557 r = filter_callback(pakfire, archive, entry, p);
558
559 // Handle the return code
560 switch (r) {
561 case PAKFIRE_WALK_OK:
562 break;
563
564 case PAKFIRE_WALK_DONE:
565 DEBUG(pakfire, "Filter callback sent DONE\n");
566 return 0;
567
568 case PAKFIRE_WALK_SKIP:
569 DEBUG(pakfire, "Filter callback sent SKIP\n");
570 continue;
571
572 // Raise any other errors
573 default:
662ff8e8 574 DEBUG(pakfire, "Filter callback returned an error: %d\n", r);
d9302cc3
MT
575 return r;
576 }
577 }
578
579 // Run callback
580 if (callback) {
581 r = callback(pakfire, archive, entry, p);
582
583 // Handle the return code
584 switch (r) {
585 case PAKFIRE_WALK_OK:
586 break;
587
588 case PAKFIRE_WALK_DONE:
589 DEBUG(pakfire, "Callback sent DONE\n");
590 return 0;
591
592 // Raise any other errors
593 default:
594 return r;
595 }
596 }
597 }
598
599 return 0;
600}
601
79824416
MT
602// Common extraction
603
604struct pakfire_extract {
605 // Reference to Pakfire
606 struct pakfire* pakfire;
607
a3b276d1
MT
608 // Flags
609 int flags;
610
79824416
MT
611 // The archive to extract
612 struct archive* archive;
613
d05af3ba
MT
614 // The filelist of all extracted files
615 struct pakfire_filelist* filelist;
616
79824416
MT
617 // Prepend this prefix
618 char prefix[PATH_MAX];
619
620 // The writer
621 struct archive* writer;
622
623 // The progressbar
624 struct pakfire_progressbar* progressbar;
625};
626
627static int pakfire_extract_progressbar_create(struct pakfire_progressbar** progressbar,
628 const char* message, int flags) {
629 int r;
630
631 // Create the progressbar
632 r = pakfire_progressbar_create(progressbar, NULL);
633 if (r)
634 return r;
635
636 // Add message
637 if (message) {
638 r = pakfire_progressbar_add_string(*progressbar, "%s", message);
639 if (r)
640 return r;
641 }
642
643 // Add bar
644 r = pakfire_progressbar_add_bar(*progressbar);
645 if (r)
646 return r;
647
95a232a7
MT
648 // Add throughput
649 if (flags & PAKFIRE_EXTRACT_SHOW_THROUGHPUT) {
650 r = pakfire_progressbar_add_transfer_speed(*progressbar);
651 if (r)
652 return r;
653 }
654
79824416
MT
655 // Add percentage
656 r = pakfire_progressbar_add_percentage(*progressbar);
657 if (r)
658 return r;
659
660 // Success
661 return 0;
662}
663
664static void pakfire_extract_progress(void* p) {
665 struct pakfire_extract* data = (struct pakfire_extract*)p;
666
667 // Fetch how many bytes have been read
668 const size_t position = archive_filter_bytes(data->archive, -1);
669
670 // Update progressbar
671 pakfire_progressbar_update(data->progressbar, position);
672}
673
14eedd80
MT
674static int __pakfire_extract(struct pakfire* pakfire, struct archive* a,
675 struct archive_entry* entry, void* p) {
bb7f09cd 676 struct pakfire_file* file = NULL;
79824416
MT
677 char buffer[PATH_MAX];
678 int r;
679
14eedd80
MT
680 struct pakfire_extract* data = (struct pakfire_extract*)p;
681
79824416
MT
682 // Fetch path
683 const char* path = archive_entry_pathname(entry);
684
e5503d53
MT
685 // Add entry to filelist (if requested)
686 if (data->filelist) {
687 r = pakfire_file_create_from_archive_entry(&file, pakfire, entry);
688 if (r)
689 goto ERROR;
690
691 // Append the file to the list
692 r = pakfire_filelist_append(data->filelist, file);
693 if (r)
694 goto ERROR;
695 }
696
79824416
MT
697 // Prepend the prefix
698 if (*data->prefix) {
699 // Compose file path
700 r = pakfire_path_join(buffer, data->prefix, path);
56796f84 701 if (r) {
79824416
MT
702 ERROR(pakfire, "Could not compose file path: %m\n");
703 return r;
704 }
705
706 // Set file path
707 archive_entry_set_pathname(entry, buffer);
708
709 // Update hardlink destination
710 const char* link = archive_entry_hardlink(entry);
711 if (link) {
712 r = pakfire_path_join(buffer, data->prefix, link);
56796f84 713 if (r) {
79824416
MT
714 ERROR(pakfire, "Could not compose hardlink path: %m\n");
715 return r;
716 }
717
718 // Set hardlink path
719 archive_entry_set_hardlink(entry, buffer);
720 }
721 }
722
723 // Create file & extract payload
a3b276d1 724 if (data->writer) {
07c1c6e0
MT
725 // Fetch path again since we changed it
726 path = archive_entry_pathname(entry);
727
cf71c992
MT
728 DEBUG(pakfire, "Extracting %s\n", path);
729
a3b276d1
MT
730 r = archive_read_extract2(data->archive, entry, data->writer);
731 switch (r) {
732 case ARCHIVE_OK:
733 r = 0;
734 break;
79824416 735
a3b276d1
MT
736 case ARCHIVE_WARN:
737 ERROR(pakfire, "%s\n", archive_error_string(data->writer));
79824416 738
a3b276d1
MT
739 // Pretend everything has been okay
740 r = 0;
741 break;
79824416 742
a3b276d1
MT
743 case ARCHIVE_FATAL:
744 ERROR(pakfire, "%s\n", archive_error_string(data->writer));
745 r = 1;
746 break;
747 }
79824416
MT
748 }
749
bb7f09cd
MT
750ERROR:
751 if (file)
752 pakfire_file_unref(file);
753
79824416
MT
754 return r;
755}
756
757int pakfire_extract(struct pakfire* pakfire, struct archive* archive,
bb7f09cd
MT
758 size_t size, struct pakfire_filelist* filelist,
759 const char* prefix, const char* message, int flags) {
79824416
MT
760 int r = 1;
761
a623c421
MT
762 // Use an empty string if no prefix set
763 if (!prefix)
764 prefix = "";
765
79824416 766 struct pakfire_extract data = {
d05af3ba
MT
767 .pakfire = pakfire,
768 .archive = archive,
769 .filelist = filelist,
770 .flags = flags,
771 .writer = NULL,
79824416
MT
772 };
773
a3b276d1
MT
774 // Is this a dry run?
775 const int dry_run = flags & PAKFIRE_EXTRACT_DRY_RUN;
776
5880b6ac
MT
777 // Should we show a progress bar?
778 const int no_progress = flags & PAKFIRE_EXTRACT_NO_PROGRESS;
779
79824416 780 // Set prefix (including pakfire path)
77e26129
MT
781 r = pakfire_path(pakfire, data.prefix, "%s", prefix);
782 if (r)
783 goto ERROR;
79824416
MT
784
785 // Allocate writer
a3b276d1
MT
786 if (!dry_run) {
787 data.writer = pakfire_make_archive_disk_writer(pakfire, 1);
788 if (!data.writer) {
789 ERROR(pakfire, "Could not create disk writer: %m\n");
790 r = 1;
791 goto ERROR;
792 }
79824416
MT
793 }
794
795 // Create the progressbar
5880b6ac
MT
796 if (!no_progress) {
797 r = pakfire_extract_progressbar_create(&data.progressbar, message, flags);
798 if (r)
799 goto ERROR;
79824416 800
5880b6ac 801 // Register progress callback
79824416
MT
802 archive_read_extract_set_progress_callback(data.archive,
803 pakfire_extract_progress, &data);
804
5880b6ac 805 // Start progressbar
79824416 806 pakfire_progressbar_start(data.progressbar, size);
5880b6ac 807 }
79824416 808
14eedd80
MT
809 // Walk through the entire archive
810 r = pakfire_walk(pakfire, archive, __pakfire_extract, NULL, &data);
811 if (r)
812 goto ERROR;
79824416
MT
813
814 // Finish the progressbar
815 if (data.progressbar)
816 pakfire_progressbar_finish(data.progressbar);
817
818ERROR:
819 if (data.progressbar)
820 pakfire_progressbar_unref(data.progressbar);
821 if (data.writer)
822 archive_write_free(data.writer);
823
824 return r;
825}
1e0d6cca
MT
826
827// Common compression
828
829struct pakfire_compress {
830 // Reference to Pakfire
831 struct pakfire* pakfire;
832
833 // Flags
834 int flags;
835
836 // The archive to write to
837 struct archive* archive;
838
839 // The filelist of all files to write
840 struct pakfire_filelist* filelist;
841
842 // The progressbar
843 struct pakfire_progressbar* progressbar;
844
845 // Digests to write to the archive
846 int digests;
847};
848
849static int pakfire_compress_progressbar_create(struct pakfire_progressbar** progressbar,
850 const char* message, int flags) {
851 int r;
852
853 // Create the progressbar
854 r = pakfire_progressbar_create(progressbar, NULL);
855 if (r)
856 return r;
857
858 // Add message
859 if (message) {
860 r = pakfire_progressbar_add_string(*progressbar, "%s", message);
861 if (r)
862 return r;
863 }
864
865 // Add bar
866 r = pakfire_progressbar_add_bar(*progressbar);
867 if (r)
868 return r;
869
870 // Add throughput
871 if (flags & PAKFIRE_COMPRESS_SHOW_THROUGHPUT) {
872 r = pakfire_progressbar_add_transfer_speed(*progressbar);
873 if (r)
874 return r;
875 }
876
bd94d753
MT
877 // Show how many bytes have been written
878 r = pakfire_progressbar_add_bytes_transferred(*progressbar);
879 if (r)
880 return r;
881
1e0d6cca
MT
882 // Add percentage
883 r = pakfire_progressbar_add_percentage(*progressbar);
884 if (r)
885 return r;
886
887 // Success
888 return 0;
889}
890
891static int __pakfire_compress(struct pakfire* pakfire, struct pakfire_file* file, void* p) {
892 struct archive_entry* entry = NULL;
893 FILE* f = NULL;
894 int r = 1;
895
896 struct pakfire_compress* data = (struct pakfire_compress*)p;
897
898 // Fetch type
899 const mode_t type = pakfire_file_get_type(file);
900
901 // Fetch the filelist
902 const size_t size = pakfire_file_get_size(file);
903
904 // Generate file metadata into an archive entry
905 entry = pakfire_file_archive_entry(file, data->digests);
906 if (!entry) {
907 r = 1;
908 goto ERROR;
909 }
910
911 // Write the header
912 r = archive_write_header(data->archive, entry);
913 if (r) {
914 ERROR(pakfire, "Error writing file header: %s\n",
915 archive_error_string(data->archive));
916 goto ERROR;
917 }
918
919 // Copy the data of regular files
920 if (type == S_IFREG) {
921 // Open the file
922 f = pakfire_file_open(file);
923 if (!f) {
924 r = 1;
925 goto ERROR;
926 }
927
928 // Copy the payload into the archive
929 r = pakfire_archive_copy_data_from_file(pakfire, data->archive, f);
930 if (r)
931 goto ERROR;
932 }
933
934 // Write trailer
935 r = archive_write_finish_entry(data->archive);
936 if (r)
937 goto ERROR;
938
939 // Update the progressbar
940 if (data->progressbar)
941 pakfire_progressbar_increment(data->progressbar, size);
942
943ERROR:
944 if (f)
945 fclose(f);
946 if (entry)
947 archive_entry_free(entry);
948
949 return r;
950}
951
952int pakfire_compress(struct pakfire* pakfire, struct archive* archive,
953 struct pakfire_filelist* filelist, const char* message, int flags, int digests) {
954 int r = 1;
955
956 struct pakfire_compress data = {
957 .pakfire = pakfire,
958 .archive = archive,
959 .filelist = filelist,
960 .flags = flags,
961 .digests = digests,
962 };
963
964 // Should we show a progress bar?
965 const int no_progress = flags & PAKFIRE_COMPRESS_NO_PROGRESS;
966
967 // Fetch the length of the filelist
968 const size_t size = pakfire_filelist_total_size(filelist);
969
970 // Create the progressbar
971 if (!no_progress) {
972 r = pakfire_compress_progressbar_create(&data.progressbar, message, flags);
973 if (r)
974 goto ERROR;
975
976 // Start progressbar
977 pakfire_progressbar_start(data.progressbar, size);
978 }
979
980 // Walk through the entire filelist
981 r = pakfire_filelist_walk(filelist, __pakfire_compress, &data);
982 if (r)
983 goto ERROR;
984
985 // Finish the progressbar
986 if (data.progressbar)
987 pakfire_progressbar_finish(data.progressbar);
988
989ERROR:
990 if (data.progressbar)
991 pakfire_progressbar_unref(data.progressbar);
992
993 return r;
994}