Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2021 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer
11 : : * in this position and unchanged.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : *
16 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : : */
27 : :
28 : : #include <sys/uio.h>
29 : :
30 : : #include <archive.h>
31 : : #include <archive_entry.h>
32 : : #include <assert.h>
33 : : #include <bsd_compat.h>
34 : : #include <fcntl.h>
35 : : #include <fts.h>
36 : : #include <string.h>
37 : : #include <pwd.h>
38 : : #include <grp.h>
39 : : #include <errno.h>
40 : :
41 : : #include "pkg.h"
42 : : #include "private/event.h"
43 : : #include "private/pkg.h"
44 : : #include "private/packing.h"
45 : :
46 : : #define dbg(x, ...) pkg_dbg(PKG_DBG_PACKING, x, __VA_ARGS__);
47 : :
48 : : int
49 : 416 : packing_init(struct packing **pack, const char *path, pkg_formats format, int clevel,
50 : : int threads, time_t timestamp, bool overwrite, bool compat_symlink)
51 : : {
52 : : char archive_path[MAXPATHLEN];
53 : : char archive_symlink[MAXPATHLEN];
54 : : char *archive_name;
55 : : const char *ext;
56 : : const char *source_date_epoch;
57 : : char *endptr;
58 : : time_t ts;
59 : :
60 [ + - ]: 416 : assert(pack != NULL);
61 : :
62 : 416 : *pack = xcalloc(1, sizeof(struct packing));
63 : 416 : (*pack)->timestamp = timestamp;
64 : :
65 [ + + + + ]: 416 : if ((*pack)->timestamp == (time_t)-1 &&
66 : 414 : (source_date_epoch = getenv("SOURCE_DATE_EPOCH")) != NULL) {
67 : 1 : ts = (time_t)strtoimax(source_date_epoch, &endptr, 10);
68 [ - + ]: 1 : if (*endptr != '\0') {
69 : 0 : pkg_emit_error("Ignoring bad environment variable "
70 : 0 : "SOURCE_DATE_EPOCH: %s", source_date_epoch);
71 : 0 : } else {
72 : 1 : (*pack)->timestamp = ts;
73 : : }
74 : 1 : }
75 : :
76 : 416 : (*pack)->aread = archive_read_disk_new();
77 : 416 : archive_read_disk_set_standard_lookup((*pack)->aread);
78 : 416 : archive_read_disk_set_symlink_physical((*pack)->aread);
79 : :
80 : 416 : (*pack)->awrite = archive_write_new();
81 : 416 : archive_write_set_format_pax_restricted((*pack)->awrite);
82 : 416 : ext = packing_set_format((*pack)->awrite, format, clevel, threads);
83 [ + - ]: 416 : if (ext == NULL) {
84 : 0 : archive_read_close((*pack)->aread);
85 : 0 : archive_read_free((*pack)->aread);
86 : 0 : archive_write_close((*pack)->awrite);
87 : 0 : archive_write_free((*pack)->awrite);
88 : 0 : free(*pack);
89 : 0 : *pack = NULL;
90 : 0 : return (EPKG_FATAL); /* error set by _set_format() */
91 : : }
92 : 416 : snprintf(archive_path, sizeof(archive_path), "%s.pkg", path);
93 : 416 : archive_name = strrchr(archive_path, '/');
94 [ + - ]: 416 : if (archive_name == NULL)
95 : 0 : archive_name = archive_path;
96 : : else
97 : 416 : archive_name++;
98 : 832 : snprintf(archive_symlink, sizeof(archive_path), "%s.%s", path,
99 : 416 : ext);
100 : :
101 [ + + - + ]: 416 : if (!overwrite && access(archive_path, F_OK) == 0) {
102 : 1 : archive_read_close((*pack)->aread);
103 : 1 : archive_read_free((*pack)->aread);
104 : 1 : archive_write_close((*pack)->awrite);
105 : 1 : archive_write_free((*pack)->awrite);
106 : 1 : free(*pack);
107 : 1 : *pack = NULL;
108 : 1 : errno = EEXIST;
109 : 1 : return (EPKG_EXIST);
110 : : }
111 : 415 : dbg(1, "target file '%s'", archive_path);
112 [ - + - + ]: 830 : if (archive_write_open_filename(
113 : 830 : (*pack)->awrite, archive_path) != ARCHIVE_OK) {
114 : 0 : pkg_emit_errno("archive_write_open_filename",
115 : 0 : archive_path);
116 : 0 : archive_read_close((*pack)->aread);
117 : 0 : archive_read_free((*pack)->aread);
118 : 0 : archive_write_close((*pack)->awrite);
119 : 0 : archive_write_free((*pack)->awrite);
120 : 0 : *pack = NULL;
121 : 0 : return EPKG_FATAL;
122 : : }
123 : :
124 [ + + - + ]: 415 : if (compat_symlink || ctx.archive_symlink) {
125 : 151 : unlink(archive_symlink);
126 [ + - ]: 151 : if (symlink(archive_name, archive_symlink) != 0) {
127 : 0 : pkg_emit_errno("symlink", archive_symlink);
128 : 0 : }
129 : 151 : }
130 : :
131 : 415 : (*pack)->resolver = archive_entry_linkresolver_new();
132 : 830 : archive_entry_linkresolver_set_strategy((*pack)->resolver,
133 : 415 : archive_format((*pack)->awrite));
134 : :
135 : 415 : return (EPKG_OK);
136 : 416 : }
137 : :
138 : : int
139 : 546 : packing_append_iovec(struct packing *pack, const char *path, struct iovec *iov,
140 : : int niov)
141 : : {
142 : : struct archive_entry *entry;
143 : 546 : int ret = EPKG_OK, size = 0;
144 : :
145 : 546 : dbg(1, "adding file '%s'", path);
146 [ + + ]: 1100 : for (int idx = 0; idx < niov; idx++) {
147 : 554 : size += iov[idx].iov_len;
148 : 554 : }
149 : 546 : entry = archive_entry_new();
150 : 546 : archive_entry_clear(entry);
151 : 546 : archive_entry_set_filetype(entry, AE_IFREG);
152 : 546 : archive_entry_set_perm(entry, 0644);
153 : 546 : archive_entry_set_gname(entry, "wheel");
154 : 546 : archive_entry_set_uname(entry, "root");
155 : 546 : archive_entry_set_pathname(entry, path);
156 : 546 : archive_entry_set_size(entry, size);
157 [ + - ]: 546 : if (archive_write_header(pack->awrite, entry) == -1) {
158 : 0 : pkg_emit_errno("archive_write_header", path);
159 : 0 : ret = EPKG_FATAL;
160 : 0 : goto cleanup;
161 : : }
162 : :
163 [ + + ]: 1100 : for (int idx = 0; idx < niov; idx++) {
164 : : const char *buffer;
165 : :
166 : 554 : buffer = iov[idx].iov_base;
167 : 554 : size = iov[idx].iov_len;
168 [ + - ]: 554 : if (archive_write_data(pack->awrite, buffer, size) == -1) {
169 : 0 : pkg_emit_errno("archive_write_data", path);
170 : 0 : ret = EPKG_FATAL;
171 : 0 : }
172 : 1100 : }
173 : :
174 : : cleanup:
175 : 546 : archive_entry_free(entry);
176 : :
177 : 546 : return (ret);
178 : : }
179 : :
180 : : int
181 : 526 : packing_append_buffer(struct packing *pack, const char *buffer,
182 : : const char *path, int size)
183 : : {
184 : : struct iovec iov;
185 : :
186 : 526 : dbg(1, "adding file '%s'", path);
187 : 526 : iov.iov_base = __DECONST(char *, buffer);
188 : 526 : iov.iov_len = size;
189 : 526 : return (packing_append_iovec(pack, path, &iov, 1));
190 : : }
191 : :
192 : : int
193 : 371 : packing_append_file_attr(struct packing *pack, const char *filepath,
194 : : const char *newpath, const char *uname, const char *gname, mode_t perm,
195 : : u_long fflags)
196 : : {
197 : : int fd;
198 : 371 : int retcode = EPKG_OK;
199 : : int ret;
200 : : struct stat st;
201 : : struct archive_entry *entry, *sparse_entry;
202 : : bool unset_timestamp;
203 : : char buf[32768];
204 : : int len;
205 : :
206 : 371 : entry = archive_entry_new();
207 : 371 : archive_entry_copy_sourcepath(entry, filepath);
208 : :
209 : 371 : dbg(1, "adding file '%s'", filepath);
210 : :
211 [ - + ]: 371 : if (lstat(filepath, &st) != 0) {
212 : 0 : pkg_emit_errno("lstat", filepath);
213 : 0 : retcode = EPKG_FATAL;
214 : 0 : goto cleanup;
215 : : }
216 : :
217 : 371 : ret = archive_read_disk_entry_from_file(pack->aread, entry, -1,
218 : : &st);
219 [ - + ]: 371 : if (ret != ARCHIVE_OK) {
220 : 0 : pkg_emit_error("%s: %s", filepath,
221 : 0 : archive_error_string(pack->aread));
222 : 0 : retcode = EPKG_FATAL;
223 : 0 : goto cleanup;
224 : : }
225 : :
226 [ - + ]: 371 : if (newpath != NULL)
227 : 371 : archive_entry_set_pathname(entry, newpath);
228 : :
229 [ + + ]: 371 : if (archive_entry_filetype(entry) != AE_IFREG) {
230 : 19 : archive_entry_set_size(entry, 0);
231 : 19 : }
232 : :
233 [ + - + + ]: 371 : if (uname != NULL && uname[0] != '\0') {
234 : 227 : archive_entry_set_uname(entry, uname);
235 : 227 : }
236 : :
237 [ + - + + ]: 371 : if (gname != NULL && gname[0] != '\0') {
238 : 227 : archive_entry_set_gname(entry, gname);
239 : 227 : }
240 : :
241 [ + + ]: 371 : if (fflags > 0)
242 : 1 : archive_entry_set_fflags(entry, fflags, 0);
243 : :
244 [ + + ]: 371 : if (perm != 0)
245 : 177 : archive_entry_set_perm(entry, perm);
246 : :
247 : 371 : unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP"));
248 : :
249 [ + - ]: 371 : if (unset_timestamp) {
250 : 0 : archive_entry_unset_atime(entry);
251 : 0 : archive_entry_unset_ctime(entry);
252 : 0 : archive_entry_unset_mtime(entry);
253 : 0 : archive_entry_unset_birthtime(entry);
254 : 0 : }
255 : :
256 [ + + ]: 371 : if (pack->timestamp != (time_t) -1) {
257 : 3 : archive_entry_set_atime(entry, pack->timestamp, 0);
258 : 3 : archive_entry_set_ctime(entry, pack->timestamp, 0);
259 : 3 : archive_entry_set_mtime(entry, pack->timestamp, 0);
260 : 3 : archive_entry_set_birthtime(entry, pack->timestamp, 0);
261 : 3 : }
262 : :
263 : 371 : archive_entry_linkify(pack->resolver, &entry, &sparse_entry);
264 : :
265 [ - + # # ]: 371 : if (sparse_entry != NULL && entry == NULL)
266 : 0 : entry = sparse_entry;
267 : :
268 : 371 : archive_write_header(pack->awrite, entry);
269 : :
270 [ + + ]: 371 : if (archive_entry_size(entry) <= 0)
271 : 135 : goto cleanup;
272 : :
273 [ + - ]: 236 : if ((fd = open(filepath, O_RDONLY)) < 0) {
274 : 0 : pkg_emit_errno("open", filepath);
275 : 0 : retcode = EPKG_FATAL;
276 : 0 : goto cleanup;
277 : : }
278 : :
279 [ + + ]: 487 : while ((len = read(fd, buf, sizeof(buf))) > 0) {
280 [ - + ]: 251 : if (archive_write_data(pack->awrite, buf, len) == -1) {
281 : 0 : pkg_emit_errno("archive_write_data", "archive write error");
282 : 0 : retcode = EPKG_FATAL;
283 : 0 : break;
284 : : }
285 : : }
286 : :
287 [ + - ]: 236 : if (len == -1) {
288 : 0 : pkg_emit_errno("read", "file read error");
289 : 0 : retcode = EPKG_FATAL;
290 : 0 : }
291 : 236 : close(fd);
292 : :
293 : : cleanup:
294 : 371 : archive_entry_free(entry);
295 : 371 : return (retcode);
296 : : }
297 : :
298 : : void
299 : 415 : packing_finish(struct packing *pack)
300 : : {
301 [ + - ]: 415 : if (pack == NULL)
302 : 0 : return;
303 : :
304 : 415 : archive_read_close(pack->aread);
305 : 415 : archive_read_free(pack->aread);
306 : 415 : archive_entry_linkresolver_free(pack->resolver);
307 : :
308 : 415 : archive_write_close(pack->awrite);
309 : 415 : archive_write_free(pack->awrite);
310 : :
311 : 415 : free(pack);
312 : 415 : }
313 : :
314 : : const char *
315 : 434 : packing_set_format(struct archive *a, pkg_formats format, int clevel, int threads)
316 : : {
317 : 434 : const char *notsupp_fmt = "%s is not supported, trying %s";
318 : 434 : const char *notbltin_fmt = "%s is supported, but not builtin";
319 : : int error;
320 : :
321 : : pkg_formats elected_format;
322 : :
323 : : /*
324 : : * For several of these formats, ARCHIVE_WARN will be returned when an
325 : : * external program will be used to satisfy the request.
326 : : */
327 [ + + + + : 434 : switch (format) {
+ + ]
328 : : case TZS:
329 : : #ifdef HAVE_ARCHIVE_WRITE_ADD_FILTER_ZSTD
330 : 416 : error = archive_write_add_filter_zstd(a);
331 [ + - ]: 416 : if (error == ARCHIVE_OK) {
332 : 416 : elected_format = TZS;
333 [ + + ]: 416 : if (clevel == -1)
334 : 263 : clevel = 19;
335 : 416 : goto out;
336 [ # # ]: 0 : } else if (error == ARCHIVE_WARN) {
337 : 0 : pkg_emit_error(notbltin_fmt, "zstd");
338 : 0 : return (NULL);
339 : : }
340 : : #endif
341 : 5 : pkg_emit_error(notsupp_fmt, "zstd", "xz");
342 : : /* FALLTHRU */
343 : : case TXZ:
344 [ - + ]: 5 : if (archive_write_add_filter_xz(a) == ARCHIVE_OK) {
345 : 5 : elected_format = TXZ;
346 : 5 : goto out;
347 : : }
348 : 3 : pkg_emit_error(notsupp_fmt, "xz", "bzip2");
349 : : /* FALLTHRU */
350 : : case TBZ:
351 : 3 : error = archive_write_add_filter_bzip2(a);
352 [ - + ]: 3 : if (error == ARCHIVE_OK) {
353 : 3 : elected_format = TBZ;
354 : 3 : goto out;
355 [ # # ]: 0 : } else if (error == ARCHIVE_WARN) {
356 : 0 : pkg_emit_error(notbltin_fmt, "bzip2");
357 : 0 : return (NULL);
358 : : }
359 : 3 : pkg_emit_error(notsupp_fmt, "bzip2", "gzip");
360 : : /* FALLTHRU */
361 : : case TGZ:
362 : 3 : error = archive_write_add_filter_gzip(a);
363 [ - + ]: 3 : if (error == ARCHIVE_OK) {
364 : 3 : elected_format = TGZ;
365 : 3 : goto out;
366 [ # # ]: 0 : } else if (error == ARCHIVE_WARN) {
367 : 0 : pkg_emit_error(notbltin_fmt, "gzip");
368 : 0 : return (NULL);
369 : : }
370 : 4 : pkg_emit_error(notsupp_fmt, "gzip", "plain tar");
371 : : /* FALLTHRU */
372 : : case TAR:
373 : 4 : archive_write_add_filter_none(a);
374 : 4 : elected_format = TAR;
375 : 4 : break;
376 : : default:
377 : 3 : return (NULL);
378 : 4 : }
379 : :
380 : : out:
381 [ + + ]: 431 : if (clevel == -1)
382 : 7 : clevel = 0;
383 : : /*
384 : : * N.B., we only want to whine about this if the user actually selected
385 : : * tar and specified a compress level. If we had to fallback to tar,
386 : : * that's not the user's fault.
387 : : */
388 [ + + + + ]: 431 : if (format == TAR && clevel != 0)
389 : 2 : pkg_emit_error("Plain tar and a compression level does not make sense");
390 : :
391 [ + + ]: 431 : if (elected_format != TAR) {
392 : : char buf[16];
393 [ + + ]: 427 : if (clevel != 0) {
394 : : /*
395 : : * A bit of a kludge but avoids dragging in headers for all of
396 : : * these libraries.
397 : : */
398 [ + + ]: 271 : if (clevel == INT_MIN) {
399 [ - + + + : 4 : switch (elected_format) {
+ ]
400 : : case TZS:
401 : 1 : clevel = -5;
402 : 4 : break;
403 : : case TXZ:
404 : : case TBZ:
405 : : case TGZ:
406 : 3 : clevel = 1;
407 : 3 : break;
408 : : default:
409 : 0 : __unreachable();
410 : : }
411 [ + + ]: 271 : } else if (clevel == INT_MAX) {
412 [ - + + + : 4 : switch (elected_format) {
+ ]
413 : : case TZS:
414 : 1 : clevel = 19;
415 : 4 : break;
416 : : case TXZ:
417 : : case TBZ:
418 : : case TGZ:
419 : 3 : clevel = 9;
420 : 3 : break;
421 : : default:
422 : 0 : __unreachable();
423 : : }
424 : 4 : }
425 : :
426 : 271 : snprintf(buf, sizeof(buf), "%d", clevel);
427 [ + - ]: 271 : if (archive_write_set_filter_option(a, NULL, "compression-level", buf) != ARCHIVE_OK)
428 : 0 : pkg_emit_error("bad compression-level %d", clevel);
429 : 271 : }
430 [ + + ]: 427 : if (threads >= 0) {
431 : 151 : snprintf(buf, sizeof(buf), "%d", threads);
432 [ + - ]: 151 : if (archive_write_set_filter_option(a, NULL, "threads", buf) != ARCHIVE_OK)
433 : 0 : pkg_emit_error("bad threads value %d", threads);
434 : 151 : }
435 : 427 : }
436 : :
437 : 431 : return (packing_format_to_string(elected_format));
438 : 434 : }
439 : :
440 : : pkg_formats
441 : 597 : packing_format_from_string(const char *str)
442 : : {
443 [ + + ]: 597 : if (str == NULL)
444 : 282 : return DEFAULT_COMPRESSION;
445 [ + + ]: 315 : if (STREQ(str, "tzst"))
446 : 310 : return TZS;
447 [ + + ]: 5 : if (STREQ(str, "txz"))
448 : 1 : return TXZ;
449 [ + + ]: 4 : if (STREQ(str, "tbz"))
450 : 1 : return TBZ;
451 [ + + ]: 3 : if (STREQ(str, "tgz"))
452 : 1 : return TGZ;
453 [ + + ]: 2 : if (STREQ(str, "tar"))
454 : 1 : return TAR;
455 : 1 : pkg_emit_error("unknown format %s, using txz", str);
456 : 1 : return TXZ;
457 : 597 : }
458 : :
459 : : bool
460 : 446 : packing_is_valid_format(const char *str)
461 : : {
462 [ + + ]: 446 : if (str == NULL)
463 : 1 : return (false);
464 [ + + + + ]: 724 : if (STREQ(str, "pkg") ||
465 [ + + ]: 283 : STREQ(str, "tzst") ||
466 [ + + ]: 282 : STREQ(str, "txz") ||
467 [ + + ]: 281 : STREQ(str, "tbz") ||
468 [ + + ]: 280 : STREQ(str, "tgz") ||
469 : 279 : STREQ(str, "tar"))
470 : 167 : return (true);
471 : 278 : return (false);
472 : 446 : }
473 : :
474 : : const char*
475 : 747 : packing_format_to_string(pkg_formats format)
476 : : {
477 : 747 : const char *res = NULL;
478 : :
479 [ + + + + : 747 : switch (format) {
+ + ]
480 : : case TZS:
481 : 727 : res = "tzst";
482 : 727 : break;
483 : : case TXZ:
484 : 6 : res = "txz";
485 : 6 : break;
486 : : case TBZ:
487 : 4 : res = "tbz";
488 : 4 : break;
489 : : case TGZ:
490 : 4 : res = "tgz";
491 : 4 : break;
492 : : case TAR:
493 : 5 : res = "tar";
494 : 5 : break;
495 : : }
496 : :
497 : 747 : return (res);
498 : : }
|