]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/packager.c
packager: Add function that starts putting the whole archive together
[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
436677a3 32#include <pakfire/constants.h>
1ea1f35b 33#include <pakfire/logging.h>
6aeb48e6
MT
34#include <pakfire/package.h>
35#include <pakfire/packager.h>
36#include <pakfire/pakfire.h>
37#include <pakfire/private.h>
38#include <pakfire/types.h>
39
da08f989
MT
40#define BUFFER_SIZE 64 * 1024
41
6aeb48e6
MT
42struct pakfire_packager {
43 Pakfire pakfire;
44 int nrefs;
45
46 PakfirePackage pkg;
1ea1f35b
MT
47
48 struct archive* payload;
49 char payload_path[PATH_MAX + 1];
6aeb48e6
MT
50};
51
1ea1f35b
MT
52static int pakfire_packager_create_payload(struct pakfire_packager* p, int compress) {
53 p->payload = archive_write_new();
427fdd80
MT
54 if (!p->payload) {
55 ERROR(p->pakfire, "archive_write_new() failed\n");
56 return 1;
57 }
1ea1f35b
MT
58
59 // Use the PAX format
60 int r = archive_write_set_format_pax(p->payload);
61 if (r) {
62 ERROR(p->pakfire, "Could not set format to PAX: %s\n",
63 archive_error_string(p->payload));
64 return r;
65 }
66
67 // Add filters to compress the payload
68 if (compress) {
69 // Enable Zstd
70 r = archive_write_add_filter_zstd(p->payload);
71 if (r) {
72 ERROR(p->pakfire, "Could not enable Zstandard compression: %s\n",
73 archive_error_string(p->payload));
74 return r;
75 }
76
77 // Set compression level to highest
78 r = archive_write_set_filter_option(p->payload, NULL, "compression-level", "19");
79 if (r) {
80 ERROR(p->pakfire, "Could not set Zstandard compression level: %s\n",
81 archive_error_string(p->payload));
82 return r;
83 }
84 }
85
86 // Allocate a temporary file to write the payload to
87 snprintf(p->payload_path, PATH_MAX, "/tmp/.pakfire-payload.XXXXXX");
88
89 int fd = mkostemp(p->payload_path, O_CLOEXEC);
90 if (fd < 0) {
91 ERROR(p->pakfire, "mkostemp() failed: %s\n", strerror(errno));
92 return 1;
93 }
94
95 r = archive_write_open_fd(p->payload, fd);
96 if (r)
97 return r;
98
99 return 0;
100}
101
102static void pakfire_packager_free(struct pakfire_packager* packager) {
103 if (packager->payload)
104 archive_write_free(packager->payload);
105
106 pakfire_package_unref(packager->pkg);
107 pakfire_unref(packager->pakfire);
108}
109
6aeb48e6
MT
110PAKFIRE_EXPORT int pakfire_packager_create(struct pakfire_packager** packager,
111 PakfirePackage pkg) {
112 struct pakfire_packager* p = calloc(1, sizeof(*p));
113 if (!p)
114 return ENOMEM;
115
116 // Initialize reference counting
117 p->nrefs = 1;
118
119 // Store a reference to Pakfire
120 p->pakfire = pakfire_package_get_pakfire(pkg);
121
122 // Store a reference to the package
123 p->pkg = pakfire_package_ref(pkg);
124
1ea1f35b
MT
125 // Start payload
126 int r = pakfire_packager_create_payload(p, 1);
127 if (r) {
128 pakfire_packager_free(p);
129 return r;
130 }
131
6aeb48e6
MT
132 *packager = p;
133
134 return 0;
135}
136
6aeb48e6
MT
137PAKFIRE_EXPORT struct pakfire_packager* pakfire_packager_ref(
138 struct pakfire_packager* packager) {
139 ++packager->nrefs;
140
141 return packager;
142}
143
144PAKFIRE_EXPORT struct pakfire_packager* pakfire_packager_unref(
145 struct pakfire_packager* packager) {
146 if (--packager->nrefs > 0)
147 return packager;
148
149 pakfire_packager_free(packager);
150
151 return NULL;
152}
da08f989 153
436677a3
MT
154static int pakfire_packager_write_format(struct pakfire_packager* packager,
155 struct archive* a) {
156 const char buffer[] = TO_STRING(PACKAGE_FORMAT) "\n";
157
158 // Create a new file entry
159 struct archive_entry* entry = archive_entry_new();
160 if (!entry)
161 return 1;
162
163 // Set filename
164 archive_entry_set_pathname(entry, "pakfire-format");
165
166 // This is a regular file
167 archive_entry_set_filetype(entry, AE_IFREG);
168 archive_entry_set_perm(entry, 0644);
169
170 // Set length
171 archive_entry_set_size(entry, strlen(buffer));
172
173 // This is the end of the header
174 int r = archive_write_header(a, entry);
175 if (r) {
176 ERROR(packager->pakfire, "Error writing header: %s\n", archive_error_string(a));
177 archive_entry_free(entry);
178 return r;
179 }
180
181 // Write content
182 r = archive_write_data(a, buffer, strlen(buffer));
183 if (r < 0) {
184 ERROR(packager->pakfire, "Error writing data: %s\n", archive_error_string(a));
185 archive_entry_free(entry);
186 return r;
187 }
188
189 archive_entry_free(entry);
190
191 return 0;
192}
193
194/*
195 This function is being called at the end when all data has been added to the package.
196
197 It will create a new archive and write the package to the given file descriptor.
198*/
199PAKFIRE_EXPORT char* pakfire_packager_finish(struct pakfire_packager* packager, FILE* f) {
200 char* filename = NULL;
201
202 // Open a new archive
203 struct archive* a = archive_write_new();
204 if (!a) {
205 ERROR(packager->pakfire, "archive_write_new() failed\n");
206 goto ERROR;
207 }
208
209 // Use the PAX format
210 int r = archive_write_set_format_pax(a);
211 if (r) {
212 ERROR(packager->pakfire, "Could not set format to PAX: %s\n",
213 archive_error_string(a));
214 goto ERROR;
215 }
216
217 // Write archive to f
218 r = archive_write_open_FILE(a, f);
219 if (r) {
220 ERROR(packager->pakfire, "archive_write_open_FILE() failed: %s\n",
221 archive_error_string(a));
222 goto ERROR;
223 }
224
225 // Start with the format file
226 r = pakfire_packager_write_format(packager, a);
227 if (r)
228 goto ERROR;
229
230 // XXX set filename
231
232ERROR:
233 if (a)
234 archive_free(a);
235
236 return filename;
237}
238
da08f989
MT
239PAKFIRE_EXPORT int pakfire_packager_add(struct pakfire_packager* packager,
240 const char* path) {
241 FILE* f = NULL;
242 struct stat st;
243 char buffer[BUFFER_SIZE];
244
245 // Check if path is set
246 if (!path)
247 return EINVAL;
248
249 // Stat the input file
250 int r = stat(path, &st);
251 if (r) {
252 ERROR(packager->pakfire, "stat() on %s failed: %s\n", path, strerror(errno));
253 return r;
254 }
255
256 // Create a new file entry
257 struct archive_entry* entry = archive_entry_new();
258 if (!entry)
259 return ENOMEM;
260
261 // Set path in archive
262 archive_entry_set_pathname(entry, path);
263
264 // Copy all attributes from stat()
265 archive_entry_copy_stat(entry, &st);
266
267 // Write the header
268 r = archive_write_header(packager->payload, entry);
269 if (r) {
270 ERROR(packager->pakfire, "Error writing file header: %s\n",
271 archive_error_string(packager->payload));
272 goto ERROR;
273 }
274
275 // Copy the data of regular files
276 if (archive_entry_filetype(entry) == AE_IFREG) {
277 f = fopen(path, "r");
278 if (!f) {
279 ERROR(packager->pakfire, "Could not open %s: %s\n", path, strerror(errno));
280 r = errno;
281 goto ERROR;
282 }
283
284 while (!feof(f)) {
285 // Read a block from file
286 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
287
288 if (ferror(f)) {
289 ERROR(packager->pakfire, "Error reading from file %s: %s\n",
290 path, strerror(errno));
291 r = errno;
292 goto ERROR;
293 }
294
295 // Write the block to the archive
296 ssize_t bytes_written = archive_write_data(packager->payload, buffer, bytes_read);
297 if (bytes_written < 0) {
298 ERROR(packager->pakfire, "Error writing data to archive: %s\n",
299 archive_error_string(packager->payload));
300 r = 1;
301 goto ERROR;
302 }
303 }
304 }
305
306 // Successful
307 r = 0;
308
309ERROR:
310 if (entry)
311 archive_entry_free(entry);
312
313 if (f)
314 fclose(f);
315
316 return r;
317}