Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_create.c
2065 views
1
/*-
2
* Copyright (c) 2011-2020 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Julien Laffaye <[email protected]>
4
* Copyright (c) 2014-2015 Matthew Seaman <[email protected]>
5
* Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
6
* Copyright (c) 2023 Serenity Cyber Security, LLC
7
* Author: Gleb Popov <[email protected]>
8
* All rights reserved.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer
15
* in this position and unchanged.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
21
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
24
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
*/
31
32
#include <sys/stat.h>
33
34
#include <errno.h>
35
#include <regex.h>
36
#include <fcntl.h>
37
38
#include <bsd_compat.h>
39
40
#include "pkg.h"
41
#include "private/event.h"
42
#include "private/pkg.h"
43
#include "private/pkg_abi.h"
44
#include "xmalloc.h"
45
46
#define TICK 100
47
48
static int load_metadata(struct pkg *pkg, const char *metadata, const char *plist,
49
const char *rootdir);
50
static void fixup_abi(struct pkg *pkg, const char *rootdir, bool testing);
51
static void counter_init(const char *what, int64_t max);
52
static void counter_count(void);
53
static void counter_end(void);
54
55
extern struct pkg_ctx ctx;
56
57
static int
58
pkg_create_from_dir(struct pkg *pkg, const char *root,
59
struct pkg_create *pc, struct packing *pkg_archive)
60
{
61
char fpath[MAXPATHLEN];
62
struct pkg_file *file = NULL;
63
struct pkg_dir *dir = NULL;
64
int ret;
65
struct stat st;
66
int64_t flatsize = 0;
67
int64_t nfiles;
68
const char *relocation;
69
char *manifest;
70
ucl_object_t *obj;
71
hardlinks_t hardlinks = vec_init();
72
73
if (pkg_is_valid(pkg) != EPKG_OK) {
74
pkg_emit_error("the package is not valid");
75
return (EPKG_FATAL);
76
}
77
78
relocation = pkg_kv_get(&pkg->annotations, "relocated");
79
if (relocation == NULL)
80
relocation = "";
81
if (ctx.pkg_rootdir != NULL)
82
relocation = ctx.pkg_rootdir;
83
84
/*
85
* Get / compute size / checksum if not provided in the manifest
86
*/
87
88
nfiles = pkghash_count(pkg->filehash);
89
counter_init("file sizes/checksums", nfiles);
90
91
while (pkg_files(pkg, &file) == EPKG_OK) {
92
93
snprintf(fpath, sizeof(fpath), "%s%s%s", root ? root : "",
94
relocation, file->path);
95
96
if (lstat(fpath, &st) == -1) {
97
pkg_emit_error("file '%s' is missing", fpath);
98
vec_free_and_free(&hardlinks, free);
99
return (EPKG_FATAL);
100
}
101
102
if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
103
pkg_emit_error("file '%s' is not a regular file or symlink", fpath);
104
vec_free_and_free(&hardlinks, free);
105
return (EPKG_FATAL);
106
}
107
108
if (file->size == 0)
109
file->size = (int64_t)st.st_size;
110
111
if (st.st_nlink == 1 || !check_for_hardlink(&hardlinks, &st)) {
112
flatsize += file->size;
113
}
114
115
free(file->sum);
116
file->sum = pkg_checksum_generate_file(fpath,
117
PKG_HASH_TYPE_SHA256_HEX);
118
if (file->sum == NULL) {
119
vec_free_and_free(&hardlinks, free);
120
return (EPKG_FATAL);
121
}
122
123
counter_count();
124
}
125
vec_free_and_free(&hardlinks, free);
126
127
counter_end();
128
129
pkg->flatsize = flatsize;
130
131
if (pkg->type == PKG_OLD_FILE) {
132
pkg_emit_error("Cannot create an old format package");
133
return (EPKG_FATAL);
134
}
135
136
obj = pkg_emit_object(pkg, PKG_MANIFEST_EMIT_COMPACT);
137
manifest = ucl_object_emit(obj, UCL_EMIT_JSON_COMPACT);
138
ucl_object_unref(obj);
139
packing_append_buffer(pkg_archive, manifest, "+COMPACT_MANIFEST", strlen(manifest));
140
free(manifest);
141
obj = pkg_emit_object(pkg, 0);
142
if (pc->expand_manifest) {
143
manifest = ucl_object_emit(obj, UCL_EMIT_CONFIG);
144
} else {
145
manifest = ucl_object_emit(obj, UCL_EMIT_JSON_COMPACT);
146
}
147
ucl_object_unref(obj);
148
packing_append_buffer(pkg_archive, manifest, "+MANIFEST", strlen(manifest));
149
free(manifest);
150
151
counter_init("packing files", nfiles);
152
153
while (pkg_files(pkg, &file) == EPKG_OK) {
154
char dpath[MAXPATHLEN];
155
const char *dp = file->path;
156
157
if (pkg->oprefix != NULL) {
158
size_t l = strlen(pkg->prefix);
159
if (strncmp(file->path, pkg->prefix, l) == 0 &&
160
(file->path[l] == '/' || l == 1)) {
161
snprintf(dpath, sizeof(dpath), "%s%s%s",
162
pkg->oprefix, l == 1 ? "/" : "", file->path + l);
163
dp = dpath;
164
}
165
}
166
167
snprintf(fpath, sizeof(fpath), "%s%s%s", root ? root : "",
168
relocation, file->path);
169
170
ret = packing_append_file_attr(pkg_archive, fpath, dp,
171
file->uname, file->gname, file->perm, file->fflags);
172
if (ctx.developer_mode && ret != EPKG_OK)
173
return (ret);
174
counter_count();
175
}
176
177
counter_end();
178
179
nfiles = pkghash_count(pkg->dirhash);
180
counter_init("packing directories", nfiles);
181
182
while (pkg_dirs(pkg, &dir) == EPKG_OK) {
183
snprintf(fpath, sizeof(fpath), "%s%s%s", root ? root : "",
184
relocation, dir->path);
185
186
ret = packing_append_file_attr(pkg_archive, fpath, dir->path,
187
dir->uname, dir->gname, dir->perm, dir->fflags);
188
if (ctx.developer_mode && ret != EPKG_OK)
189
return (ret);
190
counter_count();
191
}
192
193
counter_end();
194
195
return (EPKG_OK);
196
}
197
198
static struct packing *
199
pkg_create_archive(struct pkg *pkg, struct pkg_create *pc, unsigned required_flags)
200
{
201
char *pkg_path = NULL;
202
struct packing *pkg_archive = NULL;
203
204
/*
205
* Ensure that we have all the information we need
206
*/
207
if (pkg->type != PKG_OLD_FILE)
208
assert((pkg->flags & required_flags) == required_flags);
209
210
if (pkg_mkdirs(pc->outdir) != EPKG_OK)
211
return NULL;
212
213
if (pkg_asprintf(&pkg_path, "%S/%n-%v", pc->outdir, pkg, pkg) == -1) {
214
pkg_emit_errno("pkg_asprintf", "");
215
return (NULL);
216
}
217
218
if (packing_init(&pkg_archive, pkg_path, pc->format,
219
pc->compression_level, pc->compression_threads, pc->timestamp, pc->overwrite, false) != EPKG_OK) {
220
pkg_archive = NULL;
221
}
222
223
free(pkg_path);
224
225
return pkg_archive;
226
}
227
228
static const char * const scripts[] = {
229
"+INSTALL",
230
"+PRE_INSTALL",
231
"+POST_INSTALL",
232
"+POST_INSTALL",
233
"+DEINSTALL",
234
"+PRE_DEINSTALL",
235
"+POST_DEINSTALL",
236
"pkg-install",
237
"pkg-pre-install",
238
"pkg-post-install",
239
"pkg-deinstall",
240
"pkg-pre-deinstall",
241
"pkg-post-deinstall",
242
NULL
243
};
244
245
static const char * const lua_scripts[] = {
246
"pkg-pre-install.lua",
247
"pkg-post-install.lua",
248
"pkg-pre-deinstall.lua",
249
"pkg-post-deinstall.lua",
250
NULL
251
};
252
253
struct pkg_create *
254
pkg_create_new(void)
255
{
256
struct pkg_create *pc;
257
258
pc = xcalloc(1, sizeof(*pc));
259
pc->format = packing_format_from_string(ctx.compression_format);
260
pc->compression_level = ctx.compression_level;
261
pc->compression_threads = ctx.compression_threads;
262
pc->timestamp = (time_t) -1;
263
pc->overwrite = true;
264
pc->expand_manifest = false;
265
266
return (pc);
267
}
268
269
void
270
pkg_create_free(struct pkg_create *pc)
271
{
272
free(pc);
273
}
274
275
bool
276
pkg_create_set_format(struct pkg_create *pc, const char *format)
277
{
278
if (STREQ(format, "tzst"))
279
pc->format = TZS;
280
else if (STREQ(format, "txz"))
281
pc->format = TXZ;
282
else if (STREQ(format, "tbz"))
283
pc->format = TBZ;
284
else if (STREQ(format, "tgz"))
285
pc->format = TGZ;
286
else if (STREQ(format, "tar"))
287
pc->format = TAR;
288
else
289
return (false);
290
return (true);
291
}
292
293
void
294
pkg_create_set_compression_level(struct pkg_create *pc, int clevel)
295
{
296
pc->compression_level = clevel;
297
}
298
299
void
300
pkg_create_set_compression_threads(struct pkg_create *pc, int threads)
301
{
302
pc->compression_threads = threads;
303
}
304
305
void
306
pkg_create_set_expand_manifest(struct pkg_create *pc, bool expand)
307
{
308
pc->expand_manifest = expand;
309
}
310
311
void
312
pkg_create_set_rootdir(struct pkg_create *pc, const char *rootdir)
313
{
314
pc->rootdir = rootdir;
315
}
316
317
void
318
pkg_create_set_output_dir(struct pkg_create *pc, const char *outdir)
319
{
320
pc->outdir = outdir;
321
}
322
323
void
324
pkg_create_set_timestamp(struct pkg_create *pc, time_t timestamp)
325
{
326
pc->timestamp = timestamp;
327
}
328
329
void
330
pkg_create_set_overwrite(struct pkg_create *pc, bool overwrite)
331
{
332
pc->overwrite = overwrite;
333
}
334
335
static int
336
hash_file(struct pkg *pkg)
337
{
338
char hash_dest[MAXPATHLEN];
339
char filename[MAXPATHLEN];
340
341
/* Find the hash and rename the file and create a symlink */
342
pkg_snprintf(filename, sizeof(filename), "%n-%v.pkg",
343
pkg, pkg);
344
pkg->sum = pkg_checksum_file(filename,
345
PKG_HASH_TYPE_SHA256_HEX);
346
pkg_snprintf(hash_dest, sizeof(hash_dest), "%n-%v-%z.pkg",
347
pkg, pkg, pkg);
348
349
pkg_debug(1, "Rename the pkg file from: %s to: %s",
350
filename, hash_dest);
351
if (rename(filename, hash_dest) == -1) {
352
pkg_emit_errno("rename", hash_dest);
353
unlink(hash_dest);
354
return (EPKG_FATAL);
355
}
356
if (symlink(hash_dest, filename) == -1) {
357
pkg_emit_errno("symlink", hash_dest);
358
return (EPKG_FATAL);
359
}
360
return (EPKG_OK);
361
}
362
363
int
364
pkg_create_i(struct pkg_create *pc, struct pkg *pkg, bool hash)
365
{
366
struct packing *pkg_archive = NULL;
367
int ret;
368
369
unsigned required_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES |
370
PKG_LOAD_CATEGORIES | PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS |
371
PKG_LOAD_OPTIONS | PKG_LOAD_LICENSES | PKG_LOAD_LUA_SCRIPTS;
372
373
assert(pkg->type == PKG_INSTALLED || pkg->type == PKG_OLD_FILE);
374
375
pkg_archive = pkg_create_archive(pkg, pc, required_flags);
376
if (pkg_archive == NULL) {
377
if (errno == EEXIST)
378
return (EPKG_EXIST);
379
pkg_emit_error("unable to create archive");
380
return (EPKG_FATAL);
381
}
382
383
if ((ret = pkg_create_from_dir(pkg, NULL, pc, pkg_archive)) != EPKG_OK) {
384
pkg_emit_error("package creation failed");
385
}
386
packing_finish(pkg_archive);
387
388
if (hash && ret == EPKG_OK)
389
ret = hash_file(pkg);
390
391
return (ret);
392
}
393
394
int
395
pkg_create(struct pkg_create *pc, const char *metadata, const char *plist,
396
bool hash)
397
{
398
struct pkg *pkg = NULL;
399
struct packing *pkg_archive = NULL;
400
int ret = ENOMEM;
401
402
pkg_debug(1, "Creating package");
403
if (pkg_new(&pkg, PKG_FILE) != EPKG_OK) {
404
return (EPKG_FATAL);
405
}
406
407
if (load_metadata(pkg, metadata, plist, pc->rootdir) != EPKG_OK) {
408
pkg_free(pkg);
409
return (EPKG_FATAL);
410
}
411
fixup_abi(pkg, pc->rootdir, false);
412
413
pkg_archive = pkg_create_archive(pkg, pc, 0);
414
if (pkg_archive == NULL) {
415
if (errno == EEXIST) {
416
pkg_emit_notice("%s-%s already packaged, skipping...\n",
417
pkg->name, pkg->version);
418
pkg_free(pkg);
419
return (EPKG_EXIST);
420
}
421
pkg_free(pkg);
422
return (EPKG_FATAL);
423
}
424
425
if ((ret = pkg_create_from_dir(pkg, pc->rootdir, pc, pkg_archive)) != EPKG_OK)
426
pkg_emit_error("package creation failed");
427
428
packing_finish(pkg_archive);
429
if (hash && ret == EPKG_OK)
430
ret = hash_file(pkg);
431
432
pkg_free(pkg);
433
return (ret);
434
}
435
436
static int
437
pkg_load_message_from_file(int fd, struct pkg *pkg, const char *path)
438
{
439
char *buf = NULL;
440
off_t size = 0;
441
int ret;
442
ucl_object_t *obj;
443
444
assert(pkg != NULL);
445
assert(path != NULL);
446
447
if (faccessat(fd, path, F_OK, 0) == -1) {
448
return (EPKG_FATAL);
449
}
450
451
pkg_debug(1, "Reading message: '%s'", path);
452
if ((ret = file_to_bufferat(fd, path, &buf, &size)) != EPKG_OK) {
453
return (ret);
454
}
455
456
if (*buf == '[') {
457
ret = pkg_message_from_str(pkg, buf, size);
458
free(buf);
459
return (ret);
460
}
461
obj = ucl_object_fromstring_common(buf, size,
462
UCL_STRING_RAW|UCL_STRING_TRIM);
463
ret = pkg_message_from_ucl(pkg, obj);
464
ucl_object_unref(obj);
465
free(buf);
466
467
return (ret);
468
}
469
470
/* TODO use file descriptor for rootdir */
471
static int
472
load_manifest(struct pkg *pkg, const char *metadata, const char *plist,
473
const char *rootdir)
474
{
475
int ret;
476
477
ret = pkg_parse_manifest_file(pkg, metadata);
478
479
if (ret == EPKG_OK && plist != NULL)
480
ret = ports_parse_plist(pkg, plist, rootdir);
481
return (ret);
482
}
483
484
/* TODO use file descriptor for rootdir */
485
static int
486
load_metadata(struct pkg *pkg, const char *metadata, const char *plist,
487
const char *rootdir)
488
{
489
regex_t preg;
490
regmatch_t pmatch[2];
491
size_t size;
492
int fd, i;
493
494
/* Let's see if we have a directory or a manifest */
495
if ((fd = open(metadata, O_DIRECTORY|O_CLOEXEC)) == -1) {
496
if (errno == ENOTDIR)
497
return (load_manifest(pkg, metadata, plist, rootdir));
498
pkg_emit_errno("open", metadata);
499
return (EPKG_FATAL);
500
}
501
502
if ((pkg_parse_manifest_fileat(fd, pkg, "+MANIFEST")) != EPKG_OK) {
503
pkg_emit_error("Error parsing %s/+MANIFEST", metadata);
504
close(fd);
505
return (EPKG_FATAL);
506
}
507
/* ensure the uid is properly */
508
free(pkg->uid);
509
pkg->uid = xstrdup(pkg->name);
510
511
pkg_load_message_from_file(fd, pkg, "+DISPLAY");
512
if (pkg->desc == NULL)
513
pkg_set_from_fileat(fd, pkg, PKG_ATTR_DESC, "+DESC", false);
514
515
for (i = 0; scripts[i] != NULL; i++) {
516
if (faccessat(fd, scripts[i], F_OK, 0) == 0)
517
pkg_addscript_fileat(fd, pkg, scripts[i]);
518
}
519
520
for (i = 0; lua_scripts[i] != NULL; i++) {
521
if (faccessat(fd, lua_scripts[i], F_OK, 0) == 0)
522
pkg_addluascript_fileat(fd, pkg, lua_scripts[i]);
523
}
524
525
if (plist != NULL && ports_parse_plist(pkg, plist, rootdir) != EPKG_OK) {
526
return (EPKG_FATAL);
527
}
528
close(fd);
529
530
if (pkg->www == NULL) {
531
if (pkg->desc == NULL) {
532
pkg_emit_error("No www or desc defined in manifest");
533
return (EPKG_FATAL);
534
}
535
regcomp(&preg, "^WWW:[[:space:]]*(.*)$",
536
REG_EXTENDED|REG_ICASE|REG_NEWLINE);
537
if (regexec(&preg, pkg->desc, 2, pmatch, 0) == 0) {
538
size = pmatch[1].rm_eo - pmatch[1].rm_so;
539
pkg->www = xstrndup(&pkg->desc[pmatch[1].rm_so], size);
540
} else {
541
pkg->www = xstrdup("UNKNOWN");
542
}
543
regfree(&preg);
544
}
545
546
return (EPKG_OK);
547
}
548
549
static void
550
fixup_abi(struct pkg *pkg, const char *rootdir, bool testing)
551
{
552
bool defaultarch = false;
553
554
/* if no arch autodetermine it */
555
if (pkg->abi == NULL) {
556
if (ctx.abi.os == PKG_OS_FREEBSD) {
557
char *str_osversion;
558
xasprintf(&str_osversion, "%d", pkg_abi_get_freebsd_osversion(&ctx.abi));
559
pkg_kv_add(&pkg->annotations, "FreeBSD_version", str_osversion, "annotation");
560
}
561
pkg->abi = pkg_abi_to_string(&ctx.abi);
562
defaultarch = true;
563
}
564
565
if (!testing)
566
pkg_analyse_files(NULL, pkg, rootdir);
567
568
if (ctx.developer_mode)
569
suggest_arch(pkg, defaultarch);
570
}
571
572
int
573
pkg_load_metadata(struct pkg *pkg, const char *mfile, const char *md_dir,
574
const char *plist, const char *rootdir, bool testing)
575
{
576
int ret;
577
578
ret = load_metadata(pkg, md_dir != NULL ? md_dir: mfile, plist, rootdir);
579
if (ret != EPKG_OK)
580
return (ret);
581
582
fixup_abi(pkg, rootdir, testing);
583
return (ret);
584
}
585
586
static int64_t count;
587
static int64_t maxcount;
588
static const char *what;
589
590
static int magnitude(int64_t num)
591
{
592
int oom;
593
594
if (num == 0)
595
return (1);
596
if (num < 0)
597
num = -num;
598
599
for (oom = 1; num >= 10; oom++)
600
num /= 10;
601
602
return (oom);
603
}
604
605
static void
606
counter_init(const char *count_what, int64_t max)
607
{
608
count = 0;
609
what = count_what;
610
maxcount = max;
611
pkg_emit_progress_start("%-20s%*s[%jd]", what,
612
6 - magnitude(maxcount), " ", (intmax_t)maxcount);
613
614
return;
615
}
616
617
static void
618
counter_count(void)
619
{
620
count++;
621
622
if (count % TICK == 0)
623
pkg_emit_progress_tick(count, maxcount);
624
625
return;
626
}
627
628
static void
629
counter_end(void)
630
{
631
pkg_emit_progress_tick(count, maxcount);
632
return;
633
}
634
635