]>
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 | 49 | |
31480bee | 50 | struct pakfire_package* 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); | |
51105783 | 253 | free(packager); |
1ea1f35b MT |
254 | } |
255 | ||
22b8e37b | 256 | int pakfire_packager_create(struct pakfire_packager** packager, |
31480bee | 257 | struct pakfire_package* pkg) { |
6aeb48e6 MT |
258 | struct pakfire_packager* p = calloc(1, sizeof(*p)); |
259 | if (!p) | |
260 | return ENOMEM; | |
261 | ||
e282863a MT |
262 | int r = 1; |
263 | ||
05cfc2c9 MT |
264 | // Save creation time |
265 | p->time_created = time(NULL); | |
266 | ||
6aeb48e6 MT |
267 | // Initialize reference counting |
268 | p->nrefs = 1; | |
269 | ||
270 | // Store a reference to Pakfire | |
271 | p->pakfire = pakfire_package_get_pakfire(pkg); | |
272 | ||
273 | // Store a reference to the package | |
274 | p->pkg = pakfire_package_ref(pkg); | |
275 | ||
0682aeb3 | 276 | // Create reader |
66817062 MT |
277 | p->reader = pakfire_make_archive_disk_reader(p->pakfire, 1); |
278 | if (!p->reader) | |
1ba1869e | 279 | goto ERROR; |
1ea1f35b | 280 | |
0682aeb3 | 281 | // Start payload |
af14aefb | 282 | r = pakfire_packager_create_payload(p); |
0682aeb3 | 283 | if (r) |
738b3582 | 284 | goto ERROR; |
738b3582 | 285 | |
6aeb48e6 MT |
286 | *packager = p; |
287 | ||
288 | return 0; | |
1ba1869e MT |
289 | |
290 | ERROR: | |
291 | pakfire_packager_free(p); | |
292 | ||
293 | return r; | |
6aeb48e6 MT |
294 | } |
295 | ||
22b8e37b | 296 | struct pakfire_packager* pakfire_packager_ref( |
6aeb48e6 MT |
297 | struct pakfire_packager* packager) { |
298 | ++packager->nrefs; | |
299 | ||
300 | return packager; | |
301 | } | |
302 | ||
22b8e37b | 303 | struct pakfire_packager* pakfire_packager_unref( |
6aeb48e6 MT |
304 | struct pakfire_packager* packager) { |
305 | if (--packager->nrefs > 0) | |
306 | return packager; | |
307 | ||
308 | pakfire_packager_free(packager); | |
309 | ||
310 | return NULL; | |
311 | } | |
da08f989 | 312 | |
22b8e37b | 313 | const char* pakfire_packager_filename(struct pakfire_packager* packager) { |
bb06d548 MT |
314 | if (!*packager->filename) { |
315 | const char* filename = pakfire_package_get_filename(packager->pkg); | |
316 | ||
317 | // Add arch | |
318 | if (pakfire_package_is_source(packager->pkg)) | |
319 | pakfire_string_set(packager->filename, filename); | |
320 | else { | |
321 | const char* arch = pakfire_package_get_arch(packager->pkg); | |
322 | ||
323 | pakfire_string_format(packager->filename, "%s/%s", arch, filename); | |
324 | } | |
325 | } | |
326 | ||
327 | return packager->filename; | |
96d2c7bc MT |
328 | } |
329 | ||
7836e21b MT |
330 | static int pakfire_packager_copy_data(struct pakfire_packager* packager, |
331 | struct archive* a, FILE* f) { | |
332 | char buffer[BUFFER_SIZE]; | |
333 | ||
265a7974 MT |
334 | rewind(f); |
335 | ||
7836e21b MT |
336 | while (!feof(f)) { |
337 | // Read a block from file | |
338 | size_t bytes_read = fread(buffer, 1, sizeof(buffer), f); | |
339 | ||
340 | // Check if any error occured | |
341 | if (ferror(f)) { | |
b1772bfb | 342 | ERROR(packager->pakfire, "Error reading from file: %m\n"); |
7836e21b MT |
343 | return 1; |
344 | } | |
345 | ||
346 | // Write the block to the archive | |
347 | ssize_t bytes_written = archive_write_data(a, buffer, bytes_read); | |
348 | if (bytes_written < 0) { | |
349 | ERROR(packager->pakfire, "Error writing data to archive: %s\n", | |
350 | archive_error_string(a)); | |
351 | return 1; | |
352 | } | |
353 | } | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
28700a5b | 358 | static struct archive_entry* pakfire_packager_create_file( |
95b81a98 | 359 | struct pakfire_packager* packager, const char* filename, size_t size, mode_t mode) { |
98e85f1c MT |
360 | // Create a new file entry |
361 | struct archive_entry* entry = archive_entry_new(); | |
362 | if (!entry) | |
28700a5b | 363 | return NULL; |
98e85f1c MT |
364 | |
365 | // Set filename | |
366 | archive_entry_set_pathname(entry, filename); | |
367 | ||
368 | // This is a regular file | |
369 | archive_entry_set_filetype(entry, AE_IFREG); | |
95b81a98 | 370 | archive_entry_set_perm(entry, mode); |
98e85f1c | 371 | |
28700a5b MT |
372 | // Set size |
373 | archive_entry_set_size(entry, size); | |
374 | ||
05cfc2c9 MT |
375 | // Set ownership |
376 | archive_entry_set_uname(entry, "root"); | |
377 | archive_entry_set_uid(entry, 0); | |
378 | archive_entry_set_gname(entry, "root"); | |
379 | archive_entry_set_gid(entry, 0); | |
380 | ||
381 | // Set times | |
382 | archive_entry_set_birthtime(entry, packager->time_created, 0); | |
383 | archive_entry_set_ctime(entry, packager->time_created, 0); | |
384 | archive_entry_set_mtime(entry, packager->time_created, 0); | |
385 | archive_entry_set_atime(entry, packager->time_created, 0); | |
386 | ||
28700a5b MT |
387 | return entry; |
388 | } | |
389 | ||
390 | static int pakfire_packager_write_file_from_buffer(struct pakfire_packager* packager, | |
7e346194 | 391 | struct archive* a, struct archive* mtree, const char* filename, mode_t mode, const char* buffer) { |
28700a5b MT |
392 | size_t size = strlen(buffer); |
393 | ||
394 | // Create a new file | |
7e346194 MT |
395 | struct archive_entry* entry = pakfire_packager_create_file(packager, filename, size, mode); |
396 | if (!entry) { | |
397 | ERROR(packager->pakfire, "Could not create file '%s'\n", filename); | |
28700a5b | 398 | return 1; |
7e346194 | 399 | } |
98e85f1c MT |
400 | |
401 | // This is the end of the header | |
402 | int r = archive_write_header(a, entry); | |
403 | if (r) { | |
404 | ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a)); | |
265a7974 | 405 | goto ERROR; |
98e85f1c MT |
406 | } |
407 | ||
408 | // Write content | |
409 | r = archive_write_data(a, buffer, strlen(buffer)); | |
410 | if (r < 0) { | |
411 | ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a)); | |
265a7974 | 412 | goto ERROR; |
98e85f1c MT |
413 | } |
414 | ||
265a7974 MT |
415 | // Add this file to the mtree |
416 | if (mtree) { | |
417 | r = archive_write_header(mtree, entry); | |
418 | if (r) { | |
419 | ERROR(packager->pakfire, "Error adding file to mtree: %s\n", | |
420 | archive_error_string(mtree)); | |
421 | goto ERROR; | |
422 | } | |
423 | } | |
424 | ||
425 | // Success | |
426 | r = 0; | |
427 | ||
428 | ERROR: | |
98e85f1c MT |
429 | archive_entry_free(entry); |
430 | ||
265a7974 | 431 | return r; |
98e85f1c MT |
432 | } |
433 | ||
436677a3 MT |
434 | static int pakfire_packager_write_format(struct pakfire_packager* packager, |
435 | struct archive* a) { | |
436 | const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n"; | |
437 | ||
7e346194 MT |
438 | DEBUG(packager->pakfire, "Writing package format\n"); |
439 | ||
265a7974 | 440 | int r = pakfire_packager_write_file_from_buffer(packager, a, NULL, |
95b81a98 | 441 | PAKFIRE_ARCHIVE_FN_FORMAT, 0444, buffer); |
a6dd151e MT |
442 | if (r) |
443 | return r; | |
444 | ||
445 | // Add package format marker | |
446 | pakfire_package_add_requires(packager->pkg, | |
447 | "pakfire(PackageFormat-" TO_STRING(PACKAGE_FORMAT) ")"); | |
448 | ||
449 | return 0; | |
436677a3 MT |
450 | } |
451 | ||
98e85f1c | 452 | static char* pakfire_package_make_metadata(struct pakfire_packager* packager) { |
31480bee | 453 | struct pakfire_package* pkg = packager->pkg; |
98e85f1c MT |
454 | |
455 | char* buffer = NULL; | |
456 | int r; | |
457 | ||
458 | // Print version information | |
459 | r = asprintf(&buffer, "# Pakfire %s\n\n", PACKAGE_VERSION); | |
460 | if (r < 0) | |
461 | goto ERROR; | |
462 | ||
463 | // Write package information | |
464 | r = asprintf(&buffer, "%s# Package information\npackage\n", buffer); | |
465 | if (r < 0) | |
466 | goto ERROR; | |
467 | ||
468 | // Write package name | |
469 | r = asprintf(&buffer, "%s\tname = %s\n", buffer, pakfire_package_get_name(pkg)); | |
470 | if (r < 0) | |
471 | goto ERROR; | |
472 | ||
6ed66687 MT |
473 | // Write package EVR |
474 | r = asprintf(&buffer, "%s\tevr = %s\n", buffer, pakfire_package_get_evr(pkg)); | |
98e85f1c MT |
475 | if (r < 0) |
476 | goto ERROR; | |
477 | ||
478 | // Write package arch | |
479 | r = asprintf(&buffer, "%s\tarch = %s\n", buffer, pakfire_package_get_arch(pkg)); | |
480 | if (r < 0) | |
481 | goto ERROR; | |
482 | ||
483 | // Write package UUID | |
484 | r = asprintf(&buffer, "%s\tuuid = %s\n", buffer, pakfire_package_get_uuid(pkg)); | |
485 | if (r < 0) | |
486 | goto ERROR; | |
487 | ||
488 | // Write package groups | |
489 | const char* groups = pakfire_package_get_groups(pkg); | |
490 | if (groups) { | |
491 | r = asprintf(&buffer, "%s\tgroups = %s\n", buffer, groups); | |
492 | if (r < 0) | |
493 | goto ERROR; | |
494 | } | |
495 | ||
496 | // Write package maintainer | |
497 | const char* maintainer = pakfire_package_get_maintainer(pkg); | |
498 | if (maintainer) { | |
499 | r = asprintf(&buffer, "%s\tmaintainer = %s\n", buffer, maintainer); | |
500 | if (r < 0) | |
501 | goto ERROR; | |
502 | } | |
503 | ||
504 | // Write package url | |
e3c279d5 | 505 | const char* url = pakfire_package_get_url(pkg); |
98e85f1c MT |
506 | if (url) { |
507 | r = asprintf(&buffer, "%s\turl = %s\n", buffer, url); | |
508 | if (r < 0) | |
509 | goto ERROR; | |
510 | } | |
511 | ||
512 | // Write package license | |
513 | const char* license = pakfire_package_get_license(pkg); | |
514 | if (license) { | |
515 | r = asprintf(&buffer, "%s\tlicense = %s\n", buffer, license); | |
516 | if (r < 0) | |
517 | goto ERROR; | |
518 | } | |
519 | ||
520 | // Write package summary | |
521 | const char* summary = pakfire_package_get_summary(pkg); | |
522 | if (summary) { | |
523 | r = asprintf(&buffer, "%s\tsummary = %s\n", buffer, summary); | |
524 | if (r < 0) | |
525 | goto ERROR; | |
526 | } | |
527 | ||
528 | // XXX description | |
529 | ||
530 | size_t size = pakfire_package_get_installsize(pkg); | |
531 | r = asprintf(&buffer, "%s\tsize = %zu\n", buffer, size); | |
532 | if (r < 0) | |
533 | goto ERROR; | |
534 | ||
535 | // End package block | |
536 | r = asprintf(&buffer, "%send\n\n", buffer); | |
537 | if (r < 0) | |
538 | goto ERROR; | |
539 | ||
540 | // Write build information | |
541 | r = asprintf(&buffer, "%s# Build information\nbuild\n", buffer); | |
542 | if (r < 0) | |
543 | goto ERROR; | |
544 | ||
545 | // Write build host | |
546 | const char* build_host = pakfire_package_get_build_host(pkg); | |
547 | if (build_host) { | |
548 | r = asprintf(&buffer, "%s\thost = %s\n", buffer, build_host); | |
549 | if (r < 0) | |
550 | goto ERROR; | |
551 | } | |
552 | ||
98e85f1c MT |
553 | // Write build id |
554 | const char* build_id = pakfire_package_get_build_id(pkg); | |
555 | if (build_id) { | |
556 | r = asprintf(&buffer, "%s\tid = %s\n", buffer, build_id); | |
557 | if (r < 0) | |
558 | goto ERROR; | |
559 | } | |
98e85f1c MT |
560 | |
561 | // Write build host | |
562 | time_t build_time = pakfire_package_get_build_time(pkg); | |
563 | if (build_host) { | |
564 | r = asprintf(&buffer, "%s\ttime = %lu\n", buffer, build_time); | |
565 | if (r < 0) | |
566 | goto ERROR; | |
567 | } | |
568 | ||
569 | // End build block | |
570 | r = asprintf(&buffer, "%send\n\n", buffer); | |
571 | if (r < 0) | |
572 | goto ERROR; | |
573 | ||
574 | #if 0 | |
575 | // Write distribution information | |
576 | r = asprintf(&buffer, "%s# Distribution information\ndistribution\n", buffer); | |
577 | if (r < 0) | |
578 | goto ERROR; | |
579 | ||
580 | // End distribution block | |
581 | r = asprintf(&buffer, "%send\n\n", buffer); | |
582 | if (r < 0) | |
583 | goto ERROR; | |
584 | #endif | |
585 | ||
586 | // Write dependency information | |
587 | r = asprintf(&buffer, "%s# Dependency information\ndependencies\n", buffer); | |
588 | if (r < 0) | |
589 | goto ERROR; | |
590 | ||
591 | const struct dependencies { | |
592 | const char* type; | |
31480bee | 593 | char** (*func)(struct pakfire_package* pkg); |
98e85f1c MT |
594 | } dependencies[] = { |
595 | { "prerequires", pakfire_package_get_prerequires }, | |
596 | { "requires", pakfire_package_get_requires }, | |
597 | { "provides", pakfire_package_get_provides }, | |
598 | { "conflicts", pakfire_package_get_conflicts }, | |
599 | { "obsoletes", pakfire_package_get_obsoletes }, | |
600 | { "recommends", pakfire_package_get_recommends }, | |
601 | { "suggests", pakfire_package_get_suggests }, | |
602 | { "supplements", pakfire_package_get_supplements }, | |
603 | { "enhances", pakfire_package_get_enhances }, | |
604 | { NULL }, | |
605 | }; | |
606 | ||
607 | for (const struct dependencies* d = dependencies; d->type; d++) { | |
452d3833 | 608 | char** list = d->func(pkg); |
98e85f1c MT |
609 | if (!list) |
610 | continue; | |
611 | ||
98e85f1c MT |
612 | // Write header |
613 | r = asprintf(&buffer, "%s\t%s\n", buffer, d->type); | |
614 | if (r < 0) { | |
98e85f1c MT |
615 | goto ERROR; |
616 | } | |
617 | ||
452d3833 MT |
618 | for (char** dep = list; *dep; dep++) { |
619 | asprintf(&buffer, "%s\t\t%s\n", buffer, *dep); | |
620 | free(*dep); | |
98e85f1c | 621 | } |
452d3833 | 622 | free(list); |
98e85f1c MT |
623 | |
624 | // End block | |
625 | r = asprintf(&buffer, "%s\tend\n", buffer); | |
626 | if (r < 0) | |
627 | goto ERROR; | |
98e85f1c MT |
628 | } |
629 | ||
630 | // End dependencies block | |
631 | r = asprintf(&buffer, "%send\n\n", buffer); | |
632 | if (r < 0) | |
633 | goto ERROR; | |
634 | ||
635 | // EOF | |
636 | r = asprintf(&buffer, "%s# EOF\n", buffer); | |
637 | if (r < 0) | |
638 | goto ERROR; | |
639 | ||
640 | return buffer; | |
641 | ||
642 | ERROR: | |
643 | if (buffer) | |
644 | free(buffer); | |
645 | ||
646 | return NULL; | |
647 | } | |
648 | ||
649 | static int pakfire_packager_write_metadata(struct pakfire_packager* packager, | |
265a7974 | 650 | struct archive* a, struct archive* mtree) { |
98e85f1c MT |
651 | // Make metadata |
652 | char* buffer = pakfire_package_make_metadata(packager); | |
653 | if (!buffer) | |
654 | return 1; | |
655 | ||
656 | DEBUG(packager->pakfire, "Generated package metadata:\n%s", buffer); | |
657 | ||
658 | // Write buffer | |
265a7974 | 659 | int r = pakfire_packager_write_file_from_buffer(packager, a, mtree, |
95b81a98 | 660 | PAKFIRE_ARCHIVE_FN_METADATA, 0444, buffer); |
98e85f1c MT |
661 | |
662 | free(buffer); | |
663 | ||
664 | return r; | |
665 | } | |
666 | ||
4eab5079 | 667 | static int pakfire_packager_write_archive(struct pakfire_packager* packager, |
265a7974 MT |
668 | struct archive* a, struct archive* mtree, const char* filename, |
669 | struct archive** payload, FILE* f) { | |
2adc4a4a MT |
670 | struct stat st; |
671 | ||
7e346194 MT |
672 | DEBUG(packager->pakfire, "Writing '%s' to package\n", filename); |
673 | ||
2adc4a4a | 674 | // Close the payload |
4eab5079 MT |
675 | if (*payload) { |
676 | archive_write_free(*payload); | |
677 | *payload = NULL; | |
2adc4a4a MT |
678 | } |
679 | ||
680 | // Reset fd to beginning of the file | |
4eab5079 | 681 | rewind(f); |
2adc4a4a | 682 | |
4eab5079 | 683 | int fd = fileno(f); |
2adc4a4a MT |
684 | |
685 | // Stat the payload file | |
686 | int r = fstat(fd, &st); | |
687 | if (r) { | |
b1772bfb | 688 | ERROR(packager->pakfire, "stat() on fd %d failed: %m\n", fd); |
2adc4a4a MT |
689 | return 1; |
690 | } | |
691 | ||
28700a5b MT |
692 | // Create a new file |
693 | struct archive_entry* entry = pakfire_packager_create_file(packager, | |
95b81a98 | 694 | filename, st.st_size, 0444); |
2adc4a4a MT |
695 | if (!entry) |
696 | return 1; | |
697 | ||
2adc4a4a MT |
698 | // This is the end of the header |
699 | r = archive_write_header(a, entry); | |
700 | if (r) { | |
701 | ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a)); | |
702 | goto ERROR; | |
703 | } | |
704 | ||
705 | // Copy data | |
4eab5079 | 706 | r = pakfire_packager_copy_data(packager, a, f); |
7e346194 MT |
707 | if (r) { |
708 | const char* error = archive_error_string(a); | |
709 | if (!error) | |
710 | error = strerror(errno); | |
711 | ||
712 | ERROR(packager->pakfire, "Could not copy payload: %s\n", error); | |
2adc4a4a | 713 | goto ERROR; |
7e346194 | 714 | } |
2adc4a4a | 715 | |
265a7974 MT |
716 | // Add this file to the mtree |
717 | if (mtree) { | |
718 | r = archive_write_header(mtree, entry); | |
719 | if (r) { | |
720 | ERROR(packager->pakfire, "Error adding file to mtree: %s\n", | |
721 | archive_error_string(mtree)); | |
722 | goto ERROR; | |
723 | } | |
724 | ||
725 | r = pakfire_packager_copy_data(packager, mtree, f); | |
726 | if (r) { | |
727 | ERROR(packager->pakfire, "Error copying data to mtree: %s\n", | |
728 | archive_error_string(mtree)); | |
729 | goto ERROR; | |
730 | } | |
731 | } | |
732 | ||
2adc4a4a MT |
733 | // Success |
734 | r = 0; | |
735 | ||
736 | ERROR: | |
737 | archive_entry_free(entry); | |
738 | ||
739 | return r; | |
740 | } | |
741 | ||
106d2edd MT |
742 | static int pakfire_packager_write_scriptlet(struct pakfire_packager* packager, |
743 | struct archive* a, struct archive* mtree, struct pakfire_scriptlet* scriptlet) { | |
744 | char filename[PATH_MAX]; | |
745 | size_t size; | |
746 | int r; | |
747 | ||
748 | // Fetch type | |
749 | const char* type = pakfire_scriptlet_get_type(scriptlet); | |
750 | ||
751 | DEBUG(packager->pakfire, "Writing scriptlet '%s' to package\n", type); | |
752 | ||
753 | // Make filename | |
754 | r = pakfire_string_format(filename, "scriptlet/%s", type); | |
755 | if (r < 0) | |
756 | return r; | |
757 | ||
758 | // Fetch scriptlet | |
759 | const char* data = pakfire_scriptlet_get_data(scriptlet, &size); | |
760 | ||
761 | // Write file | |
95b81a98 | 762 | return pakfire_packager_write_file_from_buffer(packager, a, mtree, filename, 0544, data); |
106d2edd MT |
763 | } |
764 | ||
436677a3 MT |
765 | /* |
766 | This function is being called at the end when all data has been added to the package. | |
767 | ||
768 | It will create a new archive and write the package to the given file descriptor. | |
769 | */ | |
22b8e37b | 770 | int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) { |
265a7974 MT |
771 | struct archive* mtree = NULL; |
772 | FILE* fmtree = NULL; | |
96d2c7bc | 773 | int r = 1; |
436677a3 | 774 | |
fafe383d | 775 | // Store total instal size |
e282863a | 776 | pakfire_package_set_installsize(packager->pkg, packager->installsize); |
fafe383d | 777 | |
436677a3 MT |
778 | // Open a new archive |
779 | struct archive* a = archive_write_new(); | |
780 | if (!a) { | |
781 | ERROR(packager->pakfire, "archive_write_new() failed\n"); | |
782 | goto ERROR; | |
783 | } | |
784 | ||
785 | // Use the PAX format | |
96d2c7bc | 786 | r = archive_write_set_format_pax(a); |
436677a3 MT |
787 | if (r) { |
788 | ERROR(packager->pakfire, "Could not set format to PAX: %s\n", | |
789 | archive_error_string(a)); | |
790 | goto ERROR; | |
791 | } | |
792 | ||
793 | // Write archive to f | |
794 | r = archive_write_open_FILE(a, f); | |
795 | if (r) { | |
796 | ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n", | |
797 | archive_error_string(a)); | |
798 | goto ERROR; | |
799 | } | |
800 | ||
265a7974 MT |
801 | static const char* mtree_fields[] = { |
802 | // Disable everything | |
803 | "!all", | |
804 | ||
805 | // Include the file size | |
806 | "size", | |
807 | ||
808 | // Add the checksums | |
809 | "sha512", | |
810 | "sha256", | |
811 | ||
812 | NULL, | |
813 | }; | |
814 | ||
815 | // Create an mtree for the checksums | |
816 | r = pakfire_packager_create_mtree(packager->pakfire, &mtree, &fmtree, mtree_fields); | |
817 | if (r) { | |
818 | ERROR(packager->pakfire, "Could not create mtree\n"); | |
819 | goto ERROR; | |
820 | } | |
821 | ||
436677a3 MT |
822 | // Start with the format file |
823 | r = pakfire_packager_write_format(packager, a); | |
265a7974 MT |
824 | if (r) { |
825 | ERROR(packager->pakfire, "Could not add format file to archive: %s\n", | |
826 | archive_error_string(a)); | |
436677a3 | 827 | goto ERROR; |
265a7974 | 828 | } |
436677a3 | 829 | |
98e85f1c | 830 | // Write the metadata |
265a7974 MT |
831 | r = pakfire_packager_write_metadata(packager, a, mtree); |
832 | if (r) { | |
833 | ERROR(packager->pakfire, "Could not add metadata file to archive: %s\n", | |
834 | archive_error_string(a)); | |
98e85f1c | 835 | goto ERROR; |
265a7974 | 836 | } |
98e85f1c | 837 | |
4eab5079 | 838 | // Write the filelist in mtree format |
265a7974 | 839 | r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_FILELIST, |
4eab5079 | 840 | &packager->mtree, packager->fmtree); |
265a7974 MT |
841 | if (r) { |
842 | ERROR(packager->pakfire, "Could not add filelist to archive: %s\n", | |
843 | archive_error_string(a)); | |
4eab5079 | 844 | goto ERROR; |
265a7974 | 845 | } |
4eab5079 | 846 | |
2adc4a4a | 847 | // Write the payload |
265a7974 | 848 | r = pakfire_packager_write_archive(packager, a, mtree, PAKFIRE_ARCHIVE_FN_PAYLOAD, |
4eab5079 | 849 | &packager->payload, packager->fpayload); |
265a7974 MT |
850 | if (r) { |
851 | ERROR(packager->pakfire, "Could not add payload to archive: %s\n", | |
852 | archive_error_string(a)); | |
2adc4a4a | 853 | goto ERROR; |
265a7974 MT |
854 | } |
855 | ||
106d2edd MT |
856 | // Write scriptlets |
857 | for (unsigned int i = 0; i < packager->num_scriptlets; i++) { | |
858 | r = pakfire_packager_write_scriptlet(packager, a, mtree, packager->scriptlets[i]); | |
859 | if (r) { | |
b1772bfb | 860 | ERROR(packager->pakfire, "Could not add scriptlet to the archive: %m\n"); |
106d2edd MT |
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) { | |
b1772bfb | 911 | ERROR(packager->pakfire, "Could not generate filename for package: %m\n"); |
bb06d548 MT |
912 | r = 1; |
913 | goto ERROR; | |
914 | } | |
915 | ||
916 | // Make the package path | |
917 | r = pakfire_string_format(path, "%s/%s", target, filename); | |
918 | if (r < 0) | |
919 | goto ERROR; | |
920 | ||
921 | // Create the parent directory | |
922 | r = pakfire_mkparentdir(path, 0); | |
923 | if (r) | |
924 | goto ERROR; | |
925 | ||
48c6f2e7 | 926 | // Create a temporary file in the target directory |
bb06d548 MT |
927 | r = pakfire_string_format(tmppath, "%s.XXXXXX", path); |
928 | if (r < 0) | |
929 | goto ERROR; | |
48c6f2e7 MT |
930 | |
931 | // Create a temporary result file | |
bb06d548 | 932 | FILE* f = pakfire_mktemp(tmppath); |
48c6f2e7 MT |
933 | if (!f) |
934 | goto ERROR; | |
935 | ||
936 | // Write the finished package | |
937 | r = pakfire_packager_finish(packager, f); | |
bb06d548 MT |
938 | fclose(f); |
939 | ||
48c6f2e7 | 940 | if (r) { |
b1772bfb | 941 | ERROR(packager->pakfire, "pakfire_packager_finish() failed: %m\n"); |
48c6f2e7 MT |
942 | goto ERROR; |
943 | } | |
944 | ||
48c6f2e7 | 945 | // Move the temporary file to destination |
bb06d548 MT |
946 | r = rename(tmppath, path); |
947 | if (r) { | |
b1772bfb | 948 | ERROR(packager->pakfire, "Could not move %s to %s: %m\n", tmppath, path); |
48c6f2e7 | 949 | goto ERROR; |
bb06d548 | 950 | } |
48c6f2e7 | 951 | |
bb06d548 | 952 | INFO(packager->pakfire, "Package written to %s\n", path); |
48c6f2e7 MT |
953 | |
954 | // Success | |
955 | r = 0; | |
956 | ||
957 | ERROR: | |
958 | // Remove temporary file | |
bb06d548 MT |
959 | if (r && *tmppath) |
960 | unlink(tmppath); | |
48c6f2e7 | 961 | |
bb06d548 | 962 | return r; |
48c6f2e7 MT |
963 | } |
964 | ||
22b8e37b | 965 | int pakfire_packager_add(struct pakfire_packager* packager, |
809606fe | 966 | const char* sourcepath, const char* path) { |
da08f989 | 967 | FILE* f = NULL; |
da08f989 MT |
968 | |
969 | // Check if path is set | |
809606fe | 970 | if (!sourcepath) |
da08f989 MT |
971 | return EINVAL; |
972 | ||
809606fe MT |
973 | // Use basename if path isn't set |
974 | if (!path) { | |
975 | path = strrchr(sourcepath, '/'); | |
976 | if (path) | |
977 | path++; | |
978 | } | |
979 | ||
7836e21b MT |
980 | // Payload has already been closed |
981 | if (!packager->payload) | |
982 | return EINVAL; | |
983 | ||
da08f989 MT |
984 | // Create a new file entry |
985 | struct archive_entry* entry = archive_entry_new(); | |
986 | if (!entry) | |
987 | return ENOMEM; | |
988 | ||
7e346194 MT |
989 | DEBUG(packager->pakfire, "Adding '%s' to archive (from %s)\n", path, sourcepath); |
990 | ||
809606fe MT |
991 | // Set the source path |
992 | archive_entry_copy_sourcepath(entry, sourcepath); | |
993 | ||
da08f989 | 994 | // Set path in archive |
809606fe MT |
995 | if (path) |
996 | archive_entry_set_pathname(entry, path); | |
da08f989 | 997 | |
738b3582 MT |
998 | // Read all attributes from file |
999 | int r = archive_read_disk_entry_from_file(packager->reader, entry, -1, NULL); | |
1000 | if (r) { | |
b1772bfb | 1001 | ERROR(packager->pakfire, "Could not read attributes from %s: %m\n", path); |
738b3582 MT |
1002 | goto ERROR; |
1003 | } | |
da08f989 MT |
1004 | |
1005 | // Write the header | |
1006 | r = archive_write_header(packager->payload, entry); | |
1007 | if (r) { | |
1008 | ERROR(packager->pakfire, "Error writing file header: %s\n", | |
1009 | archive_error_string(packager->payload)); | |
1010 | goto ERROR; | |
1011 | } | |
1012 | ||
1013 | // Copy the data of regular files | |
1014 | if (archive_entry_filetype(entry) == AE_IFREG) { | |
809606fe | 1015 | f = fopen(sourcepath, "r"); |
da08f989 | 1016 | if (!f) { |
b1772bfb | 1017 | ERROR(packager->pakfire, "Could not open %s: %m\n", sourcepath); |
da08f989 MT |
1018 | r = errno; |
1019 | goto ERROR; | |
1020 | } | |
1021 | ||
7836e21b MT |
1022 | r = pakfire_packager_copy_data(packager, packager->payload, f); |
1023 | if (r) | |
1024 | goto ERROR; | |
da08f989 MT |
1025 | } |
1026 | ||
4eab5079 MT |
1027 | // Write to mtree |
1028 | r = archive_write_header(packager->mtree, entry); | |
1029 | if (r) { | |
1030 | ERROR(packager->pakfire, "Adding file to mtree failed: %s\n", | |
1031 | archive_error_string(packager->mtree)); | |
1032 | goto ERROR; | |
1033 | } | |
1034 | ||
e282863a MT |
1035 | // Increment installsize |
1036 | packager->installsize += archive_entry_size(entry); | |
1ba1869e | 1037 | |
da08f989 MT |
1038 | // Successful |
1039 | r = 0; | |
1040 | ||
1041 | ERROR: | |
1042 | if (entry) | |
1043 | archive_entry_free(entry); | |
1044 | ||
1045 | if (f) | |
1046 | fclose(f); | |
1047 | ||
1048 | return r; | |
1049 | } | |
106d2edd MT |
1050 | |
1051 | int pakfire_packager_add_scriptlet(struct pakfire_packager* packager, | |
1052 | struct pakfire_scriptlet* scriptlet) { | |
1053 | if (!scriptlet) { | |
1054 | errno = EINVAL; | |
1055 | return 1; | |
1056 | } | |
1057 | ||
1058 | // Extend array | |
1059 | packager->scriptlets = reallocarray(packager->scriptlets, | |
1060 | packager->num_scriptlets + 1, sizeof(*packager->scriptlets)); | |
1061 | if (!packager->scriptlets) | |
1062 | return 1; | |
1063 | ||
1064 | // Append scriptlet | |
1065 | packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet); | |
1066 | ||
1067 | return 0; | |
1068 | } |