]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
packager: Remove writing a filelist to the package
[people/ms/pakfire.git] / src / libpakfire / packager.c
CommitLineData
6aeb48e6
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
1ea1f35b
MT
22#include <fcntl.h>
23#include <linux/limits.h>
6aeb48e6 24#include <stdlib.h>
da08f989
MT
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <unistd.h>
6aeb48e6 28
1ea1f35b 29#include <archive.h>
da08f989 30#include <archive_entry.h>
1ea1f35b 31
2adc4a4a 32#include <pakfire/archive.h>
436677a3 33#include <pakfire/constants.h>
1ba1869e
MT
34#include <pakfire/file.h>
35#include <pakfire/filelist.h>
1ea1f35b 36#include <pakfire/logging.h>
6aeb48e6
MT
37#include <pakfire/package.h>
38#include <pakfire/packager.h>
39#include <pakfire/pakfire.h>
40#include <pakfire/private.h>
41#include <pakfire/types.h>
42
da08f989
MT
43#define BUFFER_SIZE 64 * 1024
44
6aeb48e6
MT
45struct pakfire_packager {
46 Pakfire pakfire;
47 int nrefs;
48
49 PakfirePackage pkg;
1ba1869e 50 PakfireFilelist filelist;
1ea1f35b
MT
51
52 struct archive* payload;
7836e21b 53 FILE* fpayload;
6aeb48e6
MT
54};
55
1ea1f35b 56static int pakfire_packager_create_payload(struct pakfire_packager* p, int compress) {
7836e21b
MT
57 char path[] = "/tmp/.pakfire-payload.XXXXXX";
58
1ea1f35b 59 p->payload = archive_write_new();
427fdd80
MT
60 if (!p->payload) {
61 ERROR(p->pakfire, "archive_write_new() failed\n");
62 return 1;
63 }
1ea1f35b
MT
64
65 // Use the PAX format
66 int r = archive_write_set_format_pax(p->payload);
67 if (r) {
68 ERROR(p->pakfire, "Could not set format to PAX: %s\n",
69 archive_error_string(p->payload));
70 return r;
71 }
72
73 // Add filters to compress the payload
74 if (compress) {
75 // Enable Zstd
76 r = archive_write_add_filter_zstd(p->payload);
77 if (r) {
78 ERROR(p->pakfire, "Could not enable Zstandard compression: %s\n",
79 archive_error_string(p->payload));
80 return r;
81 }
82
83 // Set compression level to highest
84 r = archive_write_set_filter_option(p->payload, NULL, "compression-level", "19");
85 if (r) {
86 ERROR(p->pakfire, "Could not set Zstandard compression level: %s\n",
87 archive_error_string(p->payload));
88 return r;
89 }
90 }
91
7836e21b
MT
92 // Create a new temporary file
93 int fd = mkostemp(path, O_CLOEXEC);
1ea1f35b
MT
94 if (fd < 0) {
95 ERROR(p->pakfire, "mkostemp() failed: %s\n", strerror(errno));
96 return 1;
97 }
98
7836e21b
MT
99 // Unlink the file straight away
100 unlink(path);
101
102 // Convert the file descriptor into a file handle
103 p->fpayload = fdopen(fd, "w+");
104 if (!p->fpayload) {
105 close(fd);
106
107 return 1;
108 }
109
110 // Write archive to file
111 r = archive_write_open_FILE(p->payload, p->fpayload);
1ea1f35b
MT
112 if (r)
113 return r;
114
115 return 0;
116}
117
118static void pakfire_packager_free(struct pakfire_packager* packager) {
119 if (packager->payload)
120 archive_write_free(packager->payload);
121
7836e21b
MT
122 if (packager->fpayload)
123 fclose(packager->fpayload);
124
1ba1869e 125 pakfire_filelist_unref(packager->filelist);
1ea1f35b
MT
126 pakfire_package_unref(packager->pkg);
127 pakfire_unref(packager->pakfire);
128}
129
6aeb48e6
MT
130PAKFIRE_EXPORT int pakfire_packager_create(struct pakfire_packager** packager,
131 PakfirePackage pkg) {
132 struct pakfire_packager* p = calloc(1, sizeof(*p));
133 if (!p)
134 return ENOMEM;
135
136 // Initialize reference counting
137 p->nrefs = 1;
138
139 // Store a reference to Pakfire
140 p->pakfire = pakfire_package_get_pakfire(pkg);
141
142 // Store a reference to the package
143 p->pkg = pakfire_package_ref(pkg);
144
1ba1869e 145 // Create a new filelist
883b3be9 146 int r = pakfire_filelist_create(&p->filelist, p->pakfire);
1ba1869e
MT
147 if (r)
148 goto ERROR;
149
1ea1f35b 150 // Start payload
1ba1869e
MT
151 r = pakfire_packager_create_payload(p, 1);
152 if (r)
153 goto ERROR;
1ea1f35b 154
6aeb48e6
MT
155 *packager = p;
156
157 return 0;
1ba1869e
MT
158
159ERROR:
160 pakfire_packager_free(p);
161
162 return r;
6aeb48e6
MT
163}
164
6aeb48e6
MT
165PAKFIRE_EXPORT struct pakfire_packager* pakfire_packager_ref(
166 struct pakfire_packager* packager) {
167 ++packager->nrefs;
168
169 return packager;
170}
171
172PAKFIRE_EXPORT struct pakfire_packager* pakfire_packager_unref(
173 struct pakfire_packager* packager) {
174 if (--packager->nrefs > 0)
175 return packager;
176
177 pakfire_packager_free(packager);
178
179 return NULL;
180}
da08f989 181
7836e21b
MT
182static int pakfire_packager_copy_data(struct pakfire_packager* packager,
183 struct archive* a, FILE* f) {
184 char buffer[BUFFER_SIZE];
185
186 while (!feof(f)) {
187 // Read a block from file
188 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
189
190 // Check if any error occured
191 if (ferror(f)) {
192 ERROR(packager->pakfire, "Error reading from file: %s\n", strerror(errno));
193 return 1;
194 }
195
196 // Write the block to the archive
197 ssize_t bytes_written = archive_write_data(a, buffer, bytes_read);
198 if (bytes_written < 0) {
199 ERROR(packager->pakfire, "Error writing data to archive: %s\n",
200 archive_error_string(a));
201 return 1;
202 }
203 }
204
205 return 0;
206}
207
436677a3
MT
208static int pakfire_packager_write_format(struct pakfire_packager* packager,
209 struct archive* a) {
210 const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n";
211
212 // Create a new file entry
213 struct archive_entry* entry = archive_entry_new();
214 if (!entry)
215 return 1;
216
217 // Set filename
b36d7344 218 archive_entry_set_pathname(entry, PAKFIRE_ARCHIVE_FN_FORMAT);
436677a3
MT
219
220 // This is a regular file
221 archive_entry_set_filetype(entry, AE_IFREG);
222 archive_entry_set_perm(entry, 0644);
223
224 // Set length
2adc4a4a 225 archive_entry_set_size(entry, strlen(buffer));
436677a3
MT
226
227 // This is the end of the header
228 int r = archive_write_header(a, entry);
229 if (r) {
230 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
231 archive_entry_free(entry);
232 return r;
233 }
234
235 // Write content
236 r = archive_write_data(a, buffer, strlen(buffer));
237 if (r < 0) {
238 ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a));
239 archive_entry_free(entry);
240 return r;
241 }
242
243 archive_entry_free(entry);
244
245 return 0;
246}
247
2adc4a4a
MT
248static int pakfire_packager_write_payload(struct pakfire_packager* packager,
249 struct archive* a) {
250 struct stat st;
251
252 // Close the payload
253 if (packager->payload) {
254 archive_write_free(packager->payload);
255 packager->payload = NULL;
256 }
257
258 // Reset fd to beginning of the file
259 rewind(packager->fpayload);
260
261 int fd = fileno(packager->fpayload);
262
263 // Stat the payload file
264 int r = fstat(fd, &st);
265 if (r) {
266 ERROR(packager->pakfire, "stat() on fd %d failed: %s\n", fd, strerror(errno));
267 return 1;
268 }
269
270 // Create a new file entry
271 struct archive_entry* entry = archive_entry_new();
272 if (!entry)
273 return 1;
274
275 // Set filename
276 archive_entry_set_pathname(entry, PAKFIRE_ARCHIVE_FN_PAYLOAD);
277
278 // This is a regular file
279 archive_entry_set_filetype(entry, AE_IFREG);
280 archive_entry_set_perm(entry, 0644);
281
282 // Set the file size
283 archive_entry_set_size(entry, st.st_size);
284
285 // This is the end of the header
286 r = archive_write_header(a, entry);
287 if (r) {
288 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
289 goto ERROR;
290 }
291
292 // Copy data
293 r = pakfire_packager_copy_data(packager, a, packager->fpayload);
294 if (r)
295 goto ERROR;
296
297 // Success
298 r = 0;
299
300ERROR:
301 archive_entry_free(entry);
302
303 return r;
304}
305
436677a3
MT
306/*
307 This function is being called at the end when all data has been added to the package.
308
309 It will create a new archive and write the package to the given file descriptor.
310*/
311PAKFIRE_EXPORT char* pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) {
312 char* filename = NULL;
313
314 // Open a new archive
315 struct archive* a = archive_write_new();
316 if (!a) {
317 ERROR(packager->pakfire, "archive_write_new() failed\n");
318 goto ERROR;
319 }
320
321 // Use the PAX format
322 int r = archive_write_set_format_pax(a);
323 if (r) {
324 ERROR(packager->pakfire, "Could not set format to PAX: %s\n",
325 archive_error_string(a));
326 goto ERROR;
327 }
328
329 // Write archive to f
330 r = archive_write_open_FILE(a, f);
331 if (r) {
332 ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n",
333 archive_error_string(a));
334 goto ERROR;
335 }
336
337 // Start with the format file
338 r = pakfire_packager_write_format(packager, a);
339 if (r)
340 goto ERROR;
341
2adc4a4a
MT
342 // Write the payload
343 r = pakfire_packager_write_payload(packager, a);
344 if (r)
345 goto ERROR;
346
436677a3
MT
347 // XXX set filename
348
349ERROR:
350 if (a)
351 archive_free(a);
352
353 return filename;
354}
355
da08f989
MT
356PAKFIRE_EXPORT int pakfire_packager_add(struct pakfire_packager* packager,
357 const char* path) {
358 FILE* f = NULL;
359 struct stat st;
da08f989
MT
360
361 // Check if path is set
362 if (!path)
363 return EINVAL;
364
7836e21b
MT
365 // Payload has already been closed
366 if (!packager->payload)
367 return EINVAL;
368
da08f989
MT
369 // Stat the input file
370 int r = stat(path, &st);
371 if (r) {
372 ERROR(packager->pakfire, "stat() on %s failed: %s\n", path, strerror(errno));
373 return r;
374 }
375
376 // Create a new file entry
377 struct archive_entry* entry = archive_entry_new();
378 if (!entry)
379 return ENOMEM;
380
381 // Set path in archive
382 archive_entry_set_pathname(entry, path);
383
384 // Copy all attributes from stat()
385 archive_entry_copy_stat(entry, &st);
386
387 // Write the header
388 r = archive_write_header(packager->payload, entry);
389 if (r) {
390 ERROR(packager->pakfire, "Error writing file header: %s\n",
391 archive_error_string(packager->payload));
392 goto ERROR;
393 }
394
395 // Copy the data of regular files
396 if (archive_entry_filetype(entry) == AE_IFREG) {
397 f = fopen(path, "r");
398 if (!f) {
399 ERROR(packager->pakfire, "Could not open %s: %s\n", path, strerror(errno));
400 r = errno;
401 goto ERROR;
402 }
403
7836e21b
MT
404 r = pakfire_packager_copy_data(packager, packager->payload, f);
405 if (r)
406 goto ERROR;
da08f989
MT
407 }
408
c138b06e
MT
409 // Create a file
410 PakfireFile file;
411 r = pakfire_file_create(&file, packager->pakfire);
412 if (r)
413 goto ERROR;
414
415 r = pakfire_file_copy_archive_entry(file, entry);
416 if (r)
417 goto ERROR;
418
1ba1869e
MT
419 // Append the file to the filelist
420 pakfire_filelist_append(packager->filelist, file);
421
da08f989
MT
422 // Successful
423 r = 0;
424
425ERROR:
1ba1869e
MT
426 pakfire_file_unref(file);
427
da08f989
MT
428 if (entry)
429 archive_entry_free(entry);
430
431 if (f)
432 fclose(f);
433
434 return r;
435}