]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/archive.c
compress: Create a unified extraction function
[people/ms/pakfire.git] / src / libpakfire / archive.c
CommitLineData
221cc3ce
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2014 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
6afc1aa6 21#include <ctype.h>
221cc3ce 22#include <fcntl.h>
618ca500 23#include <gpgme.h>
221cc3ce
MT
24#include <stdlib.h>
25#include <string.h>
6cdbff80 26#include <sys/queue.h>
221cc3ce 27#include <sys/types.h>
285b2758 28#include <sys/sendfile.h>
221cc3ce
MT
29#include <sys/stat.h>
30
31// libarchive
32#include <archive.h>
33#include <archive_entry.h>
34
20b83302
MT
35// JSON-C
36#include <json.h>
37
4885bc0d
MT
38// openssl
39#include <openssl/crypto.h>
40#include <openssl/err.h>
41#include <openssl/evp.h>
4efcd464 42
221cc3ce 43#include <pakfire/archive.h>
79824416 44#include <pakfire/compress.h>
221cc3ce 45#include <pakfire/file.h>
5e9463ec 46#include <pakfire/filelist.h>
618ca500
MT
47#include <pakfire/i18n.h>
48#include <pakfire/key.h>
a324666b 49#include <pakfire/logging.h>
312fd26f 50#include <pakfire/package.h>
618ca500 51#include <pakfire/pakfire.h>
35ebb186 52#include <pakfire/parser.h>
9f953e68 53#include <pakfire/private.h>
4f85d6ee 54#include <pakfire/progressbar.h>
ce389fc8 55#include <pakfire/pwd.h>
e7da5970 56#include <pakfire/repo.h>
101264c8 57#include <pakfire/scriptlet.h>
221cc3ce
MT
58#include <pakfire/util.h>
59
2ab9cb5e
MT
60enum pakfire_archive_verify_flags {
61 PAKFIRE_ARCHIVE_VERIFY_ALL,
62 PAKFIRE_ARCHIVE_VERIFY_BEST,
63};
64
e6a473a3
MT
65static const char* pakfire_archive_files_without_chksums[] = {
66 "pakfire-format",
67 "chksums",
68 NULL,
69};
70
6cdbff80
MT
71struct pakfire_archive_chksum {
72 STAILQ_ENTRY(pakfire_archive_chksum) nodes;
73
74 char path[PATH_MAX];
5b2f4d99
MT
75
76 unsigned char digest_sha512[64];
77 unsigned char digest_sha256[32];
6cdbff80 78};
e1545ccb 79
900faa2f 80struct pakfire_archive {
ac4c607b 81 struct pakfire* pakfire;
79bd093e
MT
82 int nrefs;
83
84 char path[PATH_MAX];
f99c69bb 85 FILE* f;
31480bee 86 struct pakfire_package* package;
e1545ccb
MT
87
88 // metadata
539e9063 89 unsigned int format;
20b83302 90 struct json_object* metadata;
657a5c72 91 struct pakfire_parser* parser;
e1545ccb 92
1bbbfb9e 93 struct pakfire_filelist* filelist;
6cdbff80
MT
94
95 // Checksums
96 STAILQ_HEAD(chksums, pakfire_archive_chksum) chksums;
e1545ccb 97
101264c8
MT
98 // Scriptlets
99 struct pakfire_scriptlet** scriptlets;
e49b6359 100 int scriptlets_loaded;
101264c8 101
6bf26d8f
MT
102 // Verify Status
103 int verify;
e1545ccb
MT
104};
105
9d01571f
MT
106static const char* pakfire_archive_legacy_filename(
107 struct pakfire_archive* archive, const char* filename) {
108 // Do nothing for new package formats
109 if (archive->format >= 6)
110 return filename;
111
112 const struct files {
113 const char* file;
114 const char* legacy;
115 } filenames[] = {
116 { "CHKSUMS", "chksums" },
117 { "DATA", "data.img" },
118 { "FILELIST", "filelist"},
119 { "PKGINFO", "info" },
120 { NULL, NULL },
121 };
122
123 for (const struct files* f = filenames; f->file; f++) {
124 if (strcmp(f->file, filename) == 0)
125 return f->legacy;
126 }
127
128 // Nothing found
129 return filename;
130}
131
f99c69bb
MT
132/*
133 A helper function to close the archive and reset our data structures
134*/
900faa2f 135static void close_archive(struct pakfire_archive* archive, struct archive* a) {
adf32d5b
MT
136 if (a)
137 archive_read_free(a);
f99c69bb
MT
138
139 // Rewind the file descriptor
140 rewind(archive->f);
141}
142
143/*
144 A helper function that opens the archive for reading
145*/
900faa2f 146static int open_archive(struct pakfire_archive* archive, struct archive** a) {
221cc3ce 147 *a = archive_read_new();
fc7feea4
MT
148 if (!*a)
149 return ENOMEM;
150
151 // All packages must be uncompressed tar balls
152 archive_read_support_format_tar(*a);
153
154 // Try opening the archive file
f99c69bb 155 int r = archive_read_open_FILE(*a, archive->f);
fc7feea4
MT
156 if (r) {
157 ERROR(archive->pakfire, "Could not open archive %s: %s\n",
158 archive->path, archive_error_string(*a));
f99c69bb 159 goto ERROR;
221cc3ce
MT
160 }
161
f99c69bb 162 // Success
fc7feea4 163 return 0;
f99c69bb
MT
164
165ERROR:
166 close_archive(archive, *a);
167 *a = NULL;
168
169 return r;
221cc3ce
MT
170}
171
74f822bc 172static int pakfire_archive_walk_entries(struct pakfire_archive* archive, struct archive* a,
1bd3e384
MT
173 int (*callback)(struct pakfire_archive* archive, struct archive* a,
174 struct archive_entry* e, int flags, void* data),
175 int flags, void* data, off_t* offset) {
74f822bc
MT
176 struct archive_entry* e = NULL;
177
178 // Walk through the archive
179 while (1) {
180 int r = archive_read_next_header(a, &e);
181
182 // Return OK when we reached the end of the archive
183 if (r == ARCHIVE_EOF) {
184 if (offset)
185 *offset = archive_read_header_position(a);
186
187 return ARCHIVE_OK;
188 }
189
190 // Raise any other errors
191 else if (r)
192 return r;
193
194 // Run callback
195 if (callback) {
1bd3e384 196 r = callback(archive, a, e, flags, data);
74f822bc
MT
197 if (r)
198 return r;
199 }
200 }
201
202 return 0;
203}
204
205static int pakfire_archive_walk(struct pakfire_archive* archive,
1bd3e384
MT
206 int (*callback)(struct pakfire_archive* archive, struct archive* a,
207 struct archive_entry* e, int flags, void* data),
208 int flags, void* data, off_t* offset) {
74f822bc
MT
209 struct archive* a;
210
211 // Open the archive file
212 int r = open_archive(archive, &a);
213 if (r)
214 return r;
215
216 // Walk through the archive
1bd3e384 217 r = pakfire_archive_walk_entries(archive, a, callback, flags, data, offset);
74f822bc
MT
218
219 // Close the archive
220 close_archive(archive, a);
221
222 return r;
223}
224
8a2f6c78
MT
225static int find_archive_entry(struct pakfire_archive* archive, struct archive_entry** entry,
226 struct archive* a, const char* filename) {
227 while (1) {
228 int r = archive_read_next_header(a, entry);
221cc3ce 229
8a2f6c78
MT
230 // Nothing found
231 if (r == ARCHIVE_EOF) {
232 break;
233
234 // Some error has occured
235 } else if (r)
236 return r;
237
238 // Compare the name
221cc3ce
MT
239 const char* entry_name = archive_entry_pathname(*entry);
240
8a2f6c78
MT
241 // Match?
242 if (strcmp(entry_name, filename) == 0)
221cc3ce 243 return 0;
221cc3ce
MT
244 }
245
9658e24a
MT
246 DEBUG(archive->pakfire, "Could not find an entry named '%s'\n", filename);
247 errno = ENOENT;
8a2f6c78
MT
248
249 // Nothing found
221cc3ce
MT
250 *entry = NULL;
251 return 1;
252}
253
9e339d9c
MT
254/*
255 A helper function that opens the archive and finds a certain file in it
256*/
900faa2f 257static int open_archive_and_find(struct pakfire_archive* archive, struct archive** a,
9e339d9c
MT
258 struct archive_entry** entry, const char* filename) {
259 int r = open_archive(archive, a);
260 if (r)
261 return r;
262
9d01571f
MT
263 // Fix filename
264 filename = pakfire_archive_legacy_filename(archive, filename);
265
8a2f6c78 266 r = find_archive_entry(archive, entry, *a, filename);
914c554e
MT
267
268 // Close archive on error
269 if (r)
270 close_archive(archive, *a);
271
272 return r;
9e339d9c
MT
273}
274
900faa2f 275static int open_archive_and_read(struct pakfire_archive* archive, const char* filename,
9e339d9c
MT
276 char** data, size_t* size) {
277 struct archive* a = NULL;
278 struct archive_entry* e = NULL;
279
280 // Open the archive and find the right file
281 int r = open_archive_and_find(archive, &a, &e, filename);
282 if (r)
283 return r;
284
285 // Read the file into memory
286 r = pakfire_archive_copy_data_to_buffer(archive->pakfire, a, e, data, size);
287
288 // Close the archive
289 close_archive(archive, a);
290
291 return r;
292}
293
74f822bc
MT
294/*
295 This function finds the end of the archive so that we can append more files
296*/
297static off_t pakfire_archive_find_end(struct pakfire_archive* archive, off_t* offset) {
1bd3e384 298 return pakfire_archive_walk(archive, NULL, 0, NULL, offset);
74f822bc
MT
299}
300
6a642ca8
MT
301static la_ssize_t pakfire_archive_read_callback(struct archive* a,
302 void* client_data, const void** buffer) {
303 struct archive* archive = (struct archive*)client_data;
221cc3ce 304
6a642ca8
MT
305 size_t len = 0;
306 off_t offset = 0;
221cc3ce 307
6a642ca8
MT
308 // Try reading the next block (without copying it)
309 int r = archive_read_data_block(archive, buffer, &len, &offset);
221cc3ce 310
6a642ca8
MT
311 switch (r) {
312 case ARCHIVE_OK:
313 return len;
221cc3ce 314
6a642ca8
MT
315 // Return zero to signal that everything was read
316 case ARCHIVE_EOF:
317 return 0;
318
319 // Return -1 on any other errors
320 default:
321 return -1;
322 }
221cc3ce
MT
323}
324
900faa2f 325static struct archive* pakfire_archive_open_payload(struct pakfire_archive* archive,
9e339d9c
MT
326 struct archive** a, size_t* size) {
327 struct archive_entry* entry = NULL;
adf32d5b 328 struct archive* payload = NULL;
9e339d9c 329
6a642ca8 330 // Find the payload
9d01571f 331 int r = open_archive_and_find(archive, a, &entry, "DATA");
6a642ca8 332 if (r)
adf32d5b 333 goto ERROR;
6a642ca8 334
4f85d6ee
MT
335 // Store size
336 if (size)
337 *size = archive_entry_size(entry);
338
6a642ca8 339 // Allocate a new archive object
adf32d5b 340 payload = archive_read_new();
6a642ca8 341 if (!payload)
adf32d5b 342 goto ERROR;
6a642ca8
MT
343
344 // All of our packages are tar balls
345 archive_read_support_format_tar(payload);
221cc3ce 346
6a642ca8 347 // They are compressed using XZ or ZSTD
843c5eb4 348 if (archive->format >= 6)
f85c6b76
MT
349 archive_read_support_filter_zstd(payload);
350 else
351 archive_read_support_filter_xz(payload);
221cc3ce 352
6a642ca8 353 // Try opening the payload archive
be38d82a 354 r = archive_read_open2(payload, *a, NULL, pakfire_archive_read_callback, NULL, NULL);
6a642ca8
MT
355 if (r) {
356 ERROR(archive->pakfire, "Could not open payload archive: %s\n",
357 archive_error_string(payload));
9e339d9c 358 goto ERROR;
6a642ca8 359 }
221cc3ce 360
6a642ca8 361 return payload;
9e339d9c
MT
362
363ERROR:
adf32d5b
MT
364 if (payload)
365 archive_read_free(payload);
9e339d9c
MT
366
367 return NULL;
221cc3ce
MT
368}
369
6cdbff80
MT
370// Checksum Stuff
371
900faa2f 372static int pakfire_archive_add_chksum(struct pakfire_archive* archive, const char* path,
26f52a59 373 const unsigned char* digest_sha512, const unsigned char* digest_sha256) {
6cdbff80 374 int r = 1;
12656820 375
5b2f4d99
MT
376 // Path must be set
377 if (!path) {
378 errno = EINVAL;
379 return 1;
380 }
381
382 // At least one of the digests must be set
383 if (!digest_sha512 && !digest_sha256) {
384 errno = EINVAL;
385 return 1;
386 }
387
6cdbff80
MT
388 // Allocate a new chksum object
389 struct pakfire_archive_chksum* chksum = calloc(1, sizeof(*chksum));
390 if (!chksum)
391 return ENOMEM;
6afc1aa6 392
6cdbff80
MT
393 // Store path
394 r = pakfire_string_set(chksum->path, path);
395 if (r < 0)
396 goto ERROR;
4885bc0d 397
5b2f4d99
MT
398 // SHA512
399 if (digest_sha512)
400 memcpy(chksum->digest_sha512, digest_sha512, sizeof(chksum->digest_sha512));
4885bc0d 401
5b2f4d99
MT
402 // SHA256
403 if (digest_sha256)
404 memcpy(chksum->digest_sha256, digest_sha256, sizeof(chksum->digest_sha256));
6cdbff80
MT
405
406 // Append it
407 STAILQ_INSERT_TAIL(&archive->chksums, chksum, nodes);
408
5b2f4d99 409 DEBUG(archive->pakfire, "Added checksum for %s\n", path);
6cdbff80
MT
410
411 return 0;
4885bc0d 412
6cdbff80
MT
413ERROR:
414 free(chksum);
415
416 return r;
417}
4efcd464 418
6cdbff80 419static struct pakfire_archive_chksum* pakfire_archive_find_chksum(
900faa2f 420 struct pakfire_archive* archive, const char* path) {
6cdbff80 421 struct pakfire_archive_chksum* chksum = NULL;
4efcd464 422
6cdbff80
MT
423 STAILQ_FOREACH(chksum, &archive->chksums, nodes) {
424 if (strcmp(chksum->path, path) == 0)
425 return chksum;
4efcd464
MT
426 }
427
428 // Nothing found
429 return NULL;
430}
431
6c23f67d
MT
432static int __pakfire_archive_chksum_has_digest(const unsigned char* digest, const size_t length) {
433 for (unsigned int i = 0; i < length; i++) {
434 if (digest[i])
435 return 1;
436 }
437
438 return 0;
439}
440
441#define pakfire_archive_chksum_has_digest(digest) \
442 __pakfire_archive_chksum_has_digest(digest, sizeof(digest))
443
900faa2f 444static void pakfire_archive_free_chksums(struct pakfire_archive* archive) {
6cdbff80
MT
445 struct pakfire_archive_chksum* chksum;
446
447 while ((chksum = STAILQ_FIRST(&archive->chksums))) {
448 STAILQ_REMOVE_HEAD(&archive->chksums, nodes);
449
450 free(chksum);
451 }
452}
453
900faa2f 454static void pakfire_archive_free(struct pakfire_archive* archive) {
f99c69bb
MT
455 // Close the file
456 if (archive->f)
457 fclose(archive->f);
458
6cdbff80
MT
459 // Free all checksums
460 pakfire_archive_free_chksums(archive);
461
101264c8
MT
462 // Free scriptlets
463 if (archive->scriptlets) {
e49b6359
MT
464 for (struct pakfire_scriptlet** scriptlet = archive->scriptlets; *scriptlet; scriptlet++)
465 pakfire_scriptlet_unref(*scriptlet);
101264c8
MT
466 free(archive->scriptlets);
467 }
468
2688763e
MT
469 if (archive->filelist)
470 pakfire_filelist_unref(archive->filelist);
49378184
MT
471 if (archive->package)
472 pakfire_package_unref(archive->package);
20b83302
MT
473 if (archive->metadata)
474 json_object_put(archive->metadata);
38cd2477
MT
475 if (archive->parser)
476 pakfire_parser_unref(archive->parser);
618ca500 477 pakfire_unref(archive->pakfire);
f0d6233d 478 free(archive);
221cc3ce
MT
479}
480
ac4c607b 481static int pakfire_archive_create(struct pakfire_archive** archive, struct pakfire* pakfire) {
900faa2f 482 struct pakfire_archive* a = calloc(1, sizeof(*a));
cf3e773a
MT
483 if (!a)
484 return ENOMEM;
485
cf3e773a
MT
486 a->pakfire = pakfire_ref(pakfire);
487 a->nrefs = 1;
488
cf3e773a
MT
489 STAILQ_INIT(&a->chksums);
490
cf3e773a
MT
491 *archive = a;
492
493 return 0;
cf3e773a
MT
494}
495
900faa2f 496PAKFIRE_EXPORT struct pakfire_archive* pakfire_archive_ref(struct pakfire_archive* archive) {
cf3e773a
MT
497 ++archive->nrefs;
498
499 return archive;
500}
501
900faa2f 502PAKFIRE_EXPORT struct pakfire_archive* pakfire_archive_unref(struct pakfire_archive* archive) {
b7600b1d 503 if (--archive->nrefs > 0)
75c60de7 504 return archive;
b7600b1d
MT
505
506 pakfire_archive_free(archive);
1dc5d1a5 507
75c60de7 508 return NULL;
b7600b1d
MT
509}
510
900faa2f 511static struct pakfire_package* pakfire_archive_get_package(struct pakfire_archive* archive) {
49378184 512 if (!archive->package) {
6097f779 513 int r = pakfire_archive_make_package(archive, NULL, &archive->package);
bb6ad70b 514 if (r)
49378184
MT
515 return NULL;
516 }
517
518 return pakfire_package_ref(archive->package);
519}
520
4372da14
MT
521// Metadata
522
20b83302
MT
523static int pakfire_archive_parse_json_metadata(struct pakfire_archive* archive) {
524 char* data = NULL;
525 size_t size = 0;
526
527 // Do nothing if metadata has already been loaded
528 if (archive->metadata)
529 return 0;
530
531 // Read the metadata file
532 int r = open_archive_and_read(archive, "PKGINFO", &data, &size);
533 if (r)
534 return r;
535
536 // Create tokener
537 struct json_tokener* tokener = json_tokener_new();
538 if (!tokener) {
539 ERROR(archive->pakfire, "Could not allocate JSON tokener: %m\n");
540 goto ERROR;
541 }
542
543 // Parse JSON from buffer
544 archive->metadata = json_tokener_parse_ex(tokener, data, size);
545 if (!archive->metadata) {
546 enum json_tokener_error error = json_tokener_get_error(tokener);
547
548 ERROR(archive->pakfire, "JSON parsing error: %s\n",
549 json_tokener_error_desc(error));
550 goto ERROR;
551 }
552
553 DEBUG(archive->pakfire, "Successfully parsed package metadata:\n%s\n",
554 json_object_to_json_string_ext(archive->metadata,
555 JSON_C_TO_STRING_PRETTY|JSON_C_TO_STRING_PRETTY_TAB));
556
557ERROR:
558 if (tokener)
559 json_tokener_free(tokener);
560
561 return r;
562}
563
564static int pakfire_archive_parse_legacy_metadata(struct pakfire_archive* archive) {
4372da14
MT
565 char* data = NULL;
566 size_t size;
567
568 // Do nothing if this has already been loaded
569 if (archive->parser)
570 return 0;
571
9e339d9c 572 // Read the metadata file
9d01571f 573 int r = open_archive_and_read(archive, "PKGINFO", &data, &size);
4372da14 574 if (r)
9e339d9c 575 return r;
4372da14
MT
576
577 // Allocate a new parser
578 archive->parser = pakfire_parser_create(archive->pakfire, NULL, NULL, 0);
579 if (!archive->parser)
580 goto ERROR;
581
582 // Parse the metadata
583 r = pakfire_parser_parse_data(archive->parser, data, size, NULL);
584 if (r) {
585 ERROR(archive->pakfire, "Error parsing metadata\n");
586 goto ERROR;
587 }
588
589 // Success
590 r = 0;
591 goto CLEANUP;
592
593ERROR:
594 if (archive->parser) {
595 pakfire_parser_unref(archive->parser);
596 archive->parser = NULL;
597 }
598
599CLEANUP:
600 if (data)
601 free(data);
4372da14
MT
602
603 return r;
604}
605
20b83302
MT
606static int pakfire_archive_parse_metadata(struct pakfire_archive* archive) {
607 // Parse JSON for newer formats
608 if (archive->format >= 6)
609 return pakfire_archive_parse_json_metadata(archive);
610
611 // Use legacy parser for others
612 return pakfire_archive_parse_legacy_metadata(archive);
613}
614
539e9063
MT
615/*
616 This function tries (very easily) to find out whether something is a valid
617 archive or not.
618*/
900faa2f 619static int pakfire_archive_read_format(struct pakfire_archive* archive, struct archive* a) {
539e9063
MT
620 struct archive_entry* entry = NULL;
621 char* data = NULL;
622 size_t size;
623
624 // Read the first header
625 int r = archive_read_next_header(a, &entry);
626 if (r) {
627 ERROR(archive->pakfire, "Could not read first tar header: %s\n",
628 archive_error_string(a));
629 goto ERROR;
630 }
631
04f4f88c
MT
632 const char* entry_name = archive_entry_pathname(entry);
633
634 // Check for filename (must be "pakfire-format")
635 if (strcmp(entry_name, "pakfire-format") != 0) {
636 DEBUG(archive->pakfire, "First file is not named \"pakfire-format\"");
637 r = 1;
638 goto ERROR;
639 }
640
641 // The file cannot be larger than a couple of bytes
642 size = archive_entry_size(entry);
643 if (size == 0 || size >= 128) {
644 DEBUG(archive->pakfire, "pakfire-format is of an invalid size: %zu bytes\n", size);
645 r = 1;
646 goto ERROR;
647 }
648
539e9063
MT
649 // Read the entire payload
650 r = pakfire_archive_copy_data_to_buffer(archive->pakfire, a, entry, &data, &size);
651 if (r) {
652 ERROR(archive->pakfire, "Could not read data of first file: %s\n",
653 archive_error_string(a));
654 goto ERROR;
655 }
656
657 // Parse the format
658 archive->format = strtoul(data, NULL, 10);
659
660 DEBUG(archive->pakfire, "Archive format is %d\n", archive->format);
661
662 // XXX check if this version is supported
663
664ERROR:
665 if (data)
666 free(data);
667
04f4f88c 668 return r;
539e9063
MT
669}
670
900faa2f 671static int pakfire_archive_try_open(struct pakfire_archive* archive, const char* path) {
9e339d9c
MT
672 struct archive* a = NULL;
673
79bd093e
MT
674 if (!path)
675 return EINVAL;
676
539e9063
MT
677 DEBUG(archive->pakfire, "Opening archive %s\n", path);
678
79bd093e
MT
679 // Store path
680 pakfire_string_set(archive->path, path);
cf3e773a 681
f99c69bb 682 // Open the file (and keep the file descriptor open)
1222938f 683 archive->f = fopen(archive->path, "r+");
f99c69bb
MT
684 if (!archive->f)
685 return 1;
686
cf3e773a 687 // Open the archive file for reading.
f99c69bb 688 int r = open_archive(archive, &a);
cf3e773a
MT
689 if (r)
690 goto ERROR;
691
539e9063
MT
692 // Read the format of this archive
693 r = pakfire_archive_read_format(archive, a);
694 if (r)
cf3e773a 695 goto ERROR;
cf3e773a 696
9e339d9c
MT
697 // Success
698 r = 0;
cf3e773a 699
9e339d9c
MT
700 // Reset errno
701 errno = 0;
cf3e773a
MT
702
703ERROR:
704 if (a)
9e339d9c 705 close_archive(archive, a);
cf3e773a
MT
706
707 return r;
708}
709
ac4c607b 710PAKFIRE_EXPORT int pakfire_archive_open(struct pakfire_archive** archive, struct pakfire* pakfire, const char* path) {
cf3e773a
MT
711 int r = pakfire_archive_create(archive, pakfire);
712 if (r)
713 return r;
714
715 r = pakfire_archive_try_open(*archive, path);
716 if (r)
717 goto ERROR;
718
719 return 0;
720
721ERROR:
722 pakfire_archive_unref(*archive);
e9c99cb1 723 *archive = NULL;
cf3e773a
MT
724
725 return r;
726}
727
20b83302
MT
728static struct json_object* pakfire_archive_metadata_get_object(
729 struct pakfire_archive* archive, const char* key1, const char* key2) {
730 int r = pakfire_archive_parse_metadata(archive);
731 if (r)
732 return NULL;
733
734 struct json_object* object = archive->metadata;
735
736 const char* keys[] = {
737 key1,
738 key2,
739 NULL,
740 };
741
742 // Walk through all keys
743 for (const char** key = keys; *key; key++) {
744 // Try finding a matching JSON object
745 r = json_object_object_get_ex(object, *key, &object);
746 if (!r) {
747 DEBUG(archive->pakfire, "Could not find JSON object at '%s': %m\n", *key);
748 break;
749 }
750 }
751
752 return object;
753}
754
755static const char* pakfire_archive_metadata_get(
756 struct pakfire_archive* archive, const char* key1, const char* key2) {
757 // Try finding an object
758 struct json_object* object = pakfire_archive_metadata_get_object(archive, key1, key2);
759 if (!object)
760 return NULL;
761
762 // Return the object as string
763 return json_object_get_string(object);
764}
765
766static int64_t pakfire_archive_metadata_get_int64(
767 struct pakfire_archive* archive, const char* key1, const char* key2) {
768 // Try finding an object
769 struct json_object* object = pakfire_archive_metadata_get_object(archive, key1, key2);
770 if (!object)
771 return 0;
772
773 // Return the object as integer
774 return json_object_get_int64(object);
775}
cf3e773a 776
900faa2f 777PAKFIRE_EXPORT char* pakfire_archive_get(struct pakfire_archive* archive, const char* namespace, const char* key) {
4372da14
MT
778 int r = pakfire_archive_parse_metadata(archive);
779 if (r)
780 return NULL;
781
aa75cc88 782 return pakfire_parser_get(archive->parser, namespace, key);
312fd26f
MT
783}
784
900faa2f 785PAKFIRE_EXPORT int pakfire_archive_read(struct pakfire_archive* archive, const char* filename,
5e09732d 786 char** data, size_t* size) {
9e339d9c
MT
787 struct archive* a = NULL;
788 struct archive_entry* entry = NULL;
a2c5efba 789 int r = 0;
221cc3ce 790
5e09732d
MT
791 if (!filename || !data || !size) {
792 errno = EINVAL;
793 return 1;
794 }
795
bd6c66de
MT
796 // Strip leading / from filenames, because the payload does
797 // not have leading slashes.
798 while (*filename == '/')
799 filename++;
800
9e339d9c 801 struct archive* payload = pakfire_archive_open_payload(archive, &a, NULL);
bd6c66de
MT
802 if (!payload)
803 goto ERROR;
221cc3ce 804
a2c5efba 805 r = find_archive_entry(archive, &entry, payload, filename);
9e339d9c 806 if (r)
bd6c66de 807 goto ERROR;
221cc3ce 808
bd6c66de 809 r = pakfire_archive_copy_data_to_buffer(archive->pakfire, payload, entry,
5e09732d 810 data, size);
221cc3ce 811
bd6c66de
MT
812ERROR:
813 if (payload)
814 archive_read_free(payload);
9e339d9c 815 close_archive(archive, a);
221cc3ce
MT
816
817 return r;
818}
819
285b2758
MT
820int pakfire_archive_copy(struct pakfire_archive* archive, const char* path) {
821 if (!path) {
822 errno = EINVAL;
823 return 1;
824 }
825
826 // Determine the file size
827 ssize_t size = pakfire_archive_get_size(archive);
828 if (size < 0)
829 return 1;
830
831 DEBUG(archive->pakfire, "Copying %s to %s...\n", archive->path, path);
832
833 // Open destination file
834 FILE* f = fopen(path, "w");
835 if (!f)
836 return 1;
837
838 int r = 1;
839
840 // Copy everything
841 ssize_t bytes_written = sendfile(fileno(f), fileno(archive->f), NULL, size);
842 if (bytes_written != size)
843 goto ERROR;
844
845 // Success
846 r = 0;
847
848ERROR:
849 fclose(f);
850
851 // Delete the file on error
852 if (r)
853 unlink(path);
854
855 rewind(archive->f);
856
857 return r;
858}
859
79824416
MT
860PAKFIRE_EXPORT int pakfire_archive_extract(struct pakfire_archive* archive) {
861 struct pakfire_package* pkg = NULL;
862 const char* prefix = NULL;
863 struct archive* a = NULL;
864 struct archive* payload = NULL;
865 size_t size = 0;
f7a82ba4 866 int r = 1;
94131aa0 867
79824416
MT
868 // Fetch package
869 pkg = pakfire_archive_get_package(archive);
f7a82ba4
MT
870 if (!pkg)
871 goto ERROR;
4f85d6ee 872
79824416
MT
873 // Fetch NEVRA
874 const char* nevra = pakfire_package_get_nevra(pkg);
4f85d6ee 875
79824416 876 DEBUG(archive->pakfire, "Extracting %s\n", archive->path);
4f85d6ee 877
79824416
MT
878 // Set prefix for source packages
879 if (pakfire_package_is_source(pkg))
880 prefix = "/usr/src/packages";
4f85d6ee 881
bd6c66de 882 // Open payload
9e339d9c 883 payload = pakfire_archive_open_payload(archive, &a, &size);
bd6c66de
MT
884 if (!payload)
885 goto ERROR;
221cc3ce 886
79824416
MT
887 // Extract
888 r = pakfire_extract(archive->pakfire, payload, size, prefix, nevra, 0);
889 if (r)
b1875f57
MT
890 goto ERROR;
891
52adc63f 892ERROR:
79824416
MT
893 if (pkg)
894 pakfire_package_unref(pkg);
52adc63f
MT
895 if (payload)
896 archive_read_free(payload);
adf32d5b
MT
897 if (a)
898 close_archive(archive, a);
221cc3ce
MT
899
900 return r;
901}
902
900faa2f 903PAKFIRE_EXPORT const char* pakfire_archive_get_path(struct pakfire_archive* archive) {
221cc3ce
MT
904 return archive->path;
905}
906
900faa2f 907PAKFIRE_EXPORT unsigned int pakfire_archive_get_format(struct pakfire_archive* archive) {
221cc3ce
MT
908 return archive->format;
909}
910
900faa2f 911static int pakfire_archive_load_filelist_mtree(struct pakfire_archive* archive) {
d0985e31
MT
912 struct archive* a;
913 struct archive_entry* entry;
914 int r;
915
916 // Find filelist
9d01571f 917 r = open_archive_and_find(archive, &a, &entry, "FILELIST");
b40854fa
MT
918 if (r) {
919 // Ignore if filelist doesn't exist
920 if (errno == ENOENT)
921 return 0;
922
d0985e31 923 return r;
b40854fa 924 }
d0985e31
MT
925
926 // Allocate a new archive reader
927 struct archive* mtree = archive_read_new();
928 if (!mtree) {
929 ERROR(archive->pakfire, "Could not allocate mtree reader\n");
930 goto ERROR;
931 }
932
933 // Enable reading the mtree format
934 r = archive_read_support_format_mtree(mtree);
935 if (r) {
936 ERROR(archive->pakfire, "Could not enable mtree format: %s\n",
937 archive_error_string(mtree));
938 goto ERROR;
939 }
940
941 // Filelists can be Zstandard-compressed
942 r = archive_read_support_filter_zstd(mtree);
943 if (r)
944 goto ERROR;
945
946 // Try opening the mtree
947 r = archive_read_open2(mtree, a, NULL, pakfire_archive_read_callback, NULL, NULL);
948 if (r) {
bbc713f9
MT
949 ERROR(archive->pakfire, "Could not open filelist mtree in %s: %s\n",
950 archive->path, archive_error_string(mtree));
d0985e31
MT
951 goto ERROR;
952 }
953
954 // Read filelist
955 r = pakfire_filelist_create(&archive->filelist, archive->pakfire);
956 if (r)
957 goto ERROR;
958
959 for (;;) {
5803b5f6 960 struct pakfire_file* file;
d0985e31
MT
961
962 r = archive_read_next_header(mtree, &entry);
c731012d
MT
963 if (r == ARCHIVE_EOF) {
964 r = 0;
d0985e31 965 break;
c731012d 966 } else if (r)
d0985e31
MT
967 goto ERROR;
968
969 // Create a new file object
970 r = pakfire_file_create_from_archive_entry(&file, archive->pakfire, entry);
971 if (r)
972 goto ERROR;
973
d0985e31
MT
974 // Append to filelist
975 r = pakfire_filelist_append(archive->filelist, file);
976 pakfire_file_unref(file);
977 if (r)
978 goto ERROR;
979 }
980
981ERROR:
982 // Destroy the filelist on error
983 if (r && archive->filelist) {
984 pakfire_filelist_unref(archive->filelist);
985 archive->filelist = NULL;
986 }
987
988 if (mtree)
989 archive_read_free(mtree);
990 close_archive(archive, a);
991
992 return r;
993}
994
900faa2f 995static int pakfire_archive_load_filelist_legacy(struct pakfire_archive* archive) {
ac6bafe1
MT
996 char* data = NULL;
997 size_t size;
998
9e339d9c 999 // Read filelist
9d01571f 1000 int r = open_archive_and_read(archive, "FILELIST", &data, &size);
b40854fa
MT
1001 if (r) {
1002 if (errno == ENOENT)
1003 return 0;
1004
9e339d9c 1005 return r;
b40854fa 1006 }
ac6bafe1
MT
1007
1008 DEBUG(archive->pakfire, "Read filelist:\n%.*s\n", (int)size, data);
1009
1010 r = pakfire_filelist_create_from_file(&archive->filelist,
1011 archive->pakfire, data, archive->format);
7c42071c 1012 if (r)
b1772bfb 1013 ERROR(archive->pakfire, "Could not parse filelist: %m\n");
ac6bafe1 1014
7c42071c
MT
1015 if (data)
1016 free(data);
ac6bafe1 1017
ac6bafe1
MT
1018 // Destroy the filelist on error
1019 if (r && archive->filelist) {
1020 pakfire_filelist_unref(archive->filelist);
1021 archive->filelist = NULL;
1022 }
1023
ac6bafe1
MT
1024 return r;
1025}
1026
900faa2f 1027static int pakfire_archive_load_filelist(struct pakfire_archive* archive) {
d0985e31
MT
1028 if (archive->format >= 6)
1029 return pakfire_archive_load_filelist_mtree(archive);
1030 else
1031 return pakfire_archive_load_filelist_legacy(archive);
1032}
1033
1bbbfb9e 1034PAKFIRE_EXPORT struct pakfire_filelist* pakfire_archive_get_filelist(struct pakfire_archive* archive) {
4140a923
MT
1035 if (!archive->filelist) {
1036 int r = pakfire_archive_load_filelist(archive);
1037 if (r)
1038 return NULL;
1039 }
4140a923 1040
72e1652b
MT
1041 if (!archive->filelist)
1042 return NULL;
1043
4140a923 1044 return pakfire_filelist_ref(archive->filelist);
221cc3ce 1045}
618ca500 1046
b6a5c6ee 1047static int pakfire_archive_load_checksums_mtree(struct pakfire_archive* archive) {
9e144f8c
MT
1048 struct archive* a = NULL;
1049 struct archive_entry* entry = NULL;
1050 int r;
1051
1052 // Find chksums
9d01571f 1053 r = open_archive_and_find(archive, &a, &entry, "FILELIST");
9e144f8c
MT
1054 if (r)
1055 return r;
1056
1057 // Allocate a new archive reader
1058 struct archive* mtree = archive_read_new();
1059 if (!mtree) {
1060 ERROR(archive->pakfire, "Could not allocate mtree reader\n");
1061 goto ERROR;
1062 }
1063
1064 // Enable reading the mtree format
1065 r = archive_read_support_format_mtree(mtree);
1066 if (r) {
1067 ERROR(archive->pakfire, "Could not enable mtree format: %s\n",
1068 archive_error_string(mtree));
1069 goto ERROR;
1070 }
1071
1072 // Filelists can be Zstandard-compressed
1073 r = archive_read_support_filter_zstd(mtree);
1074 if (r)
1075 goto ERROR;
1076
1077 // Try opening the mtree
1078 r = archive_read_open2(mtree, a, NULL, pakfire_archive_read_callback, NULL, NULL);
1079 if (r) {
bbc713f9
MT
1080 ERROR(archive->pakfire, "Could not open checksum mtree in %s: %s\n",
1081 archive->path, archive_error_string(mtree));
9e144f8c
MT
1082 goto ERROR;
1083 }
1084
1085 for (;;) {
1086 // Read next entry
1087 r = archive_read_next_header(mtree, &entry);
1088
1089 // We have reached the end of the list
1090 if (r == ARCHIVE_EOF)
1091 break;
1092
1093 // An unexpected error has occurred
1094 else if (r) {
1095 ERROR(archive->pakfire, "Could not read mtree entry: %s\n",
1096 archive_error_string(mtree));
1097 goto ERROR;
1098 }
1099
1100 // Fetch pathname
1101 const char* path = archive_entry_pathname(entry);
1102
1103 // Fetch all supported digests
1104 const unsigned char* digest_sha512 = archive_entry_digest(
1105 entry, ARCHIVE_ENTRY_DIGEST_SHA512);
1106 const unsigned char* digest_sha256 = archive_entry_digest(
1107 entry, ARCHIVE_ENTRY_DIGEST_SHA256);
1108
1109 // Add the checksum to the internal list
5ecc1942 1110 r = pakfire_archive_add_chksum(archive, path + 2, digest_sha512, digest_sha256);
9e144f8c
MT
1111 if (r)
1112 goto ERROR;
1113 }
1114
1115 // Success
1116 r = 0;
1117
1118ERROR:
1119 if (mtree)
1120 archive_read_free(mtree);
1121 close_archive(archive, a);
1122
1123 return r;
b6a5c6ee
MT
1124}
1125
c4251bab
MT
1126static int pakfire_archive_load_checksums_legacy_line(
1127 struct pakfire_archive* archive, char* line) {
1128 char path[PATH_MAX] = "";
1129 char hexdigest[LINE_MAX] = "";
1130 char* tok = NULL;
1131
1132 unsigned int i = 0;
1133 int r = 1;
1134
1135 char* word = strtok_r(line, " ", &tok);
1136 while (word) {
1137 switch (i) {
1138 // path
1139 case 0:
1140 r = pakfire_string_set(path, word);
1141 if (r < 0)
1142 return r;
1143 break;
1144
1145 // hexdigest
1146 case 1:
1147 r = pakfire_string_set(hexdigest, word);
1148 if (r < 0)
1149 return r;
1150 break;
1151
1152 default:
1153 ERROR(archive->pakfire, "Invalid line in checksums:\n%s\n", line);
1154 r = 1;
1155 return r;
1156 }
1157
1158 word = strtok_r(NULL, " ", &tok);
1159 i++;
1160 }
1161
1162 // Check if we have input for path and hexdigest
1163 if (!*path || !*hexdigest) {
1164 errno = EINVAL;
1165 return r;
1166 }
1167
1168 unsigned char digest[EVP_MAX_MD_SIZE];
1169
1170 // Convert hexdigest to digest
01f8f6b0 1171 r = pakfire_unhexlify(digest, hexdigest);
c4251bab
MT
1172 if (r)
1173 return r;
1174
1175 // Add a chksum object
1176 r = pakfire_archive_add_chksum(archive, path, digest, NULL);
1177 if (r)
1178 return r;
1179
1180 // Success
b6a5c6ee
MT
1181 return 0;
1182}
1183
c4251bab
MT
1184static int pakfire_archive_load_checksums_legacy(struct pakfire_archive* archive) {
1185 char* buffer = NULL;
1186 size_t length = 0;
1187
1188 // Find chksums
9d01571f 1189 int r = open_archive_and_read(archive, "CHKSUMS", &buffer, &length);
c4251bab
MT
1190 if (r)
1191 return r;
1192
1193 // Empty file
1194 if (!length) {
1195 ERROR(archive->pakfire, "Checksums file was unexpectedly empty\n");
1196 return 1;
1197 }
1198
1199 char line[LINE_MAX];
1200 char* l = line;
1201
1202 // Split the input into lines and call pakfire_archive_load_checksums_legacy_line
1203 for (char* p = buffer; (size_t)(p - buffer) < length; p++) {
1204 if (*p == '\n' || p == (buffer + length)) {
1205 // Terminate line
1206 *l = '\0';
1207
1208 // Process the line
1209 r = pakfire_archive_load_checksums_legacy_line(archive, line);
1210 if (r)
1211 goto ERROR;
1212
1213 // Reset line pointer
1214 l = line;
1215
1216 continue;
1217 }
1218
1219 // Copy character to line
1220 *l++ = *p;
1221
1222 // Check if line has enough space
1223 if ((size_t)(l - line) >= sizeof(line)) {
1224 errno = ENOBUFS;
1225 goto ERROR;
1226 }
1227 }
1228
1229ERROR:
1230 if (buffer)
1231 free(buffer);
1232 return r;
1233}
1234
b6a5c6ee 1235static int pakfire_archive_load_checksums(struct pakfire_archive* archive) {
9e144f8c
MT
1236 // Do nothing if the list has already been loaded
1237 if (!STAILQ_EMPTY(&archive->chksums))
1238 return 0;
1239
f05171a4
MT
1240 DEBUG(archive->pakfire, "Loading checksums from archive %p\n", archive);
1241
b6a5c6ee
MT
1242 if (archive->format >= 6)
1243 return pakfire_archive_load_checksums_mtree(archive);
1244 else
1245 return pakfire_archive_load_checksums_legacy(archive);
1246}
1247
6c23f67d
MT
1248struct pakfire_archive_validator {
1249 EVP_MD_CTX* ctx;
1250 const unsigned char* digest;
1251};
1252
1253static int pakfire_archive_verify_add_validator(struct pakfire_archive_validator*** list,
2ab9cb5e
MT
1254 struct pakfire* pakfire, const EVP_MD* md, const unsigned char* digest,
1255 enum pakfire_archive_verify_flags flags) {
1256
1257 switch (flags) {
1258 // Fall through and add the validator
1259 case PAKFIRE_ARCHIVE_VERIFY_ALL:
1260 break;
1261
1262 // We only accept one validator, so this function becomes a no-op when list
1263 // has any validators already
1264 case PAKFIRE_ARCHIVE_VERIFY_BEST:
1265 if (*list)
1266 return 0;
1267 break;
1268
1269 default:
1270 errno = EINVAL;
1271 return 1;
1272 }
1273
1274 // Allocate validator
6c23f67d
MT
1275 struct pakfire_archive_validator* v = calloc(1, sizeof(*v));
1276 if (!v)
1277 return 1;
1278
1279 int r = 1;
1280
1281 // Allocate an EVP context
1282 v->ctx = EVP_MD_CTX_new();
1283 if (!v->ctx) {
1284 ERROR(pakfire, "Could not allocate an EVP context\n");
1285 goto ERROR;
1286 }
1287
1288 // Initialise the hash algorithm
1289 r = EVP_DigestInit_ex(v->ctx, md, NULL);
1290 if (r != 1) {
1291 ERROR(pakfire, "Could not initialize hash algorithm: %s\n",
1292 ERR_error_string(ERR_get_error(), NULL));
1293 r = 1;
1294 goto ERROR;
1295 }
1296
1297 // Store digest
1298 v->digest = digest;
1299
1300 unsigned int counter = 0;
1301
1302 // Calculate list size
1303 if (*list) {
1304 for (struct pakfire_archive_validator** l = *list; *l; l++)
1305 counter++;
1306 }
1307
1308 // Grow list
1309 *list = reallocarray(*list, counter + 2, sizeof(**list));
1310 if (!*list) {
1311 r = 1;
1312 goto ERROR;
1313 }
1314
1315 // Append validator
1316 (*list)[counter] = v;
1317
1318 // Terminate list
1319 (*list)[counter + 1] = NULL;
1320
1321 // Success
1322 return 0;
1323
1324ERROR:
1325 if (v->ctx)
1326 EVP_MD_CTX_free(v->ctx);
1327 free(v);
1328
1329 return r;
1330}
1331
1332static int pakfire_archive_verify_file(struct pakfire_archive* archive,
1333 struct archive* a, struct archive_entry* entry, int flags, void* data) {
1334 const char* path = archive_entry_pathname(entry);
1335
e6a473a3
MT
1336 // Signatures do not have checksums
1337 if (pakfire_string_startswith(path, "signatures/"))
1338 return 0;
1339
1340 // Some files do not have checksums
1341 for (const char** file = pakfire_archive_files_without_chksums; *file; file++) {
1342 if (strcmp(*file, path) == 0)
1343 return 0;
1344 }
1345
6c23f67d
MT
1346 // Fetch the checksum
1347 struct pakfire_archive_chksum* chksum = pakfire_archive_find_chksum(archive, path);
1348 if (!chksum) {
e6a473a3
MT
1349 ERROR(archive->pakfire, "No checksum found for %s\n", path);
1350 return 1;
6c23f67d
MT
1351 }
1352
1353 struct pakfire_archive_validator** validators = NULL;
1354 int r;
1355
1356 // SHA512
1357 if (pakfire_archive_chksum_has_digest(chksum->digest_sha512)) {
1358 r = pakfire_archive_verify_add_validator(&validators, archive->pakfire,
2ab9cb5e 1359 EVP_sha512(), chksum->digest_sha512, flags);
6c23f67d
MT
1360 if (r)
1361 return r;
1362 }
1363
1364 // SHA256
1365 if (pakfire_archive_chksum_has_digest(chksum->digest_sha256)) {
1366 r = pakfire_archive_verify_add_validator(&validators, archive->pakfire,
2ab9cb5e 1367 EVP_sha256(), chksum->digest_sha256, flags);
6c23f67d
MT
1368 if (r)
1369 return r;
1370 }
1371
1372 // Check if anything was selected (this should not happen)
1373 if (!validators) {
1374 ERROR(archive->pakfire, "No validators selected\n");
1375 return 1;
1376 }
1377
1378 DEBUG(archive->pakfire, "Verifying %s\n", path);
1379
1380 const void* buffer = NULL;
1381 size_t size = 0;
1382 off_t offset = 0;
1383
1384 // Read the payload of the file and feed it into the validators
1385 for (;;) {
23b93958 1386 r = archive_read_data_block(a, &buffer, &size, &offset);
6c23f67d
MT
1387 if (r == ARCHIVE_EOF)
1388 break;
1389
1390 // End on any errors
1391 if (r) {
1392 r = 1;
1393 goto ERROR;
1394 }
1395
1396 // Update all hash digests
1397 for (struct pakfire_archive_validator** v = validators; *v; v++) {
1398 r = EVP_DigestUpdate((*v)->ctx, buffer, size);
1399 if (r != 1) {
1400 ERROR(archive->pakfire, "%s\n", ERR_error_string(ERR_get_error(), NULL));
1401 r = 1;
1402 goto ERROR;
1403 }
1404 }
1405 }
1406
1407 unsigned char digest[EVP_MAX_MD_SIZE];
1408
1409 // Finalize all digests
1410 for (struct pakfire_archive_validator** v = validators; *v; v++) {
1411 // Reset digest array size
1412 unsigned int length = sizeof(digest);
1413
1414 // Finish computation
1415 r = EVP_DigestFinal_ex((*v)->ctx, digest, &length);
1416 if (r != 1) {
1417 ERROR(archive->pakfire, "%s\n", ERR_error_string(ERR_get_error(), NULL));
1418 r = 1;
1419 goto ERROR;
1420 }
1421
1422 // Compare digests
1423 r = CRYPTO_memcmp(digest, (*v)->digest, length);
1424
1425 // Handle mismatches
1426 if (r) {
1427 ERROR(archive->pakfire, "Checksum of %s did not match\n", chksum->path);
fa106e1b
MT
1428
1429 char* hexdigest_expected = pakfire_hexlify((*v)->digest);
1430 if (hexdigest_expected) {
1431 ERROR(archive->pakfire, " Expected: %s\n", hexdigest_expected);
1432 free(hexdigest_expected);
1433 }
1434
1435 char* hexdigest_computed = pakfire_hexlify(digest);
1436 if (hexdigest_computed) {
1437 ERROR(archive->pakfire, " Computed: %s\n", hexdigest_computed);
1438 free(hexdigest_computed);
1439 }
1440
6c23f67d
MT
1441 goto ERROR;
1442 }
1443 }
1444
1445 DEBUG(archive->pakfire, "All (checked) checksums of %s matched\n", chksum->path);
1446
1447 // Success
1448 r = 0;
1449
1450ERROR:
1451 if (validators) {
1452 for (struct pakfire_archive_validator** v = validators; *v; v++) {
1453 EVP_MD_CTX_free((*v)->ctx);
1454 free(*v);
1455 }
1456 free(validators);
1457 }
1458
1459 return r;
1460}
1461
1462static int pakfire_archive_verify_checksums(struct pakfire_archive* archive, int flags) {
f05171a4
MT
1463 int r;
1464
1465 // Read checksums from archive
1466 r = pakfire_archive_load_checksums(archive);
1467 if (r)
1468 return r;
1469
6c23f67d
MT
1470 // Iterate over all files and verify them if a checksum is present
1471 r = pakfire_archive_walk(archive, pakfire_archive_verify_file, flags, NULL, NULL);
1472 if (r) {
1473 ERROR(archive->pakfire, "File verification failed\n");
1474 return r;
1475 }
1476
f05171a4
MT
1477 return 0;
1478}
1479
5bf664c6
MT
1480struct pakfire_archive_signature_check {
1481 gpgme_data_t checksums;
1482};
1483
6bf26d8f
MT
1484/*
1485 This function is called to examine whether we have a signature and if so verify it
1486*/
900faa2f 1487static int pakfire_archive_verify_signature(struct pakfire_archive* archive, struct archive* a,
1bd3e384 1488 struct archive_entry* e, int flags, void* data) {
6bf26d8f
MT
1489 const char* entry_name = archive_entry_pathname(e);
1490
1491 // This is not a signature
1492 if (!pakfire_string_startswith(entry_name, "signatures/"))
1493 return 0;
1494
1495 // Fetch GPGME context
1496 gpgme_ctx_t gpgctx = pakfire_get_gpgctx(archive->pakfire);
1497 if (!gpgctx)
1498 return 1;
1499
5bf664c6 1500 struct pakfire_archive_signature_check* check = (struct pakfire_archive_signature_check*)data;
6bf26d8f
MT
1501
1502 char* buffer = NULL;
1503 size_t size = 0;
1504
1505 // Load the signature into memory
1506 int r = pakfire_archive_copy_data_to_buffer(archive->pakfire, a, e, &buffer, &size);
1507 if (r)
1508 return 1;
1509
1510 gpgme_data_t signature;
1511 gpgme_error_t error;
1512
1513 // Make signature readable for GPGME
1514 error = gpgme_data_new_from_mem(&signature, buffer, size, 0);
1515 if (error != GPG_ERR_NO_ERROR) {
1516 r = 1;
1517 goto ERROR;
1518 }
1519
6bf26d8f 1520 // Perform verification
5bf664c6 1521 error = gpgme_op_verify(gpgctx, signature, check->checksums, NULL);
6bf26d8f
MT
1522 if (error != GPG_ERR_NO_ERROR)
1523 goto ERROR;
1524
1525 // Run the operation
1526 gpgme_verify_result_t result = gpgme_op_verify_result(gpgctx);
1527
1528 // Check if any signatures have been returned
1529 if (!result || !result->signatures)
1530 goto ERROR;
1531
6bf26d8f
MT
1532 // Walk through all signatures
1533 for (gpgme_signature_t sig = result->signatures; sig; sig = sig->next) {
e3dbe10b
MT
1534 // Log some information about this signature
1535 DEBUG(archive->pakfire, "Found signature %s\n", sig->fpr);
1536 DEBUG(archive->pakfire, " Status : %s\n", gpgme_strerror(sig->status));
1537 DEBUG(archive->pakfire, " Timestamp : %lu\n", sig->timestamp);
1538 if (sig->exp_timestamp)
1539 DEBUG(archive->pakfire, " Expires : %lu\n", sig->exp_timestamp);
1540 DEBUG(archive->pakfire, " Validity : %s\n", gpgme_strerror(sig->validity_reason));
1541
6bf26d8f
MT
1542 switch (gpg_err_code(sig->status)) {
1543 // All good
1544 case GPG_ERR_NO_ERROR:
4033ec60 1545 archive->verify = PAKFIRE_ARCHIVE_VERIFY_OK;
6bf26d8f
MT
1546 break;
1547
1548 // Key has expired (still good)
1549 case GPG_ERR_KEY_EXPIRED:
4033ec60 1550 archive->verify = PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED;
6bf26d8f
MT
1551 break;
1552
1553 // Signature has expired (bad)
1554 case GPG_ERR_SIG_EXPIRED:
4033ec60 1555 archive->verify = PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED;
6bf26d8f
MT
1556 break;
1557
1558 // We don't have the key
1559 case GPG_ERR_NO_PUBKEY:
4033ec60 1560 archive->verify = PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN;
6bf26d8f
MT
1561 break;
1562
1563 // Bad signature (or any other errors)
1564 case GPG_ERR_BAD_SIGNATURE:
1565 default:
4033ec60 1566 archive->verify = PAKFIRE_ARCHIVE_VERIFY_INVALID;
6bf26d8f
MT
1567 break;
1568 }
1569 }
1570
1571ERROR:
1572 // Free signature
1573 gpgme_data_release(signature);
1574 if (buffer)
1575 free(buffer);
1576
1577 return r;
1578}
1579
1580/*
1581 This function walks through the archive looking for signatures and verifies them
1582*/
5fa89cc9
MT
1583static int pakfire_archive_verify_signatures(struct pakfire_archive* archive,
1584 struct pakfire_key*** keys) {
6bf26d8f
MT
1585 char* buffer = NULL;
1586 size_t size = 0;
1587
5944cda3
MT
1588 // Fetch GPGME context (to initialize GPGME)
1589 gpgme_ctx_t gpgctx = pakfire_get_gpgctx(archive->pakfire);
1590 if (!gpgctx)
1591 return 1;
1592
6bf26d8f
MT
1593 // Find checksums
1594 int r = open_archive_and_read(archive, "chksums", &buffer, &size);
1595 if (r) {
1596 ERROR(archive->pakfire, "Could not open chksums file: %m\n");
1597 return r;
1598 }
1599
5bf664c6 1600 struct pakfire_archive_signature_check check;
6bf26d8f
MT
1601
1602 // Convert checksums readable for GPGME
5bf664c6 1603 gpgme_error_t error = gpgme_data_new_from_mem(&check.checksums, buffer, size, 0);
6bf26d8f 1604 if (error != GPG_ERR_NO_ERROR) {
5944cda3 1605 ERROR(archive->pakfire, "Could not initialize chksums: %s\n", gpgme_strerror(error));
6bf26d8f
MT
1606 r = 1;
1607 goto ERROR;
1608 }
1609
457eb699
MT
1610 // No signatures yet (will be reset later, maybe)
1611 archive->verify = PAKFIRE_ARCHIVE_VERIFY_NOT_SIGNED;
1612
6bf26d8f 1613 // Verify all signatures
5bf664c6 1614 r = pakfire_archive_walk(archive, pakfire_archive_verify_signature, 0, &check, NULL);
6bf26d8f
MT
1615
1616ERROR:
5bf664c6 1617 gpgme_data_release(check.checksums);
6bf26d8f
MT
1618 if (buffer)
1619 free(buffer);
1620
1621 return r;
1622}
1623
d2b1e88a 1624PAKFIRE_EXPORT int pakfire_archive_verify(struct pakfire_archive* archive,
5fa89cc9
MT
1625 pakfire_archive_verify_status_t* status, struct pakfire_key*** keys) {
1626 int r;
1627
98394ca4
MT
1628 DEBUG(archive->pakfire, "Verifying archive %p\n", archive);
1629
6bf26d8f 1630 // Return previous result if this has already been called
d2b1e88a 1631 if (archive->verify == PAKFIRE_ARCHIVE_VERIFY_UNKNOWN) {
f05171a4 1632 // Verify all signatures
23b93958 1633 r = pakfire_archive_verify_signatures(archive, keys);
d2b1e88a 1634 if (r)
5fa89cc9 1635 goto ERROR;
f05171a4
MT
1636
1637 // Verify checksums
2ab9cb5e 1638 r = pakfire_archive_verify_checksums(archive, PAKFIRE_ARCHIVE_VERIFY_BEST);
f05171a4 1639 if (r)
5fa89cc9 1640 goto ERROR;
d2b1e88a 1641 }
6bf26d8f 1642
d2b1e88a
MT
1643 // Store result
1644 *status = archive->verify;
52839d2f 1645
2bcdb544
MT
1646 // Log error
1647 ERROR(archive->pakfire, "Archive verification for %s has failed: %s\n",
1648 archive->path, pakfire_archive_verify_strerror(archive->verify));
1649
d2b1e88a 1650 return 0;
5fa89cc9
MT
1651
1652ERROR:
1653 if (keys && *keys) {
1654 for (struct pakfire_key** key = *keys; *key; key++)
1655 pakfire_key_unref(*key);
1656 free(*keys);
1657 }
1658
1659 return r;
618ca500
MT
1660}
1661
9f953e68 1662PAKFIRE_EXPORT const char* pakfire_archive_verify_strerror(pakfire_archive_verify_status_t status) {
618ca500 1663 switch (status) {
8b1ca890
MT
1664 case PAKFIRE_ARCHIVE_VERIFY_UNKNOWN:
1665 return _("Unknown");
1666
1667 case PAKFIRE_ARCHIVE_VERIFY_NOT_SIGNED:
1668 return _("Not signed");
1669
618ca500
MT
1670 case PAKFIRE_ARCHIVE_VERIFY_OK:
1671 return _("Verify OK");
1672
4efcd464
MT
1673 case PAKFIRE_ARCHIVE_VERIFY_ERROR:
1674 return _("Error performing validation");
1675
618ca500
MT
1676 case PAKFIRE_ARCHIVE_VERIFY_INVALID:
1677 return _("Invalid signature");
1678
1679 case PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED:
1680 return _("Signature expired");
1681
1682 case PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED:
1683 return _("Key expired");
1684
1685 case PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN:
1686 return _("Key unknown");
1687 }
1688
8b1ca890 1689 return _("Unknown error");
618ca500 1690}
312fd26f 1691
b6a5c6ee 1692static int pakfire_archive_create_signature(struct pakfire_archive* archive,
307188ab 1693 struct pakfire_key* key, char** signature, size_t* signature_length, time_t* timestamp) {
b6a5c6ee
MT
1694 char* buffer = NULL;
1695 size_t length = 0;
1696
1697 // Read chksums
1698 int r = open_archive_and_read(archive, "chksums", &buffer, &length);
95c4b89c
MT
1699 if (r) {
1700 ERROR(archive->pakfire, "Could not read chksums file: %m\n");
b6a5c6ee 1701 goto ERROR;
95c4b89c 1702 }
b6a5c6ee
MT
1703
1704 // Use the key to sign the buffer
307188ab 1705 r = pakfire_key_sign(key, buffer, length, signature, signature_length, timestamp);
b6a5c6ee
MT
1706 if (r)
1707 goto ERROR;
1708
1709ERROR:
1710 if (buffer)
1711 free(buffer);
1712
1713 return r;
1714}
1715
95c4b89c
MT
1716/*
1717 This function appends a single file to the archive
1718
1719 Unfortunately libarchive cannot do this, so we have to manually find the end of the
1720 archive and write another entry...
1721*/
1722static int pakfire_archive_append_file(struct pakfire_archive* archive,
1723 struct archive_entry* entry, const char* buffer, const size_t length) {
1724 off_t offset = 0;
1725
1726 // Find the end of the archive (exclusing the tar trailer)
1727 int r = pakfire_archive_find_end(archive, &offset);
1728 if (r)
1729 return r;
1730
1731 // Create a new archive writer
1732 struct archive* a = archive_write_new();
1733 if (!a) {
1734 ERROR(archive->pakfire, "Could not create an archive writer: %m\n");
1735 return 1;
1736 }
1737
1738 // Use the PAX format
1739 r = archive_write_set_format_pax(a);
1740 if (r) {
1741 ERROR(archive->pakfire, "Could not set format to PAX: %s\n", archive_error_string(a));
1742 goto ERROR;
1743 }
1744
1745 // Seek to the end of the archive
1746 r = fseek(archive->f, offset, SEEK_SET);
1747 if (r) {
1748 ERROR(archive->pakfire, "Could not seek to position %jd in archive: %m\n", offset);
1749 return r;
1750 }
1751
1752 // Write archive to f
1753 r = archive_write_open_FILE(a, archive->f);
1754 if (r) {
1755 ERROR(archive->pakfire, "archive_write_open_FILE() failed: %s\n",
1756 archive_error_string(a));
1757 goto ERROR;
1758 }
1759
1760 // Write the header
1761 r = archive_write_header(a, entry);
1762 if (r) {
1763 ERROR(archive->pakfire, "Error writing file header: %s\n",
1764 archive_error_string(a));
1765 goto ERROR;
1766 }
1767
1768 // Write the payload
1769 ssize_t bytes_written = archive_write_data(a, buffer, length);
1770 if (bytes_written < 0) {
1771 ERROR(archive->pakfire, "Error writing data: %s\n",
1772 archive_error_string(a));
1773 goto ERROR;
1774 }
1775
1776 // Finish writing
1777 r = archive_write_finish_entry(a);
1778 if (r) {
1779 ERROR(archive->pakfire, "Could not finish entry: %s\n",
1780 archive_error_string(a));
1781 goto ERROR;
1782 }
1783
1784 // Success
1785 r = 0;
1786
1787ERROR:
1788 archive_write_free(a);
1789
1790 return r;
1791}
1792
1793static int pakfire_archive_append_signature(struct pakfire_archive* archive,
1794 struct pakfire_key* key, const char* signature, size_t length, time_t timestamp) {
1795 char path[PATH_MAX];
1796 int r;
1797
1798 // Make filename
1799 r = pakfire_string_format(path, "signatures/%s", pakfire_key_get_fingerprint(key));
1800 if (r < 0)
1801 return 1;
1802
1803 // Create a new file entry
1804 struct archive_entry* entry = archive_entry_new();
1805 if (!entry)
1806 return 1;
1807
1808 // Set filename
1809 archive_entry_set_pathname(entry, path);
1810
1811 // This is a regular file
1812 archive_entry_set_filetype(entry, AE_IFREG);
1813 archive_entry_set_perm(entry, 0444);
1814
1815 // Set size
1816 archive_entry_set_size(entry, length);
1817
1818 // Set ownership
1819 archive_entry_set_uname(entry, "root");
1820 archive_entry_set_uid(entry, 0);
1821 archive_entry_set_gname(entry, "root");
1822 archive_entry_set_gid(entry, 0);
1823
1824 // Set times
1825 archive_entry_set_birthtime(entry, timestamp, 0);
1826 archive_entry_set_ctime(entry, timestamp, 0);
1827 archive_entry_set_mtime(entry, timestamp, 0);
1828 archive_entry_set_atime(entry, timestamp, 0);
1829
1830 // Write entry
1831 r = pakfire_archive_append_file(archive, entry, signature, length);
1832 archive_entry_free(entry);
1833
1834 return r;
1835}
1836
b6a5c6ee
MT
1837PAKFIRE_EXPORT int pakfire_archive_sign(struct pakfire_archive* archive, struct pakfire_key* key) {
1838 int r;
1839
2ab9cb5e
MT
1840 // Verify checksums
1841 r = pakfire_archive_verify_checksums(archive, PAKFIRE_ARCHIVE_VERIFY_ALL);
1842 if (r) {
1843 ERROR(archive->pakfire, "The archive checksums don't match\n");
1844 return r;
1845 }
b6a5c6ee
MT
1846
1847 char* signature = NULL;
1848 size_t signature_length = 0;
307188ab 1849 time_t timestamp = 0;
b6a5c6ee
MT
1850
1851 // Create the signature
307188ab
MT
1852 r = pakfire_archive_create_signature(archive, key,
1853 &signature, &signature_length, &timestamp);
b6a5c6ee
MT
1854 if (r)
1855 return r;
1856
95c4b89c
MT
1857 // Append signature to archive
1858 r = pakfire_archive_append_signature(archive, key,
1859 signature, signature_length, timestamp);
1860 if (r)
1861 return r;
b6a5c6ee
MT
1862
1863 return 0;
1864}
1865
ae9ac5cc 1866PAKFIRE_EXPORT ssize_t pakfire_archive_get_size(struct pakfire_archive* archive) {
165b2786
MT
1867 struct stat buf;
1868
fe1b4c1b 1869 int r = fstat(fileno(archive->f), &buf);
165b2786 1870 if (r) {
b1772bfb 1871 ERROR(archive->pakfire, "Could not stat %s: %m\n", archive->path);
165b2786
MT
1872
1873 return -1;
1874 }
1875
1876 return buf.st_size;
213d0220
MT
1877}
1878
e32162cc
MT
1879int pakfire_archive_digest(struct pakfire_archive* archive,
1880 enum pakfire_digests type, unsigned char* digest, size_t* length) {
e61716e4
MT
1881 int r = 1;
1882
1883 const EVP_MD* md = NULL;
1884
1885 // Select hash function
e32162cc 1886 switch (type) {
7cea394c 1887 case PAKFIRE_DIGEST_SHA512:
e61716e4
MT
1888 md = EVP_sha512();
1889 break;
1890
7cea394c 1891 case PAKFIRE_DIGEST_SHA256:
e61716e4
MT
1892 md = EVP_sha256();
1893 break;
1894
1895 default:
1896 errno = ENOTSUP;
1897 return 1;
1898 }
1899
1900 // Initialize context
1901 EVP_MD_CTX* ctx = EVP_MD_CTX_new();
1902 if (!ctx) {
1903 ERROR(archive->pakfire, "Could not initialize EVP context: %m\n");
1904 goto ERROR;
1905 }
1906
1907 r = EVP_DigestInit_ex(ctx, md, NULL);
1908 if (r != 1) {
1909 ERROR(archive->pakfire, "EVP_DigestInit_ex failed: %s\n",
1910 ERR_error_string(ERR_get_error(), NULL));
1911 r = 1;
1912 goto ERROR;
1913 }
1914
1915 char buffer[64 * 1024];
1916
1917 // Feed archive into the hash functions
1918 while (!feof(archive->f)) {
1919 size_t bytes_read = fread(buffer, 1, sizeof(buffer), archive->f);
1920
1921 if (ferror(archive->f)) {
1922 ERROR(archive->pakfire, "Error reading from file: %m\n");
1923 goto ERROR;
1924 }
1925
1926 r = EVP_DigestUpdate(ctx, buffer, bytes_read);
1927 if (r != 1) {
1928 ERROR(archive->pakfire, "EVP_DigestUpdate failed: %s\n",
1929 ERR_error_string(ERR_get_error(), NULL));
1930 r = 1;
1931 goto ERROR;
1932 }
1933 }
1934
1935 // Finalise hash function
e32162cc 1936 r = EVP_DigestFinal_ex(ctx, digest, (unsigned int*)length);
e61716e4
MT
1937 if (r != 1) {
1938 ERROR(archive->pakfire, "EVP_DigestFinal_ex failed: %s\n",
1939 ERR_error_string(ERR_get_error(), NULL));
1940 r = 1;
1941 goto ERROR;
1942 }
1943
1944 // Success
1945 r = 0;
1946
1947ERROR:
1948 // Cleanup
1949 if (ctx)
1950 EVP_MD_CTX_free(ctx);
1951
1952 rewind(archive->f);
1953
1954 return r;
1955}
1956
20b83302 1957static int pakfire_archive_make_package_from_json(struct pakfire_archive* archive,
b31767bd 1958 struct pakfire_repo* repo, struct pakfire_package** package) {
e61716e4
MT
1959 unsigned char digest[EVP_MAX_MD_SIZE];
1960 size_t digest_length = 0;
1961 int r;
1962
1963 // Calculate digest
20b83302 1964 // This must be done before we create the package object
e32162cc 1965 r = pakfire_archive_digest(archive, PAKFIRE_DIGEST_SHA512, digest, &digest_length);
23cd7632
MT
1966 if (r) {
1967 ERROR(archive->pakfire, "Could not calculate digest of %s: %m\n", archive->path);
e61716e4 1968 return r;
23cd7632 1969 }
6097f779 1970
20b83302
MT
1971 // Fetch the most basic package information
1972 const char* name = pakfire_archive_metadata_get(archive, "name", NULL);
1973 const char* evr = pakfire_archive_metadata_get(archive, "evr", NULL);
1974 const char* arch = pakfire_archive_metadata_get(archive, "arch", NULL);
6097f779 1975
20b83302
MT
1976 // Create a new package object
1977 struct pakfire_package* pkg = pakfire_package_create(archive->pakfire,
1978 repo, name, evr, arch);
1979 if (!pkg)
1980 return 1;
1981
1982#ifdef ENABLE_DEBUG
1983 const char* nevra = pakfire_package_get_nevra(pkg);
1984 DEBUG(archive->pakfire, "Created package %s (%p) from archive %p\n",
1985 nevra, pkg, archive);
1986#endif
1987
1988 // Set path
1989 pakfire_package_set_path(pkg, archive->path);
1990
1991 // Set digest
1992 pakfire_package_set_digest(pkg, PAKFIRE_DIGEST_SHA512, digest);
1993
d9475a74
MT
1994 // Vendor
1995 const char* vendor = pakfire_archive_metadata_get(archive, "vendor", NULL);
1996 if (vendor)
1997 pakfire_package_set_vendor(pkg, vendor);
1998
ca002cae
MT
1999 // Distribution
2000 const char* distribution = pakfire_archive_metadata_get(archive, "distribution", NULL);
2001 if (distribution)
2002 pakfire_package_set_distribution(pkg, distribution);
2003
20b83302
MT
2004 // UUID
2005 const char* uuid = pakfire_archive_metadata_get(archive, "uuid", NULL);
2006 if (uuid)
2007 pakfire_package_set_uuid(pkg, uuid);
2008
2009 // Groups
2010 const char* groups = pakfire_archive_metadata_get(archive, "groups", NULL);
2011 if (groups)
2012 pakfire_package_set_groups(pkg, groups);
2013
2014 // Maintainer
2015 const char* maintainer = pakfire_archive_metadata_get(archive, "maintainer", NULL);
2016 if (maintainer)
2017 pakfire_package_set_maintainer(pkg, maintainer);
2018
2019 // URL
2020 const char* url = pakfire_archive_metadata_get(archive, "url", NULL);
2021 if (url)
2022 pakfire_package_set_url(pkg, url);
2023
2024 // License
2025 const char* license = pakfire_archive_metadata_get(archive, "license", NULL);
2026 if (license)
2027 pakfire_package_set_license(pkg, license);
2028
2029 // Summary
2030 const char* summary = pakfire_archive_metadata_get(archive, "summary", NULL);
2031 if (summary)
2032 pakfire_package_set_summary(pkg, summary);
2033
2034 // Description
2035 const char* description = pakfire_archive_metadata_get(archive, "description", NULL);
2036 if (description)
2037 pakfire_package_set_description(pkg, description);
2038
2039 // Installed package size
2040 size_t installsize = pakfire_archive_metadata_get_int64(archive, "size", NULL);
2041 if (installsize)
2042 pakfire_package_set_installsize(pkg, installsize);
2043
2044 // Build Host
2045 const char* build_host = pakfire_archive_metadata_get(archive, "build", "host");
2046 if (build_host)
2047 pakfire_package_set_build_host(pkg, build_host);
2048
2049 // Build ID
2050 const char* build_id = pakfire_archive_metadata_get(archive, "build", "id");
2051 if (build_id)
2052 pakfire_package_set_build_id(pkg, build_id);
2053
2054 // Build Time
2055 time_t build_time = pakfire_archive_metadata_get_int64(archive, "build", "time");
2056 if (build_time)
2057 pakfire_package_set_build_time(pkg, build_time);
2058
571539a7
MT
2059 // Source package
2060 const char* source_name = pakfire_archive_metadata_get(archive, "build", "source-name");
2061 if (source_name)
2062 pakfire_package_set_source_name(pkg, source_name);
2063
2064 // Source EVR
2065 const char* source_evr = pakfire_archive_metadata_get(archive, "build", "source-evr");
2066 if (source_evr)
2067 pakfire_package_set_source_evr(pkg, source_evr);
2068
2069 // Source arch
2070 const char* source_arch = pakfire_archive_metadata_get(archive, "build", "source-arch");
2071 if (source_arch)
2072 pakfire_package_set_source_arch(pkg, source_arch);
2073
20b83302
MT
2074 // Dependencies
2075 const struct dependencies {
2076 const char* type;
2077 void (*func)(struct pakfire_package*, const char* s);
2078 } dependencies[] = {
2079 { "provides", pakfire_package_add_provides },
2080 { "prerequires", pakfire_package_add_prerequires },
2081 { "requires", pakfire_package_add_requires },
2082 { "conflicts", pakfire_package_add_conflicts },
2083 { "obsoletes", pakfire_package_add_obsoletes },
2084 { "recommends", pakfire_package_add_recommends },
2085 { "suggests", pakfire_package_add_suggests },
2086 { "supplements", pakfire_package_add_supplements },
2087 { "enhances", pakfire_package_add_enhances },
2088 { NULL, NULL },
2089 };
2090
2091 for (const struct dependencies* deps = dependencies; deps->type; deps++) {
2092 struct json_object* array = pakfire_archive_metadata_get_object(
2093 archive, "dependencies", deps->type);
2094 if (!array)
2095 continue;
2096
2097 // Determine the length of the array
2098 const size_t length = json_object_array_length(array);
2099 if (!length)
2100 continue;
2101
2102 // Walk through all items in this array
2103 for (unsigned int i = 0; i < length; i++) {
2104 struct json_object* item = json_object_array_get_idx(array, i);
2105 if (!item)
2106 continue;
2107
2108 // Extract the string value
2109 const char* string = json_object_get_string(item);
2110 if (!string)
2111 continue;
2112
2113 // Add the dependency to the package
2114 deps->func(pkg, string);
2115 }
2116 }
2117
2118 // Import filelist
2119 struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive);
2120 if (filelist) {
2121 pakfire_package_set_filelist(pkg, filelist);
2122 pakfire_filelist_unref(filelist);
2123 }
2124
2125 // Success!
2126 *package = pkg;
2127 return 0;
2128}
2129
2130static int pakfire_archive_make_legacy_package(struct pakfire_archive* archive,
2131 struct pakfire_repo* repo, struct pakfire_package** package) {
2132 unsigned char digest[EVP_MAX_MD_SIZE];
2133 size_t digest_length = 0;
2134 int r;
2135
2136 // Calculate digest
2137 r = pakfire_archive_digest(archive, PAKFIRE_DIGEST_SHA512, digest, &digest_length);
2138 if (r) {
2139 ERROR(archive->pakfire, "Could not calculate digest of %s: %m\n", archive->path);
2140 return r;
6097f779
MT
2141 }
2142
aa75cc88 2143 char* name = pakfire_archive_get(archive, "package", "name");
e095a065 2144 char* arch = pakfire_archive_get(archive, "package", "arch");
312fd26f 2145
94131aa0
MT
2146 char* evr = pakfire_archive_get(archive, "package", "evr");
2147 if (!evr) {
23b93958
MT
2148 char* epoch = pakfire_archive_get(archive, "package", "epoch");
2149 char* version = pakfire_archive_get(archive, "package", "version");
2150 char* release = pakfire_archive_get(archive, "package", "release");
94131aa0 2151
23b93958 2152 evr = pakfire_package_join_evr(epoch, version, release);
94131aa0 2153
23b93958
MT
2154 if (epoch)
2155 free(epoch);
2156 if (version)
2157 free(version);
2158 if (release)
2159 free(release);
94131aa0 2160 }
312fd26f 2161
4e99996c
MT
2162 /*
2163 Check architecture
2164 Legacy package formats use "arch = all" for source packages.
2165 We need to check for "type = source" and then reset arch
2166 */
fd00906a 2167 if (arch && strcmp(arch, "all") == 0) {
4e99996c
MT
2168 char* type = pakfire_archive_get(archive, "package", "type");
2169 if (type) {
2170 if (strcmp(type, "source") == 0) {
2171 free(arch);
2172 arch = NULL;
2173 }
2174 }
2175 free(type);
2176 }
2177
31480bee 2178 struct pakfire_package* pkg = pakfire_package_create(
04a4a654 2179 archive->pakfire, repo, name, evr, (arch) ? arch : "src"
312fd26f
MT
2180 );
2181
94131aa0
MT
2182 if (name)
2183 free(name);
2184 if (evr)
2185 free(evr);
04a4a654 2186 if (arch)
f0d6233d 2187 free(arch);
312fd26f
MT
2188
2189#ifdef ENABLE_DEBUG
40fcf5ff 2190 const char* nevra = pakfire_package_get_nevra(pkg);
312fd26f
MT
2191 DEBUG(archive->pakfire, "Created package %s (%p) from archive %p\n",
2192 nevra, pkg, archive);
312fd26f
MT
2193#endif
2194
b12a5d7d
MT
2195 // Set path
2196 pakfire_package_set_path(pkg, archive->path);
f44bec4a 2197
312fd26f 2198 // Set UUID
aa75cc88 2199 char* uuid = pakfire_archive_get(archive, "package", "uuid");
312fd26f
MT
2200 if (uuid) {
2201 pakfire_package_set_uuid(pkg, uuid);
f0d6233d 2202 free(uuid);
312fd26f
MT
2203 }
2204
2205 // Set groups
aa75cc88 2206 char* groups = pakfire_archive_get(archive, "package", "groups");
312fd26f
MT
2207 if (groups) {
2208 pakfire_package_set_groups(pkg, groups);
f0d6233d 2209 free(groups);
312fd26f 2210 }
312fd26f
MT
2211
2212 // Set maintainer
aa75cc88 2213 char* maintainer = pakfire_archive_get(archive, "package", "maintainer");
312fd26f
MT
2214 if (maintainer) {
2215 pakfire_package_set_maintainer(pkg, maintainer);
f0d6233d 2216 free(maintainer);
312fd26f
MT
2217 }
2218
2219 // Set URL
aa75cc88 2220 char* url = pakfire_archive_get(archive, "package", "url");
312fd26f
MT
2221 if (url) {
2222 pakfire_package_set_url(pkg, url);
f0d6233d 2223 free(url);
312fd26f
MT
2224 }
2225
2226 // Set license
aa75cc88 2227 char* license = pakfire_archive_get(archive, "package", "license");
312fd26f
MT
2228 if (license) {
2229 pakfire_package_set_license(pkg, license);
f0d6233d 2230 free(license);
312fd26f
MT
2231 }
2232
2233 // Set summary
aa75cc88 2234 char* summary = pakfire_archive_get(archive, "package", "summary");
312fd26f
MT
2235 if (summary) {
2236 pakfire_package_set_summary(pkg, summary);
f0d6233d 2237 free(summary);
312fd26f
MT
2238 }
2239
2240 // Set description
aa75cc88 2241 char* description = pakfire_archive_get(archive, "package", "description");
312fd26f
MT
2242 if (description) {
2243 pakfire_package_set_description(pkg, description);
f0d6233d 2244 free(description);
312fd26f
MT
2245 }
2246
213d0220
MT
2247 // Get package size
2248 pakfire_package_set_downloadsize(pkg, pakfire_archive_get_size(archive));
312fd26f
MT
2249
2250 // Get install size
aa75cc88 2251 char* size = pakfire_archive_get(archive, "package", "size");
312fd26f 2252 if (size) {
2c1c417f 2253 size_t s = strtoul(size, NULL, 10);
f0d6233d 2254 free(size);
312fd26f
MT
2255
2256 pakfire_package_set_installsize(pkg, s);
2257 }
2258
4ee70c19 2259 // Set vendor
aa75cc88 2260 char* vendor = pakfire_archive_get(archive, "distribution", "vendor");
4ee70c19
MT
2261 if (vendor) {
2262 pakfire_package_set_vendor(pkg, vendor);
f0d6233d 2263 free(vendor);
4ee70c19
MT
2264 }
2265
2266 // Set build host
aa75cc88 2267 char* build_host = pakfire_archive_get(archive, "build", "host");
2ffc704d
MT
2268 if (build_host) {
2269 pakfire_package_set_build_host(pkg, build_host);
2270 free(build_host);
4ee70c19
MT
2271 }
2272
7996020a
MT
2273 // Set build ID
2274 char* build_id = pakfire_archive_get(archive, "build", "id");
2275 if (build_id) {
2276 pakfire_package_set_build_id(pkg, build_id);
2277 free(build_id);
2278 }
2279
4ee70c19 2280 // Set build time
aa75cc88 2281 char* build_time = pakfire_archive_get(archive, "build", "time");
2ffc704d
MT
2282 if (build_time) {
2283 time_t t = strtoull(build_time, NULL, 10);
2284 free(build_time);
4ee70c19 2285
2ffc704d 2286 pakfire_package_set_build_time(pkg, t);
4ee70c19 2287 }
312fd26f 2288
dec8207e 2289 // Relations
dec8207e 2290
d6ea32df
MT
2291 const struct __relation {
2292 const char* type;
31480bee 2293 void (*func)(struct pakfire_package*, const char* dep);
d6ea32df 2294 } relations[] = {
452d3833
MT
2295 { "provides", pakfire_package_add_provides },
2296 { "prerequires", pakfire_package_add_prerequires },
2297 { "requires", pakfire_package_add_requires },
2298 { "conflicts", pakfire_package_add_conflicts },
2299 { "obsoletes", pakfire_package_add_obsoletes },
2300 { "recommends", pakfire_package_add_recommends },
2301 { "suggests", pakfire_package_add_suggests },
2302 { "supplements", pakfire_package_add_supplements },
2303 { "enhances", pakfire_package_add_enhances },
d6ea32df
MT
2304 { NULL, NULL },
2305 };
2306
2307 for (const struct __relation* relation = relations; relation->type; relation++) {
23b93958
MT
2308 char* rels = pakfire_archive_get(archive, "dependencies", relation->type);
2309 if (rels) {
2310 pakfire_str2deps(archive->pakfire, pkg, relation->func, rels);
2311 free(rels);
2312 }
dec8207e
MT
2313 }
2314
e61716e4 2315 // Set digests
7cea394c 2316 pakfire_package_set_digest(pkg, PAKFIRE_DIGEST_SHA512, digest);
e61716e4 2317
b31767bd
MT
2318 *package = pkg;
2319
20b83302
MT
2320 return 0;
2321}
2322
2323/*
2324 Copy all metadata from this archive to the package object
2325*/
2326PAKFIRE_EXPORT int pakfire_archive_make_package(struct pakfire_archive* archive,
2327 struct pakfire_repo* repo, struct pakfire_package** package) {
2328 struct pakfire_repo* dummy = NULL;
2329 int r;
2330
2331 // Use dummy repo if no repository was passed
2332 if (!repo) {
2333 dummy = pakfire_get_repo(archive->pakfire, PAKFIRE_REPO_DUMMY);
2334 if (!dummy)
2335 return 1;
2336
2337 repo = dummy;
2338 }
2339
2340 // Make package from JSON metadata
2341 if (archive->format >= 6)
2342 r = pakfire_archive_make_package_from_json(archive, repo, package);
2343
2344 // Use legacy stuff
2345 else
2346 r = pakfire_archive_make_legacy_package(archive, repo, package);
2347
e1a62423
MT
2348 // Import filelist
2349 struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive);
2350 if (filelist) {
2351 pakfire_package_set_filelist(*package, filelist);
2352 pakfire_filelist_unref(filelist);
2353 }
2354
20b83302 2355 // Free dummy repository
6097f779
MT
2356 if (dummy)
2357 pakfire_repo_unref(dummy);
2358
20b83302 2359 return r;
312fd26f 2360}
101264c8 2361
e49b6359
MT
2362static int pakfire_archive_load_scriptlet(struct pakfire_archive* archive,
2363 struct archive* a, struct archive_entry* e, int flags, void* data) {
2364 const char* path = archive_entry_pathname(e);
2365
2366 // Skip if this isn't a scriptlet
2367 if (!pakfire_string_startswith(path, "scriptlet/"))
2368 return 0;
2369
2370 char* buffer = NULL;
2371 size_t size = 0;
2372 int r;
2373
2374 // Fetch scriptlet
2375 r = pakfire_archive_copy_data_to_buffer(archive->pakfire, a, e, &buffer, &size);
2376 if (r)
2377 goto ERROR;
2378
2379 struct pakfire_scriptlet* scriptlet = NULL;
2380 const char* type = path + strlen("scriptlet/");
2381
2382 // Allocate a scriptlet
2383 r = pakfire_scriptlet_create(&scriptlet, archive->pakfire, type, buffer, size);
2384 if (r)
2385 goto ERROR;
2386
2387 int* counter = (int*)data;
2388
2389 // Make space for the new scriptlet
2390 archive->scriptlets = reallocarray(archive->scriptlets, *counter + 2,
2391 sizeof(*archive->scriptlets));
2392
2393 archive->scriptlets[(*counter)++] = scriptlet;
2394
2395ERROR:
2396 if (buffer)
2397 free(buffer);
2398
2399 return r;
2400}
2401
2402static int pakfire_archive_load_scriptlets(struct pakfire_archive* archive) {
2403 // Nothing to do
2404 if (archive->scriptlets_loaded)
2405 return 0;
2406
2407 int counter = 0;
2408
2409 return pakfire_archive_walk(archive, pakfire_archive_load_scriptlet, 0, &counter, NULL);
2410}
2411
101264c8 2412struct pakfire_scriptlet* pakfire_archive_get_scriptlet(
900faa2f 2413 struct pakfire_archive* archive, const char* type) {
e49b6359
MT
2414 int r = pakfire_archive_load_scriptlets(archive);
2415 if (r) {
2416 ERROR(archive->pakfire, "Could not load scriptlets: %m\n");
2417 return NULL;
2418 }
101264c8 2419
a6795d55
MT
2420 if (archive->scriptlets) {
2421 for (struct pakfire_scriptlet** scriptlet = archive->scriptlets; *scriptlet; scriptlet++) {
2422 const char* t = pakfire_scriptlet_get_type(*scriptlet);
a0097ba2 2423
a6795d55
MT
2424 if (strcmp(t, type) == 0)
2425 return pakfire_scriptlet_ref(*scriptlet);
2426 }
101264c8
MT
2427 }
2428
2429 return NULL;
2430}