]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
util: Add new dependency parsing function
[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>
1ba1869e
MT
35#include <pakfire/file.h>
36#include <pakfire/filelist.h>
1ea1f35b 37#include <pakfire/logging.h>
6aeb48e6
MT
38#include <pakfire/package.h>
39#include <pakfire/packager.h>
40#include <pakfire/pakfire.h>
41#include <pakfire/private.h>
677e9e5b 42#include <pakfire/pwd.h>
6aeb48e6
MT
43#include <pakfire/types.h>
44
da08f989
MT
45#define BUFFER_SIZE 64 * 1024
46
6aeb48e6
MT
47struct pakfire_packager {
48 Pakfire pakfire;
49 int nrefs;
05cfc2c9 50 time_t time_created;
6aeb48e6
MT
51
52 PakfirePackage pkg;
1ba1869e 53 PakfireFilelist filelist;
1ea1f35b 54
738b3582
MT
55 // Reader
56 struct archive* reader;
57
58 // Payload
1ea1f35b 59 struct archive* payload;
7836e21b 60 FILE* fpayload;
6aeb48e6
MT
61};
62
af14aefb 63static int pakfire_packager_create_payload(struct pakfire_packager* p) {
7836e21b
MT
64 char path[] = "/tmp/.pakfire-payload.XXXXXX";
65
af14aefb
MT
66 // Do not compress source packages
67 const int compress = !pakfire_package_is_source(p->pkg);
68
1ea1f35b 69 p->payload = archive_write_new();
427fdd80
MT
70 if (!p->payload) {
71 ERROR(p->pakfire, "archive_write_new() failed\n");
72 return 1;
73 }
1ea1f35b
MT
74
75 // Use the PAX format
76 int r = archive_write_set_format_pax(p->payload);
77 if (r) {
78 ERROR(p->pakfire, "Could not set format to PAX: %s\n",
79 archive_error_string(p->payload));
80 return r;
81 }
82
83 // Add filters to compress the payload
84 if (compress) {
85 // Enable Zstd
86 r = archive_write_add_filter_zstd(p->payload);
87 if (r) {
88 ERROR(p->pakfire, "Could not enable Zstandard compression: %s\n",
89 archive_error_string(p->payload));
90 return r;
91 }
92
93 // Set compression level to highest
be69fe38 94 r = archive_write_set_filter_option(p->payload, NULL, "compression-level", "22");
1ea1f35b
MT
95 if (r) {
96 ERROR(p->pakfire, "Could not set Zstandard compression level: %s\n",
97 archive_error_string(p->payload));
98 return r;
99 }
100 }
101
7836e21b
MT
102 // Create a new temporary file
103 int fd = mkostemp(path, O_CLOEXEC);
1ea1f35b
MT
104 if (fd < 0) {
105 ERROR(p->pakfire, "mkostemp() failed: %s\n", strerror(errno));
106 return 1;
107 }
108
7836e21b
MT
109 // Unlink the file straight away
110 unlink(path);
111
112 // Convert the file descriptor into a file handle
113 p->fpayload = fdopen(fd, "w+");
114 if (!p->fpayload) {
115 close(fd);
116
117 return 1;
118 }
119
120 // Write archive to file
121 r = archive_write_open_FILE(p->payload, p->fpayload);
1ea1f35b
MT
122 if (r)
123 return r;
124
125 return 0;
126}
127
128static void pakfire_packager_free(struct pakfire_packager* packager) {
129 if (packager->payload)
130 archive_write_free(packager->payload);
131
7836e21b
MT
132 if (packager->fpayload)
133 fclose(packager->fpayload);
134
738b3582 135 if (packager->reader)
66817062 136 archive_read_free(packager->reader);
738b3582 137
1ba1869e 138 pakfire_filelist_unref(packager->filelist);
1ea1f35b
MT
139 pakfire_package_unref(packager->pkg);
140 pakfire_unref(packager->pakfire);
141}
142
6aeb48e6
MT
143PAKFIRE_EXPORT int pakfire_packager_create(struct pakfire_packager** packager,
144 PakfirePackage pkg) {
145 struct pakfire_packager* p = calloc(1, sizeof(*p));
146 if (!p)
147 return ENOMEM;
148
05cfc2c9
MT
149 // Save creation time
150 p->time_created = time(NULL);
151
6aeb48e6
MT
152 // Initialize reference counting
153 p->nrefs = 1;
154
155 // Store a reference to Pakfire
156 p->pakfire = pakfire_package_get_pakfire(pkg);
157
158 // Store a reference to the package
159 p->pkg = pakfire_package_ref(pkg);
160
1ba1869e 161 // Create a new filelist
883b3be9 162 int r = pakfire_filelist_create(&p->filelist, p->pakfire);
1ba1869e
MT
163 if (r)
164 goto ERROR;
165
0682aeb3 166 // Create reader
66817062
MT
167 p->reader = pakfire_make_archive_disk_reader(p->pakfire, 1);
168 if (!p->reader)
1ba1869e 169 goto ERROR;
1ea1f35b 170
0682aeb3 171 // Start payload
af14aefb 172 r = pakfire_packager_create_payload(p);
0682aeb3 173 if (r)
738b3582 174 goto ERROR;
738b3582 175
6aeb48e6
MT
176 *packager = p;
177
178 return 0;
1ba1869e
MT
179
180ERROR:
181 pakfire_packager_free(p);
182
183 return r;
6aeb48e6
MT
184}
185
6aeb48e6
MT
186PAKFIRE_EXPORT struct pakfire_packager* pakfire_packager_ref(
187 struct pakfire_packager* packager) {
188 ++packager->nrefs;
189
190 return packager;
191}
192
193PAKFIRE_EXPORT struct pakfire_packager* pakfire_packager_unref(
194 struct pakfire_packager* packager) {
195 if (--packager->nrefs > 0)
196 return packager;
197
198 pakfire_packager_free(packager);
199
200 return NULL;
201}
da08f989 202
26687bfe
MT
203PAKFIRE_EXPORT const char* pakfire_packager_filename(struct pakfire_packager* packager) {
204 return pakfire_package_get_filename(packager->pkg);
96d2c7bc
MT
205}
206
7836e21b
MT
207static int pakfire_packager_copy_data(struct pakfire_packager* packager,
208 struct archive* a, FILE* f) {
209 char buffer[BUFFER_SIZE];
210
211 while (!feof(f)) {
212 // Read a block from file
213 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
214
215 // Check if any error occured
216 if (ferror(f)) {
217 ERROR(packager->pakfire, "Error reading from file: %s\n", strerror(errno));
218 return 1;
219 }
220
221 // Write the block to the archive
222 ssize_t bytes_written = archive_write_data(a, buffer, bytes_read);
223 if (bytes_written < 0) {
224 ERROR(packager->pakfire, "Error writing data to archive: %s\n",
225 archive_error_string(a));
226 return 1;
227 }
228 }
229
230 return 0;
231}
232
28700a5b
MT
233static struct archive_entry* pakfire_packager_create_file(
234 struct pakfire_packager* packager, const char* filename, size_t size) {
98e85f1c
MT
235 // Create a new file entry
236 struct archive_entry* entry = archive_entry_new();
237 if (!entry)
28700a5b 238 return NULL;
98e85f1c
MT
239
240 // Set filename
241 archive_entry_set_pathname(entry, filename);
242
243 // This is a regular file
244 archive_entry_set_filetype(entry, AE_IFREG);
245 archive_entry_set_perm(entry, 0644);
246
28700a5b
MT
247 // Set size
248 archive_entry_set_size(entry, size);
249
05cfc2c9
MT
250 // Set ownership
251 archive_entry_set_uname(entry, "root");
252 archive_entry_set_uid(entry, 0);
253 archive_entry_set_gname(entry, "root");
254 archive_entry_set_gid(entry, 0);
255
256 // Set times
257 archive_entry_set_birthtime(entry, packager->time_created, 0);
258 archive_entry_set_ctime(entry, packager->time_created, 0);
259 archive_entry_set_mtime(entry, packager->time_created, 0);
260 archive_entry_set_atime(entry, packager->time_created, 0);
261
28700a5b
MT
262 return entry;
263}
264
265static int pakfire_packager_write_file_from_buffer(struct pakfire_packager* packager,
266 struct archive* a, const char* filename, const char* buffer) {
267 size_t size = strlen(buffer);
268
269 // Create a new file
270 struct archive_entry* entry = pakfire_packager_create_file(packager, filename, size);
271 if (!entry)
272 return 1;
98e85f1c
MT
273
274 // This is the end of the header
275 int r = archive_write_header(a, entry);
276 if (r) {
277 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
278 archive_entry_free(entry);
279 return r;
280 }
281
282 // Write content
283 r = archive_write_data(a, buffer, strlen(buffer));
284 if (r < 0) {
285 ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a));
286 archive_entry_free(entry);
287 return r;
288 }
289
290 archive_entry_free(entry);
291
292 return 0;
293}
294
436677a3
MT
295static int pakfire_packager_write_format(struct pakfire_packager* packager,
296 struct archive* a) {
297 const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n";
298
a18d6f8b
MT
299 return pakfire_packager_write_file_from_buffer(packager, a,
300 PAKFIRE_ARCHIVE_FN_FORMAT, buffer);
436677a3
MT
301}
302
98e85f1c
MT
303static char* pakfire_package_make_metadata(struct pakfire_packager* packager) {
304 PakfirePackage pkg = packager->pkg;
305
306 char* buffer = NULL;
307 int r;
308
309 // Print version information
310 r = asprintf(&buffer, "# Pakfire %s\n\n", PACKAGE_VERSION);
311 if (r < 0)
312 goto ERROR;
313
314 // Write package information
315 r = asprintf(&buffer, "%s# Package information\npackage\n", buffer);
316 if (r < 0)
317 goto ERROR;
318
319 // Write package name
320 r = asprintf(&buffer, "%s\tname = %s\n", buffer, pakfire_package_get_name(pkg));
321 if (r < 0)
322 goto ERROR;
323
6ed66687
MT
324 // Write package EVR
325 r = asprintf(&buffer, "%s\tevr = %s\n", buffer, pakfire_package_get_evr(pkg));
98e85f1c
MT
326 if (r < 0)
327 goto ERROR;
328
329 // Write package arch
330 r = asprintf(&buffer, "%s\tarch = %s\n", buffer, pakfire_package_get_arch(pkg));
331 if (r < 0)
332 goto ERROR;
333
334 // Write package UUID
335 r = asprintf(&buffer, "%s\tuuid = %s\n", buffer, pakfire_package_get_uuid(pkg));
336 if (r < 0)
337 goto ERROR;
338
339 // Write package groups
340 const char* groups = pakfire_package_get_groups(pkg);
341 if (groups) {
342 r = asprintf(&buffer, "%s\tgroups = %s\n", buffer, groups);
343 if (r < 0)
344 goto ERROR;
345 }
346
347 // Write package maintainer
348 const char* maintainer = pakfire_package_get_maintainer(pkg);
349 if (maintainer) {
350 r = asprintf(&buffer, "%s\tmaintainer = %s\n", buffer, maintainer);
351 if (r < 0)
352 goto ERROR;
353 }
354
355 // Write package url
e3c279d5 356 const char* url = pakfire_package_get_url(pkg);
98e85f1c
MT
357 if (url) {
358 r = asprintf(&buffer, "%s\turl = %s\n", buffer, url);
359 if (r < 0)
360 goto ERROR;
361 }
362
363 // Write package license
364 const char* license = pakfire_package_get_license(pkg);
365 if (license) {
366 r = asprintf(&buffer, "%s\tlicense = %s\n", buffer, license);
367 if (r < 0)
368 goto ERROR;
369 }
370
371 // Write package summary
372 const char* summary = pakfire_package_get_summary(pkg);
373 if (summary) {
374 r = asprintf(&buffer, "%s\tsummary = %s\n", buffer, summary);
375 if (r < 0)
376 goto ERROR;
377 }
378
379 // XXX description
380
381 size_t size = pakfire_package_get_installsize(pkg);
382 r = asprintf(&buffer, "%s\tsize = %zu\n", buffer, size);
383 if (r < 0)
384 goto ERROR;
385
386 // End package block
387 r = asprintf(&buffer, "%send\n\n", buffer);
388 if (r < 0)
389 goto ERROR;
390
391 // Write build information
392 r = asprintf(&buffer, "%s# Build information\nbuild\n", buffer);
393 if (r < 0)
394 goto ERROR;
395
396 // Write build host
397 const char* build_host = pakfire_package_get_build_host(pkg);
398 if (build_host) {
399 r = asprintf(&buffer, "%s\thost = %s\n", buffer, build_host);
400 if (r < 0)
401 goto ERROR;
402 }
403
404#if 0
405 // Write build id
406 const char* build_id = pakfire_package_get_build_id(pkg);
407 if (build_id) {
408 r = asprintf(&buffer, "%s\tid = %s\n", buffer, build_id);
409 if (r < 0)
410 goto ERROR;
411 }
412#endif
413
414 // Write build host
415 time_t build_time = pakfire_package_get_build_time(pkg);
416 if (build_host) {
417 r = asprintf(&buffer, "%s\ttime = %lu\n", buffer, build_time);
418 if (r < 0)
419 goto ERROR;
420 }
421
422 // End build block
423 r = asprintf(&buffer, "%send\n\n", buffer);
424 if (r < 0)
425 goto ERROR;
426
427#if 0
428 // Write distribution information
429 r = asprintf(&buffer, "%s# Distribution information\ndistribution\n", buffer);
430 if (r < 0)
431 goto ERROR;
432
433 // End distribution block
434 r = asprintf(&buffer, "%send\n\n", buffer);
435 if (r < 0)
436 goto ERROR;
437#endif
438
439 // Write dependency information
440 r = asprintf(&buffer, "%s# Dependency information\ndependencies\n", buffer);
441 if (r < 0)
442 goto ERROR;
443
444 const struct dependencies {
445 const char* type;
446 PakfireRelationList (*func)(PakfirePackage pkg);
447 } dependencies[] = {
448 { "prerequires", pakfire_package_get_prerequires },
449 { "requires", pakfire_package_get_requires },
450 { "provides", pakfire_package_get_provides },
451 { "conflicts", pakfire_package_get_conflicts },
452 { "obsoletes", pakfire_package_get_obsoletes },
453 { "recommends", pakfire_package_get_recommends },
454 { "suggests", pakfire_package_get_suggests },
455 { "supplements", pakfire_package_get_supplements },
456 { "enhances", pakfire_package_get_enhances },
457 { NULL },
458 };
459
460 for (const struct dependencies* d = dependencies; d->type; d++) {
461 PakfireRelationList list = d->func(pkg);
462 if (!list)
463 continue;
464
465 size_t l = pakfire_relationlist_size(list);
466
467 // Skip for empty lists
468 if (l == 0) {
469 pakfire_relationlist_unref(list);
470 continue;
471 }
472
473 // Write header
474 r = asprintf(&buffer, "%s\t%s\n", buffer, d->type);
475 if (r < 0) {
476 pakfire_relationlist_unref(list);
477 goto ERROR;
478 }
479
480 for (unsigned int i = 0; i < l; i++) {
481 PakfireRelation rel = pakfire_relationlist_get(list, i);
482
483 char* s = pakfire_relation_str(rel);
484 if (s) {
485 r = asprintf(&buffer, "%s\t\t%s\n", buffer, s);
486 free(s);
487 if (r < 0) {
488 pakfire_relationlist_unref(list);
489 pakfire_relation_unref(rel);
490 goto ERROR;
491 }
492 }
493
494 pakfire_relation_unref(rel);
495 }
496
497 // End block
498 r = asprintf(&buffer, "%s\tend\n", buffer);
499 if (r < 0)
500 goto ERROR;
501
502 pakfire_relationlist_unref(list);
503 }
504
505 // End dependencies block
506 r = asprintf(&buffer, "%send\n\n", buffer);
507 if (r < 0)
508 goto ERROR;
509
510 // EOF
511 r = asprintf(&buffer, "%s# EOF\n", buffer);
512 if (r < 0)
513 goto ERROR;
514
515 return buffer;
516
517ERROR:
518 if (buffer)
519 free(buffer);
520
521 return NULL;
522}
523
524static int pakfire_packager_write_metadata(struct pakfire_packager* packager,
525 struct archive* a) {
526 // Make metadata
527 char* buffer = pakfire_package_make_metadata(packager);
528 if (!buffer)
529 return 1;
530
531 DEBUG(packager->pakfire, "Generated package metadata:\n%s", buffer);
532
533 // Write buffer
534 int r = pakfire_packager_write_file_from_buffer(packager, a,
535 PAKFIRE_ARCHIVE_FN_METADATA, buffer);
536
537 free(buffer);
538
539 return r;
540}
541
2adc4a4a
MT
542static int pakfire_packager_write_payload(struct pakfire_packager* packager,
543 struct archive* a) {
544 struct stat st;
545
546 // Close the payload
547 if (packager->payload) {
548 archive_write_free(packager->payload);
549 packager->payload = NULL;
550 }
551
552 // Reset fd to beginning of the file
553 rewind(packager->fpayload);
554
555 int fd = fileno(packager->fpayload);
556
557 // Stat the payload file
558 int r = fstat(fd, &st);
559 if (r) {
560 ERROR(packager->pakfire, "stat() on fd %d failed: %s\n", fd, strerror(errno));
561 return 1;
562 }
563
28700a5b
MT
564 // Create a new file
565 struct archive_entry* entry = pakfire_packager_create_file(packager,
566 PAKFIRE_ARCHIVE_FN_PAYLOAD, st.st_size);
2adc4a4a
MT
567 if (!entry)
568 return 1;
569
2adc4a4a
MT
570 // This is the end of the header
571 r = archive_write_header(a, entry);
572 if (r) {
573 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
574 goto ERROR;
575 }
576
577 // Copy data
578 r = pakfire_packager_copy_data(packager, a, packager->fpayload);
579 if (r)
580 goto ERROR;
581
582 // Success
583 r = 0;
584
585ERROR:
586 archive_entry_free(entry);
587
588 return r;
589}
590
436677a3
MT
591/*
592 This function is being called at the end when all data has been added to the package.
593
594 It will create a new archive and write the package to the given file descriptor.
595*/
96d2c7bc
MT
596PAKFIRE_EXPORT int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) {
597 int r = 1;
436677a3 598
fafe383d
MT
599 // Store total instal size
600 pakfire_package_set_installsize(packager->pkg,
601 pakfire_filelist_total_filesize(packager->filelist));
602
436677a3
MT
603 // Open a new archive
604 struct archive* a = archive_write_new();
605 if (!a) {
606 ERROR(packager->pakfire, "archive_write_new() failed\n");
607 goto ERROR;
608 }
609
610 // Use the PAX format
96d2c7bc 611 r = archive_write_set_format_pax(a);
436677a3
MT
612 if (r) {
613 ERROR(packager->pakfire, "Could not set format to PAX: %s\n",
614 archive_error_string(a));
615 goto ERROR;
616 }
617
618 // Write archive to f
619 r = archive_write_open_FILE(a, f);
620 if (r) {
621 ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n",
622 archive_error_string(a));
623 goto ERROR;
624 }
625
626 // Start with the format file
627 r = pakfire_packager_write_format(packager, a);
628 if (r)
629 goto ERROR;
630
98e85f1c
MT
631 // Write the metadata
632 r = pakfire_packager_write_metadata(packager, a);
633 if (r)
634 goto ERROR;
635
2adc4a4a
MT
636 // Write the payload
637 r = pakfire_packager_write_payload(packager, a);
638 if (r)
639 goto ERROR;
640
96d2c7bc
MT
641 // Success
642 r = 0;
436677a3
MT
643
644ERROR:
645 if (a)
646 archive_free(a);
647
96d2c7bc 648 return r;
436677a3
MT
649}
650
da08f989 651PAKFIRE_EXPORT int pakfire_packager_add(struct pakfire_packager* packager,
809606fe 652 const char* sourcepath, const char* path) {
da08f989 653 FILE* f = NULL;
da08f989
MT
654
655 // Check if path is set
809606fe 656 if (!sourcepath)
da08f989
MT
657 return EINVAL;
658
809606fe
MT
659 // Use basename if path isn't set
660 if (!path) {
661 path = strrchr(sourcepath, '/');
662 if (path)
663 path++;
664 }
665
7836e21b
MT
666 // Payload has already been closed
667 if (!packager->payload)
668 return EINVAL;
669
da08f989
MT
670 // Create a new file entry
671 struct archive_entry* entry = archive_entry_new();
672 if (!entry)
673 return ENOMEM;
674
809606fe
MT
675 // Set the source path
676 archive_entry_copy_sourcepath(entry, sourcepath);
677
da08f989 678 // Set path in archive
809606fe
MT
679 if (path)
680 archive_entry_set_pathname(entry, path);
da08f989 681
738b3582
MT
682 // Read all attributes from file
683 int r = archive_read_disk_entry_from_file(packager->reader, entry, -1, NULL);
684 if (r) {
685 ERROR(packager->pakfire, "Could not read attributes from %s: %s\n",
686 path, strerror(errno));
687 goto ERROR;
688 }
da08f989
MT
689
690 // Write the header
691 r = archive_write_header(packager->payload, entry);
692 if (r) {
693 ERROR(packager->pakfire, "Error writing file header: %s\n",
694 archive_error_string(packager->payload));
695 goto ERROR;
696 }
697
698 // Copy the data of regular files
699 if (archive_entry_filetype(entry) == AE_IFREG) {
809606fe 700 f = fopen(sourcepath, "r");
da08f989 701 if (!f) {
809606fe 702 ERROR(packager->pakfire, "Could not open %s: %s\n", sourcepath, strerror(errno));
da08f989
MT
703 r = errno;
704 goto ERROR;
705 }
706
7836e21b
MT
707 r = pakfire_packager_copy_data(packager, packager->payload, f);
708 if (r)
709 goto ERROR;
da08f989
MT
710 }
711
c138b06e
MT
712 // Create a file
713 PakfireFile file;
714 r = pakfire_file_create(&file, packager->pakfire);
715 if (r)
716 goto ERROR;
717
718 r = pakfire_file_copy_archive_entry(file, entry);
719 if (r)
720 goto ERROR;
721
1ba1869e
MT
722 // Append the file to the filelist
723 pakfire_filelist_append(packager->filelist, file);
724
da08f989
MT
725 // Successful
726 r = 0;
727
728ERROR:
1ba1869e
MT
729 pakfire_file_unref(file);
730
da08f989
MT
731 if (entry)
732 archive_entry_free(entry);
733
734 if (f)
735 fclose(f);
736
737 return r;
738}