]>
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> |
f57ad279 | 40 | #include <pakfire/util.h> |
6aeb48e6 | 41 | |
da08f989 MT |
42 | #define BUFFER_SIZE 64 * 1024 |
43 | ||
6aeb48e6 | 44 | struct pakfire_packager { |
ac4c607b | 45 | struct pakfire* pakfire; |
6aeb48e6 | 46 | int nrefs; |
05cfc2c9 | 47 | time_t time_created; |
6aeb48e6 | 48 | |
31480bee | 49 | struct pakfire_package* pkg; |
bb06d548 | 50 | char filename[PATH_MAX]; |
1ea1f35b | 51 | |
738b3582 MT |
52 | // Reader |
53 | struct archive* reader; | |
54 | ||
55 | // Payload | |
1ea1f35b | 56 | struct archive* payload; |
7836e21b | 57 | FILE* fpayload; |
4eab5079 MT |
58 | struct archive* mtree; |
59 | FILE* fmtree; | |
e282863a | 60 | size_t installsize; |
106d2edd MT |
61 | |
62 | struct pakfire_scriptlet** scriptlets; | |
63 | unsigned int num_scriptlets; | |
6aeb48e6 MT |
64 | }; |
65 | ||
ac4c607b | 66 | static int pakfire_packager_create_mtree(struct pakfire* pakfire, struct archive** mtree, |
265a7974 | 67 | FILE** f, const char** fields) { |
96de0b5d | 68 | char path[] = PAKFIRE_PRIVATE_DIR "/tmp/.pakfire-mtree.XXXXXX"; |
265a7974 MT |
69 | int r; |
70 | ||
71 | // Create an mtree | |
72 | *mtree = archive_write_new(); | |
73 | if (!*mtree) { | |
74 | ERROR(pakfire, "archive_write_new() failed\n"); | |
75 | goto ERROR; | |
76 | } | |
77 | ||
78 | // Use mtree format | |
79 | r = archive_write_set_format_mtree(*mtree); | |
80 | if (r) { | |
81 | ERROR(pakfire, "Could not set format to mtree: %s\n", | |
82 | archive_error_string(*mtree)); | |
83 | goto ERROR; | |
84 | } | |
85 | ||
86 | // Always compress using Zstd | |
87 | r = archive_write_add_filter_zstd(*mtree); | |
88 | if (r) { | |
89 | ERROR(pakfire, "Could not enable Zstandard compression: %s\n", | |
90 | archive_error_string(*mtree)); | |
91 | goto ERROR; | |
92 | } | |
93 | ||
94 | // Enable mtree fields | |
95 | if (fields) { | |
96 | for (const char** field = fields; *field; field++) { | |
97 | r = archive_write_set_options(*mtree, *field); | |
98 | if (r) { | |
99 | ERROR(pakfire, "Could not set mtree options %s: %s\n", | |
100 | *field, archive_error_string(*mtree)); | |
101 | goto ERROR; | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
106 | // Create a new temporary file | |
107 | *f = pakfire_mktemp(path); | |
108 | if (!*f) | |
109 | goto ERROR; | |
110 | ||
111 | // Unlink the file straight away | |
112 | unlink(path); | |
113 | ||
114 | // Write mtree to file | |
115 | r = archive_write_open_FILE(*mtree, *f); | |
116 | if (r) | |
117 | goto ERROR; | |
118 | ||
119 | return 0; | |
120 | ||
121 | ERROR: | |
122 | if (*mtree) { | |
123 | archive_write_free(*mtree); | |
124 | *mtree = NULL; | |
125 | } | |
126 | ||
127 | if (*f) { | |
128 | fclose(*f); | |
129 | *f = NULL; | |
130 | } | |
131 | ||
132 | return 1; | |
133 | } | |
134 | ||
af14aefb | 135 | static int pakfire_packager_create_payload(struct pakfire_packager* p) { |
96de0b5d | 136 | char path[] = PAKFIRE_PRIVATE_DIR "/tmp/.pakfire-payload.XXXXXX"; |
7836e21b | 137 | |
af14aefb MT |
138 | // Do not compress source packages |
139 | const int compress = !pakfire_package_is_source(p->pkg); | |
140 | ||
1ea1f35b | 141 | p->payload = archive_write_new(); |
427fdd80 MT |
142 | if (!p->payload) { |
143 | ERROR(p->pakfire, "archive_write_new() failed\n"); | |
144 | return 1; | |
145 | } | |
1ea1f35b MT |
146 | |
147 | // Use the PAX format | |
148 | int r = archive_write_set_format_pax(p->payload); | |
149 | if (r) { | |
150 | ERROR(p->pakfire, "Could not set format to PAX: %s\n", | |
151 | archive_error_string(p->payload)); | |
152 | return r; | |
153 | } | |
154 | ||
155 | // Add filters to compress the payload | |
156 | if (compress) { | |
157 | // Enable Zstd | |
158 | r = archive_write_add_filter_zstd(p->payload); | |
159 | if (r) { | |
160 | ERROR(p->pakfire, "Could not enable Zstandard compression: %s\n", | |
161 | archive_error_string(p->payload)); | |
162 | return r; | |
163 | } | |
164 | ||
165 | // Set compression level to highest | |
be69fe38 | 166 | r = archive_write_set_filter_option(p->payload, NULL, "compression-level", "22"); |
1ea1f35b MT |
167 | if (r) { |
168 | ERROR(p->pakfire, "Could not set Zstandard compression level: %s\n", | |
169 | archive_error_string(p->payload)); | |
170 | return r; | |
171 | } | |
ae8f5905 MT |
172 | |
173 | // Add feature marker | |
174 | pakfire_package_add_requires(p->pkg, "pakfire(Compress-Zstandard)"); | |
0dd87cbe MT |
175 | |
176 | // Do not pad the last block | |
177 | archive_write_set_bytes_in_last_block(p->payload, 1); | |
1ea1f35b MT |
178 | } |
179 | ||
7836e21b | 180 | // Create a new temporary file |
265a7974 | 181 | p->fpayload = pakfire_mktemp(path); |
f57ad279 | 182 | if (!p->fpayload) |
1ea1f35b | 183 | return 1; |
1ea1f35b | 184 | |
7836e21b | 185 | // Unlink the file straight away |
265a7974 | 186 | unlink(path); |
7836e21b | 187 | |
7836e21b MT |
188 | // Write archive to file |
189 | r = archive_write_open_FILE(p->payload, p->fpayload); | |
1ea1f35b MT |
190 | if (r) |
191 | return r; | |
192 | ||
4eab5079 MT |
193 | static const char* mtree_fields[] = { |
194 | // Disable the default | |
195 | "!all", | |
196 | ||
197 | // Enable standard file fields | |
198 | "device", | |
199 | "gname", | |
200 | "link", | |
201 | "mode", | |
202 | "nlink", | |
203 | "size", | |
204 | "time", | |
205 | "type", | |
206 | "uname", | |
207 | ||
208 | // Enable cryptographic checksums | |
209 | "sha512", | |
210 | "sha256", | |
211 | ||
212 | NULL, | |
213 | }; | |
214 | ||
265a7974 MT |
215 | // Create an mtree |
216 | r = pakfire_packager_create_mtree(p->pakfire, &p->mtree, &p->fmtree, mtree_fields); | |
217 | if (r) { | |
218 | ERROR(p->pakfire, "Could not create mtree\n"); | |
4eab5079 | 219 | return r; |
265a7974 | 220 | } |
4eab5079 | 221 | |
1ea1f35b MT |
222 | return 0; |
223 | } | |
224 | ||
225 | static void pakfire_packager_free(struct pakfire_packager* packager) { | |
106d2edd MT |
226 | // Scriptlets |
227 | if (packager->scriptlets) { | |
228 | for (unsigned int i = 0; i < packager->num_scriptlets; i++) | |
9dd7b1aa | 229 | pakfire_scriptlet_unref(packager->scriptlets[i]); |
106d2edd MT |
230 | free(packager->scriptlets); |
231 | } | |
232 | ||
4eab5079 | 233 | // Payload |
1ea1f35b MT |
234 | if (packager->payload) |
235 | archive_write_free(packager->payload); | |
236 | ||
7836e21b MT |
237 | if (packager->fpayload) |
238 | fclose(packager->fpayload); | |
239 | ||
4eab5079 MT |
240 | // mtree |
241 | if (packager->mtree) | |
242 | archive_write_free(packager->mtree); | |
243 | ||
244 | if (packager->fmtree) | |
245 | fclose(packager->fmtree); | |
246 | ||
738b3582 | 247 | if (packager->reader) |
66817062 | 248 | archive_read_free(packager->reader); |
738b3582 | 249 | |
1ea1f35b MT |
250 | pakfire_package_unref(packager->pkg); |
251 | pakfire_unref(packager->pakfire); | |
51105783 | 252 | free(packager); |
1ea1f35b MT |
253 | } |
254 | ||
22b8e37b | 255 | int pakfire_packager_create(struct pakfire_packager** packager, |
31480bee | 256 | struct pakfire_package* pkg) { |
6aeb48e6 MT |
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)) { | |
b1772bfb | 341 | ERROR(packager->pakfire, "Error reading from file: %m\n"); |
7836e21b MT |
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 | } | |
7e6379d0 MT |
422 | |
423 | // Write content | |
424 | r = archive_write_data(mtree, buffer, strlen(buffer)); | |
425 | if (r < 0) { | |
426 | ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(mtree)); | |
427 | goto ERROR; | |
428 | } | |
265a7974 MT |
429 | } |
430 | ||
431 | // Success | |
432 | r = 0; | |
433 | ||
434 | ERROR: | |
98e85f1c MT |
435 | archive_entry_free(entry); |
436 | ||
265a7974 | 437 | return r; |
98e85f1c MT |
438 | } |
439 | ||
436677a3 MT |
440 | static int pakfire_packager_write_format(struct pakfire_packager* packager, |
441 | struct archive* a) { | |
442 | const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n"; | |
443 | ||
7e346194 MT |
444 | DEBUG(packager->pakfire, "Writing package format\n"); |
445 | ||
265a7974 | 446 | int r = pakfire_packager_write_file_from_buffer(packager, a, NULL, |
0dbfb5e1 | 447 | "pakfire-format", 0444, buffer); |
a6dd151e MT |
448 | if (r) |
449 | return r; | |
450 | ||
451 | // Add package format marker | |
452 | pakfire_package_add_requires(packager->pkg, | |
453 | "pakfire(PackageFormat-" TO_STRING(PACKAGE_FORMAT) ")"); | |
454 | ||
455 | return 0; | |
436677a3 MT |
456 | } |
457 | ||
98e85f1c | 458 | static char* pakfire_package_make_metadata(struct pakfire_packager* packager) { |
31480bee | 459 | struct pakfire_package* pkg = packager->pkg; |
98e85f1c MT |
460 | |
461 | char* buffer = NULL; | |
462 | int r; | |
463 | ||
464 | // Print version information | |
465 | r = asprintf(&buffer, "# Pakfire %s\n\n", PACKAGE_VERSION); | |
466 | if (r < 0) | |
467 | goto ERROR; | |
468 | ||
469 | // Write package information | |
470 | r = asprintf(&buffer, "%s# Package information\npackage\n", buffer); | |
471 | if (r < 0) | |
472 | goto ERROR; | |
473 | ||
474 | // Write package name | |
475 | r = asprintf(&buffer, "%s\tname = %s\n", buffer, pakfire_package_get_name(pkg)); | |
476 | if (r < 0) | |
477 | goto ERROR; | |
478 | ||
6ed66687 MT |
479 | // Write package EVR |
480 | r = asprintf(&buffer, "%s\tevr = %s\n", buffer, pakfire_package_get_evr(pkg)); | |
98e85f1c MT |
481 | if (r < 0) |
482 | goto ERROR; | |
483 | ||
484 | // Write package arch | |
485 | r = asprintf(&buffer, "%s\tarch = %s\n", buffer, pakfire_package_get_arch(pkg)); | |
486 | if (r < 0) | |
487 | goto ERROR; | |
488 | ||
489 | // Write package UUID | |
490 | r = asprintf(&buffer, "%s\tuuid = %s\n", buffer, pakfire_package_get_uuid(pkg)); | |
491 | if (r < 0) | |
492 | goto ERROR; | |
493 | ||
494 | // Write package groups | |
495 | const char* groups = pakfire_package_get_groups(pkg); | |
496 | if (groups) { | |
497 | r = asprintf(&buffer, "%s\tgroups = %s\n", buffer, groups); | |
498 | if (r < 0) | |
499 | goto ERROR; | |
500 | } | |
501 | ||
502 | // Write package maintainer | |
503 | const char* maintainer = pakfire_package_get_maintainer(pkg); | |
504 | if (maintainer) { | |
505 | r = asprintf(&buffer, "%s\tmaintainer = %s\n", buffer, maintainer); | |
506 | if (r < 0) | |
507 | goto ERROR; | |
508 | } | |
509 | ||
510 | // Write package url | |
e3c279d5 | 511 | const char* url = pakfire_package_get_url(pkg); |
98e85f1c MT |
512 | if (url) { |
513 | r = asprintf(&buffer, "%s\turl = %s\n", buffer, url); | |
514 | if (r < 0) | |
515 | goto ERROR; | |
516 | } | |
517 | ||
518 | // Write package license | |
519 | const char* license = pakfire_package_get_license(pkg); | |
520 | if (license) { | |
521 | r = asprintf(&buffer, "%s\tlicense = %s\n", buffer, license); | |
522 | if (r < 0) | |
523 | goto ERROR; | |
524 | } | |
525 | ||
526 | // Write package summary | |
527 | const char* summary = pakfire_package_get_summary(pkg); | |
528 | if (summary) { | |
529 | r = asprintf(&buffer, "%s\tsummary = %s\n", buffer, summary); | |
530 | if (r < 0) | |
531 | goto ERROR; | |
532 | } | |
533 | ||
534 | // XXX description | |
535 | ||
536 | size_t size = pakfire_package_get_installsize(pkg); | |
537 | r = asprintf(&buffer, "%s\tsize = %zu\n", buffer, size); | |
538 | if (r < 0) | |
539 | goto ERROR; | |
540 | ||
541 | // End package block | |
542 | r = asprintf(&buffer, "%send\n\n", buffer); | |
543 | if (r < 0) | |
544 | goto ERROR; | |
545 | ||
546 | // Write build information | |
547 | r = asprintf(&buffer, "%s# Build information\nbuild\n", buffer); | |
548 | if (r < 0) | |
549 | goto ERROR; | |
550 | ||
551 | // Write build host | |
552 | const char* build_host = pakfire_package_get_build_host(pkg); | |
553 | if (build_host) { | |
554 | r = asprintf(&buffer, "%s\thost = %s\n", buffer, build_host); | |
555 | if (r < 0) | |
556 | goto ERROR; | |
557 | } | |
558 | ||
98e85f1c MT |
559 | // Write build id |
560 | const char* build_id = pakfire_package_get_build_id(pkg); | |
561 | if (build_id) { | |
562 | r = asprintf(&buffer, "%s\tid = %s\n", buffer, build_id); | |
563 | if (r < 0) | |
564 | goto ERROR; | |
565 | } | |
98e85f1c MT |
566 | |
567 | // Write build host | |
568 | time_t build_time = pakfire_package_get_build_time(pkg); | |
569 | if (build_host) { | |
570 | r = asprintf(&buffer, "%s\ttime = %lu\n", buffer, build_time); | |
571 | if (r < 0) | |
572 | goto ERROR; | |
573 | } | |
574 | ||
575 | // End build block | |
576 | r = asprintf(&buffer, "%send\n\n", buffer); | |
577 | if (r < 0) | |
578 | goto ERROR; | |
579 | ||
580 | #if 0 | |
581 | // Write distribution information | |
582 | r = asprintf(&buffer, "%s# Distribution information\ndistribution\n", buffer); | |
583 | if (r < 0) | |
584 | goto ERROR; | |
585 | ||
586 | // End distribution block | |
587 | r = asprintf(&buffer, "%send\n\n", buffer); | |
588 | if (r < 0) | |
589 | goto ERROR; | |
590 | #endif | |
591 | ||
592 | // Write dependency information | |
593 | r = asprintf(&buffer, "%s# Dependency information\ndependencies\n", buffer); | |
594 | if (r < 0) | |
595 | goto ERROR; | |
596 | ||
597 | const struct dependencies { | |
598 | const char* type; | |
31480bee | 599 | char** (*func)(struct pakfire_package* pkg); |
98e85f1c MT |
600 | } dependencies[] = { |
601 | { "prerequires", pakfire_package_get_prerequires }, | |
602 | { "requires", pakfire_package_get_requires }, | |
603 | { "provides", pakfire_package_get_provides }, | |
604 | { "conflicts", pakfire_package_get_conflicts }, | |
605 | { "obsoletes", pakfire_package_get_obsoletes }, | |
606 | { "recommends", pakfire_package_get_recommends }, | |
607 | { "suggests", pakfire_package_get_suggests }, | |
608 | { "supplements", pakfire_package_get_supplements }, | |
609 | { "enhances", pakfire_package_get_enhances }, | |
610 | { NULL }, | |
611 | }; | |
612 | ||
613 | for (const struct dependencies* d = dependencies; d->type; d++) { | |
452d3833 | 614 | char** list = d->func(pkg); |
98e85f1c MT |
615 | if (!list) |
616 | continue; | |
617 | ||
98e85f1c MT |
618 | // Write header |
619 | r = asprintf(&buffer, "%s\t%s\n", buffer, d->type); | |
620 | if (r < 0) { | |
98e85f1c MT |
621 | goto ERROR; |
622 | } | |
623 | ||
452d3833 MT |
624 | for (char** dep = list; *dep; dep++) { |
625 | asprintf(&buffer, "%s\t\t%s\n", buffer, *dep); | |
626 | free(*dep); | |
98e85f1c | 627 | } |
452d3833 | 628 | free(list); |
98e85f1c MT |
629 | |
630 | // End block | |
631 | r = asprintf(&buffer, "%s\tend\n", buffer); | |
632 | if (r < 0) | |
633 | goto ERROR; | |
98e85f1c MT |
634 | } |
635 | ||
636 | // End dependencies block | |
637 | r = asprintf(&buffer, "%send\n\n", buffer); | |
638 | if (r < 0) | |
639 | goto ERROR; | |
640 | ||
641 | // EOF | |
642 | r = asprintf(&buffer, "%s# EOF\n", buffer); | |
643 | if (r < 0) | |
644 | goto ERROR; | |
645 | ||
646 | return buffer; | |
647 | ||
648 | ERROR: | |
649 | if (buffer) | |
650 | free(buffer); | |
651 | ||
652 | return NULL; | |
653 | } | |
654 | ||
655 | static int pakfire_packager_write_metadata(struct pakfire_packager* packager, | |
265a7974 | 656 | struct archive* a, struct archive* mtree) { |
98e85f1c MT |
657 | // Make metadata |
658 | char* buffer = pakfire_package_make_metadata(packager); | |
659 | if (!buffer) | |
660 | return 1; | |
661 | ||
662 | DEBUG(packager->pakfire, "Generated package metadata:\n%s", buffer); | |
663 | ||
664 | // Write buffer | |
265a7974 | 665 | int r = pakfire_packager_write_file_from_buffer(packager, a, mtree, |
0dbfb5e1 | 666 | "info", 0444, buffer); |
98e85f1c MT |
667 | |
668 | free(buffer); | |
669 | ||
670 | return r; | |
671 | } | |
672 | ||
4eab5079 | 673 | static int pakfire_packager_write_archive(struct pakfire_packager* packager, |
265a7974 MT |
674 | struct archive* a, struct archive* mtree, const char* filename, |
675 | struct archive** payload, FILE* f) { | |
2adc4a4a MT |
676 | struct stat st; |
677 | ||
7e346194 MT |
678 | DEBUG(packager->pakfire, "Writing '%s' to package\n", filename); |
679 | ||
2adc4a4a | 680 | // Close the payload |
4eab5079 MT |
681 | if (*payload) { |
682 | archive_write_free(*payload); | |
683 | *payload = NULL; | |
2adc4a4a MT |
684 | } |
685 | ||
686 | // Reset fd to beginning of the file | |
4eab5079 | 687 | rewind(f); |
2adc4a4a | 688 | |
4eab5079 | 689 | int fd = fileno(f); |
2adc4a4a MT |
690 | |
691 | // Stat the payload file | |
692 | int r = fstat(fd, &st); | |
693 | if (r) { | |
b1772bfb | 694 | ERROR(packager->pakfire, "stat() on fd %d failed: %m\n", fd); |
2adc4a4a MT |
695 | return 1; |
696 | } | |
697 | ||
28700a5b MT |
698 | // Create a new file |
699 | struct archive_entry* entry = pakfire_packager_create_file(packager, | |
95b81a98 | 700 | filename, st.st_size, 0444); |
2adc4a4a MT |
701 | if (!entry) |
702 | return 1; | |
703 | ||
2adc4a4a MT |
704 | // This is the end of the header |
705 | r = archive_write_header(a, entry); | |
706 | if (r) { | |
707 | ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a)); | |
708 | goto ERROR; | |
709 | } | |
710 | ||
711 | // Copy data | |
4eab5079 | 712 | r = pakfire_packager_copy_data(packager, a, f); |
7e346194 MT |
713 | if (r) { |
714 | const char* error = archive_error_string(a); | |
715 | if (!error) | |
716 | error = strerror(errno); | |
717 | ||
718 | ERROR(packager->pakfire, "Could not copy payload: %s\n", error); | |
2adc4a4a | 719 | goto ERROR; |
7e346194 | 720 | } |
2adc4a4a | 721 | |
265a7974 MT |
722 | // Add this file to the mtree |
723 | if (mtree) { | |
724 | r = archive_write_header(mtree, entry); | |
725 | if (r) { | |
726 | ERROR(packager->pakfire, "Error adding file to mtree: %s\n", | |
727 | archive_error_string(mtree)); | |
728 | goto ERROR; | |
729 | } | |
730 | ||
731 | r = pakfire_packager_copy_data(packager, mtree, f); | |
732 | if (r) { | |
733 | ERROR(packager->pakfire, "Error copying data to mtree: %s\n", | |
734 | archive_error_string(mtree)); | |
735 | goto ERROR; | |
736 | } | |
737 | } | |
738 | ||
2adc4a4a MT |
739 | // Success |
740 | r = 0; | |
741 | ||
742 | ERROR: | |
743 | archive_entry_free(entry); | |
744 | ||
745 | return r; | |
746 | } | |
747 | ||
106d2edd MT |
748 | static int pakfire_packager_write_scriptlet(struct pakfire_packager* packager, |
749 | struct archive* a, struct archive* mtree, struct pakfire_scriptlet* scriptlet) { | |
750 | char filename[PATH_MAX]; | |
751 | size_t size; | |
752 | int r; | |
753 | ||
754 | // Fetch type | |
755 | const char* type = pakfire_scriptlet_get_type(scriptlet); | |
756 | ||
757 | DEBUG(packager->pakfire, "Writing scriptlet '%s' to package\n", type); | |
758 | ||
759 | // Make filename | |
760 | r = pakfire_string_format(filename, "scriptlet/%s", type); | |
761 | if (r < 0) | |
762 | return r; | |
763 | ||
764 | // Fetch scriptlet | |
765 | const char* data = pakfire_scriptlet_get_data(scriptlet, &size); | |
766 | ||
767 | // Write file | |
95b81a98 | 768 | return pakfire_packager_write_file_from_buffer(packager, a, mtree, filename, 0544, data); |
106d2edd MT |
769 | } |
770 | ||
436677a3 MT |
771 | /* |
772 | This function is being called at the end when all data has been added to the package. | |
773 | ||
774 | It will create a new archive and write the package to the given file descriptor. | |
775 | */ | |
22b8e37b | 776 | int pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) { |
265a7974 MT |
777 | struct archive* mtree = NULL; |
778 | FILE* fmtree = NULL; | |
96d2c7bc | 779 | int r = 1; |
436677a3 | 780 | |
508e2030 | 781 | // Store total install size |
e282863a | 782 | pakfire_package_set_installsize(packager->pkg, packager->installsize); |
fafe383d | 783 | |
508e2030 MT |
784 | // Dump package metadata |
785 | char* dump = pakfire_package_dump(packager->pkg, PAKFIRE_PKG_DUMP_LONG); | |
786 | if (dump) { | |
787 | INFO(packager->pakfire, "%s\n", dump); | |
788 | free(dump); | |
789 | } | |
790 | ||
436677a3 MT |
791 | // Open a new archive |
792 | struct archive* a = archive_write_new(); | |
793 | if (!a) { | |
794 | ERROR(packager->pakfire, "archive_write_new() failed\n"); | |
795 | goto ERROR; | |
796 | } | |
797 | ||
798 | // Use the PAX format | |
96d2c7bc | 799 | r = archive_write_set_format_pax(a); |
436677a3 MT |
800 | if (r) { |
801 | ERROR(packager->pakfire, "Could not set format to PAX: %s\n", | |
802 | archive_error_string(a)); | |
803 | goto ERROR; | |
804 | } | |
805 | ||
806 | // Write archive to f | |
807 | r = archive_write_open_FILE(a, f); | |
808 | if (r) { | |
809 | ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n", | |
810 | archive_error_string(a)); | |
811 | goto ERROR; | |
812 | } | |
813 | ||
265a7974 MT |
814 | static const char* mtree_fields[] = { |
815 | // Disable everything | |
816 | "!all", | |
817 | ||
7afc8d8f MT |
818 | // File type is mandatory |
819 | "type", | |
820 | ||
265a7974 MT |
821 | // Include the file size |
822 | "size", | |
823 | ||
824 | // Add the checksums | |
825 | "sha512", | |
826 | "sha256", | |
827 | ||
828 | NULL, | |
829 | }; | |
830 | ||
831 | // Create an mtree for the checksums | |
832 | r = pakfire_packager_create_mtree(packager->pakfire, &mtree, &fmtree, mtree_fields); | |
833 | if (r) { | |
834 | ERROR(packager->pakfire, "Could not create mtree\n"); | |
835 | goto ERROR; | |
836 | } | |
837 | ||
436677a3 MT |
838 | // Start with the format file |
839 | r = pakfire_packager_write_format(packager, a); | |
265a7974 MT |
840 | if (r) { |
841 | ERROR(packager->pakfire, "Could not add format file to archive: %s\n", | |
842 | archive_error_string(a)); | |
436677a3 | 843 | goto ERROR; |
265a7974 | 844 | } |
436677a3 | 845 | |
98e85f1c | 846 | // Write the metadata |
265a7974 MT |
847 | r = pakfire_packager_write_metadata(packager, a, mtree); |
848 | if (r) { | |
849 | ERROR(packager->pakfire, "Could not add metadata file to archive: %s\n", | |
850 | archive_error_string(a)); | |
98e85f1c | 851 | goto ERROR; |
265a7974 | 852 | } |
98e85f1c | 853 | |
4eab5079 | 854 | // Write the filelist in mtree format |
0dbfb5e1 | 855 | r = pakfire_packager_write_archive(packager, a, mtree, "filelist", |
4eab5079 | 856 | &packager->mtree, packager->fmtree); |
265a7974 MT |
857 | if (r) { |
858 | ERROR(packager->pakfire, "Could not add filelist to archive: %s\n", | |
859 | archive_error_string(a)); | |
4eab5079 | 860 | goto ERROR; |
265a7974 | 861 | } |
4eab5079 | 862 | |
2adc4a4a | 863 | // Write the payload |
0dbfb5e1 | 864 | r = pakfire_packager_write_archive(packager, a, mtree, "data.img", |
4eab5079 | 865 | &packager->payload, packager->fpayload); |
265a7974 MT |
866 | if (r) { |
867 | ERROR(packager->pakfire, "Could not add payload to archive: %s\n", | |
868 | archive_error_string(a)); | |
2adc4a4a | 869 | goto ERROR; |
265a7974 MT |
870 | } |
871 | ||
106d2edd MT |
872 | // Write scriptlets |
873 | for (unsigned int i = 0; i < packager->num_scriptlets; i++) { | |
874 | r = pakfire_packager_write_scriptlet(packager, a, mtree, packager->scriptlets[i]); | |
875 | if (r) { | |
b1772bfb | 876 | ERROR(packager->pakfire, "Could not add scriptlet to the archive: %m\n"); |
106d2edd MT |
877 | goto ERROR; |
878 | } | |
879 | } | |
880 | ||
265a7974 MT |
881 | // Finish checksums |
882 | r = archive_write_finish_entry(mtree); | |
883 | if (r) { | |
884 | ERROR(packager->pakfire, "Could not finish mtree: %s\n", | |
885 | archive_error_string(mtree)); | |
886 | goto ERROR; | |
887 | } | |
888 | ||
bb06d548 | 889 | // Write checksums |
0dbfb5e1 | 890 | r = pakfire_packager_write_archive(packager, a, NULL, "chksums", &mtree, fmtree); |
265a7974 MT |
891 | if (r) { |
892 | ERROR(packager->pakfire, "Could not add checksums to archive: %s\n", | |
893 | archive_error_string(a)); | |
894 | goto ERROR; | |
895 | } | |
2adc4a4a | 896 | |
96d2c7bc MT |
897 | // Success |
898 | r = 0; | |
436677a3 MT |
899 | |
900 | ERROR: | |
901 | if (a) | |
265a7974 MT |
902 | archive_write_free(a); |
903 | if (mtree) | |
904 | archive_write_free(mtree); | |
905 | if (fmtree) | |
906 | fclose(fmtree); | |
436677a3 | 907 | |
96d2c7bc | 908 | return r; |
436677a3 MT |
909 | } |
910 | ||
22b8e37b | 911 | int pakfire_packager_finish_to_directory(struct pakfire_packager* packager, |
45448dbd | 912 | const char* target, char** result) { |
48c6f2e7 | 913 | char path[PATH_MAX]; |
bb06d548 | 914 | char tmppath[PATH_MAX]; |
48c6f2e7 MT |
915 | int r = 1; |
916 | ||
917 | // target cannot be empty | |
918 | if (!target) { | |
919 | errno = EINVAL; | |
920 | return 1; | |
921 | } | |
922 | ||
bb06d548 MT |
923 | // Get the filename of the package |
924 | const char* filename = pakfire_packager_filename(packager); | |
925 | if (!filename) { | |
b1772bfb | 926 | ERROR(packager->pakfire, "Could not generate filename for package: %m\n"); |
bb06d548 MT |
927 | r = 1; |
928 | goto ERROR; | |
929 | } | |
930 | ||
931 | // Make the package path | |
932 | r = pakfire_string_format(path, "%s/%s", target, filename); | |
933 | if (r < 0) | |
934 | goto ERROR; | |
935 | ||
936 | // Create the parent directory | |
937 | r = pakfire_mkparentdir(path, 0); | |
938 | if (r) | |
939 | goto ERROR; | |
940 | ||
48c6f2e7 | 941 | // Create a temporary file in the target directory |
bb06d548 MT |
942 | r = pakfire_string_format(tmppath, "%s.XXXXXX", path); |
943 | if (r < 0) | |
944 | goto ERROR; | |
48c6f2e7 MT |
945 | |
946 | // Create a temporary result file | |
bb06d548 | 947 | FILE* f = pakfire_mktemp(tmppath); |
48c6f2e7 MT |
948 | if (!f) |
949 | goto ERROR; | |
950 | ||
951 | // Write the finished package | |
952 | r = pakfire_packager_finish(packager, f); | |
bb06d548 MT |
953 | fclose(f); |
954 | ||
48c6f2e7 | 955 | if (r) { |
b1772bfb | 956 | ERROR(packager->pakfire, "pakfire_packager_finish() failed: %m\n"); |
48c6f2e7 MT |
957 | goto ERROR; |
958 | } | |
959 | ||
48c6f2e7 | 960 | // Move the temporary file to destination |
bb06d548 MT |
961 | r = rename(tmppath, path); |
962 | if (r) { | |
b1772bfb | 963 | ERROR(packager->pakfire, "Could not move %s to %s: %m\n", tmppath, path); |
48c6f2e7 | 964 | goto ERROR; |
bb06d548 | 965 | } |
48c6f2e7 | 966 | |
bb06d548 | 967 | INFO(packager->pakfire, "Package written to %s\n", path); |
48c6f2e7 | 968 | |
45448dbd MT |
969 | // Store result path if requested |
970 | if (result) { | |
971 | *result = strdup(path); | |
972 | if (!*result) { | |
973 | r = 1; | |
974 | goto ERROR; | |
975 | } | |
976 | } | |
977 | ||
48c6f2e7 MT |
978 | // Success |
979 | r = 0; | |
980 | ||
981 | ERROR: | |
982 | // Remove temporary file | |
bb06d548 MT |
983 | if (r && *tmppath) |
984 | unlink(tmppath); | |
48c6f2e7 | 985 | |
bb06d548 | 986 | return r; |
48c6f2e7 MT |
987 | } |
988 | ||
22b8e37b | 989 | int pakfire_packager_add(struct pakfire_packager* packager, |
809606fe | 990 | const char* sourcepath, const char* path) { |
da08f989 | 991 | FILE* f = NULL; |
da08f989 MT |
992 | |
993 | // Check if path is set | |
809606fe | 994 | if (!sourcepath) |
da08f989 MT |
995 | return EINVAL; |
996 | ||
809606fe MT |
997 | // Use basename if path isn't set |
998 | if (!path) { | |
999 | path = strrchr(sourcepath, '/'); | |
1000 | if (path) | |
1001 | path++; | |
1002 | } | |
1003 | ||
7836e21b MT |
1004 | // Payload has already been closed |
1005 | if (!packager->payload) | |
1006 | return EINVAL; | |
1007 | ||
da08f989 MT |
1008 | // Create a new file entry |
1009 | struct archive_entry* entry = archive_entry_new(); | |
1010 | if (!entry) | |
1011 | return ENOMEM; | |
1012 | ||
7e346194 MT |
1013 | DEBUG(packager->pakfire, "Adding '%s' to archive (from %s)\n", path, sourcepath); |
1014 | ||
809606fe MT |
1015 | // Set the source path |
1016 | archive_entry_copy_sourcepath(entry, sourcepath); | |
1017 | ||
da08f989 | 1018 | // Set path in archive |
809606fe MT |
1019 | if (path) |
1020 | archive_entry_set_pathname(entry, path); | |
da08f989 | 1021 | |
738b3582 MT |
1022 | // Read all attributes from file |
1023 | int r = archive_read_disk_entry_from_file(packager->reader, entry, -1, NULL); | |
1024 | if (r) { | |
b1772bfb | 1025 | ERROR(packager->pakfire, "Could not read attributes from %s: %m\n", path); |
738b3582 MT |
1026 | goto ERROR; |
1027 | } | |
da08f989 MT |
1028 | |
1029 | // Write the header | |
1030 | r = archive_write_header(packager->payload, entry); | |
1031 | if (r) { | |
1032 | ERROR(packager->pakfire, "Error writing file header: %s\n", | |
1033 | archive_error_string(packager->payload)); | |
1034 | goto ERROR; | |
1035 | } | |
1036 | ||
1037 | // Copy the data of regular files | |
1038 | if (archive_entry_filetype(entry) == AE_IFREG) { | |
809606fe | 1039 | f = fopen(sourcepath, "r"); |
da08f989 | 1040 | if (!f) { |
b1772bfb | 1041 | ERROR(packager->pakfire, "Could not open %s: %m\n", sourcepath); |
da08f989 MT |
1042 | r = errno; |
1043 | goto ERROR; | |
1044 | } | |
1045 | ||
7836e21b MT |
1046 | r = pakfire_packager_copy_data(packager, packager->payload, f); |
1047 | if (r) | |
1048 | goto ERROR; | |
da08f989 MT |
1049 | } |
1050 | ||
4eab5079 MT |
1051 | // Write to mtree |
1052 | r = archive_write_header(packager->mtree, entry); | |
1053 | if (r) { | |
1054 | ERROR(packager->pakfire, "Adding file to mtree failed: %s\n", | |
1055 | archive_error_string(packager->mtree)); | |
1056 | goto ERROR; | |
1057 | } | |
1058 | ||
e282863a MT |
1059 | // Increment installsize |
1060 | packager->installsize += archive_entry_size(entry); | |
1ba1869e | 1061 | |
da08f989 MT |
1062 | // Successful |
1063 | r = 0; | |
1064 | ||
1065 | ERROR: | |
1066 | if (entry) | |
1067 | archive_entry_free(entry); | |
1068 | ||
1069 | if (f) | |
1070 | fclose(f); | |
1071 | ||
1072 | return r; | |
1073 | } | |
106d2edd MT |
1074 | |
1075 | int pakfire_packager_add_scriptlet(struct pakfire_packager* packager, | |
1076 | struct pakfire_scriptlet* scriptlet) { | |
1077 | if (!scriptlet) { | |
1078 | errno = EINVAL; | |
1079 | return 1; | |
1080 | } | |
1081 | ||
1082 | // Extend array | |
1083 | packager->scriptlets = reallocarray(packager->scriptlets, | |
1084 | packager->num_scriptlets + 1, sizeof(*packager->scriptlets)); | |
1085 | if (!packager->scriptlets) | |
1086 | return 1; | |
1087 | ||
1088 | // Append scriptlet | |
1089 | packager->scriptlets[packager->num_scriptlets++] = pakfire_scriptlet_ref(scriptlet); | |
1090 | ||
1091 | return 0; | |
1092 | } |