]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
filelist: Change type from PakfireFilelist to struct pakfire_filelist
[people/ms/pakfire.git] / src / libpakfire / packager.c
CommitLineData
6aeb48e6
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
21#include <errno.h>
1ea1f35b
MT
22#include <fcntl.h>
23#include <linux/limits.h>
6aeb48e6 24#include <stdlib.h>
da08f989
MT
25#include <sys/stat.h>
26#include <sys/types.h>
05cfc2c9 27#include <time.h>
da08f989 28#include <unistd.h>
6aeb48e6 29
1ea1f35b 30#include <archive.h>
da08f989 31#include <archive_entry.h>
1ea1f35b 32
2adc4a4a 33#include <pakfire/archive.h>
436677a3 34#include <pakfire/constants.h>
1ea1f35b 35#include <pakfire/logging.h>
6aeb48e6
MT
36#include <pakfire/package.h>
37#include <pakfire/packager.h>
38#include <pakfire/pakfire.h>
677e9e5b 39#include <pakfire/pwd.h>
6aeb48e6 40#include <pakfire/types.h>
f57ad279 41#include <pakfire/util.h>
6aeb48e6 42
da08f989
MT
43#define BUFFER_SIZE 64 * 1024
44
6aeb48e6
MT
45struct pakfire_packager {
46 Pakfire pakfire;
47 int nrefs;
05cfc2c9 48 time_t time_created;
6aeb48e6 49
31480bee 50 struct pakfire_package* pkg;
bb06d548 51 char filename[PATH_MAX];
1ea1f35b 52
738b3582
MT
53 // Reader
54 struct archive* reader;
55
56 // Payload
1ea1f35b 57 struct archive* payload;
7836e21b 58 FILE* fpayload;
4eab5079
MT
59 struct archive* mtree;
60 FILE* fmtree;
e282863a 61 size_t installsize;
106d2edd
MT
62
63 struct pakfire_scriptlet** scriptlets;
64 unsigned int num_scriptlets;
6aeb48e6
MT
65};
66
265a7974
MT
67static int pakfire_packager_create_mtree(Pakfire pakfire, struct archive** mtree,
68 FILE** f, const char** fields) {
96de0b5d 69 char path[] = PAKFIRE_PRIVATE_DIR "/tmp/.pakfire-mtree.XXXXXX";
265a7974
MT
70 int r;
71
72 // Create an mtree
73 *mtree = archive_write_new();
74 if (!*mtree) {
75 ERROR(pakfire, "archive_write_new() failed\n");
76 goto ERROR;
77 }
78
79 // Use mtree format
80 r = archive_write_set_format_mtree(*mtree);
81 if (r) {
82 ERROR(pakfire, "Could not set format to mtree: %s\n",
83 archive_error_string(*mtree));
84 goto ERROR;
85 }
86
87 // Always compress using Zstd
88 r = archive_write_add_filter_zstd(*mtree);
89 if (r) {
90 ERROR(pakfire, "Could not enable Zstandard compression: %s\n",
91 archive_error_string(*mtree));
92 goto ERROR;
93 }
94
95 // Enable mtree fields
96 if (fields) {
97 for (const char** field = fields; *field; field++) {
98 r = archive_write_set_options(*mtree, *field);
99 if (r) {
100 ERROR(pakfire, "Could not set mtree options %s: %s\n",
101 *field, archive_error_string(*mtree));
102 goto ERROR;
103 }
104 }
105 }
106
107 // Create a new temporary file
108 *f = pakfire_mktemp(path);
109 if (!*f)
110 goto ERROR;
111
112 // Unlink the file straight away
113 unlink(path);
114
115 // Write mtree to file
116 r = archive_write_open_FILE(*mtree, *f);
117 if (r)
118 goto ERROR;
119
120 return 0;
121
122ERROR:
123 if (*mtree) {
124 archive_write_free(*mtree);
125 *mtree = NULL;
126 }
127
128 if (*f) {
129 fclose(*f);
130 *f = NULL;
131 }
132
133 return 1;
134}
135
af14aefb 136static int pakfire_packager_create_payload(struct pakfire_packager* p) {
96de0b5d 137 char path[] = PAKFIRE_PRIVATE_DIR "/tmp/.pakfire-payload.XXXXXX";
7836e21b 138
af14aefb
MT
139 // Do not compress source packages
140 const int compress = !pakfire_package_is_source(p->pkg);
141
1ea1f35b 142 p->payload = archive_write_new();
427fdd80
MT
143 if (!p->payload) {
144 ERROR(p->pakfire, "archive_write_new() failed\n");
145 return 1;
146 }
1ea1f35b
MT
147
148 // Use the PAX format
149 int r = archive_write_set_format_pax(p->payload);
150 if (r) {
151 ERROR(p->pakfire, "Could not set format to PAX: %s\n",
152 archive_error_string(p->payload));
153 return r;
154 }
155
156 // Add filters to compress the payload
157 if (compress) {
158 // Enable Zstd
159 r = archive_write_add_filter_zstd(p->payload);
160 if (r) {
161 ERROR(p->pakfire, "Could not enable Zstandard compression: %s\n",
162 archive_error_string(p->payload));
163 return r;
164 }
165
166 // Set compression level to highest
be69fe38 167 r = archive_write_set_filter_option(p->payload, NULL, "compression-level", "22");
1ea1f35b
MT
168 if (r) {
169 ERROR(p->pakfire, "Could not set Zstandard compression level: %s\n",
170 archive_error_string(p->payload));
171 return r;
172 }
ae8f5905
MT
173
174 // Add feature marker
175 pakfire_package_add_requires(p->pkg, "pakfire(Compress-Zstandard)");
0dd87cbe
MT
176
177 // Do not pad the last block
178 archive_write_set_bytes_in_last_block(p->payload, 1);
1ea1f35b
MT
179 }
180
7836e21b 181 // Create a new temporary file
265a7974 182 p->fpayload = pakfire_mktemp(path);
f57ad279 183 if (!p->fpayload)
1ea1f35b 184 return 1;
1ea1f35b 185
7836e21b 186 // Unlink the file straight away
265a7974 187 unlink(path);
7836e21b 188
7836e21b
MT
189 // Write archive to file
190 r = archive_write_open_FILE(p->payload, p->fpayload);
1ea1f35b
MT
191 if (r)
192 return r;
193
4eab5079
MT
194 static const char* mtree_fields[] = {
195 // Disable the default
196 "!all",
197
198 // Enable standard file fields
199 "device",
200 "gname",
201 "link",
202 "mode",
203 "nlink",
204 "size",
205 "time",
206 "type",
207 "uname",
208
209 // Enable cryptographic checksums
210 "sha512",
211 "sha256",
212
213 NULL,
214 };
215
265a7974
MT
216 // Create an mtree
217 r = pakfire_packager_create_mtree(p->pakfire, &p->mtree, &p->fmtree, mtree_fields);
218 if (r) {
219 ERROR(p->pakfire, "Could not create mtree\n");
4eab5079 220 return r;
265a7974 221 }
4eab5079 222
1ea1f35b
MT
223 return 0;
224}
225
226static void pakfire_packager_free(struct pakfire_packager* packager) {
106d2edd
MT
227 // Scriptlets
228 if (packager->scriptlets) {
229 for (unsigned int i = 0; i < packager->num_scriptlets; i++)
9dd7b1aa 230 pakfire_scriptlet_unref(packager->scriptlets[i]);
106d2edd
MT
231 free(packager->scriptlets);
232 }
233
4eab5079 234 // Payload
1ea1f35b
MT
235 if (packager->payload)
236 archive_write_free(packager->payload);
237
7836e21b
MT
238 if (packager->fpayload)
239 fclose(packager->fpayload);
240
4eab5079
MT
241 // mtree
242 if (packager->mtree)
243 archive_write_free(packager->mtree);
244
245 if (packager->fmtree)
246 fclose(packager->fmtree);
247
738b3582 248 if (packager->reader)
66817062 249 archive_read_free(packager->reader);
738b3582 250
1ea1f35b
MT
251 pakfire_package_unref(packager->pkg);
252 pakfire_unref(packager->pakfire);
51105783 253 free(packager);
1ea1f35b
MT
254}
255
22b8e37b 256int pakfire_packager_create(struct pakfire_packager** packager,
31480bee 257 struct pakfire_package* pkg) {
6aeb48e6
MT
258 struct pakfire_packager* p = calloc(1, sizeof(*p));
259 if (!p)
260 return ENOMEM;
261
e282863a
MT
262 int r = 1;
263
05cfc2c9
MT
264 // Save creation time
265 p->time_created = time(NULL);
266
6aeb48e6
MT
267 // Initialize reference counting
268 p->nrefs = 1;
269
270 // Store a reference to Pakfire
271 p->pakfire = pakfire_package_get_pakfire(pkg);
272
273 // Store a reference to the package
274 p->pkg = pakfire_package_ref(pkg);
275
0682aeb3 276 // Create reader
66817062
MT
277 p->reader = pakfire_make_archive_disk_reader(p->pakfire, 1);
278 if (!p->reader)
1ba1869e 279 goto ERROR;
1ea1f35b 280
0682aeb3 281 // Start payload
af14aefb 282 r = pakfire_packager_create_payload(p);
0682aeb3 283 if (r)
738b3582 284 goto ERROR;
738b3582 285
6aeb48e6
MT
286 *packager = p;
287
288 return 0;
1ba1869e
MT
289
290ERROR:
291 pakfire_packager_free(p);
292
293 return r;
6aeb48e6
MT
294}
295
22b8e37b 296struct pakfire_packager* pakfire_packager_ref(
6aeb48e6
MT
297 struct pakfire_packager* packager) {
298 ++packager->nrefs;
299
300 return packager;
301}
302
22b8e37b 303struct pakfire_packager* pakfire_packager_unref(
6aeb48e6
MT
304 struct pakfire_packager* packager) {
305 if (--packager->nrefs > 0)
306 return packager;
307
308 pakfire_packager_free(packager);
309
310 return NULL;
311}
da08f989 312
22b8e37b 313const char* pakfire_packager_filename(struct pakfire_packager* packager) {
bb06d548
MT
314 if (!*packager->filename) {
315 const char* filename = pakfire_package_get_filename(packager->pkg);
316
317 // Add arch
318 if (pakfire_package_is_source(packager->pkg))
319 pakfire_string_set(packager->filename, filename);
320 else {
321 const char* arch = pakfire_package_get_arch(packager->pkg);
322
323 pakfire_string_format(packager->filename, "%s/%s", arch, filename);
324 }
325 }
326
327 return packager->filename;
96d2c7bc
MT
328}
329
7836e21b
MT
330static int pakfire_packager_copy_data(struct pakfire_packager* packager,
331 struct archive* a, FILE* f) {
332 char buffer[BUFFER_SIZE];
333
265a7974
MT
334 rewind(f);
335
7836e21b
MT
336 while (!feof(f)) {
337 // Read a block from file
338 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
339
340 // Check if any error occured
341 if (ferror(f)) {
b1772bfb 342 ERROR(packager->pakfire, "Error reading from file: %m\n");
7836e21b
MT
343 return 1;
344 }
345
346 // Write the block to the archive
347 ssize_t bytes_written = archive_write_data(a, buffer, bytes_read);
348 if (bytes_written < 0) {
349 ERROR(packager->pakfire, "Error writing data to archive: %s\n",
350 archive_error_string(a));
351 return 1;
352 }
353 }
354
355 return 0;
356}
357
28700a5b 358static struct archive_entry* pakfire_packager_create_file(
95b81a98 359 struct pakfire_packager* packager, const char* filename, size_t size, mode_t mode) {
98e85f1c
MT
360 // Create a new file entry
361 struct archive_entry* entry = archive_entry_new();
362 if (!entry)
28700a5b 363 return NULL;
98e85f1c
MT
364
365 // Set filename
366 archive_entry_set_pathname(entry, filename);
367
368 // This is a regular file
369 archive_entry_set_filetype(entry, AE_IFREG);
95b81a98 370 archive_entry_set_perm(entry, mode);
98e85f1c 371
28700a5b
MT
372 // Set size
373 archive_entry_set_size(entry, size);
374
05cfc2c9
MT
375 // Set ownership
376 archive_entry_set_uname(entry, "root");
377 archive_entry_set_uid(entry, 0);
378 archive_entry_set_gname(entry, "root");
379 archive_entry_set_gid(entry, 0);
380
381 // Set times
382 archive_entry_set_birthtime(entry, packager->time_created, 0);
383 archive_entry_set_ctime(entry, packager->time_created, 0);
384 archive_entry_set_mtime(entry, packager->time_created, 0);
385 archive_entry_set_atime(entry, packager->time_created, 0);
386
28700a5b
MT
387 return entry;
388}
389
390static int pakfire_packager_write_file_from_buffer(struct pakfire_packager* packager,
7e346194 391 struct archive* a, struct archive* mtree, const char* filename, mode_t mode, const char* buffer) {
28700a5b
MT
392 size_t size = strlen(buffer);
393
394 // Create a new file
7e346194
MT
395 struct archive_entry* entry = pakfire_packager_create_file(packager, filename, size, mode);
396 if (!entry) {
397 ERROR(packager->pakfire, "Could not create file '%s'\n", filename);
28700a5b 398 return 1;
7e346194 399 }
98e85f1c
MT
400
401 // This is the end of the header
402 int r = archive_write_header(a, entry);
403 if (r) {
404 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
265a7974 405 goto ERROR;
98e85f1c
MT
406 }
407
408 // Write content
409 r = archive_write_data(a, buffer, strlen(buffer));
410 if (r < 0) {
411 ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a));
265a7974 412 goto ERROR;
98e85f1c
MT
413 }
414
265a7974
MT
415 // Add this file to the mtree
416 if (mtree) {
417 r = archive_write_header(mtree, entry);
418 if (r) {
419 ERROR(packager->pakfire, "Error adding file to mtree: %s\n",
420 archive_error_string(mtree));
421 goto ERROR;
422 }
423 }
424
425 // Success
426 r = 0;
427
428ERROR:
98e85f1c
MT
429 archive_entry_free(entry);
430
265a7974 431 return r;
98e85f1c
MT
432}
433
436677a3
MT
434static int pakfire_packager_write_format(struct pakfire_packager* packager,
435 struct archive* a) {
436 const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n";
437
7e346194
MT
438 DEBUG(packager->pakfire, "Writing package format\n");
439
265a7974 440 int r = pakfire_packager_write_file_from_buffer(packager, a, NULL,
95b81a98 441 PAKFIRE_ARCHIVE_FN_FORMAT, 0444, buffer);
a6dd151e
MT
442 if (r)
443 return r;
444
445 // Add package format marker
446 pakfire_package_add_requires(packager->pkg,
447 "pakfire(PackageFormat-" TO_STRING(PACKAGE_FORMAT) ")");
448
449 return 0;
436677a3
MT
450}
451
98e85f1c 452static char* pakfire_package_make_metadata(struct pakfire_packager* packager) {
31480bee 453 struct pakfire_package* pkg = packager->pkg;
98e85f1c
MT
454
455 char* buffer = NULL;
456 int r;
457
458 // Print version information
459 r = asprintf(&buffer, "# Pakfire %s\n\n", PACKAGE_VERSION);
460 if (r < 0)
461 goto ERROR;
462
463 // Write package information
464 r = asprintf(&buffer, "%s# Package information\npackage\n", buffer);
465 if (r < 0)
466 goto ERROR;
467
468 // Write package name
469 r = asprintf(&buffer, "%s\tname = %s\n", buffer, pakfire_package_get_name(pkg));
470 if (r < 0)
471 goto ERROR;
472
6ed66687
MT
473 // Write package EVR
474 r = asprintf(&buffer, "%s\tevr = %s\n", buffer, pakfire_package_get_evr(pkg));
98e85f1c
MT
475 if (r < 0)
476 goto ERROR;
477
478 // Write package arch
479 r = asprintf(&buffer, "%s\tarch = %s\n", buffer, pakfire_package_get_arch(pkg));
480 if (r < 0)
481 goto ERROR;
482
483 // Write package UUID
484 r = asprintf(&buffer, "%s\tuuid = %s\n", buffer, pakfire_package_get_uuid(pkg));
485 if (r < 0)
486 goto ERROR;
487
488 // Write package groups
489 const char* groups = pakfire_package_get_groups(pkg);
490 if (groups) {
491 r = asprintf(&buffer, "%s\tgroups = %s\n", buffer, groups);
492 if (r < 0)
493 goto ERROR;
494 }
495
496 // Write package maintainer
497 const char* maintainer = pakfire_package_get_maintainer(pkg);
498 if (maintainer) {
499 r = asprintf(&buffer, "%s\tmaintainer = %s\n", buffer, maintainer);
500 if (r < 0)
501 goto ERROR;
502 }
503
504 // Write package url
e3c279d5 505 const char* url = pakfire_package_get_url(pkg);
98e85f1c
MT
506 if (url) {
507 r = asprintf(&buffer, "%s\turl = %s\n", buffer, url);
508 if (r < 0)
509 goto ERROR;
510 }
511
512 // Write package license
513 const char* license = pakfire_package_get_license(pkg);
514 if (license) {
515 r = asprintf(&buffer, "%s\tlicense = %s\n", buffer, license);
516 if (r < 0)
517 goto ERROR;
518 }
519
520 // Write package summary
521 const char* summary = pakfire_package_get_summary(pkg);
522 if (summary) {
523 r = asprintf(&buffer, "%s\tsummary = %s\n", buffer, summary);
524 if (r < 0)
525 goto ERROR;
526 }
527
528 // XXX description
529
530 size_t size = pakfire_package_get_installsize(pkg);
531 r = asprintf(&buffer, "%s\tsize = %zu\n", buffer, size);
532 if (r < 0)
533 goto ERROR;
534
535 // End package block
536 r = asprintf(&buffer, "%send\n\n", buffer);
537 if (r < 0)
538 goto ERROR;
539
540 // Write build information
541 r = asprintf(&buffer, "%s# Build information\nbuild\n", buffer);
542 if (r < 0)
543 goto ERROR;
544
545 // Write build host
546 const char* build_host = pakfire_package_get_build_host(pkg);
547 if (build_host) {
548 r = asprintf(&buffer, "%s\thost = %s\n", buffer, build_host);
549 if (r < 0)
550 goto ERROR;
551 }
552
98e85f1c
MT
553 // Write build id
554 const char* build_id = pakfire_package_get_build_id(pkg);
555 if (build_id) {
556 r = asprintf(&buffer, "%s\tid = %s\n", buffer, build_id);
557 if (r < 0)
558 goto ERROR;
559 }
98e85f1c
MT
560
561 // Write build host
562 time_t build_time = pakfire_package_get_build_time(pkg);
563 if (build_host) {
564 r = asprintf(&buffer, "%s\ttime = %lu\n", buffer, build_time);
565 if (r < 0)
566 goto ERROR;
567 }
568
569 // End build block
570 r = asprintf(&buffer, "%send\n\n", buffer);
571 if (r < 0)
572 goto ERROR;
573
574#if 0
575 // Write distribution information
576 r = asprintf(&buffer, "%s# Distribution information\ndistribution\n", buffer);
577 if (r < 0)
578 goto ERROR;
579
580 // End distribution block
581 r = asprintf(&buffer, "%send\n\n", buffer);
582 if (r < 0)
583 goto ERROR;
584#endif
585
586 // Write dependency information
587 r = asprintf(&buffer, "%s# Dependency information\ndependencies\n", buffer);
588 if (r < 0)
589 goto ERROR;
590
591 const struct dependencies {
592 const char* type;
31480bee 593 char** (*func)(struct pakfire_package* pkg);
98e85f1c
MT
594 } dependencies[] = {
595 { "prerequires", pakfire_package_get_prerequires },
596 { "requires", pakfire_package_get_requires },
597 { "provides", pakfire_package_get_provides },
598 { "conflicts", pakfire_package_get_conflicts },
599 { "obsoletes", pakfire_package_get_obsoletes },
600 { "recommends", pakfire_package_get_recommends },
601 { "suggests", pakfire_package_get_suggests },
602 { "supplements", pakfire_package_get_supplements },
603 { "enhances", pakfire_package_get_enhances },
604 { NULL },
605 };
606
607 for (const struct dependencies* d = dependencies; d->type; d++) {
452d3833 608 char** list = d->func(pkg);
98e85f1c
MT
609 if (!list)
610 continue;
611
98e85f1c
MT
612 // Write header
613 r = asprintf(&buffer, "%s\t%s\n", buffer, d->type);
614 if (r < 0) {
98e85f1c
MT
615 goto ERROR;
616 }
617
452d3833
MT
618 for (char** dep = list; *dep; dep++) {
619 asprintf(&buffer, "%s\t\t%s\n", buffer, *dep);
620 free(*dep);
98e85f1c 621 }
452d3833 622 free(list);
98e85f1c
MT
623
624 // End block
625 r = asprintf(&buffer, "%s\tend\n", buffer);
626 if (r < 0)
627 goto ERROR;
98e85f1c
MT
628 }
629
630 // End dependencies block
631 r = asprintf(&buffer, "%send\n\n", buffer);
632 if (r < 0)
633 goto ERROR;
634
635 // EOF
636 r = asprintf(&buffer, "%s# EOF\n", buffer);
637 if (r < 0)
638 goto ERROR;
639
640 return buffer;
641
642ERROR:
643 if (buffer)
644 free(buffer);
645
646 return NULL;
647}
648
649static int pakfire_packager_write_metadata(struct pakfire_packager* packager,
265a7974 650 struct archive* a, struct archive* mtree) {
98e85f1c
MT
651 // Make metadata
652 char* buffer = pakfire_package_make_metadata(packager);
653 if (!buffer)
654 return 1;
655
656 DEBUG(packager->pakfire, "Generated package metadata:\n%s", buffer);
657
658 // Write buffer
265a7974 659 int r = pakfire_packager_write_file_from_buffer(packager, a, mtree,
95b81a98 660 PAKFIRE_ARCHIVE_FN_METADATA, 0444, buffer);
98e85f1c
MT
661
662 free(buffer);
663
664 return r;
665}
666
4eab5079 667static int pakfire_packager_write_archive(struct pakfire_packager* packager,
265a7974
MT
668 struct archive* a, struct archive* mtree, const char* filename,
669 struct archive** payload, FILE* f) {
2adc4a4a
MT
670 struct stat st;
671
7e346194
MT
672 DEBUG(packager->pakfire, "Writing '%s' to package\n", filename);
673
2adc4a4a 674 // Close the payload
4eab5079
MT
675 if (*payload) {
676 archive_write_free(*payload);
677 *payload = NULL;
2adc4a4a
MT
678 }
679
680 // Reset fd to beginning of the file
4eab5079 681 rewind(f);
2adc4a4a 682
4eab5079 683 int fd = fileno(f);
2adc4a4a
MT
684
685 // Stat the payload file
686 int r = fstat(fd, &st);
687 if (r) {
b1772bfb 688 ERROR(packager->pakfire, "stat() on fd %d failed: %m\n", fd);
2adc4a4a
MT
689 return 1;
690 }
691
28700a5b
MT
692 // Create a new file
693 struct archive_entry* entry = pakfire_packager_create_file(packager,
95b81a98 694 filename, st.st_size, 0444);
2adc4a4a
MT
695 if (!entry)
696 return 1;
697
2adc4a4a
MT
698 // This is the end of the header
699 r = archive_write_header(a, entry);
700 if (r) {
701 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
702 goto ERROR;
703 }
704
705 // Copy data
4eab5079 706 r = pakfire_packager_copy_data(packager, a, f);
7e346194
MT
707 if (r) {
708 const char* error = archive_error_string(a);
709 if (!error)
710 error = strerror(errno);
711
712 ERROR(packager->pakfire, "Could not copy payload: %s\n", error);
2adc4a4a 713 goto ERROR;
7e346194 714 }
2adc4a4a 715
265a7974
MT
716 // Add this file to the mtree
717 if (mtree) {
718 r = archive_write_header(mtree, entry);
719 if (r) {
720 ERROR(packager->pakfire, "Error adding file to mtree: %s\n",
721 archive_error_string(mtree));
722 goto ERROR;
723 }
724
725 r = pakfire_packager_copy_data(packager, mtree, f);
726 if (r) {
727 ERROR(packager->pakfire, "Error copying data to mtree: %s\n",
728 archive_error_string(mtree));
729 goto ERROR;
730 }
731 }
732
2adc4a4a
MT
733 // Success
734 r = 0;
735
736ERROR:
737 archive_entry_free(entry);
738
739 return r;
740}
741
106d2edd
MT
742static int pakfire_packager_write_scriptlet(struct pakfire_packager* packager,
743 struct archive* a, struct archive* mtree, struct pakfire_scriptlet* scriptlet) {
744 char filename[PATH_MAX];
745 size_t size;
746 int r;
747
748 // Fetch type
749 const char* type = pakfire_scriptlet_get_type(scriptlet);
750
751 DEBUG(packager->pakfire, "Writing scriptlet '%s' to package\n", type);
752
753 // Make filename
754 r = pakfire_string_format(filename, "scriptlet/%s", type);
755 if (r < 0)
756 return r;
757
758 // Fetch scriptlet
759 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
760
761 // Write file
95b81a98 762 return pakfire_packager_write_file_from_buffer(packager, a, mtree, filename, 0544, data);
106d2edd
MT
763}
764
436677a3
MT
765/*
766 This function is being called at the end when all data has been added to the package.
767
768 It will create a new archive and write the package to the given file descriptor.
769*/
22b8e37b 770int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) {
265a7974
MT
771 struct archive* mtree = NULL;
772 FILE* fmtree = NULL;
96d2c7bc 773 int r = 1;
436677a3 774
fafe383d 775 // Store total instal size
e282863a 776 pakfire_package_set_installsize(packager->pkg, packager->installsize);
fafe383d 777
436677a3
MT
778 // Open a new archive
779 struct archive* a = archive_write_new();
780 if (!a) {
781 ERROR(packager->pakfire, "archive_write_new() failed\n");
782 goto ERROR;
783 }
784
785 // Use the PAX format
96d2c7bc 786 r = archive_write_set_format_pax(a);
436677a3
MT
787 if (r) {
788 ERROR(packager->pakfire, "Could not set format to PAX: %s\n",
789 archive_error_string(a));
790 goto ERROR;
791 }
792
793 // Write archive to f
794 r = archive_write_open_FILE(a, f);
795 if (r) {
796 ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n",
797 archive_error_string(a));
798 goto ERROR;
799 }
800
265a7974
MT
801 static const char* mtree_fields[] = {
802 // Disable everything
803 "!all",
804
805 // Include the file size
806 "size",
807
808 // Add the checksums
809 "sha512",
810 "sha256",
811
812 NULL,
813 };
814
815 // Create an mtree for the checksums
816 r = pakfire_packager_create_mtree(packager->pakfire, &mtree, &fmtree, mtree_fields);
817 if (r) {
818 ERROR(packager->pakfire, "Could not create mtree\n");
819 goto ERROR;
820 }
821
436677a3
MT
822 // Start with the format file
823 r = pakfire_packager_write_format(packager, a);
265a7974
MT
824 if (r) {
825 ERROR(packager->pakfire, "Could not add format file to archive: %s\n",
826 archive_error_string(a));
436677a3 827 goto ERROR;
265a7974 828 }
436677a3 829
98e85f1c 830 // Write the metadata
265a7974
MT
831 r = pakfire_packager_write_metadata(packager, a, mtree);
832 if (r) {
833 ERROR(packager->pakfire, "Could not add metadata file to archive: %s\n",
834 archive_error_string(a));
98e85f1c 835 goto ERROR;
265a7974 836 }
98e85f1c 837
4eab5079 838 // Write the filelist in mtree format
265a7974 839 r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_FILELIST,
4eab5079 840 &packager->mtree, packager->fmtree);
265a7974
MT
841 if (r) {
842 ERROR(packager->pakfire, "Could not add filelist to archive: %s\n",
843 archive_error_string(a));
4eab5079 844 goto ERROR;
265a7974 845 }
4eab5079 846
2adc4a4a 847 // Write the payload
265a7974 848 r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_PAYLOAD,
4eab5079 849 &packager->payload, packager->fpayload);
265a7974
MT
850 if (r) {
851 ERROR(packager->pakfire, "Could not add payload to archive: %s\n",
852 archive_error_string(a));
2adc4a4a 853 goto ERROR;
265a7974
MT
854 }
855
106d2edd
MT
856 // Write scriptlets
857 for (unsigned int i = 0; i < packager->num_scriptlets; i++) {
858 r = pakfire_packager_write_scriptlet(packager, a, mtree, packager->scriptlets[i]);
859 if (r) {
b1772bfb 860 ERROR(packager->pakfire, "Could not add scriptlet to the archive: %m\n");
106d2edd
MT
861 goto ERROR;
862 }
863 }
864
265a7974
MT
865 // Finish checksums
866 r = archive_write_finish_entry(mtree);
867 if (r) {
868 ERROR(packager->pakfire, "Could not finish mtree: %s\n",
869 archive_error_string(mtree));
870 goto ERROR;
871 }
872
bb06d548 873 // Write checksums
265a7974
MT
874 r = pakfire_packager_write_archive(packager, a, NULL, PAKFIRE_ARCHIVE_FN_CHECKSUMS,
875 &mtree, fmtree);
876 if (r) {
877 ERROR(packager->pakfire, "Could not add checksums to archive: %s\n",
878 archive_error_string(a));
879 goto ERROR;
880 }
2adc4a4a 881
96d2c7bc
MT
882 // Success
883 r = 0;
436677a3
MT
884
885ERROR:
886 if (a)
265a7974
MT
887 archive_write_free(a);
888 if (mtree)
889 archive_write_free(mtree);
890 if (fmtree)
891 fclose(fmtree);
436677a3 892
96d2c7bc 893 return r;
436677a3
MT
894}
895
22b8e37b 896int pakfire_packager_finish_to_directory(struct pakfire_packager* packager,
48c6f2e7 897 const char* target) {
48c6f2e7 898 char path[PATH_MAX];
bb06d548 899 char tmppath[PATH_MAX];
48c6f2e7
MT
900 int r = 1;
901
902 // target cannot be empty
903 if (!target) {
904 errno = EINVAL;
905 return 1;
906 }
907
bb06d548
MT
908 // Get the filename of the package
909 const char* filename = pakfire_packager_filename(packager);
910 if (!filename) {
b1772bfb 911 ERROR(packager->pakfire, "Could not generate filename for package: %m\n");
bb06d548
MT
912 r = 1;
913 goto ERROR;
914 }
915
916 // Make the package path
917 r = pakfire_string_format(path, "%s/%s", target, filename);
918 if (r < 0)
919 goto ERROR;
920
921 // Create the parent directory
922 r = pakfire_mkparentdir(path, 0);
923 if (r)
924 goto ERROR;
925
48c6f2e7 926 // Create a temporary file in the target directory
bb06d548
MT
927 r = pakfire_string_format(tmppath, "%s.XXXXXX", path);
928 if (r < 0)
929 goto ERROR;
48c6f2e7
MT
930
931 // Create a temporary result file
bb06d548 932 FILE* f = pakfire_mktemp(tmppath);
48c6f2e7
MT
933 if (!f)
934 goto ERROR;
935
936 // Write the finished package
937 r = pakfire_packager_finish(packager, f);
bb06d548
MT
938 fclose(f);
939
48c6f2e7 940 if (r) {
b1772bfb 941 ERROR(packager->pakfire, "pakfire_packager_finish() failed: %m\n");
48c6f2e7
MT
942 goto ERROR;
943 }
944
48c6f2e7 945 // Move the temporary file to destination
bb06d548
MT
946 r = rename(tmppath, path);
947 if (r) {
b1772bfb 948 ERROR(packager->pakfire, "Could not move %s to %s: %m\n", tmppath, path);
48c6f2e7 949 goto ERROR;
bb06d548 950 }
48c6f2e7 951
bb06d548 952 INFO(packager->pakfire, "Package written to %s\n", path);
48c6f2e7
MT
953
954 // Success
955 r = 0;
956
957ERROR:
958 // Remove temporary file
bb06d548
MT
959 if (r && *tmppath)
960 unlink(tmppath);
48c6f2e7 961
bb06d548 962 return r;
48c6f2e7
MT
963}
964
22b8e37b 965int pakfire_packager_add(struct pakfire_packager* packager,
809606fe 966 const char* sourcepath, const char* path) {
da08f989 967 FILE* f = NULL;
da08f989
MT
968
969 // Check if path is set
809606fe 970 if (!sourcepath)
da08f989
MT
971 return EINVAL;
972
809606fe
MT
973 // Use basename if path isn't set
974 if (!path) {
975 path = strrchr(sourcepath, '/');
976 if (path)
977 path++;
978 }
979
7836e21b
MT
980 // Payload has already been closed
981 if (!packager->payload)
982 return EINVAL;
983
da08f989
MT
984 // Create a new file entry
985 struct archive_entry* entry = archive_entry_new();
986 if (!entry)
987 return ENOMEM;
988
7e346194
MT
989 DEBUG(packager->pakfire, "Adding '%s' to archive (from %s)\n", path, sourcepath);
990
809606fe
MT
991 // Set the source path
992 archive_entry_copy_sourcepath(entry, sourcepath);
993
da08f989 994 // Set path in archive
809606fe
MT
995 if (path)
996 archive_entry_set_pathname(entry, path);
da08f989 997
738b3582
MT
998 // Read all attributes from file
999 int r = archive_read_disk_entry_from_file(packager->reader, entry, -1, NULL);
1000 if (r) {
b1772bfb 1001 ERROR(packager->pakfire, "Could not read attributes from %s: %m\n", path);
738b3582
MT
1002 goto ERROR;
1003 }
da08f989
MT
1004
1005 // Write the header
1006 r = archive_write_header(packager->payload, entry);
1007 if (r) {
1008 ERROR(packager->pakfire, "Error writing file header: %s\n",
1009 archive_error_string(packager->payload));
1010 goto ERROR;
1011 }
1012
1013 // Copy the data of regular files
1014 if (archive_entry_filetype(entry) == AE_IFREG) {
809606fe 1015 f = fopen(sourcepath, "r");
da08f989 1016 if (!f) {
b1772bfb 1017 ERROR(packager->pakfire, "Could not open %s: %m\n", sourcepath);
da08f989
MT
1018 r = errno;
1019 goto ERROR;
1020 }
1021
7836e21b
MT
1022 r = pakfire_packager_copy_data(packager, packager->payload, f);
1023 if (r)
1024 goto ERROR;
da08f989
MT
1025 }
1026
4eab5079
MT
1027 // Write to mtree
1028 r = archive_write_header(packager->mtree, entry);
1029 if (r) {
1030 ERROR(packager->pakfire, "Adding file to mtree failed: %s\n",
1031 archive_error_string(packager->mtree));
1032 goto ERROR;
1033 }
1034
e282863a
MT
1035 // Increment installsize
1036 packager->installsize += archive_entry_size(entry);
1ba1869e 1037
da08f989
MT
1038 // Successful
1039 r = 0;
1040
1041ERROR:
1042 if (entry)
1043 archive_entry_free(entry);
1044
1045 if (f)
1046 fclose(f);
1047
1048 return r;
1049}
106d2edd
MT
1050
1051int pakfire_packager_add_scriptlet(struct pakfire_packager* packager,
1052 struct pakfire_scriptlet* scriptlet) {
1053 if (!scriptlet) {
1054 errno = EINVAL;
1055 return 1;
1056 }
1057
1058 // Extend array
1059 packager->scriptlets = reallocarray(packager->scriptlets,
1060 packager->num_scriptlets + 1, sizeof(*packager->scriptlets));
1061 if (!packager->scriptlets)
1062 return 1;
1063
1064 // Append scriptlet
1065 packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet);
1066
1067 return 0;
1068}