]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
libpakfire: Make packager private
[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
MT
49
50 PakfirePackage 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) {
69 char path[] = "/tmp/.pakfire-mtree.XXXXXX";
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) {
265a7974 137 char path[] = "/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);
253}
254
22b8e37b 255int pakfire_packager_create(struct pakfire_packager** packager,
6aeb48e6
MT
256 PakfirePackage pkg) {
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)) {
341 ERROR(packager->pakfire, "Error reading from file: %s\n", strerror(errno));
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 }
422 }
423
424 // Success
425 r = 0;
426
427ERROR:
98e85f1c
MT
428 archive_entry_free(entry);
429
265a7974 430 return r;
98e85f1c
MT
431}
432
436677a3
MT
433static int pakfire_packager_write_format(struct pakfire_packager* packager,
434 struct archive* a) {
435 const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n";
436
7e346194
MT
437 DEBUG(packager->pakfire, "Writing package format\n");
438
265a7974 439 int r = pakfire_packager_write_file_from_buffer(packager, a, NULL,
95b81a98 440 PAKFIRE_ARCHIVE_FN_FORMAT, 0444, buffer);
a6dd151e
MT
441 if (r)
442 return r;
443
444 // Add package format marker
445 pakfire_package_add_requires(packager->pkg,
446 "pakfire(PackageFormat-" TO_STRING(PACKAGE_FORMAT) ")");
447
448 return 0;
436677a3
MT
449}
450
98e85f1c
MT
451static char* pakfire_package_make_metadata(struct pakfire_packager* packager) {
452 PakfirePackage pkg = packager->pkg;
453
454 char* buffer = NULL;
455 int r;
456
457 // Print version information
458 r = asprintf(&buffer, "# Pakfire %s\n\n", PACKAGE_VERSION);
459 if (r < 0)
460 goto ERROR;
461
462 // Write package information
463 r = asprintf(&buffer, "%s# Package information\npackage\n", buffer);
464 if (r < 0)
465 goto ERROR;
466
467 // Write package name
468 r = asprintf(&buffer, "%s\tname = %s\n", buffer, pakfire_package_get_name(pkg));
469 if (r < 0)
470 goto ERROR;
471
6ed66687
MT
472 // Write package EVR
473 r = asprintf(&buffer, "%s\tevr = %s\n", buffer, pakfire_package_get_evr(pkg));
98e85f1c
MT
474 if (r < 0)
475 goto ERROR;
476
477 // Write package arch
478 r = asprintf(&buffer, "%s\tarch = %s\n", buffer, pakfire_package_get_arch(pkg));
479 if (r < 0)
480 goto ERROR;
481
482 // Write package UUID
483 r = asprintf(&buffer, "%s\tuuid = %s\n", buffer, pakfire_package_get_uuid(pkg));
484 if (r < 0)
485 goto ERROR;
486
487 // Write package groups
488 const char* groups = pakfire_package_get_groups(pkg);
489 if (groups) {
490 r = asprintf(&buffer, "%s\tgroups = %s\n", buffer, groups);
491 if (r < 0)
492 goto ERROR;
493 }
494
495 // Write package maintainer
496 const char* maintainer = pakfire_package_get_maintainer(pkg);
497 if (maintainer) {
498 r = asprintf(&buffer, "%s\tmaintainer = %s\n", buffer, maintainer);
499 if (r < 0)
500 goto ERROR;
501 }
502
503 // Write package url
e3c279d5 504 const char* url = pakfire_package_get_url(pkg);
98e85f1c
MT
505 if (url) {
506 r = asprintf(&buffer, "%s\turl = %s\n", buffer, url);
507 if (r < 0)
508 goto ERROR;
509 }
510
511 // Write package license
512 const char* license = pakfire_package_get_license(pkg);
513 if (license) {
514 r = asprintf(&buffer, "%s\tlicense = %s\n", buffer, license);
515 if (r < 0)
516 goto ERROR;
517 }
518
519 // Write package summary
520 const char* summary = pakfire_package_get_summary(pkg);
521 if (summary) {
522 r = asprintf(&buffer, "%s\tsummary = %s\n", buffer, summary);
523 if (r < 0)
524 goto ERROR;
525 }
526
527 // XXX description
528
529 size_t size = pakfire_package_get_installsize(pkg);
530 r = asprintf(&buffer, "%s\tsize = %zu\n", buffer, size);
531 if (r < 0)
532 goto ERROR;
533
534 // End package block
535 r = asprintf(&buffer, "%send\n\n", buffer);
536 if (r < 0)
537 goto ERROR;
538
539 // Write build information
540 r = asprintf(&buffer, "%s# Build information\nbuild\n", buffer);
541 if (r < 0)
542 goto ERROR;
543
544 // Write build host
545 const char* build_host = pakfire_package_get_build_host(pkg);
546 if (build_host) {
547 r = asprintf(&buffer, "%s\thost = %s\n", buffer, build_host);
548 if (r < 0)
549 goto ERROR;
550 }
551
552#if 0
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 }
560#endif
561
562 // Write build host
563 time_t build_time = pakfire_package_get_build_time(pkg);
564 if (build_host) {
565 r = asprintf(&buffer, "%s\ttime = %lu\n", buffer, build_time);
566 if (r < 0)
567 goto ERROR;
568 }
569
570 // End build block
571 r = asprintf(&buffer, "%send\n\n", buffer);
572 if (r < 0)
573 goto ERROR;
574
575#if 0
576 // Write distribution information
577 r = asprintf(&buffer, "%s# Distribution information\ndistribution\n", buffer);
578 if (r < 0)
579 goto ERROR;
580
581 // End distribution block
582 r = asprintf(&buffer, "%send\n\n", buffer);
583 if (r < 0)
584 goto ERROR;
585#endif
586
587 // Write dependency information
588 r = asprintf(&buffer, "%s# Dependency information\ndependencies\n", buffer);
589 if (r < 0)
590 goto ERROR;
591
592 const struct dependencies {
593 const char* type;
452d3833 594 char** (*func)(PakfirePackage pkg);
98e85f1c
MT
595 } dependencies[] = {
596 { "prerequires", pakfire_package_get_prerequires },
597 { "requires", pakfire_package_get_requires },
598 { "provides", pakfire_package_get_provides },
599 { "conflicts", pakfire_package_get_conflicts },
600 { "obsoletes", pakfire_package_get_obsoletes },
601 { "recommends", pakfire_package_get_recommends },
602 { "suggests", pakfire_package_get_suggests },
603 { "supplements", pakfire_package_get_supplements },
604 { "enhances", pakfire_package_get_enhances },
605 { NULL },
606 };
607
608 for (const struct dependencies* d = dependencies; d->type; d++) {
452d3833 609 char** list = d->func(pkg);
98e85f1c
MT
610 if (!list)
611 continue;
612
98e85f1c
MT
613 // Write header
614 r = asprintf(&buffer, "%s\t%s\n", buffer, d->type);
615 if (r < 0) {
98e85f1c
MT
616 goto ERROR;
617 }
618
452d3833
MT
619 for (char** dep = list; *dep; dep++) {
620 asprintf(&buffer, "%s\t\t%s\n", buffer, *dep);
621 free(*dep);
98e85f1c 622 }
452d3833 623 free(list);
98e85f1c
MT
624
625 // End block
626 r = asprintf(&buffer, "%s\tend\n", buffer);
627 if (r < 0)
628 goto ERROR;
98e85f1c
MT
629 }
630
631 // End dependencies block
632 r = asprintf(&buffer, "%send\n\n", buffer);
633 if (r < 0)
634 goto ERROR;
635
636 // EOF
637 r = asprintf(&buffer, "%s# EOF\n", buffer);
638 if (r < 0)
639 goto ERROR;
640
641 return buffer;
642
643ERROR:
644 if (buffer)
645 free(buffer);
646
647 return NULL;
648}
649
650static int pakfire_packager_write_metadata(struct pakfire_packager* packager,
265a7974 651 struct archive* a, struct archive* mtree) {
98e85f1c
MT
652 // Make metadata
653 char* buffer = pakfire_package_make_metadata(packager);
654 if (!buffer)
655 return 1;
656
657 DEBUG(packager->pakfire, "Generated package metadata:\n%s", buffer);
658
659 // Write buffer
265a7974 660 int r = pakfire_packager_write_file_from_buffer(packager, a, mtree,
95b81a98 661 PAKFIRE_ARCHIVE_FN_METADATA, 0444, buffer);
98e85f1c
MT
662
663 free(buffer);
664
665 return r;
666}
667
4eab5079 668static int pakfire_packager_write_archive(struct pakfire_packager* packager,
265a7974
MT
669 struct archive* a, struct archive* mtree, const char* filename,
670 struct archive** payload, FILE* f) {
2adc4a4a
MT
671 struct stat st;
672
7e346194
MT
673 DEBUG(packager->pakfire, "Writing '%s' to package\n", filename);
674
2adc4a4a 675 // Close the payload
4eab5079
MT
676 if (*payload) {
677 archive_write_free(*payload);
678 *payload = NULL;
2adc4a4a
MT
679 }
680
681 // Reset fd to beginning of the file
4eab5079 682 rewind(f);
2adc4a4a 683
4eab5079 684 int fd = fileno(f);
2adc4a4a
MT
685
686 // Stat the payload file
687 int r = fstat(fd, &st);
688 if (r) {
689 ERROR(packager->pakfire, "stat() on fd %d failed: %s\n", fd, strerror(errno));
690 return 1;
691 }
692
28700a5b
MT
693 // Create a new file
694 struct archive_entry* entry = pakfire_packager_create_file(packager,
95b81a98 695 filename, st.st_size, 0444);
2adc4a4a
MT
696 if (!entry)
697 return 1;
698
2adc4a4a
MT
699 // This is the end of the header
700 r = archive_write_header(a, entry);
701 if (r) {
702 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
703 goto ERROR;
704 }
705
706 // Copy data
4eab5079 707 r = pakfire_packager_copy_data(packager, a, f);
7e346194
MT
708 if (r) {
709 const char* error = archive_error_string(a);
710 if (!error)
711 error = strerror(errno);
712
713 ERROR(packager->pakfire, "Could not copy payload: %s\n", error);
2adc4a4a 714 goto ERROR;
7e346194 715 }
2adc4a4a 716
265a7974
MT
717 // Add this file to the mtree
718 if (mtree) {
719 r = archive_write_header(mtree, entry);
720 if (r) {
721 ERROR(packager->pakfire, "Error adding file to mtree: %s\n",
722 archive_error_string(mtree));
723 goto ERROR;
724 }
725
726 r = pakfire_packager_copy_data(packager, mtree, f);
727 if (r) {
728 ERROR(packager->pakfire, "Error copying data to mtree: %s\n",
729 archive_error_string(mtree));
730 goto ERROR;
731 }
732 }
733
2adc4a4a
MT
734 // Success
735 r = 0;
736
737ERROR:
738 archive_entry_free(entry);
739
740 return r;
741}
742
106d2edd
MT
743static int pakfire_packager_write_scriptlet(struct pakfire_packager* packager,
744 struct archive* a, struct archive* mtree, struct pakfire_scriptlet* scriptlet) {
745 char filename[PATH_MAX];
746 size_t size;
747 int r;
748
749 // Fetch type
750 const char* type = pakfire_scriptlet_get_type(scriptlet);
751
752 DEBUG(packager->pakfire, "Writing scriptlet '%s' to package\n", type);
753
754 // Make filename
755 r = pakfire_string_format(filename, "scriptlet/%s", type);
756 if (r < 0)
757 return r;
758
759 // Fetch scriptlet
760 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
761
762 // Write file
95b81a98 763 return pakfire_packager_write_file_from_buffer(packager, a, mtree, filename, 0544, data);
106d2edd
MT
764}
765
436677a3
MT
766/*
767 This function is being called at the end when all data has been added to the package.
768
769 It will create a new archive and write the package to the given file descriptor.
770*/
22b8e37b 771int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) {
265a7974
MT
772 struct archive* mtree = NULL;
773 FILE* fmtree = NULL;
96d2c7bc 774 int r = 1;
436677a3 775
fafe383d 776 // Store total instal size
e282863a 777 pakfire_package_set_installsize(packager->pkg, packager->installsize);
fafe383d 778
436677a3
MT
779 // Open a new archive
780 struct archive* a = archive_write_new();
781 if (!a) {
782 ERROR(packager->pakfire, "archive_write_new() failed\n");
783 goto ERROR;
784 }
785
786 // Use the PAX format
96d2c7bc 787 r = archive_write_set_format_pax(a);
436677a3
MT
788 if (r) {
789 ERROR(packager->pakfire, "Could not set format to PAX: %s\n",
790 archive_error_string(a));
791 goto ERROR;
792 }
793
794 // Write archive to f
795 r = archive_write_open_FILE(a, f);
796 if (r) {
797 ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n",
798 archive_error_string(a));
799 goto ERROR;
800 }
801
265a7974
MT
802 static const char* mtree_fields[] = {
803 // Disable everything
804 "!all",
805
806 // Include the file size
807 "size",
808
809 // Add the checksums
810 "sha512",
811 "sha256",
812
813 NULL,
814 };
815
816 // Create an mtree for the checksums
817 r = pakfire_packager_create_mtree(packager->pakfire, &mtree, &fmtree, mtree_fields);
818 if (r) {
819 ERROR(packager->pakfire, "Could not create mtree\n");
820 goto ERROR;
821 }
822
436677a3
MT
823 // Start with the format file
824 r = pakfire_packager_write_format(packager, a);
265a7974
MT
825 if (r) {
826 ERROR(packager->pakfire, "Could not add format file to archive: %s\n",
827 archive_error_string(a));
436677a3 828 goto ERROR;
265a7974 829 }
436677a3 830
98e85f1c 831 // Write the metadata
265a7974
MT
832 r = pakfire_packager_write_metadata(packager, a, mtree);
833 if (r) {
834 ERROR(packager->pakfire, "Could not add metadata file to archive: %s\n",
835 archive_error_string(a));
98e85f1c 836 goto ERROR;
265a7974 837 }
98e85f1c 838
4eab5079 839 // Write the filelist in mtree format
265a7974 840 r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_FILELIST,
4eab5079 841 &packager->mtree, packager->fmtree);
265a7974
MT
842 if (r) {
843 ERROR(packager->pakfire, "Could not add filelist to archive: %s\n",
844 archive_error_string(a));
4eab5079 845 goto ERROR;
265a7974 846 }
4eab5079 847
2adc4a4a 848 // Write the payload
265a7974 849 r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_PAYLOAD,
4eab5079 850 &packager->payload, packager->fpayload);
265a7974
MT
851 if (r) {
852 ERROR(packager->pakfire, "Could not add payload to archive: %s\n",
853 archive_error_string(a));
2adc4a4a 854 goto ERROR;
265a7974
MT
855 }
856
106d2edd
MT
857 // Write scriptlets
858 for (unsigned int i = 0; i < packager->num_scriptlets; i++) {
859 r = pakfire_packager_write_scriptlet(packager, a, mtree, packager->scriptlets[i]);
860 if (r) {
861 ERROR(packager->pakfire, "Could not add scriptlet to the archive: %s\n",
862 strerror(errno));
863 goto ERROR;
864 }
865 }
866
265a7974
MT
867 // Finish checksums
868 r = archive_write_finish_entry(mtree);
869 if (r) {
870 ERROR(packager->pakfire, "Could not finish mtree: %s\n",
871 archive_error_string(mtree));
872 goto ERROR;
873 }
874
bb06d548 875 // Write checksums
265a7974
MT
876 r = pakfire_packager_write_archive(packager, a, NULL, PAKFIRE_ARCHIVE_FN_CHECKSUMS,
877 &mtree, fmtree);
878 if (r) {
879 ERROR(packager->pakfire, "Could not add checksums to archive: %s\n",
880 archive_error_string(a));
881 goto ERROR;
882 }
2adc4a4a 883
96d2c7bc
MT
884 // Success
885 r = 0;
436677a3
MT
886
887ERROR:
888 if (a)
265a7974
MT
889 archive_write_free(a);
890 if (mtree)
891 archive_write_free(mtree);
892 if (fmtree)
893 fclose(fmtree);
436677a3 894
96d2c7bc 895 return r;
436677a3
MT
896}
897
22b8e37b 898int pakfire_packager_finish_to_directory(struct pakfire_packager* packager,
48c6f2e7 899 const char* target) {
48c6f2e7 900 char path[PATH_MAX];
bb06d548 901 char tmppath[PATH_MAX];
48c6f2e7
MT
902 int r = 1;
903
904 // target cannot be empty
905 if (!target) {
906 errno = EINVAL;
907 return 1;
908 }
909
bb06d548
MT
910 // Get the filename of the package
911 const char* filename = pakfire_packager_filename(packager);
912 if (!filename) {
913 ERROR(packager->pakfire, "Could not generate filename for package: %s\n",
914 strerror(errno));
915 r = 1;
916 goto ERROR;
917 }
918
919 // Make the package path
920 r = pakfire_string_format(path, "%s/%s", target, filename);
921 if (r < 0)
922 goto ERROR;
923
924 // Create the parent directory
925 r = pakfire_mkparentdir(path, 0);
926 if (r)
927 goto ERROR;
928
48c6f2e7 929 // Create a temporary file in the target directory
bb06d548
MT
930 r = pakfire_string_format(tmppath, "%s.XXXXXX", path);
931 if (r < 0)
932 goto ERROR;
48c6f2e7
MT
933
934 // Create a temporary result file
bb06d548 935 FILE* f = pakfire_mktemp(tmppath);
48c6f2e7
MT
936 if (!f)
937 goto ERROR;
938
939 // Write the finished package
940 r = pakfire_packager_finish(packager, f);
bb06d548
MT
941 fclose(f);
942
48c6f2e7
MT
943 if (r) {
944 ERROR(packager->pakfire, "pakfire_packager_finish() failed: %s\n",
945 strerror(errno));
946 goto ERROR;
947 }
948
48c6f2e7 949 // Move the temporary file to destination
bb06d548
MT
950 r = rename(tmppath, path);
951 if (r) {
952 ERROR(packager->pakfire, "Could not move %s to %s: %s\n",
953 tmppath, path, strerror(errno));
48c6f2e7 954 goto ERROR;
bb06d548 955 }
48c6f2e7 956
bb06d548 957 INFO(packager->pakfire, "Package written to %s\n", path);
48c6f2e7
MT
958
959 // Success
960 r = 0;
961
962ERROR:
963 // Remove temporary file
bb06d548
MT
964 if (r && *tmppath)
965 unlink(tmppath);
48c6f2e7 966
bb06d548 967 return r;
48c6f2e7
MT
968}
969
22b8e37b 970int pakfire_packager_add(struct pakfire_packager* packager,
809606fe 971 const char* sourcepath, const char* path) {
da08f989 972 FILE* f = NULL;
da08f989
MT
973
974 // Check if path is set
809606fe 975 if (!sourcepath)
da08f989
MT
976 return EINVAL;
977
809606fe
MT
978 // Use basename if path isn't set
979 if (!path) {
980 path = strrchr(sourcepath, '/');
981 if (path)
982 path++;
983 }
984
7836e21b
MT
985 // Payload has already been closed
986 if (!packager->payload)
987 return EINVAL;
988
da08f989
MT
989 // Create a new file entry
990 struct archive_entry* entry = archive_entry_new();
991 if (!entry)
992 return ENOMEM;
993
7e346194
MT
994 DEBUG(packager->pakfire, "Adding '%s' to archive (from %s)\n", path, sourcepath);
995
809606fe
MT
996 // Set the source path
997 archive_entry_copy_sourcepath(entry, sourcepath);
998
da08f989 999 // Set path in archive
809606fe
MT
1000 if (path)
1001 archive_entry_set_pathname(entry, path);
da08f989 1002
738b3582
MT
1003 // Read all attributes from file
1004 int r = archive_read_disk_entry_from_file(packager->reader, entry, -1, NULL);
1005 if (r) {
1006 ERROR(packager->pakfire, "Could not read attributes from %s: %s\n",
1007 path, strerror(errno));
1008 goto ERROR;
1009 }
da08f989
MT
1010
1011 // Write the header
1012 r = archive_write_header(packager->payload, entry);
1013 if (r) {
1014 ERROR(packager->pakfire, "Error writing file header: %s\n",
1015 archive_error_string(packager->payload));
1016 goto ERROR;
1017 }
1018
1019 // Copy the data of regular files
1020 if (archive_entry_filetype(entry) == AE_IFREG) {
809606fe 1021 f = fopen(sourcepath, "r");
da08f989 1022 if (!f) {
809606fe 1023 ERROR(packager->pakfire, "Could not open %s: %s\n", sourcepath, strerror(errno));
da08f989
MT
1024 r = errno;
1025 goto ERROR;
1026 }
1027
7836e21b
MT
1028 r = pakfire_packager_copy_data(packager, packager->payload, f);
1029 if (r)
1030 goto ERROR;
da08f989
MT
1031 }
1032
4eab5079
MT
1033 // Write to mtree
1034 r = archive_write_header(packager->mtree, entry);
1035 if (r) {
1036 ERROR(packager->pakfire, "Adding file to mtree failed: %s\n",
1037 archive_error_string(packager->mtree));
1038 goto ERROR;
1039 }
1040
e282863a
MT
1041 // Increment installsize
1042 packager->installsize += archive_entry_size(entry);
1ba1869e 1043
da08f989
MT
1044 // Successful
1045 r = 0;
1046
1047ERROR:
1048 if (entry)
1049 archive_entry_free(entry);
1050
1051 if (f)
1052 fclose(f);
1053
1054 return r;
1055}
106d2edd
MT
1056
1057int pakfire_packager_add_scriptlet(struct pakfire_packager* packager,
1058 struct pakfire_scriptlet* scriptlet) {
1059 if (!scriptlet) {
1060 errno = EINVAL;
1061 return 1;
1062 }
1063
1064 // Extend array
1065 packager->scriptlets = reallocarray(packager->scriptlets,
1066 packager->num_scriptlets + 1, sizeof(*packager->scriptlets));
1067 if (!packager->scriptlets)
1068 return 1;
1069
1070 // Append scriptlet
1071 packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet);
1072
1073 return 0;
1074}