Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/packing.c
2065 views
1
/*-
2
* Copyright (c) 2011-2021 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011 Will Andrews <[email protected]>
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
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
assert(pack != NULL);
61
62
*pack = xcalloc(1, sizeof(struct packing));
63
(*pack)->timestamp = timestamp;
64
65
if ((*pack)->timestamp == (time_t)-1 &&
66
(source_date_epoch = getenv("SOURCE_DATE_EPOCH")) != NULL) {
67
ts = (time_t)strtoimax(source_date_epoch, &endptr, 10);
68
if (*endptr != '\0') {
69
pkg_emit_error("Ignoring bad environment variable "
70
"SOURCE_DATE_EPOCH: %s", source_date_epoch);
71
} else {
72
(*pack)->timestamp = ts;
73
}
74
}
75
76
(*pack)->aread = archive_read_disk_new();
77
archive_read_disk_set_standard_lookup((*pack)->aread);
78
archive_read_disk_set_symlink_physical((*pack)->aread);
79
80
(*pack)->awrite = archive_write_new();
81
archive_write_set_format_pax_restricted((*pack)->awrite);
82
ext = packing_set_format((*pack)->awrite, format, clevel, threads);
83
if (ext == NULL) {
84
archive_read_close((*pack)->aread);
85
archive_read_free((*pack)->aread);
86
archive_write_close((*pack)->awrite);
87
archive_write_free((*pack)->awrite);
88
free(*pack);
89
*pack = NULL;
90
return (EPKG_FATAL); /* error set by _set_format() */
91
}
92
snprintf(archive_path, sizeof(archive_path), "%s.pkg", path);
93
archive_name = strrchr(archive_path, '/');
94
if (archive_name == NULL)
95
archive_name = archive_path;
96
else
97
archive_name++;
98
snprintf(archive_symlink, sizeof(archive_path), "%s.%s", path,
99
ext);
100
101
if (!overwrite && access(archive_path, F_OK) == 0) {
102
archive_read_close((*pack)->aread);
103
archive_read_free((*pack)->aread);
104
archive_write_close((*pack)->awrite);
105
archive_write_free((*pack)->awrite);
106
free(*pack);
107
*pack = NULL;
108
errno = EEXIST;
109
return (EPKG_EXIST);
110
}
111
dbg(1, "target file '%s'", archive_path);
112
if (archive_write_open_filename(
113
(*pack)->awrite, archive_path) != ARCHIVE_OK) {
114
pkg_emit_errno("archive_write_open_filename",
115
archive_path);
116
archive_read_close((*pack)->aread);
117
archive_read_free((*pack)->aread);
118
archive_write_close((*pack)->awrite);
119
archive_write_free((*pack)->awrite);
120
*pack = NULL;
121
return EPKG_FATAL;
122
}
123
124
if (compat_symlink || ctx.archive_symlink) {
125
unlink(archive_symlink);
126
if (symlink(archive_name, archive_symlink) != 0) {
127
pkg_emit_errno("symlink", archive_symlink);
128
}
129
}
130
131
(*pack)->resolver = archive_entry_linkresolver_new();
132
archive_entry_linkresolver_set_strategy((*pack)->resolver,
133
archive_format((*pack)->awrite));
134
135
return (EPKG_OK);
136
}
137
138
int
139
packing_append_iovec(struct packing *pack, const char *path, struct iovec *iov,
140
int niov)
141
{
142
struct archive_entry *entry;
143
int ret = EPKG_OK, size = 0;
144
145
dbg(1, "adding file '%s'", path);
146
for (int idx = 0; idx < niov; idx++) {
147
size += iov[idx].iov_len;
148
}
149
entry = archive_entry_new();
150
archive_entry_clear(entry);
151
archive_entry_set_filetype(entry, AE_IFREG);
152
archive_entry_set_perm(entry, 0644);
153
archive_entry_set_gname(entry, "wheel");
154
archive_entry_set_uname(entry, "root");
155
archive_entry_set_pathname(entry, path);
156
archive_entry_set_size(entry, size);
157
if (archive_write_header(pack->awrite, entry) == -1) {
158
pkg_emit_errno("archive_write_header", path);
159
ret = EPKG_FATAL;
160
goto cleanup;
161
}
162
163
for (int idx = 0; idx < niov; idx++) {
164
const char *buffer;
165
166
buffer = iov[idx].iov_base;
167
size = iov[idx].iov_len;
168
if (archive_write_data(pack->awrite, buffer, size) == -1) {
169
pkg_emit_errno("archive_write_data", path);
170
ret = EPKG_FATAL;
171
}
172
}
173
174
cleanup:
175
archive_entry_free(entry);
176
177
return (ret);
178
}
179
180
int
181
packing_append_buffer(struct packing *pack, const char *buffer,
182
const char *path, int size)
183
{
184
struct iovec iov;
185
186
dbg(1, "adding file '%s'", path);
187
iov.iov_base = __DECONST(char *, buffer);
188
iov.iov_len = size;
189
return (packing_append_iovec(pack, path, &iov, 1));
190
}
191
192
int
193
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
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
entry = archive_entry_new();
207
archive_entry_copy_sourcepath(entry, filepath);
208
209
dbg(1, "adding file '%s'", filepath);
210
211
if (lstat(filepath, &st) != 0) {
212
pkg_emit_errno("lstat", filepath);
213
retcode = EPKG_FATAL;
214
goto cleanup;
215
}
216
217
ret = archive_read_disk_entry_from_file(pack->aread, entry, -1,
218
&st);
219
if (ret != ARCHIVE_OK) {
220
pkg_emit_error("%s: %s", filepath,
221
archive_error_string(pack->aread));
222
retcode = EPKG_FATAL;
223
goto cleanup;
224
}
225
226
if (newpath != NULL)
227
archive_entry_set_pathname(entry, newpath);
228
229
if (archive_entry_filetype(entry) != AE_IFREG) {
230
archive_entry_set_size(entry, 0);
231
}
232
233
if (uname != NULL && uname[0] != '\0') {
234
archive_entry_set_uname(entry, uname);
235
}
236
#ifdef __FreeBSD__
237
/*
238
* Set this so that libarchive does not embed the current user's ID,
239
* breaking reproducibility.
240
*/
241
archive_entry_set_uid(entry, 65534 /* nobody */);
242
#endif
243
244
if (gname != NULL && gname[0] != '\0') {
245
archive_entry_set_gname(entry, gname);
246
}
247
#ifdef __FreeBSD__
248
/*
249
* Set this so that libarchive does not embed the current user's ID,
250
* breaking reproducibility.
251
*/
252
archive_entry_set_gid(entry, 65534 /* nobody */);
253
#endif
254
255
if (fflags > 0)
256
archive_entry_set_fflags(entry, fflags, 0);
257
258
if (perm != 0)
259
archive_entry_set_perm(entry, perm);
260
261
unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP"));
262
263
if (unset_timestamp) {
264
archive_entry_unset_atime(entry);
265
archive_entry_unset_ctime(entry);
266
archive_entry_unset_mtime(entry);
267
archive_entry_unset_birthtime(entry);
268
}
269
270
if (pack->timestamp != (time_t) -1) {
271
archive_entry_set_atime(entry, pack->timestamp, 0);
272
archive_entry_set_ctime(entry, pack->timestamp, 0);
273
archive_entry_set_mtime(entry, pack->timestamp, 0);
274
archive_entry_set_birthtime(entry, pack->timestamp, 0);
275
}
276
277
archive_entry_linkify(pack->resolver, &entry, &sparse_entry);
278
279
if (sparse_entry != NULL && entry == NULL)
280
entry = sparse_entry;
281
282
archive_write_header(pack->awrite, entry);
283
284
if (archive_entry_size(entry) <= 0)
285
goto cleanup;
286
287
if ((fd = open(filepath, O_RDONLY)) < 0) {
288
pkg_emit_errno("open", filepath);
289
retcode = EPKG_FATAL;
290
goto cleanup;
291
}
292
293
while ((len = read(fd, buf, sizeof(buf))) > 0) {
294
if (archive_write_data(pack->awrite, buf, len) == -1) {
295
pkg_emit_errno("archive_write_data", "archive write error");
296
retcode = EPKG_FATAL;
297
break;
298
}
299
}
300
301
if (len == -1) {
302
pkg_emit_errno("read", "file read error");
303
retcode = EPKG_FATAL;
304
}
305
close(fd);
306
307
cleanup:
308
archive_entry_free(entry);
309
return (retcode);
310
}
311
312
void
313
packing_finish(struct packing *pack)
314
{
315
if (pack == NULL)
316
return;
317
318
archive_read_close(pack->aread);
319
archive_read_free(pack->aread);
320
archive_entry_linkresolver_free(pack->resolver);
321
322
archive_write_close(pack->awrite);
323
archive_write_free(pack->awrite);
324
325
free(pack);
326
}
327
328
const char *
329
packing_set_format(struct archive *a, pkg_formats format, int clevel, int threads)
330
{
331
const char *notsupp_fmt = "%s is not supported, trying %s";
332
const char *notbltin_fmt = "%s is supported, but not builtin";
333
int error;
334
335
pkg_formats elected_format;
336
337
/*
338
* For several of these formats, ARCHIVE_WARN will be returned when an
339
* external program will be used to satisfy the request.
340
*/
341
switch (format) {
342
case TZS:
343
#ifdef HAVE_ARCHIVE_WRITE_ADD_FILTER_ZSTD
344
error = archive_write_add_filter_zstd(a);
345
if (error == ARCHIVE_OK) {
346
elected_format = TZS;
347
if (clevel == -1)
348
clevel = 19;
349
goto out;
350
} else if (error == ARCHIVE_WARN) {
351
pkg_emit_error(notbltin_fmt, "zstd");
352
return (NULL);
353
}
354
#endif
355
pkg_emit_error(notsupp_fmt, "zstd", "xz");
356
/* FALLTHRU */
357
case TXZ:
358
if (archive_write_add_filter_xz(a) == ARCHIVE_OK) {
359
elected_format = TXZ;
360
goto out;
361
}
362
pkg_emit_error(notsupp_fmt, "xz", "bzip2");
363
/* FALLTHRU */
364
case TBZ:
365
error = archive_write_add_filter_bzip2(a);
366
if (error == ARCHIVE_OK) {
367
elected_format = TBZ;
368
goto out;
369
} else if (error == ARCHIVE_WARN) {
370
pkg_emit_error(notbltin_fmt, "bzip2");
371
return (NULL);
372
}
373
pkg_emit_error(notsupp_fmt, "bzip2", "gzip");
374
/* FALLTHRU */
375
case TGZ:
376
error = archive_write_add_filter_gzip(a);
377
if (error == ARCHIVE_OK) {
378
elected_format = TGZ;
379
goto out;
380
} else if (error == ARCHIVE_WARN) {
381
pkg_emit_error(notbltin_fmt, "gzip");
382
return (NULL);
383
}
384
pkg_emit_error(notsupp_fmt, "gzip", "plain tar");
385
/* FALLTHRU */
386
case TAR:
387
archive_write_add_filter_none(a);
388
elected_format = TAR;
389
break;
390
default:
391
return (NULL);
392
}
393
394
out:
395
if (clevel == -1)
396
clevel = 0;
397
/*
398
* N.B., we only want to whine about this if the user actually selected
399
* tar and specified a compress level. If we had to fallback to tar,
400
* that's not the user's fault.
401
*/
402
if (format == TAR && clevel != 0)
403
pkg_emit_error("Plain tar and a compression level does not make sense");
404
405
if (elected_format != TAR) {
406
char buf[16];
407
if (clevel != 0) {
408
/*
409
* A bit of a kludge but avoids dragging in headers for all of
410
* these libraries.
411
*/
412
if (clevel == INT_MIN) {
413
switch (elected_format) {
414
case TZS:
415
clevel = -5;
416
break;
417
case TXZ:
418
case TBZ:
419
case TGZ:
420
clevel = 1;
421
break;
422
default:
423
__unreachable();
424
}
425
} else if (clevel == INT_MAX) {
426
switch (elected_format) {
427
case TZS:
428
clevel = 19;
429
break;
430
case TXZ:
431
case TBZ:
432
case TGZ:
433
clevel = 9;
434
break;
435
default:
436
__unreachable();
437
}
438
}
439
440
snprintf(buf, sizeof(buf), "%d", clevel);
441
if (archive_write_set_filter_option(a, NULL, "compression-level", buf) != ARCHIVE_OK)
442
pkg_emit_error("bad compression-level %d", clevel);
443
}
444
if (threads >= 0) {
445
snprintf(buf, sizeof(buf), "%d", threads);
446
if (archive_write_set_filter_option(a, NULL, "threads", buf) != ARCHIVE_OK)
447
pkg_emit_error("bad threads value %d", threads);
448
}
449
}
450
451
return (packing_format_to_string(elected_format));
452
}
453
454
pkg_formats
455
packing_format_from_string(const char *str)
456
{
457
if (str == NULL)
458
return DEFAULT_COMPRESSION;
459
if (STREQ(str, "tzst"))
460
return TZS;
461
if (STREQ(str, "txz"))
462
return TXZ;
463
if (STREQ(str, "tbz"))
464
return TBZ;
465
if (STREQ(str, "tgz"))
466
return TGZ;
467
if (STREQ(str, "tar"))
468
return TAR;
469
pkg_emit_error("unknown format %s, using txz", str);
470
return TXZ;
471
}
472
473
bool
474
packing_is_valid_format(const char *str)
475
{
476
if (str == NULL)
477
return (false);
478
if (STREQ(str, "pkg") ||
479
STREQ(str, "tzst") ||
480
STREQ(str, "txz") ||
481
STREQ(str, "tbz") ||
482
STREQ(str, "tgz") ||
483
STREQ(str, "tar"))
484
return (true);
485
return (false);
486
}
487
488
const char*
489
packing_format_to_string(pkg_formats format)
490
{
491
const char *res = NULL;
492
493
switch (format) {
494
case TZS:
495
res = "tzst";
496
break;
497
case TXZ:
498
res = "txz";
499
break;
500
case TBZ:
501
res = "tbz";
502
break;
503
case TGZ:
504
res = "tgz";
505
break;
506
case TAR:
507
res = "tar";
508
break;
509
}
510
511
return (res);
512
}
513
514