]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
build: Abort if there are any unpackaged files
[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);
131c1449
MT
447 if (r)
448 goto ERROR;
265a7974 449
2013270b
MT
450 // Flush all buffers to disk
451 fflush(f);
452
96d2c7bc
MT
453 // Success
454 r = 0;
436677a3
MT
455
456ERROR:
457 if (a)
265a7974 458 archive_write_free(a);
436677a3 459
96d2c7bc 460 return r;
436677a3
MT
461}
462
22b8e37b 463int pakfire_packager_finish_to_directory(struct pakfire_packager* packager,
45448dbd 464 const char* target, char** result) {
48c6f2e7 465 char path[PATH_MAX];
bb06d548 466 char tmppath[PATH_MAX];
131c1449 467 FILE* f = NULL;
48c6f2e7
MT
468 int r = 1;
469
470 // target cannot be empty
471 if (!target) {
472 errno = EINVAL;
473 return 1;
474 }
475
bb06d548
MT
476 // Get the filename of the package
477 const char* filename = pakfire_packager_filename(packager);
478 if (!filename) {
b1772bfb 479 ERROR(packager->pakfire, "Could not generate filename for package: %m\n");
bb06d548
MT
480 r = 1;
481 goto ERROR;
482 }
483
484 // Make the package path
485 r = pakfire_string_format(path, "%s/%s", target, filename);
a60955af 486 if (r)
bb06d548
MT
487 goto ERROR;
488
489 // Create the parent directory
520ce66c 490 r = pakfire_mkparentdir(path, 0755);
bb06d548
MT
491 if (r)
492 goto ERROR;
493
48c6f2e7 494 // Create a temporary file in the target directory
bb06d548 495 r = pakfire_string_format(tmppath, "%s.XXXXXX", path);
a60955af 496 if (r)
bb06d548 497 goto ERROR;
48c6f2e7
MT
498
499 // Create a temporary result file
131c1449 500 f = pakfire_mktemp(tmppath);
48c6f2e7
MT
501 if (!f)
502 goto ERROR;
503
504 // Write the finished package
505 r = pakfire_packager_finish(packager, f);
506 if (r) {
b1772bfb 507 ERROR(packager->pakfire, "pakfire_packager_finish() failed: %m\n");
48c6f2e7
MT
508 goto ERROR;
509 }
510
48c6f2e7 511 // Move the temporary file to destination
bb06d548
MT
512 r = rename(tmppath, path);
513 if (r) {
b1772bfb 514 ERROR(packager->pakfire, "Could not move %s to %s: %m\n", tmppath, path);
48c6f2e7 515 goto ERROR;
bb06d548 516 }
48c6f2e7 517
bb06d548 518 INFO(packager->pakfire, "Package written to %s\n", path);
48c6f2e7 519
45448dbd
MT
520 // Store result path if requested
521 if (result) {
522 *result = strdup(path);
523 if (!*result) {
524 r = 1;
525 goto ERROR;
526 }
527 }
528
48c6f2e7
MT
529 // Success
530 r = 0;
531
532ERROR:
131c1449
MT
533 if (f)
534 fclose(f);
535
48c6f2e7 536 // Remove temporary file
bb06d548
MT
537 if (r && *tmppath)
538 unlink(tmppath);
48c6f2e7 539
bb06d548 540 return r;
48c6f2e7
MT
541}
542
80a108bf 543int pakfire_packager_add_file(struct pakfire_packager* packager, struct pakfire_file* file) {
85b59296
MT
544 // Check input
545 if (!file) {
255d4488
MT
546 errno = EINVAL;
547 return 1;
548 }
da08f989 549
85b59296
MT
550 // Fetch path
551 const char* path = pakfire_file_get_path(file);
da08f989 552
85b59296
MT
553 // Files cannot have an empty path
554 if (!*path) {
555 ERROR(packager->pakfire, "Cannot add a file with an empty path\n");
556 errno = EPERM;
557 return 1;
da08f989 558
85b59296
MT
559 // Hidden files cannot be added
560 } else if (*path == '.') {
561 ERROR(packager->pakfire, "Hidden files cannot be added to a package: %s\n", path);
562 errno = EPERM;
563 return 1;
738b3582 564 }
da08f989 565
85b59296
MT
566 DEBUG(packager->pakfire, "Adding file to payload: %s\n", path);
567
2ed2b519
MT
568 // Overwrite a couple of things for source archives
569 if (pakfire_package_is_source(packager->pkg)) {
570 // Reset permissions
85b59296 571 pakfire_file_set_perms(file, 0644);
2ed2b519
MT
572
573 // Reset file ownership
85b59296
MT
574 pakfire_file_set_user(file, "root");
575 pakfire_file_set_group(file, "root");
2ed2b519
MT
576 }
577
6bf9841b
MT
578 // Append the file to the filelist
579 return pakfire_filelist_append(packager->filelist, file);
da08f989 580}
106d2edd 581
85b59296
MT
582int pakfire_packager_add(struct pakfire_packager* packager,
583 const char* sourcepath, const char* path) {
584 struct pakfire_file* file = NULL;
585 int r = 1;
586
61b0856b
MT
587 // Create file
588 r = pakfire_file_create_from_path(&file, packager->pakfire, sourcepath);
85b59296
MT
589 if (r)
590 goto ERROR;
591
61b0856b
MT
592 // Assign a new path for inside the archive
593 if (path) {
594 r = pakfire_file_set_path(file, path);
595 if (r)
596 goto ERROR;
597 }
598
85b59296
MT
599 // Call the main function
600 r = pakfire_packager_add_file(packager, file);
601
602ERROR:
603 if (file)
604 pakfire_file_unref(file);
605
606 return r;
607}
608
5d5da764
MT
609static int __pakfire_packager_add_files(struct pakfire* pakfire,
610 struct pakfire_file* file, void* p) {
611 struct pakfire_packager* packager = (struct pakfire_packager*)p;
814b7fee 612
5d5da764
MT
613 return pakfire_packager_add_file(packager, file);
614}
814b7fee 615
5d5da764
MT
616int pakfire_packager_add_files(
617 struct pakfire_packager* packager, struct pakfire_filelist* filelist) {
618 // Add all files on the filelist
619 return pakfire_filelist_walk(
620 packager->filelist, __pakfire_packager_add_files, packager);
814b7fee
MT
621}
622
106d2edd
MT
623int pakfire_packager_add_scriptlet(struct pakfire_packager* packager,
624 struct pakfire_scriptlet* scriptlet) {
625 if (!scriptlet) {
626 errno = EINVAL;
627 return 1;
628 }
629
630 // Extend array
631 packager->scriptlets = reallocarray(packager->scriptlets,
632 packager->num_scriptlets + 1, sizeof(*packager->scriptlets));
633 if (!packager->scriptlets)
634 return 1;
635
636 // Append scriptlet
637 packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet);
638
639 return 0;
640}