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 : :
29 : : #include <archive.h>
30 : : #include <archive_entry.h>
31 : : #include <assert.h>
32 : : #include <bsd_compat.h>
33 : : #include <fcntl.h>
34 : : #include <fts.h>
35 : : #include <string.h>
36 : : #include <pwd.h>
37 : : #include <grp.h>
38 : : #include <errno.h>
39 : :
40 : : #include "pkg.h"
41 : : #include "private/event.h"
42 : : #include "private/pkg.h"
43 : :
44 : : static const char *packing_set_format(struct archive *a, pkg_formats format, int clevel);
45 : :
46 : : struct packing {
47 : : struct archive *aread;
48 : : struct archive *awrite;
49 : : struct archive_entry_linkresolver *resolver;
50 : : time_t timestamp;
51 : : };
52 : :
53 : : int
54 : 1562 : packing_init(struct packing **pack, const char *path, pkg_formats format, int clevel,
55 : : time_t timestamp, bool overwrite, bool compat_symlink)
56 : : {
57 : : char archive_path[MAXPATHLEN];
58 : : char archive_symlink[MAXPATHLEN];
59 : : char *archive_name;
60 : : const char *ext;
61 : : const char *source_date_epoch;
62 : : char *endptr;
63 : : time_t ts;
64 : :
65 [ + - ]: 1562 : assert(pack != NULL);
66 : :
67 : 1562 : *pack = xcalloc(1, sizeof(struct packing));
68 : 1562 : (*pack)->timestamp = timestamp;
69 : :
70 [ + + + + ]: 1562 : if ((*pack)->timestamp == (time_t)-1 &&
71 : 1554 : (source_date_epoch = getenv("SOURCE_DATE_EPOCH")) != NULL) {
72 : 4 : ts = (time_t)strtoimax(source_date_epoch, &endptr, 10);
73 [ - + ]: 4 : if (*endptr != '\0') {
74 : 0 : pkg_emit_error("Ignoring bad environment variable "
75 : 0 : "SOURCE_DATE_EPOCH: %s", source_date_epoch);
76 : 0 : } else {
77 : 4 : (*pack)->timestamp = ts;
78 : : }
79 : 4 : }
80 : :
81 : 1562 : (*pack)->aread = archive_read_disk_new();
82 : 1562 : archive_read_disk_set_standard_lookup((*pack)->aread);
83 : 1562 : archive_read_disk_set_symlink_physical((*pack)->aread);
84 : :
85 : 1562 : (*pack)->awrite = archive_write_new();
86 : 1562 : archive_write_set_format_pax_restricted((*pack)->awrite);
87 : 1562 : ext = packing_set_format((*pack)->awrite, format, clevel);
88 [ + - ]: 1562 : if (ext == NULL) {
89 : 0 : archive_read_close((*pack)->aread);
90 : 0 : archive_read_free((*pack)->aread);
91 : 0 : archive_write_close((*pack)->awrite);
92 : 0 : archive_write_free((*pack)->awrite);
93 : 0 : free(*pack);
94 : 0 : *pack = NULL;
95 : 0 : return (EPKG_FATAL); /* error set by _set_format() */
96 : : }
97 : 1562 : snprintf(archive_path, sizeof(archive_path), "%s.pkg", path);
98 : 1562 : archive_name = strrchr(archive_path, '/');
99 [ + - ]: 1562 : if (archive_name == NULL)
100 : 0 : archive_name = archive_path;
101 : : else
102 : 1562 : archive_name++;
103 : 3124 : snprintf(archive_symlink, sizeof(archive_path), "%s.%s", path,
104 : 1562 : ext);
105 : :
106 [ + + + - ]: 1562 : if (!overwrite && access(archive_path, F_OK) == 0) {
107 : 4 : archive_read_close((*pack)->aread);
108 : 4 : archive_read_free((*pack)->aread);
109 : 4 : archive_write_close((*pack)->awrite);
110 : 4 : archive_write_free((*pack)->awrite);
111 : 4 : free(*pack);
112 : 4 : *pack = NULL;
113 : 4 : errno = EEXIST;
114 : 4 : return (EPKG_EXIST);
115 : : }
116 : 1558 : pkg_debug(1, "Packing to file '%s'", archive_path);
117 [ - + - + ]: 3116 : if (archive_write_open_filename(
118 : 3116 : (*pack)->awrite, archive_path) != ARCHIVE_OK) {
119 : 0 : pkg_emit_errno("archive_write_open_filename",
120 : 0 : archive_path);
121 : 0 : archive_read_close((*pack)->aread);
122 : 0 : archive_read_free((*pack)->aread);
123 : 0 : archive_write_close((*pack)->awrite);
124 : 0 : archive_write_free((*pack)->awrite);
125 : 0 : *pack = NULL;
126 : 0 : return EPKG_FATAL;
127 : : }
128 : :
129 [ + + - + ]: 1558 : if (compat_symlink || ctx.archive_symlink) {
130 : 562 : unlink(archive_symlink);
131 [ - + ]: 562 : if (symlink(archive_name, archive_symlink) != 0) {
132 : 0 : pkg_emit_errno("symlink", archive_symlink);
133 : 0 : }
134 : 562 : }
135 : :
136 : 1558 : (*pack)->resolver = archive_entry_linkresolver_new();
137 : 3116 : archive_entry_linkresolver_set_strategy((*pack)->resolver,
138 : 1558 : archive_format((*pack)->awrite));
139 : :
140 : 1558 : return (EPKG_OK);
141 : 1562 : }
142 : :
143 : : int
144 : 2032 : packing_append_buffer(struct packing *pack, const char *buffer,
145 : : const char *path, int size)
146 : : {
147 : : struct archive_entry *entry;
148 : 2032 : int ret = EPKG_OK;
149 : :
150 : 2032 : entry = archive_entry_new();
151 : 2032 : archive_entry_clear(entry);
152 : 2032 : archive_entry_set_filetype(entry, AE_IFREG);
153 : 2032 : archive_entry_set_perm(entry, 0644);
154 : 2032 : archive_entry_set_gname(entry, "wheel");
155 : 2032 : archive_entry_set_uname(entry, "root");
156 : 2032 : archive_entry_set_pathname(entry, path);
157 : 2032 : archive_entry_set_size(entry, size);
158 [ + - ]: 2032 : if (archive_write_header(pack->awrite, entry) == -1) {
159 : 0 : pkg_emit_errno("archive_write_header", path);
160 : 0 : ret = EPKG_FATAL;
161 : 0 : goto cleanup;
162 : : }
163 : :
164 [ - + ]: 2032 : if (archive_write_data(pack->awrite, buffer, size) == -1) {
165 : 0 : pkg_emit_errno("archive_write_data", path);
166 : 0 : ret = EPKG_FATAL;
167 : 0 : }
168 : :
169 : : cleanup:
170 : 2032 : archive_entry_free(entry);
171 : :
172 : 2032 : return (ret);
173 : : }
174 : :
175 : : int
176 : 1343 : packing_append_file_attr(struct packing *pack, const char *filepath,
177 : : const char *newpath, const char *uname, const char *gname, mode_t perm,
178 : : u_long fflags)
179 : : {
180 : : int fd;
181 : 1343 : int retcode = EPKG_OK;
182 : : int ret;
183 : : struct stat st;
184 : : struct archive_entry *entry, *sparse_entry;
185 : : bool unset_timestamp;
186 : : char buf[32768];
187 : : int len;
188 : :
189 : 1343 : entry = archive_entry_new();
190 : 1343 : archive_entry_copy_sourcepath(entry, filepath);
191 : :
192 : 1343 : pkg_debug(2, "Packing file '%s'", filepath);
193 : :
194 [ - + ]: 1343 : if (lstat(filepath, &st) != 0) {
195 : 0 : pkg_emit_errno("lstat", filepath);
196 : 0 : retcode = EPKG_FATAL;
197 : 0 : goto cleanup;
198 : : }
199 : :
200 : 1343 : ret = archive_read_disk_entry_from_file(pack->aread, entry, -1,
201 : : &st);
202 [ - + ]: 1343 : if (ret != ARCHIVE_OK) {
203 : 0 : pkg_emit_error("%s: %s", filepath,
204 : 0 : archive_error_string(pack->aread));
205 : 0 : retcode = EPKG_FATAL;
206 : 0 : goto cleanup;
207 : : }
208 : :
209 [ - + ]: 1343 : if (newpath != NULL)
210 : 1343 : archive_entry_set_pathname(entry, newpath);
211 : :
212 [ + + ]: 1343 : if (archive_entry_filetype(entry) != AE_IFREG) {
213 : 72 : archive_entry_set_size(entry, 0);
214 : 72 : }
215 : :
216 [ + - + + ]: 1343 : if (uname != NULL && uname[0] != '\0') {
217 : 788 : archive_entry_set_uname(entry, uname);
218 : 788 : }
219 : :
220 [ + - + + ]: 1343 : if (gname != NULL && gname[0] != '\0') {
221 : 788 : archive_entry_set_gname(entry, gname);
222 : 788 : }
223 : :
224 [ + + ]: 1343 : if (fflags > 0)
225 : 4 : archive_entry_set_fflags(entry, fflags, 0);
226 : :
227 [ + + ]: 1343 : if (perm != 0)
228 : 586 : archive_entry_set_perm(entry, perm);
229 : :
230 : 1343 : unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP"));
231 : :
232 [ + - ]: 1343 : if (unset_timestamp) {
233 : 0 : archive_entry_unset_atime(entry);
234 : 0 : archive_entry_unset_ctime(entry);
235 : 0 : archive_entry_unset_mtime(entry);
236 : 0 : archive_entry_unset_birthtime(entry);
237 : 0 : }
238 : :
239 [ + + ]: 1343 : if (pack->timestamp != (time_t) -1) {
240 : 12 : archive_entry_set_atime(entry, pack->timestamp, 0);
241 : 12 : archive_entry_set_ctime(entry, pack->timestamp, 0);
242 : 12 : archive_entry_set_mtime(entry, pack->timestamp, 0);
243 : 12 : archive_entry_set_birthtime(entry, pack->timestamp, 0);
244 : 12 : }
245 : :
246 : 1343 : archive_entry_linkify(pack->resolver, &entry, &sparse_entry);
247 : :
248 [ - + # # ]: 1343 : if (sparse_entry != NULL && entry == NULL)
249 : 0 : entry = sparse_entry;
250 : :
251 : 1343 : archive_write_header(pack->awrite, entry);
252 : :
253 [ + + ]: 1343 : if (archive_entry_size(entry) <= 0)
254 : 560 : goto cleanup;
255 : :
256 [ + - ]: 783 : if ((fd = open(filepath, O_RDONLY)) < 0) {
257 : 0 : pkg_emit_errno("open", filepath);
258 : 0 : retcode = EPKG_FATAL;
259 : 0 : goto cleanup;
260 : : }
261 : :
262 [ + + ]: 1566 : while ((len = read(fd, buf, sizeof(buf))) > 0) {
263 [ + - ]: 783 : if (archive_write_data(pack->awrite, buf, len) == -1) {
264 : 0 : pkg_emit_errno("archive_write_data", "archive write error");
265 : 0 : retcode = EPKG_FATAL;
266 : 0 : break;
267 : : }
268 : : }
269 : :
270 [ - + ]: 783 : if (len == -1) {
271 : 0 : pkg_emit_errno("read", "file read error");
272 : 0 : retcode = EPKG_FATAL;
273 : 0 : }
274 : 783 : close(fd);
275 : :
276 : : cleanup:
277 : 1343 : archive_entry_free(entry);
278 : 1343 : return (retcode);
279 : : }
280 : :
281 : : int
282 : 0 : packing_append_tree(struct packing *pack, const char *treepath,
283 : : const char *newroot)
284 : : {
285 : 0 : FTS *fts = NULL;
286 : 0 : FTSENT *fts_e = NULL;
287 : : size_t treelen;
288 : 0 : xstring *sb = NULL;
289 : 0 : char *paths[2] = { __DECONST(char *, treepath), NULL };
290 : :
291 : 0 : treelen = strlen(treepath);
292 : 0 : fts = fts_open(paths, FTS_PHYSICAL | FTS_XDEV, NULL);
293 [ # # ]: 0 : if (fts == NULL)
294 : 0 : goto cleanup;
295 : :
296 [ # # ]: 0 : while ((fts_e = fts_read(fts)) != NULL) {
297 [ # # ]: 0 : xstring_renew(sb);
298 [ # # # ]: 0 : switch(fts_e->fts_info) {
299 : : case FTS_D:
300 : : case FTS_DEFAULT:
301 : : case FTS_F:
302 : : case FTS_SL:
303 : : case FTS_SLNONE:
304 : : /* Entries not within this tree are irrelevant. */
305 [ # # ]: 0 : if (fts_e->fts_pathlen <= treelen)
306 : 0 : break;
307 : 0 : xstring_reset(sb);
308 : : /* Strip the prefix to obtain the target path */
309 [ # # ]: 0 : if (newroot) /* Prepend a root if one is specified */
310 : 0 : fputs(newroot, sb->fp);
311 : : /* +1 = skip trailing slash */
312 : 0 : fputs(fts_e->fts_path + treelen + 1, sb->fp);
313 : 0 : fflush(sb->fp);
314 : 0 : packing_append_file_attr(pack, fts_e->fts_name,
315 : 0 : sb->buf, NULL, NULL, 0, 0);
316 : 0 : break;
317 : : case FTS_DC:
318 : : case FTS_DNR:
319 : : case FTS_ERR:
320 : : case FTS_NS:
321 : : /* XXX error cases, check fts_e->fts_errno and
322 : : * bubble up the call chain */
323 : 0 : break;
324 : : default:
325 : 0 : break;
326 : : }
327 : : }
328 : 0 : xstring_free(sb);
329 : : cleanup:
330 : 0 : fts_close(fts);
331 : 0 : return EPKG_OK;
332 : : }
333 : :
334 : : void
335 : 1558 : packing_finish(struct packing *pack)
336 : : {
337 [ - + ]: 1558 : if (pack == NULL)
338 : 0 : return;
339 : :
340 : 1558 : archive_read_close(pack->aread);
341 : 1558 : archive_read_free(pack->aread);
342 : 1558 : archive_entry_linkresolver_free(pack->resolver);
343 : :
344 : 1558 : archive_write_close(pack->awrite);
345 : 1558 : archive_write_free(pack->awrite);
346 : :
347 : 1558 : free(pack);
348 : 1558 : }
349 : :
350 : : static const char *
351 : 1562 : packing_set_format(struct archive *a, pkg_formats format, int clevel)
352 : : {
353 : 1562 : const char *notsupp_fmt = "%s is not supported, trying %s";
354 : :
355 : : pkg_formats elected_format;
356 : :
357 [ - + - - : 1562 : switch (format) {
+ - ]
358 : : case TZS:
359 : : #ifdef HAVE_ARCHIVE_WRITE_ADD_FILTER_ZSTD
360 [ # # ]: 0 : if (archive_write_add_filter_zstd(a) == ARCHIVE_OK) {
361 : 0 : elected_format = TZS;
362 [ # # ]: 0 : if (clevel == -1)
363 : 0 : clevel = 19;
364 : 0 : goto out;
365 : : }
366 : : #endif
367 : 0 : pkg_emit_error(notsupp_fmt, "zstd", "xz");
368 : : /* FALLTHRU */
369 : : case TXZ:
370 [ - + ]: 1558 : if (archive_write_add_filter_xz(a) == ARCHIVE_OK) {
371 : 1558 : elected_format = TXZ;
372 : 1558 : goto out;
373 : : }
374 : 0 : pkg_emit_error(notsupp_fmt, "xz", "bzip2");
375 : : /* FALLTHRU */
376 : : case TBZ:
377 [ # # ]: 0 : if (archive_write_add_filter_bzip2(a) == ARCHIVE_OK) {
378 : 0 : elected_format = TBZ;
379 : 0 : goto out;
380 : : }
381 : 0 : pkg_emit_error(notsupp_fmt, "bzip2", "gzip");
382 : : /* FALLTHRU */
383 : : case TGZ:
384 [ # # ]: 0 : if (archive_write_add_filter_gzip(a) == ARCHIVE_OK) {
385 : 0 : elected_format = TGZ;
386 : 0 : goto out;
387 : : }
388 : 0 : pkg_emit_error(notsupp_fmt, "gzip", "plain tar");
389 : : /* FALLTHRU */
390 : : case TAR:
391 : 4 : archive_write_add_filter_none(a);
392 : 4 : elected_format = TAR;
393 : 4 : break;
394 : : default:
395 : 0 : return (NULL);
396 : 4 : }
397 : :
398 : : out:
399 [ + + ]: 1562 : if (clevel == -1)
400 : 1000 : clevel = 0;
401 : : /*
402 : : * N.B., we only want to whine about this if the user actually selected
403 : : * tar and specified a compress level. If we had to fallback to tar,
404 : : * that's not the user's fault.
405 : : */
406 [ + + - + ]: 1562 : if (format == TAR && clevel != 0)
407 : 0 : pkg_emit_error("Plain tar and a compression level does not make sense");
408 : :
409 [ + + + - ]: 1562 : if (elected_format != TAR && clevel != 0) {
410 : : char buf[16];
411 : :
412 : : /*
413 : : * A bit of a kludge but avoids dragging in headers for all of
414 : : * these libraries.
415 : : */
416 [ # # ]: 0 : if (clevel == INT_MIN) {
417 [ # # # ]: 0 : switch (elected_format) {
418 : : case TZS:
419 : 0 : clevel = -5;
420 : 0 : break;
421 : : case TXZ:
422 : : case TBZ:
423 : : case TGZ:
424 : 0 : clevel = 1;
425 : 0 : break;
426 : : default:
427 : 0 : __unreachable();
428 : : }
429 [ # # ]: 0 : } else if (clevel == INT_MAX) {
430 [ # # # ]: 0 : switch (elected_format) {
431 : : case TZS:
432 : 0 : clevel = 19;
433 : 0 : break;
434 : : case TXZ:
435 : : case TBZ:
436 : : case TGZ:
437 : 0 : clevel = 9;
438 : 0 : break;
439 : : default:
440 : 0 : __unreachable();
441 : : }
442 : 0 : }
443 : :
444 : 0 : snprintf(buf, sizeof(buf), "%d", clevel);
445 [ # # ]: 0 : if (archive_write_set_filter_option(a, NULL, "compression-level", buf) != ARCHIVE_OK)
446 : 0 : pkg_emit_error("bad compression-level %d", clevel);
447 : 0 : }
448 : :
449 : 1562 : return (packing_format_to_string(elected_format));
450 : 1562 : }
451 : :
452 : : pkg_formats
453 : 1300 : packing_format_from_string(const char *str)
454 : : {
455 [ + - ]: 1300 : if (str == NULL)
456 : 0 : return DEFAULT_COMPRESSION;
457 [ + - ]: 1300 : if (strcmp(str, "tzst") == 0)
458 : 0 : return TZS;
459 [ - + ]: 1300 : if (strcmp(str, "txz") == 0)
460 : 1300 : return TXZ;
461 [ # # ]: 0 : if (strcmp(str, "tbz") == 0)
462 : 0 : return TBZ;
463 [ # # ]: 0 : if (strcmp(str, "tgz") == 0)
464 : 0 : return TGZ;
465 [ # # ]: 0 : if (strcmp(str, "tar") == 0)
466 : 0 : return TAR;
467 : 0 : pkg_emit_error("unknown format %s, using txz", str);
468 : 0 : return TXZ;
469 : 1300 : }
470 : :
471 : : bool
472 : 1626 : packing_is_valid_format(const char *str)
473 : : {
474 [ + - ]: 1626 : if (str == NULL)
475 : 0 : return (false);
476 [ + + + - ]: 2670 : if ((strcmp(str, "pkg") == 0) ||
477 [ + - ]: 1044 : (strcmp(str, "tzst") == 0) ||
478 [ + - ]: 1044 : (strcmp(str, "txz") == 0) ||
479 [ + - ]: 1044 : (strcmp(str, "tbz") == 0) ||
480 [ + - ]: 1044 : (strcmp(str, "tgz") == 0) ||
481 : 1044 : (strcmp(str, "tar") == 0))
482 : 582 : return (true);
483 : 1044 : return (false);
484 : 1626 : }
485 : :
486 : : const char*
487 : 2775 : packing_format_to_string(pkg_formats format)
488 : : {
489 : 2775 : const char *res = NULL;
490 : :
491 [ - + - - : 2775 : switch (format) {
+ - ]
492 : : case TZS:
493 : 0 : res = "tzst";
494 : 0 : break;
495 : : case TXZ:
496 : 2771 : res = "txz";
497 : 2771 : break;
498 : : case TBZ:
499 : 0 : res = "tbz";
500 : 0 : break;
501 : : case TGZ:
502 : 0 : res = "tgz";
503 : 0 : break;
504 : : case TAR:
505 : 4 : res = "tar";
506 : 4 : break;
507 : : }
508 : :
509 : 2775 : return (res);
510 : : }
|