]>
Commit | Line | Data |
---|---|---|
6aeb48e6 MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2021 Pakfire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | #include <errno.h> | |
1ea1f35b MT |
22 | #include <fcntl.h> |
23 | #include <linux/limits.h> | |
6aeb48e6 | 24 | #include <stdlib.h> |
da08f989 MT |
25 | #include <sys/stat.h> |
26 | #include <sys/types.h> | |
05cfc2c9 | 27 | #include <time.h> |
da08f989 | 28 | #include <unistd.h> |
6aeb48e6 | 29 | |
1ea1f35b | 30 | #include <archive.h> |
da08f989 | 31 | #include <archive_entry.h> |
1ea1f35b | 32 | |
2adc4a4a | 33 | #include <pakfire/archive.h> |
436677a3 | 34 | #include <pakfire/constants.h> |
1ea1f35b | 35 | #include <pakfire/logging.h> |
6aeb48e6 MT |
36 | #include <pakfire/package.h> |
37 | #include <pakfire/packager.h> | |
38 | #include <pakfire/pakfire.h> | |
677e9e5b | 39 | #include <pakfire/pwd.h> |
6aeb48e6 | 40 | #include <pakfire/types.h> |
f57ad279 | 41 | #include <pakfire/util.h> |
6aeb48e6 | 42 | |
da08f989 MT |
43 | #define BUFFER_SIZE 64 * 1024 |
44 | ||
6aeb48e6 MT |
45 | struct pakfire_packager { |
46 | Pakfire pakfire; | |
47 | int nrefs; | |
05cfc2c9 | 48 | time_t time_created; |
6aeb48e6 MT |
49 | |
50 | PakfirePackage pkg; | |
bb06d548 | 51 | char filename[PATH_MAX]; |
1ea1f35b | 52 | |
738b3582 MT |
53 | // Reader |
54 | struct archive* reader; | |
55 | ||
56 | // Payload | |
1ea1f35b | 57 | struct archive* payload; |
7836e21b | 58 | FILE* fpayload; |
4eab5079 MT |
59 | struct archive* mtree; |
60 | FILE* fmtree; | |
e282863a | 61 | size_t installsize; |
106d2edd MT |
62 | |
63 | struct pakfire_scriptlet** scriptlets; | |
64 | unsigned int num_scriptlets; | |
6aeb48e6 MT |
65 | }; |
66 | ||
265a7974 MT |
67 | static int pakfire_packager_create_mtree(Pakfire pakfire, struct archive** mtree, |
68 | FILE** f, const char** fields) { | |
96de0b5d | 69 | char path[] = PAKFIRE_PRIVATE_DIR "/tmp/.pakfire-mtree.XXXXXX"; |
265a7974 MT |
70 | int r; |
71 | ||
72 | // Create an mtree | |
73 | *mtree = archive_write_new(); | |
74 | if (!*mtree) { | |
75 | ERROR(pakfire, "archive_write_new() failed\n"); | |
76 | goto ERROR; | |
77 | } | |
78 | ||
79 | // Use mtree format | |
80 | r = archive_write_set_format_mtree(*mtree); | |
81 | if (r) { | |
82 | ERROR(pakfire, "Could not set format to mtree: %s\n", | |
83 | archive_error_string(*mtree)); | |
84 | goto ERROR; | |
85 | } | |
86 | ||
87 | // Always compress using Zstd | |
88 | r = archive_write_add_filter_zstd(*mtree); | |
89 | if (r) { | |
90 | ERROR(pakfire, "Could not enable Zstandard compression: %s\n", | |
91 | archive_error_string(*mtree)); | |
92 | goto ERROR; | |
93 | } | |
94 | ||
95 | // Enable mtree fields | |
96 | if (fields) { | |
97 | for (const char** field = fields; *field; field++) { | |
98 | r = archive_write_set_options(*mtree, *field); | |
99 | if (r) { | |
100 | ERROR(pakfire, "Could not set mtree options %s: %s\n", | |
101 | *field, archive_error_string(*mtree)); | |
102 | goto ERROR; | |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | // Create a new temporary file | |
108 | *f = pakfire_mktemp(path); | |
109 | if (!*f) | |
110 | goto ERROR; | |
111 | ||
112 | // Unlink the file straight away | |
113 | unlink(path); | |
114 | ||
115 | // Write mtree to file | |
116 | r = archive_write_open_FILE(*mtree, *f); | |
117 | if (r) | |
118 | goto ERROR; | |
119 | ||
120 | return 0; | |
121 | ||
122 | ERROR: | |
123 | if (*mtree) { | |
124 | archive_write_free(*mtree); | |
125 | *mtree = NULL; | |
126 | } | |
127 | ||
128 | if (*f) { | |
129 | fclose(*f); | |
130 | *f = NULL; | |
131 | } | |
132 | ||
133 | return 1; | |
134 | } | |
135 | ||
af14aefb | 136 | static int pakfire_packager_create_payload(struct pakfire_packager* p) { |
96de0b5d | 137 | char path[] = PAKFIRE_PRIVATE_DIR "/tmp/.pakfire-payload.XXXXXX"; |
7836e21b | 138 | |
af14aefb MT |
139 | // Do not compress source packages |
140 | const int compress = !pakfire_package_is_source(p->pkg); | |
141 | ||
1ea1f35b | 142 | p->payload = archive_write_new(); |
427fdd80 MT |
143 | if (!p->payload) { |
144 | ERROR(p->pakfire, "archive_write_new() failed\n"); | |
145 | return 1; | |
146 | } | |
1ea1f35b MT |
147 | |
148 | // Use the PAX format | |
149 | int r = archive_write_set_format_pax(p->payload); | |
150 | if (r) { | |
151 | ERROR(p->pakfire, "Could not set format to PAX: %s\n", | |
152 | archive_error_string(p->payload)); | |
153 | return r; | |
154 | } | |
155 | ||
156 | // Add filters to compress the payload | |
157 | if (compress) { | |
158 | // Enable Zstd | |
159 | r = archive_write_add_filter_zstd(p->payload); | |
160 | if (r) { | |
161 | ERROR(p->pakfire, "Could not enable Zstandard compression: %s\n", | |
162 | archive_error_string(p->payload)); | |
163 | return r; | |
164 | } | |
165 | ||
166 | // Set compression level to highest | |
be69fe38 | 167 | r = archive_write_set_filter_option(p->payload, NULL, "compression-level", "22"); |
1ea1f35b MT |
168 | if (r) { |
169 | ERROR(p->pakfire, "Could not set Zstandard compression level: %s\n", | |
170 | archive_error_string(p->payload)); | |
171 | return r; | |
172 | } | |
ae8f5905 MT |
173 | |
174 | // Add feature marker | |
175 | pakfire_package_add_requires(p->pkg, "pakfire(Compress-Zstandard)"); | |
0dd87cbe MT |
176 | |
177 | // Do not pad the last block | |
178 | archive_write_set_bytes_in_last_block(p->payload, 1); | |
1ea1f35b MT |
179 | } |
180 | ||
7836e21b | 181 | // Create a new temporary file |
265a7974 | 182 | p->fpayload = pakfire_mktemp(path); |
f57ad279 | 183 | if (!p->fpayload) |
1ea1f35b | 184 | return 1; |
1ea1f35b | 185 | |
7836e21b | 186 | // Unlink the file straight away |
265a7974 | 187 | unlink(path); |
7836e21b | 188 | |
7836e21b MT |
189 | // Write archive to file |
190 | r = archive_write_open_FILE(p->payload, p->fpayload); | |
1ea1f35b MT |
191 | if (r) |
192 | return r; | |
193 | ||
4eab5079 MT |
194 | static const char* mtree_fields[] = { |
195 | // Disable the default | |
196 | "!all", | |
197 | ||
198 | // Enable standard file fields | |
199 | "device", | |
200 | "gname", | |
201 | "link", | |
202 | "mode", | |
203 | "nlink", | |
204 | "size", | |
205 | "time", | |
206 | "type", | |
207 | "uname", | |
208 | ||
209 | // Enable cryptographic checksums | |
210 | "sha512", | |
211 | "sha256", | |
212 | ||
213 | NULL, | |
214 | }; | |
215 | ||
265a7974 MT |
216 | // Create an mtree |
217 | r = pakfire_packager_create_mtree(p->pakfire, &p->mtree, &p->fmtree, mtree_fields); | |
218 | if (r) { | |
219 | ERROR(p->pakfire, "Could not create mtree\n"); | |
4eab5079 | 220 | return r; |
265a7974 | 221 | } |
4eab5079 | 222 | |
1ea1f35b MT |
223 | return 0; |
224 | } | |
225 | ||
226 | static void pakfire_packager_free(struct pakfire_packager* packager) { | |
106d2edd MT |
227 | // Scriptlets |
228 | if (packager->scriptlets) { | |
229 | for (unsigned int i = 0; i < packager->num_scriptlets; i++) | |
9dd7b1aa | 230 | pakfire_scriptlet_unref(packager->scriptlets[i]); |
106d2edd MT |
231 | free(packager->scriptlets); |
232 | } | |
233 | ||
4eab5079 | 234 | // Payload |
1ea1f35b MT |
235 | if (packager->payload) |
236 | archive_write_free(packager->payload); | |
237 | ||
7836e21b MT |
238 | if (packager->fpayload) |
239 | fclose(packager->fpayload); | |
240 | ||
4eab5079 MT |
241 | // mtree |
242 | if (packager->mtree) | |
243 | archive_write_free(packager->mtree); | |
244 | ||
245 | if (packager->fmtree) | |
246 | fclose(packager->fmtree); | |
247 | ||
738b3582 | 248 | if (packager->reader) |
66817062 | 249 | archive_read_free(packager->reader); |
738b3582 | 250 | |
1ea1f35b MT |
251 | pakfire_package_unref(packager->pkg); |
252 | pakfire_unref(packager->pakfire); | |
253 | } | |
254 | ||
22b8e37b | 255 | int pakfire_packager_create(struct pakfire_packager** packager, |
6aeb48e6 MT |
256 | PakfirePackage pkg) { |
257 | struct pakfire_packager* p = calloc(1, sizeof(*p)); | |
258 | if (!p) | |
259 | return ENOMEM; | |
260 | ||
e282863a MT |
261 | int r = 1; |
262 | ||
05cfc2c9 MT |
263 | // Save creation time |
264 | p->time_created = time(NULL); | |
265 | ||
6aeb48e6 MT |
266 | // Initialize reference counting |
267 | p->nrefs = 1; | |
268 | ||
269 | // Store a reference to Pakfire | |
270 | p->pakfire = pakfire_package_get_pakfire(pkg); | |
271 | ||
272 | // Store a reference to the package | |
273 | p->pkg = pakfire_package_ref(pkg); | |
274 | ||
0682aeb3 | 275 | // Create reader |
66817062 MT |
276 | p->reader = pakfire_make_archive_disk_reader(p->pakfire, 1); |
277 | if (!p->reader) | |
1ba1869e | 278 | goto ERROR; |
1ea1f35b | 279 | |
0682aeb3 | 280 | // Start payload |
af14aefb | 281 | r = pakfire_packager_create_payload(p); |
0682aeb3 | 282 | if (r) |
738b3582 | 283 | goto ERROR; |
738b3582 | 284 | |
6aeb48e6 MT |
285 | *packager = p; |
286 | ||
287 | return 0; | |
1ba1869e MT |
288 | |
289 | ERROR: | |
290 | pakfire_packager_free(p); | |
291 | ||
292 | return r; | |
6aeb48e6 MT |
293 | } |
294 | ||
22b8e37b | 295 | struct pakfire_packager* pakfire_packager_ref( |
6aeb48e6 MT |
296 | struct pakfire_packager* packager) { |
297 | ++packager->nrefs; | |
298 | ||
299 | return packager; | |
300 | } | |
301 | ||
22b8e37b | 302 | struct pakfire_packager* pakfire_packager_unref( |
6aeb48e6 MT |
303 | struct pakfire_packager* packager) { |
304 | if (--packager->nrefs > 0) | |
305 | return packager; | |
306 | ||
307 | pakfire_packager_free(packager); | |
308 | ||
309 | return NULL; | |
310 | } | |
da08f989 | 311 | |
22b8e37b | 312 | const char* pakfire_packager_filename(struct pakfire_packager* packager) { |
bb06d548 MT |
313 | if (!*packager->filename) { |
314 | const char* filename = pakfire_package_get_filename(packager->pkg); | |
315 | ||
316 | // Add arch | |
317 | if (pakfire_package_is_source(packager->pkg)) | |
318 | pakfire_string_set(packager->filename, filename); | |
319 | else { | |
320 | const char* arch = pakfire_package_get_arch(packager->pkg); | |
321 | ||
322 | pakfire_string_format(packager->filename, "%s/%s", arch, filename); | |
323 | } | |
324 | } | |
325 | ||
326 | return packager->filename; | |
96d2c7bc MT |
327 | } |
328 | ||
7836e21b MT |
329 | static int pakfire_packager_copy_data(struct pakfire_packager* packager, |
330 | struct archive* a, FILE* f) { | |
331 | char buffer[BUFFER_SIZE]; | |
332 | ||
265a7974 MT |
333 | rewind(f); |
334 | ||
7836e21b MT |
335 | while (!feof(f)) { |
336 | // Read a block from file | |
337 | size_t bytes_read = fread(buffer, 1, sizeof(buffer), f); | |
338 | ||
339 | // Check if any error occured | |
340 | if (ferror(f)) { | |
341 | ERROR(packager->pakfire, "Error reading from file: %s\n", strerror(errno)); | |
342 | return 1; | |
343 | } | |
344 | ||
345 | // Write the block to the archive | |
346 | ssize_t bytes_written = archive_write_data(a, buffer, bytes_read); | |
347 | if (bytes_written < 0) { | |
348 | ERROR(packager->pakfire, "Error writing data to archive: %s\n", | |
349 | archive_error_string(a)); | |
350 | return 1; | |
351 | } | |
352 | } | |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
28700a5b | 357 | static struct archive_entry* pakfire_packager_create_file( |
95b81a98 | 358 | struct pakfire_packager* packager, const char* filename, size_t size, mode_t mode) { |
98e85f1c MT |
359 | // Create a new file entry |
360 | struct archive_entry* entry = archive_entry_new(); | |
361 | if (!entry) | |
28700a5b | 362 | return NULL; |
98e85f1c MT |
363 | |
364 | // Set filename | |
365 | archive_entry_set_pathname(entry, filename); | |
366 | ||
367 | // This is a regular file | |
368 | archive_entry_set_filetype(entry, AE_IFREG); | |
95b81a98 | 369 | archive_entry_set_perm(entry, mode); |
98e85f1c | 370 | |
28700a5b MT |
371 | // Set size |
372 | archive_entry_set_size(entry, size); | |
373 | ||
05cfc2c9 MT |
374 | // Set ownership |
375 | archive_entry_set_uname(entry, "root"); | |
376 | archive_entry_set_uid(entry, 0); | |
377 | archive_entry_set_gname(entry, "root"); | |
378 | archive_entry_set_gid(entry, 0); | |
379 | ||
380 | // Set times | |
381 | archive_entry_set_birthtime(entry, packager->time_created, 0); | |
382 | archive_entry_set_ctime(entry, packager->time_created, 0); | |
383 | archive_entry_set_mtime(entry, packager->time_created, 0); | |
384 | archive_entry_set_atime(entry, packager->time_created, 0); | |
385 | ||
28700a5b MT |
386 | return entry; |
387 | } | |
388 | ||
389 | static int pakfire_packager_write_file_from_buffer(struct pakfire_packager* packager, | |
7e346194 | 390 | struct archive* a, struct archive* mtree, const char* filename, mode_t mode, const char* buffer) { |
28700a5b MT |
391 | size_t size = strlen(buffer); |
392 | ||
393 | // Create a new file | |
7e346194 MT |
394 | struct archive_entry* entry = pakfire_packager_create_file(packager, filename, size, mode); |
395 | if (!entry) { | |
396 | ERROR(packager->pakfire, "Could not create file '%s'\n", filename); | |
28700a5b | 397 | return 1; |
7e346194 | 398 | } |
98e85f1c MT |
399 | |
400 | // This is the end of the header | |
401 | int r = archive_write_header(a, entry); | |
402 | if (r) { | |
403 | ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a)); | |
265a7974 | 404 | goto ERROR; |
98e85f1c MT |
405 | } |
406 | ||
407 | // Write content | |
408 | r = archive_write_data(a, buffer, strlen(buffer)); | |
409 | if (r < 0) { | |
410 | ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a)); | |
265a7974 | 411 | goto ERROR; |
98e85f1c MT |
412 | } |
413 | ||
265a7974 MT |
414 | // Add this file to the mtree |
415 | if (mtree) { | |
416 | r = archive_write_header(mtree, entry); | |
417 | if (r) { | |
418 | ERROR(packager->pakfire, "Error adding file to mtree: %s\n", | |
419 | archive_error_string(mtree)); | |
420 | goto ERROR; | |
421 | } | |
422 | } | |
423 | ||
424 | // Success | |
425 | r = 0; | |
426 | ||
427 | ERROR: | |
98e85f1c MT |
428 | archive_entry_free(entry); |
429 | ||
265a7974 | 430 | return r; |
98e85f1c MT |
431 | } |
432 | ||
436677a3 MT |
433 | static int pakfire_packager_write_format(struct pakfire_packager* packager, |
434 | struct archive* a) { | |
435 | const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n"; | |
436 | ||
7e346194 MT |
437 | DEBUG(packager->pakfire, "Writing package format\n"); |
438 | ||
265a7974 | 439 | int r = pakfire_packager_write_file_from_buffer(packager, a, NULL, |
95b81a98 | 440 | PAKFIRE_ARCHIVE_FN_FORMAT, 0444, buffer); |
a6dd151e MT |
441 | if (r) |
442 | return r; | |
443 | ||
444 | // Add package format marker | |
445 | pakfire_package_add_requires(packager->pkg, | |
446 | "pakfire(PackageFormat-" TO_STRING(PACKAGE_FORMAT) ")"); | |
447 | ||
448 | return 0; | |
436677a3 MT |
449 | } |
450 | ||
98e85f1c MT |
451 | static char* pakfire_package_make_metadata(struct pakfire_packager* packager) { |
452 | PakfirePackage pkg = packager->pkg; | |
453 | ||
454 | char* buffer = NULL; | |
455 | int r; | |
456 | ||
457 | // Print version information | |
458 | r = asprintf(&buffer, "# Pakfire %s\n\n", PACKAGE_VERSION); | |
459 | if (r < 0) | |
460 | goto ERROR; | |
461 | ||
462 | // Write package information | |
463 | r = asprintf(&buffer, "%s# Package information\npackage\n", buffer); | |
464 | if (r < 0) | |
465 | goto ERROR; | |
466 | ||
467 | // Write package name | |
468 | r = asprintf(&buffer, "%s\tname = %s\n", buffer, pakfire_package_get_name(pkg)); | |
469 | if (r < 0) | |
470 | goto ERROR; | |
471 | ||
6ed66687 MT |
472 | // Write package EVR |
473 | r = asprintf(&buffer, "%s\tevr = %s\n", buffer, pakfire_package_get_evr(pkg)); | |
98e85f1c MT |
474 | if (r < 0) |
475 | goto ERROR; | |
476 | ||
477 | // Write package arch | |
478 | r = asprintf(&buffer, "%s\tarch = %s\n", buffer, pakfire_package_get_arch(pkg)); | |
479 | if (r < 0) | |
480 | goto ERROR; | |
481 | ||
482 | // Write package UUID | |
483 | r = asprintf(&buffer, "%s\tuuid = %s\n", buffer, pakfire_package_get_uuid(pkg)); | |
484 | if (r < 0) | |
485 | goto ERROR; | |
486 | ||
487 | // Write package groups | |
488 | const char* groups = pakfire_package_get_groups(pkg); | |
489 | if (groups) { | |
490 | r = asprintf(&buffer, "%s\tgroups = %s\n", buffer, groups); | |
491 | if (r < 0) | |
492 | goto ERROR; | |
493 | } | |
494 | ||
495 | // Write package maintainer | |
496 | const char* maintainer = pakfire_package_get_maintainer(pkg); | |
497 | if (maintainer) { | |
498 | r = asprintf(&buffer, "%s\tmaintainer = %s\n", buffer, maintainer); | |
499 | if (r < 0) | |
500 | goto ERROR; | |
501 | } | |
502 | ||
503 | // Write package url | |
e3c279d5 | 504 | const char* url = pakfire_package_get_url(pkg); |
98e85f1c MT |
505 | if (url) { |
506 | r = asprintf(&buffer, "%s\turl = %s\n", buffer, url); | |
507 | if (r < 0) | |
508 | goto ERROR; | |
509 | } | |
510 | ||
511 | // Write package license | |
512 | const char* license = pakfire_package_get_license(pkg); | |
513 | if (license) { | |
514 | r = asprintf(&buffer, "%s\tlicense = %s\n", buffer, license); | |
515 | if (r < 0) | |
516 | goto ERROR; | |
517 | } | |
518 | ||
519 | // Write package summary | |
520 | const char* summary = pakfire_package_get_summary(pkg); | |
521 | if (summary) { | |
522 | r = asprintf(&buffer, "%s\tsummary = %s\n", buffer, summary); | |
523 | if (r < 0) | |
524 | goto ERROR; | |
525 | } | |
526 | ||
527 | // XXX description | |
528 | ||
529 | size_t size = pakfire_package_get_installsize(pkg); | |
530 | r = asprintf(&buffer, "%s\tsize = %zu\n", buffer, size); | |
531 | if (r < 0) | |
532 | goto ERROR; | |
533 | ||
534 | // End package block | |
535 | r = asprintf(&buffer, "%send\n\n", buffer); | |
536 | if (r < 0) | |
537 | goto ERROR; | |
538 | ||
539 | // Write build information | |
540 | r = asprintf(&buffer, "%s# Build information\nbuild\n", buffer); | |
541 | if (r < 0) | |
542 | goto ERROR; | |
543 | ||
544 | // Write build host | |
545 | const char* build_host = pakfire_package_get_build_host(pkg); | |
546 | if (build_host) { | |
547 | r = asprintf(&buffer, "%s\thost = %s\n", buffer, build_host); | |
548 | if (r < 0) | |
549 | goto ERROR; | |
550 | } | |
551 | ||
98e85f1c MT |
552 | // Write build id |
553 | const char* build_id = pakfire_package_get_build_id(pkg); | |
554 | if (build_id) { | |
555 | r = asprintf(&buffer, "%s\tid = %s\n", buffer, build_id); | |
556 | if (r < 0) | |
557 | goto ERROR; | |
558 | } | |
98e85f1c MT |
559 | |
560 | // Write build host | |
561 | time_t build_time = pakfire_package_get_build_time(pkg); | |
562 | if (build_host) { | |
563 | r = asprintf(&buffer, "%s\ttime = %lu\n", buffer, build_time); | |
564 | if (r < 0) | |
565 | goto ERROR; | |
566 | } | |
567 | ||
568 | // End build block | |
569 | r = asprintf(&buffer, "%send\n\n", buffer); | |
570 | if (r < 0) | |
571 | goto ERROR; | |
572 | ||
573 | #if 0 | |
574 | // Write distribution information | |
575 | r = asprintf(&buffer, "%s# Distribution information\ndistribution\n", buffer); | |
576 | if (r < 0) | |
577 | goto ERROR; | |
578 | ||
579 | // End distribution block | |
580 | r = asprintf(&buffer, "%send\n\n", buffer); | |
581 | if (r < 0) | |
582 | goto ERROR; | |
583 | #endif | |
584 | ||
585 | // Write dependency information | |
586 | r = asprintf(&buffer, "%s# Dependency information\ndependencies\n", buffer); | |
587 | if (r < 0) | |
588 | goto ERROR; | |
589 | ||
590 | const struct dependencies { | |
591 | const char* type; | |
452d3833 | 592 | char** (*func)(PakfirePackage pkg); |
98e85f1c MT |
593 | } dependencies[] = { |
594 | { "prerequires", pakfire_package_get_prerequires }, | |
595 | { "requires", pakfire_package_get_requires }, | |
596 | { "provides", pakfire_package_get_provides }, | |
597 | { "conflicts", pakfire_package_get_conflicts }, | |
598 | { "obsoletes", pakfire_package_get_obsoletes }, | |
599 | { "recommends", pakfire_package_get_recommends }, | |
600 | { "suggests", pakfire_package_get_suggests }, | |
601 | { "supplements", pakfire_package_get_supplements }, | |
602 | { "enhances", pakfire_package_get_enhances }, | |
603 | { NULL }, | |
604 | }; | |
605 | ||
606 | for (const struct dependencies* d = dependencies; d->type; d++) { | |
452d3833 | 607 | char** list = d->func(pkg); |
98e85f1c MT |
608 | if (!list) |
609 | continue; | |
610 | ||
98e85f1c MT |
611 | // Write header |
612 | r = asprintf(&buffer, "%s\t%s\n", buffer, d->type); | |
613 | if (r < 0) { | |
98e85f1c MT |
614 | goto ERROR; |
615 | } | |
616 | ||
452d3833 MT |
617 | for (char** dep = list; *dep; dep++) { |
618 | asprintf(&buffer, "%s\t\t%s\n", buffer, *dep); | |
619 | free(*dep); | |
98e85f1c | 620 | } |
452d3833 | 621 | free(list); |
98e85f1c MT |
622 | |
623 | // End block | |
624 | r = asprintf(&buffer, "%s\tend\n", buffer); | |
625 | if (r < 0) | |
626 | goto ERROR; | |
98e85f1c MT |
627 | } |
628 | ||
629 | // End dependencies block | |
630 | r = asprintf(&buffer, "%send\n\n", buffer); | |
631 | if (r < 0) | |
632 | goto ERROR; | |
633 | ||
634 | // EOF | |
635 | r = asprintf(&buffer, "%s# EOF\n", buffer); | |
636 | if (r < 0) | |
637 | goto ERROR; | |
638 | ||
639 | return buffer; | |
640 | ||
641 | ERROR: | |
642 | if (buffer) | |
643 | free(buffer); | |
644 | ||
645 | return NULL; | |
646 | } | |
647 | ||
648 | static int pakfire_packager_write_metadata(struct pakfire_packager* packager, | |
265a7974 | 649 | struct archive* a, struct archive* mtree) { |
98e85f1c MT |
650 | // Make metadata |
651 | char* buffer = pakfire_package_make_metadata(packager); | |
652 | if (!buffer) | |
653 | return 1; | |
654 | ||
655 | DEBUG(packager->pakfire, "Generated package metadata:\n%s", buffer); | |
656 | ||
657 | // Write buffer | |
265a7974 | 658 | int r = pakfire_packager_write_file_from_buffer(packager, a, mtree, |
95b81a98 | 659 | PAKFIRE_ARCHIVE_FN_METADATA, 0444, buffer); |
98e85f1c MT |
660 | |
661 | free(buffer); | |
662 | ||
663 | return r; | |
664 | } | |
665 | ||
4eab5079 | 666 | static int pakfire_packager_write_archive(struct pakfire_packager* packager, |
265a7974 MT |
667 | struct archive* a, struct archive* mtree, const char* filename, |
668 | struct archive** payload, FILE* f) { | |
2adc4a4a MT |
669 | struct stat st; |
670 | ||
7e346194 MT |
671 | DEBUG(packager->pakfire, "Writing '%s' to package\n", filename); |
672 | ||
2adc4a4a | 673 | // Close the payload |
4eab5079 MT |
674 | if (*payload) { |
675 | archive_write_free(*payload); | |
676 | *payload = NULL; | |
2adc4a4a MT |
677 | } |
678 | ||
679 | // Reset fd to beginning of the file | |
4eab5079 | 680 | rewind(f); |
2adc4a4a | 681 | |
4eab5079 | 682 | int fd = fileno(f); |
2adc4a4a MT |
683 | |
684 | // Stat the payload file | |
685 | int r = fstat(fd, &st); | |
686 | if (r) { | |
687 | ERROR(packager->pakfire, "stat() on fd %d failed: %s\n", fd, strerror(errno)); | |
688 | return 1; | |
689 | } | |
690 | ||
28700a5b MT |
691 | // Create a new file |
692 | struct archive_entry* entry = pakfire_packager_create_file(packager, | |
95b81a98 | 693 | filename, st.st_size, 0444); |
2adc4a4a MT |
694 | if (!entry) |
695 | return 1; | |
696 | ||
2adc4a4a MT |
697 | // This is the end of the header |
698 | r = archive_write_header(a, entry); | |
699 | if (r) { | |
700 | ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a)); | |
701 | goto ERROR; | |
702 | } | |
703 | ||
704 | // Copy data | |
4eab5079 | 705 | r = pakfire_packager_copy_data(packager, a, f); |
7e346194 MT |
706 | if (r) { |
707 | const char* error = archive_error_string(a); | |
708 | if (!error) | |
709 | error = strerror(errno); | |
710 | ||
711 | ERROR(packager->pakfire, "Could not copy payload: %s\n", error); | |
2adc4a4a | 712 | goto ERROR; |
7e346194 | 713 | } |
2adc4a4a | 714 | |
265a7974 MT |
715 | // Add this file to the mtree |
716 | if (mtree) { | |
717 | r = archive_write_header(mtree, entry); | |
718 | if (r) { | |
719 | ERROR(packager->pakfire, "Error adding file to mtree: %s\n", | |
720 | archive_error_string(mtree)); | |
721 | goto ERROR; | |
722 | } | |
723 | ||
724 | r = pakfire_packager_copy_data(packager, mtree, f); | |
725 | if (r) { | |
726 | ERROR(packager->pakfire, "Error copying data to mtree: %s\n", | |
727 | archive_error_string(mtree)); | |
728 | goto ERROR; | |
729 | } | |
730 | } | |
731 | ||
2adc4a4a MT |
732 | // Success |
733 | r = 0; | |
734 | ||
735 | ERROR: | |
736 | archive_entry_free(entry); | |
737 | ||
738 | return r; | |
739 | } | |
740 | ||
106d2edd MT |
741 | static int pakfire_packager_write_scriptlet(struct pakfire_packager* packager, |
742 | struct archive* a, struct archive* mtree, struct pakfire_scriptlet* scriptlet) { | |
743 | char filename[PATH_MAX]; | |
744 | size_t size; | |
745 | int r; | |
746 | ||
747 | // Fetch type | |
748 | const char* type = pakfire_scriptlet_get_type(scriptlet); | |
749 | ||
750 | DEBUG(packager->pakfire, "Writing scriptlet '%s' to package\n", type); | |
751 | ||
752 | // Make filename | |
753 | r = pakfire_string_format(filename, "scriptlet/%s", type); | |
754 | if (r < 0) | |
755 | return r; | |
756 | ||
757 | // Fetch scriptlet | |
758 | const char* data = pakfire_scriptlet_get_data(scriptlet, &size); | |
759 | ||
760 | // Write file | |
95b81a98 | 761 | return pakfire_packager_write_file_from_buffer(packager, a, mtree, filename, 0544, data); |
106d2edd MT |
762 | } |
763 | ||
436677a3 MT |
764 | /* |
765 | This function is being called at the end when all data has been added to the package. | |
766 | ||
767 | It will create a new archive and write the package to the given file descriptor. | |
768 | */ | |
22b8e37b | 769 | int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) { |
265a7974 MT |
770 | struct archive* mtree = NULL; |
771 | FILE* fmtree = NULL; | |
96d2c7bc | 772 | int r = 1; |
436677a3 | 773 | |
fafe383d | 774 | // Store total instal size |
e282863a | 775 | pakfire_package_set_installsize(packager->pkg, packager->installsize); |
fafe383d | 776 | |
436677a3 MT |
777 | // Open a new archive |
778 | struct archive* a = archive_write_new(); | |
779 | if (!a) { | |
780 | ERROR(packager->pakfire, "archive_write_new() failed\n"); | |
781 | goto ERROR; | |
782 | } | |
783 | ||
784 | // Use the PAX format | |
96d2c7bc | 785 | r = archive_write_set_format_pax(a); |
436677a3 MT |
786 | if (r) { |
787 | ERROR(packager->pakfire, "Could not set format to PAX: %s\n", | |
788 | archive_error_string(a)); | |
789 | goto ERROR; | |
790 | } | |
791 | ||
792 | // Write archive to f | |
793 | r = archive_write_open_FILE(a, f); | |
794 | if (r) { | |
795 | ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n", | |
796 | archive_error_string(a)); | |
797 | goto ERROR; | |
798 | } | |
799 | ||
265a7974 MT |
800 | static const char* mtree_fields[] = { |
801 | // Disable everything | |
802 | "!all", | |
803 | ||
804 | // Include the file size | |
805 | "size", | |
806 | ||
807 | // Add the checksums | |
808 | "sha512", | |
809 | "sha256", | |
810 | ||
811 | NULL, | |
812 | }; | |
813 | ||
814 | // Create an mtree for the checksums | |
815 | r = pakfire_packager_create_mtree(packager->pakfire, &mtree, &fmtree, mtree_fields); | |
816 | if (r) { | |
817 | ERROR(packager->pakfire, "Could not create mtree\n"); | |
818 | goto ERROR; | |
819 | } | |
820 | ||
436677a3 MT |
821 | // Start with the format file |
822 | r = pakfire_packager_write_format(packager, a); | |
265a7974 MT |
823 | if (r) { |
824 | ERROR(packager->pakfire, "Could not add format file to archive: %s\n", | |
825 | archive_error_string(a)); | |
436677a3 | 826 | goto ERROR; |
265a7974 | 827 | } |
436677a3 | 828 | |
98e85f1c | 829 | // Write the metadata |
265a7974 MT |
830 | r = pakfire_packager_write_metadata(packager, a, mtree); |
831 | if (r) { | |
832 | ERROR(packager->pakfire, "Could not add metadata file to archive: %s\n", | |
833 | archive_error_string(a)); | |
98e85f1c | 834 | goto ERROR; |
265a7974 | 835 | } |
98e85f1c | 836 | |
4eab5079 | 837 | // Write the filelist in mtree format |
265a7974 | 838 | r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_FILELIST, |
4eab5079 | 839 | &packager->mtree, packager->fmtree); |
265a7974 MT |
840 | if (r) { |
841 | ERROR(packager->pakfire, "Could not add filelist to archive: %s\n", | |
842 | archive_error_string(a)); | |
4eab5079 | 843 | goto ERROR; |
265a7974 | 844 | } |
4eab5079 | 845 | |
2adc4a4a | 846 | // Write the payload |
265a7974 | 847 | r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_PAYLOAD, |
4eab5079 | 848 | &packager->payload, packager->fpayload); |
265a7974 MT |
849 | if (r) { |
850 | ERROR(packager->pakfire, "Could not add payload to archive: %s\n", | |
851 | archive_error_string(a)); | |
2adc4a4a | 852 | goto ERROR; |
265a7974 MT |
853 | } |
854 | ||
106d2edd MT |
855 | // Write scriptlets |
856 | for (unsigned int i = 0; i < packager->num_scriptlets; i++) { | |
857 | r = pakfire_packager_write_scriptlet(packager, a, mtree, packager->scriptlets[i]); | |
858 | if (r) { | |
859 | ERROR(packager->pakfire, "Could not add scriptlet to the archive: %s\n", | |
860 | strerror(errno)); | |
861 | goto ERROR; | |
862 | } | |
863 | } | |
864 | ||
265a7974 MT |
865 | // Finish checksums |
866 | r = archive_write_finish_entry(mtree); | |
867 | if (r) { | |
868 | ERROR(packager->pakfire, "Could not finish mtree: %s\n", | |
869 | archive_error_string(mtree)); | |
870 | goto ERROR; | |
871 | } | |
872 | ||
bb06d548 | 873 | // Write checksums |
265a7974 MT |
874 | r = pakfire_packager_write_archive(packager, a, NULL, PAKFIRE_ARCHIVE_FN_CHECKSUMS, |
875 | &mtree, fmtree); | |
876 | if (r) { | |
877 | ERROR(packager->pakfire, "Could not add checksums to archive: %s\n", | |
878 | archive_error_string(a)); | |
879 | goto ERROR; | |
880 | } | |
2adc4a4a | 881 | |
96d2c7bc MT |
882 | // Success |
883 | r = 0; | |
436677a3 MT |
884 | |
885 | ERROR: | |
886 | if (a) | |
265a7974 MT |
887 | archive_write_free(a); |
888 | if (mtree) | |
889 | archive_write_free(mtree); | |
890 | if (fmtree) | |
891 | fclose(fmtree); | |
436677a3 | 892 | |
96d2c7bc | 893 | return r; |
436677a3 MT |
894 | } |
895 | ||
22b8e37b | 896 | int pakfire_packager_finish_to_directory(struct pakfire_packager* packager, |
48c6f2e7 | 897 | const char* target) { |
48c6f2e7 | 898 | char path[PATH_MAX]; |
bb06d548 | 899 | char tmppath[PATH_MAX]; |
48c6f2e7 MT |
900 | int r = 1; |
901 | ||
902 | // target cannot be empty | |
903 | if (!target) { | |
904 | errno = EINVAL; | |
905 | return 1; | |
906 | } | |
907 | ||
bb06d548 MT |
908 | // Get the filename of the package |
909 | const char* filename = pakfire_packager_filename(packager); | |
910 | if (!filename) { | |
911 | ERROR(packager->pakfire, "Could not generate filename for package: %s\n", | |
912 | strerror(errno)); | |
913 | r = 1; | |
914 | goto ERROR; | |
915 | } | |
916 | ||
917 | // Make the package path | |
918 | r = pakfire_string_format(path, "%s/%s", target, filename); | |
919 | if (r < 0) | |
920 | goto ERROR; | |
921 | ||
922 | // Create the parent directory | |
923 | r = pakfire_mkparentdir(path, 0); | |
924 | if (r) | |
925 | goto ERROR; | |
926 | ||
48c6f2e7 | 927 | // Create a temporary file in the target directory |
bb06d548 MT |
928 | r = pakfire_string_format(tmppath, "%s.XXXXXX", path); |
929 | if (r < 0) | |
930 | goto ERROR; | |
48c6f2e7 MT |
931 | |
932 | // Create a temporary result file | |
bb06d548 | 933 | FILE* f = pakfire_mktemp(tmppath); |
48c6f2e7 MT |
934 | if (!f) |
935 | goto ERROR; | |
936 | ||
937 | // Write the finished package | |
938 | r = pakfire_packager_finish(packager, f); | |
bb06d548 MT |
939 | fclose(f); |
940 | ||
48c6f2e7 MT |
941 | if (r) { |
942 | ERROR(packager->pakfire, "pakfire_packager_finish() failed: %s\n", | |
943 | strerror(errno)); | |
944 | goto ERROR; | |
945 | } | |
946 | ||
48c6f2e7 | 947 | // Move the temporary file to destination |
bb06d548 MT |
948 | r = rename(tmppath, path); |
949 | if (r) { | |
950 | ERROR(packager->pakfire, "Could not move %s to %s: %s\n", | |
951 | tmppath, path, strerror(errno)); | |
48c6f2e7 | 952 | goto ERROR; |
bb06d548 | 953 | } |
48c6f2e7 | 954 | |
bb06d548 | 955 | INFO(packager->pakfire, "Package written to %s\n", path); |
48c6f2e7 MT |
956 | |
957 | // Success | |
958 | r = 0; | |
959 | ||
960 | ERROR: | |
961 | // Remove temporary file | |
bb06d548 MT |
962 | if (r && *tmppath) |
963 | unlink(tmppath); | |
48c6f2e7 | 964 | |
bb06d548 | 965 | return r; |
48c6f2e7 MT |
966 | } |
967 | ||
22b8e37b | 968 | int pakfire_packager_add(struct pakfire_packager* packager, |
809606fe | 969 | const char* sourcepath, const char* path) { |
da08f989 | 970 | FILE* f = NULL; |
da08f989 MT |
971 | |
972 | // Check if path is set | |
809606fe | 973 | if (!sourcepath) |
da08f989 MT |
974 | return EINVAL; |
975 | ||
809606fe MT |
976 | // Use basename if path isn't set |
977 | if (!path) { | |
978 | path = strrchr(sourcepath, '/'); | |
979 | if (path) | |
980 | path++; | |
981 | } | |
982 | ||
7836e21b MT |
983 | // Payload has already been closed |
984 | if (!packager->payload) | |
985 | return EINVAL; | |
986 | ||
da08f989 MT |
987 | // Create a new file entry |
988 | struct archive_entry* entry = archive_entry_new(); | |
989 | if (!entry) | |
990 | return ENOMEM; | |
991 | ||
7e346194 MT |
992 | DEBUG(packager->pakfire, "Adding '%s' to archive (from %s)\n", path, sourcepath); |
993 | ||
809606fe MT |
994 | // Set the source path |
995 | archive_entry_copy_sourcepath(entry, sourcepath); | |
996 | ||
da08f989 | 997 | // Set path in archive |
809606fe MT |
998 | if (path) |
999 | archive_entry_set_pathname(entry, path); | |
da08f989 | 1000 | |
738b3582 MT |
1001 | // Read all attributes from file |
1002 | int r = archive_read_disk_entry_from_file(packager->reader, entry, -1, NULL); | |
1003 | if (r) { | |
1004 | ERROR(packager->pakfire, "Could not read attributes from %s: %s\n", | |
1005 | path, strerror(errno)); | |
1006 | goto ERROR; | |
1007 | } | |
da08f989 MT |
1008 | |
1009 | // Write the header | |
1010 | r = archive_write_header(packager->payload, entry); | |
1011 | if (r) { | |
1012 | ERROR(packager->pakfire, "Error writing file header: %s\n", | |
1013 | archive_error_string(packager->payload)); | |
1014 | goto ERROR; | |
1015 | } | |
1016 | ||
1017 | // Copy the data of regular files | |
1018 | if (archive_entry_filetype(entry) == AE_IFREG) { | |
809606fe | 1019 | f = fopen(sourcepath, "r"); |
da08f989 | 1020 | if (!f) { |
809606fe | 1021 | ERROR(packager->pakfire, "Could not open %s: %s\n", sourcepath, strerror(errno)); |
da08f989 MT |
1022 | r = errno; |
1023 | goto ERROR; | |
1024 | } | |
1025 | ||
7836e21b MT |
1026 | r = pakfire_packager_copy_data(packager, packager->payload, f); |
1027 | if (r) | |
1028 | goto ERROR; | |
da08f989 MT |
1029 | } |
1030 | ||
4eab5079 MT |
1031 | // Write to mtree |
1032 | r = archive_write_header(packager->mtree, entry); | |
1033 | if (r) { | |
1034 | ERROR(packager->pakfire, "Adding file to mtree failed: %s\n", | |
1035 | archive_error_string(packager->mtree)); | |
1036 | goto ERROR; | |
1037 | } | |
1038 | ||
e282863a MT |
1039 | // Increment installsize |
1040 | packager->installsize += archive_entry_size(entry); | |
1ba1869e | 1041 | |
da08f989 MT |
1042 | // Successful |
1043 | r = 0; | |
1044 | ||
1045 | ERROR: | |
1046 | if (entry) | |
1047 | archive_entry_free(entry); | |
1048 | ||
1049 | if (f) | |
1050 | fclose(f); | |
1051 | ||
1052 | return r; | |
1053 | } | |
106d2edd MT |
1054 | |
1055 | int pakfire_packager_add_scriptlet(struct pakfire_packager* packager, | |
1056 | struct pakfire_scriptlet* scriptlet) { | |
1057 | if (!scriptlet) { | |
1058 | errno = EINVAL; | |
1059 | return 1; | |
1060 | } | |
1061 | ||
1062 | // Extend array | |
1063 | packager->scriptlets = reallocarray(packager->scriptlets, | |
1064 | packager->num_scriptlets + 1, sizeof(*packager->scriptlets)); | |
1065 | if (!packager->scriptlets) | |
1066 | return 1; | |
1067 | ||
1068 | // Append scriptlet | |
1069 | packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet); | |
1070 | ||
1071 | return 0; | |
1072 | } |