]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
packager: Add pakfire features for all used digests
[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
b59c9fa2
MT
33#include <json.h>
34
2adc4a4a 35#include <pakfire/archive.h>
6bf9841b 36#include <pakfire/compress.h>
436677a3 37#include <pakfire/constants.h>
1ea1f35b 38#include <pakfire/logging.h>
6aeb48e6
MT
39#include <pakfire/package.h>
40#include <pakfire/packager.h>
41#include <pakfire/pakfire.h>
677e9e5b 42#include <pakfire/pwd.h>
d973a13d 43#include <pakfire/string.h>
f57ad279 44#include <pakfire/util.h>
6aeb48e6
MT
45
46struct pakfire_packager {
ac4c607b 47 struct pakfire* pakfire;
6aeb48e6 48 int nrefs;
05cfc2c9 49 time_t time_created;
6aeb48e6 50
31480bee 51 struct pakfire_package* pkg;
bb06d548 52 char filename[PATH_MAX];
1ea1f35b 53
738b3582
MT
54 // Reader
55 struct archive* reader;
56
57 // Payload
6bf9841b 58 struct pakfire_filelist* filelist;
106d2edd 59
9ca45f4f 60 // Scriptlets
106d2edd
MT
61 struct pakfire_scriptlet** scriptlets;
62 unsigned int num_scriptlets;
9ca45f4f
MT
63
64 // Digests
65 unsigned int digests;
6aeb48e6
MT
66};
67
1ea1f35b 68static void pakfire_packager_free(struct pakfire_packager* packager) {
106d2edd
MT
69 // Scriptlets
70 if (packager->scriptlets) {
71 for (unsigned int i = 0; i < packager->num_scriptlets; i++)
9dd7b1aa 72 pakfire_scriptlet_unref(packager->scriptlets[i]);
106d2edd
MT
73 free(packager->scriptlets);
74 }
75
738b3582 76 if (packager->reader)
66817062 77 archive_read_free(packager->reader);
738b3582 78
6bf9841b
MT
79 if (packager->filelist)
80 pakfire_filelist_unref(packager->filelist);
1ea1f35b
MT
81 pakfire_package_unref(packager->pkg);
82 pakfire_unref(packager->pakfire);
51105783 83 free(packager);
1ea1f35b
MT
84}
85
22b8e37b 86int pakfire_packager_create(struct pakfire_packager** packager,
5d7a93ec 87 struct pakfire* pakfire, struct pakfire_package* pkg) {
6aeb48e6
MT
88 struct pakfire_packager* p = calloc(1, sizeof(*p));
89 if (!p)
90 return ENOMEM;
91
e282863a
MT
92 int r = 1;
93
05cfc2c9
MT
94 // Save creation time
95 p->time_created = time(NULL);
96
6aeb48e6
MT
97 // Initialize reference counting
98 p->nrefs = 1;
99
100 // Store a reference to Pakfire
5d7a93ec 101 p->pakfire = pakfire_ref(pakfire);
6aeb48e6
MT
102
103 // Store a reference to the package
104 p->pkg = pakfire_package_ref(pkg);
105
9ca45f4f
MT
106 // Use the default digests
107 p->digests = PAKFIRE_PACKAGER_DIGESTS;
108
cd2c2cfb
MT
109 // Set build host
110 pakfire_package_set_build_host(pkg, pakfire_hostname());
111
112 // Set build time
113 pakfire_package_set_build_time(pkg, p->time_created);
114
0682aeb3 115 // Create reader
66817062
MT
116 p->reader = pakfire_make_archive_disk_reader(p->pakfire, 1);
117 if (!p->reader)
1ba1869e 118 goto ERROR;
1ea1f35b 119
6bf9841b
MT
120 // Create filelist
121 r = pakfire_filelist_create(&p->filelist, p->pakfire);
0682aeb3 122 if (r)
738b3582 123 goto ERROR;
738b3582 124
6bf9841b 125 // Add a requirement for the cryptographic algorithms we are using
9ca45f4f
MT
126 if (p->digests & PAKFIRE_DIGEST_SHA3_512)
127 pakfire_package_add_requires(p->pkg, "pakfire(Digest-SHA3-512)");
128 if (p->digests & PAKFIRE_DIGEST_SHA3_256)
129 pakfire_package_add_requires(p->pkg, "pakfire(Digest-SHA3-256)");
130 if (p->digests & PAKFIRE_DIGEST_BLAKE2B512)
131 pakfire_package_add_requires(p->pkg, "pakfire(Digest-BLAKE2b512)");
132 if (p->digests & PAKFIRE_DIGEST_BLAKE2S256)
133 pakfire_package_add_requires(p->pkg, "pakfire(Digest-BLAKE2s256)");
134 if (p->digests & PAKFIRE_DIGEST_SHA2_512)
135 pakfire_package_add_requires(p->pkg, "pakfire(Digest-SHA2-512)");
136 if (p->digests & PAKFIRE_DIGEST_SHA2_256)
137 pakfire_package_add_requires(p->pkg, "pakfire(Digest-SHA2-256)");
6bf9841b 138
6aeb48e6
MT
139 *packager = p;
140
141 return 0;
1ba1869e
MT
142
143ERROR:
144 pakfire_packager_free(p);
145
146 return r;
6aeb48e6
MT
147}
148
22b8e37b 149struct pakfire_packager* pakfire_packager_ref(
6aeb48e6
MT
150 struct pakfire_packager* packager) {
151 ++packager->nrefs;
152
153 return packager;
154}
155
22b8e37b 156struct pakfire_packager* pakfire_packager_unref(
6aeb48e6
MT
157 struct pakfire_packager* packager) {
158 if (--packager->nrefs > 0)
159 return packager;
160
161 pakfire_packager_free(packager);
6aeb48e6
MT
162 return NULL;
163}
da08f989 164
22b8e37b 165const char* pakfire_packager_filename(struct pakfire_packager* packager) {
bb06d548
MT
166 if (!*packager->filename) {
167 const char* filename = pakfire_package_get_filename(packager->pkg);
168
169 // Add arch
170 if (pakfire_package_is_source(packager->pkg))
171 pakfire_string_set(packager->filename, filename);
172 else {
173 const char* arch = pakfire_package_get_arch(packager->pkg);
174
175 pakfire_string_format(packager->filename, "%s/%s", arch, filename);
176 }
177 }
178
179 return packager->filename;
96d2c7bc
MT
180}
181
28700a5b 182static struct archive_entry* pakfire_packager_create_file(
95b81a98 183 struct pakfire_packager* packager, const char* filename, size_t size, mode_t mode) {
98e85f1c
MT
184 // Create a new file entry
185 struct archive_entry* entry = archive_entry_new();
186 if (!entry)
28700a5b 187 return NULL;
98e85f1c
MT
188
189 // Set filename
190 archive_entry_set_pathname(entry, filename);
191
192 // This is a regular file
193 archive_entry_set_filetype(entry, AE_IFREG);
95b81a98 194 archive_entry_set_perm(entry, mode);
98e85f1c 195
28700a5b
MT
196 // Set size
197 archive_entry_set_size(entry, size);
198
05cfc2c9
MT
199 // Set ownership
200 archive_entry_set_uname(entry, "root");
201 archive_entry_set_uid(entry, 0);
202 archive_entry_set_gname(entry, "root");
203 archive_entry_set_gid(entry, 0);
204
205 // Set times
206 archive_entry_set_birthtime(entry, packager->time_created, 0);
207 archive_entry_set_ctime(entry, packager->time_created, 0);
208 archive_entry_set_mtime(entry, packager->time_created, 0);
209 archive_entry_set_atime(entry, packager->time_created, 0);
210
28700a5b
MT
211 return entry;
212}
213
214static int pakfire_packager_write_file_from_buffer(struct pakfire_packager* packager,
bfdad796 215 struct archive* a, const char* filename, mode_t mode, const char* buffer) {
28700a5b
MT
216 size_t size = strlen(buffer);
217
218 // Create a new file
7e346194
MT
219 struct archive_entry* entry = pakfire_packager_create_file(packager, filename, size, mode);
220 if (!entry) {
221 ERROR(packager->pakfire, "Could not create file '%s'\n", filename);
28700a5b 222 return 1;
7e346194 223 }
98e85f1c
MT
224
225 // This is the end of the header
226 int r = archive_write_header(a, entry);
227 if (r) {
228 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
265a7974 229 goto ERROR;
98e85f1c
MT
230 }
231
232 // Write content
233 r = archive_write_data(a, buffer, strlen(buffer));
234 if (r < 0) {
235 ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a));
265a7974 236 goto ERROR;
98e85f1c
MT
237 }
238
265a7974
MT
239 // Success
240 r = 0;
241
242ERROR:
98e85f1c
MT
243 archive_entry_free(entry);
244
265a7974 245 return r;
98e85f1c
MT
246}
247
436677a3
MT
248static int pakfire_packager_write_format(struct pakfire_packager* packager,
249 struct archive* a) {
250 const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n";
251
7e346194
MT
252 DEBUG(packager->pakfire, "Writing package format\n");
253
bfdad796 254 int r = pakfire_packager_write_file_from_buffer(packager, a,
0dbfb5e1 255 "pakfire-format", 0444, buffer);
a6dd151e
MT
256 if (r)
257 return r;
258
259 // Add package format marker
260 pakfire_package_add_requires(packager->pkg,
261 "pakfire(PackageFormat-" TO_STRING(PACKAGE_FORMAT) ")");
262
263 return 0;
436677a3
MT
264}
265
b59c9fa2
MT
266static char* pakfire_packager_make_metadata(struct pakfire_packager* packager) {
267 char* result = NULL;
98e85f1c 268
b59c9fa2
MT
269 // Convert all package metadata to JSON
270 struct json_object* md = pakfire_package_to_json(packager->pkg);
271 if (!md)
98e85f1c
MT
272 goto ERROR;
273
b59c9fa2 274 // Serialize JSON to file
ef4a2e77 275 const char* s = json_object_to_json_string_ext(md, 0);
b59c9fa2 276 if (!s)
98e85f1c
MT
277 goto ERROR;
278
b59c9fa2
MT
279 // Copy result onto heap
280 result = strdup(s);
281 if (!result)
98e85f1c 282 goto ERROR;
98e85f1c
MT
283
284ERROR:
b59c9fa2
MT
285 // Free metadata
286 if (md)
287 json_object_put(md);
98e85f1c 288
b59c9fa2 289 return result;
98e85f1c
MT
290}
291
292static int pakfire_packager_write_metadata(struct pakfire_packager* packager,
bfdad796 293 struct archive* a) {
98e85f1c 294 // Make metadata
b59c9fa2 295 char* buffer = pakfire_packager_make_metadata(packager);
98e85f1c
MT
296 if (!buffer)
297 return 1;
298
b59c9fa2 299 DEBUG(packager->pakfire, "Generated package metadata:\n%s\n", buffer);
98e85f1c
MT
300
301 // Write buffer
bfdad796 302 int r = pakfire_packager_write_file_from_buffer(packager, a,
9f6b177b 303 ".PKGINFO", 0444, buffer);
98e85f1c
MT
304
305 free(buffer);
306
307 return r;
308}
309
106d2edd 310static int pakfire_packager_write_scriptlet(struct pakfire_packager* packager,
bfdad796 311 struct archive* a, struct pakfire_scriptlet* scriptlet) {
106d2edd
MT
312 char filename[PATH_MAX];
313 size_t size;
314 int r;
315
316 // Fetch type
317 const char* type = pakfire_scriptlet_get_type(scriptlet);
318
319 DEBUG(packager->pakfire, "Writing scriptlet '%s' to package\n", type);
320
321 // Make filename
9f6b177b 322 r = pakfire_string_format(filename, ".scriptlets/%s", type);
a60955af 323 if (r)
106d2edd
MT
324 return r;
325
326 // Fetch scriptlet
327 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
328
329 // Write file
bfdad796 330 return pakfire_packager_write_file_from_buffer(packager, a, filename, 0544, data);
106d2edd
MT
331}
332
436677a3
MT
333/*
334 This function is being called at the end when all data has been added to the package.
335
336 It will create a new archive and write the package to the given file descriptor.
337*/
22b8e37b 338int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) {
1b4e7c7b 339 struct archive* a = NULL;
96d2c7bc 340 int r = 1;
436677a3 341
6bf9841b 342 const char* nevra = pakfire_package_get_nevra(packager->pkg);
1b4e7c7b 343
555b6dd2
MT
344 // Add requires feature markers
345 if (pakfire_package_has_rich_deps(packager->pkg))
346 pakfire_package_add_requires(packager->pkg, "pakfire(RichDependencies)");
347
260006f2
MT
348 // Add filelist
349 r = pakfire_package_set_filelist(packager->pkg, packager->filelist);
350 if (r)
351 goto ERROR;
352
6bf9841b
MT
353 const size_t installsize = pakfire_filelist_total_size(packager->filelist);
354
508e2030 355 // Store total install size
6bf9841b 356 pakfire_package_set_installsize(packager->pkg, installsize);
fafe383d 357
508e2030
MT
358 // Dump package metadata
359 char* dump = pakfire_package_dump(packager->pkg, PAKFIRE_PKG_DUMP_LONG);
360 if (dump) {
361 INFO(packager->pakfire, "%s\n", dump);
362 free(dump);
363 }
364
436677a3 365 // Open a new archive
1b4e7c7b 366 a = archive_write_new();
436677a3
MT
367 if (!a) {
368 ERROR(packager->pakfire, "archive_write_new() failed\n");
260006f2 369 r = 1;
436677a3
MT
370 goto ERROR;
371 }
372
373 // Use the PAX format
96d2c7bc 374 r = archive_write_set_format_pax(a);
436677a3
MT
375 if (r) {
376 ERROR(packager->pakfire, "Could not set format to PAX: %s\n",
377 archive_error_string(a));
378 goto ERROR;
379 }
380
63476763
MT
381 // Store any extended attributes in the SCHILY headers
382 r = archive_write_set_format_option(a, "pax", "xattrheader", "SCHILY");
383 if (r) {
384 ERROR(packager->pakfire, "Could not set xattrheader option: %s\n",
385 archive_error_string(a));
386 return r;
387 }
388
389 // Enable Zstd
390 r = archive_write_add_filter_zstd(a);
391 if (r) {
392 ERROR(packager->pakfire, "Could not enable Zstandard compression: %s\n",
393 archive_error_string(a));
394 return r;
395 }
396
397 // Set compression level to highest
398 r = archive_write_set_filter_option(a, NULL, "compression-level", "22");
399 if (r) {
400 ERROR(packager->pakfire, "Could not set Zstandard compression level: %s\n",
401 archive_error_string(a));
402 return r;
403 }
404
405 // Add feature marker
406 pakfire_package_add_requires(packager->pkg, "pakfire(Compress-Zstandard)");
407
408 // Do not pad the last block
409 archive_write_set_bytes_in_last_block(a, 1);
410
436677a3
MT
411 // Write archive to f
412 r = archive_write_open_FILE(a, f);
413 if (r) {
414 ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n",
415 archive_error_string(a));
416 goto ERROR;
417 }
418
419 // Start with the format file
420 r = pakfire_packager_write_format(packager, a);
265a7974
MT
421 if (r) {
422 ERROR(packager->pakfire, "Could not add format file to archive: %s\n",
423 archive_error_string(a));
436677a3 424 goto ERROR;
265a7974 425 }
436677a3 426
98e85f1c 427 // Write the metadata
bfdad796 428 r = pakfire_packager_write_metadata(packager, a);
265a7974
MT
429 if (r) {
430 ERROR(packager->pakfire, "Could not add metadata file to archive: %s\n",
431 archive_error_string(a));
98e85f1c 432 goto ERROR;
265a7974 433 }
98e85f1c 434
d8856104
MT
435 // Write scriptlets
436 for (unsigned int i = 0; i < packager->num_scriptlets; i++) {
437 r = pakfire_packager_write_scriptlet(packager, a, packager->scriptlets[i]);
438 if (r) {
439 ERROR(packager->pakfire, "Could not add scriptlet to the archive: %m\n");
440 goto ERROR;
441 }
442 }
443
bfdad796 444 // Write the payload
6bf9841b
MT
445 r = pakfire_compress(packager->pakfire, a, packager->filelist, nevra,
446 PAKFIRE_COMPRESS_SHOW_THROUGHPUT, PAKFIRE_PACKAGER_DIGESTS);
265a7974 447
2013270b
MT
448 // Flush all buffers to disk
449 fflush(f);
450
96d2c7bc
MT
451 // Success
452 r = 0;
436677a3
MT
453
454ERROR:
455 if (a)
265a7974 456 archive_write_free(a);
436677a3 457
96d2c7bc 458 return r;
436677a3
MT
459}
460
22b8e37b 461int pakfire_packager_finish_to_directory(struct pakfire_packager* packager,
45448dbd 462 const char* target, char** result) {
48c6f2e7 463 char path[PATH_MAX];
bb06d548 464 char tmppath[PATH_MAX];
48c6f2e7
MT
465 int r = 1;
466
467 // target cannot be empty
468 if (!target) {
469 errno = EINVAL;
470 return 1;
471 }
472
bb06d548
MT
473 // Get the filename of the package
474 const char* filename = pakfire_packager_filename(packager);
475 if (!filename) {
b1772bfb 476 ERROR(packager->pakfire, "Could not generate filename for package: %m\n");
bb06d548
MT
477 r = 1;
478 goto ERROR;
479 }
480
481 // Make the package path
482 r = pakfire_string_format(path, "%s/%s", target, filename);
a60955af 483 if (r)
bb06d548
MT
484 goto ERROR;
485
486 // Create the parent directory
520ce66c 487 r = pakfire_mkparentdir(path, 0755);
bb06d548
MT
488 if (r)
489 goto ERROR;
490
48c6f2e7 491 // Create a temporary file in the target directory
bb06d548 492 r = pakfire_string_format(tmppath, "%s.XXXXXX", path);
a60955af 493 if (r)
bb06d548 494 goto ERROR;
48c6f2e7
MT
495
496 // Create a temporary result file
bb06d548 497 FILE* f = pakfire_mktemp(tmppath);
48c6f2e7
MT
498 if (!f)
499 goto ERROR;
500
501 // Write the finished package
502 r = pakfire_packager_finish(packager, f);
bb06d548
MT
503 fclose(f);
504
48c6f2e7 505 if (r) {
b1772bfb 506 ERROR(packager->pakfire, "pakfire_packager_finish() failed: %m\n");
48c6f2e7
MT
507 goto ERROR;
508 }
509
48c6f2e7 510 // Move the temporary file to destination
bb06d548
MT
511 r = rename(tmppath, path);
512 if (r) {
b1772bfb 513 ERROR(packager->pakfire, "Could not move %s to %s: %m\n", tmppath, path);
48c6f2e7 514 goto ERROR;
bb06d548 515 }
48c6f2e7 516
bb06d548 517 INFO(packager->pakfire, "Package written to %s\n", path);
48c6f2e7 518
45448dbd
MT
519 // Store result path if requested
520 if (result) {
521 *result = strdup(path);
522 if (!*result) {
523 r = 1;
524 goto ERROR;
525 }
526 }
527
48c6f2e7
MT
528 // Success
529 r = 0;
530
531ERROR:
532 // Remove temporary file
bb06d548
MT
533 if (r && *tmppath)
534 unlink(tmppath);
48c6f2e7 535
bb06d548 536 return r;
48c6f2e7
MT
537}
538
80a108bf 539int pakfire_packager_add_file(struct pakfire_packager* packager, struct pakfire_file* file) {
85b59296
MT
540 // Check input
541 if (!file) {
255d4488
MT
542 errno = EINVAL;
543 return 1;
544 }
da08f989 545
85b59296
MT
546 // Fetch path
547 const char* path = pakfire_file_get_path(file);
da08f989 548
85b59296
MT
549 // Files cannot have an empty path
550 if (!*path) {
551 ERROR(packager->pakfire, "Cannot add a file with an empty path\n");
552 errno = EPERM;
553 return 1;
da08f989 554
85b59296
MT
555 // Hidden files cannot be added
556 } else if (*path == '.') {
557 ERROR(packager->pakfire, "Hidden files cannot be added to a package: %s\n", path);
558 errno = EPERM;
559 return 1;
738b3582 560 }
da08f989 561
85b59296
MT
562 DEBUG(packager->pakfire, "Adding file to payload: %s\n", path);
563
2ed2b519
MT
564 // Overwrite a couple of things for source archives
565 if (pakfire_package_is_source(packager->pkg)) {
566 // Reset permissions
85b59296 567 pakfire_file_set_perms(file, 0644);
2ed2b519
MT
568
569 // Reset file ownership
85b59296
MT
570 pakfire_file_set_user(file, "root");
571 pakfire_file_set_group(file, "root");
2ed2b519
MT
572 }
573
6bf9841b
MT
574 // Append the file to the filelist
575 return pakfire_filelist_append(packager->filelist, file);
da08f989 576}
106d2edd 577
85b59296
MT
578int pakfire_packager_add(struct pakfire_packager* packager,
579 const char* sourcepath, const char* path) {
580 struct pakfire_file* file = NULL;
581 int r = 1;
582
61b0856b
MT
583 // Create file
584 r = pakfire_file_create_from_path(&file, packager->pakfire, sourcepath);
85b59296
MT
585 if (r)
586 goto ERROR;
587
61b0856b
MT
588 // Assign a new path for inside the archive
589 if (path) {
590 r = pakfire_file_set_path(file, path);
591 if (r)
592 goto ERROR;
593 }
594
85b59296
MT
595 // Call the main function
596 r = pakfire_packager_add_file(packager, file);
597
598ERROR:
599 if (file)
600 pakfire_file_unref(file);
601
602 return r;
603}
604
814b7fee
MT
605int pakfire_packager_add_files(struct pakfire_packager* packager,
606 struct pakfire_filelist* filelist, int cleanup) {
607 struct pakfire_file* file = NULL;
608 int r;
609
610 // Fetch length of the filelist
611 const size_t length = pakfire_filelist_size(filelist);
612
613 // Add all files to the package
614 for (unsigned int i = 0; i < length; i++) {
615 file = pakfire_filelist_get(filelist, i);
616
617 // Add the file to the package
618 r = pakfire_packager_add_file(packager, file);
619 if (r) {
620 pakfire_file_unref(file);
621 goto ERROR;
622 }
623
624 // Remove the file after it was packaged
625 if (cleanup) {
626 r = pakfire_file_cleanup(file);
627 if (r) {
628 pakfire_file_unref(file);
629 goto ERROR;
630 }
631 }
632
633 pakfire_file_unref(file);
634 }
635
636ERROR:
637 return r;
638}
639
106d2edd
MT
640int pakfire_packager_add_scriptlet(struct pakfire_packager* packager,
641 struct pakfire_scriptlet* scriptlet) {
642 if (!scriptlet) {
643 errno = EINVAL;
644 return 1;
645 }
646
647 // Extend array
648 packager->scriptlets = reallocarray(packager->scriptlets,
649 packager->num_scriptlets + 1, sizeof(*packager->scriptlets));
650 if (!packager->scriptlets)
651 return 1;
652
653 // Append scriptlet
654 packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet);
655
656 return 0;
657}