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