Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2024 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : : * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
5 : : * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
6 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
7 : : * Copyright (c) 2023-2024 Serenity Cyber Security, LLC
8 : : * Author: Gleb Popov <arrowd@FreeBSD.org>
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 : : #include "pkg_config.h"
32 : :
33 : : #include <sys/types.h>
34 : : #include <sys/stat.h>
35 : : #include <sys/uio.h>
36 : : #include <sys/file.h>
37 : : #include <sys/time.h>
38 : : #include <sys/wait.h>
39 : :
40 : : #include <archive_entry.h>
41 : : #include <assert.h>
42 : : #include <fts.h>
43 : : #include <libgen.h>
44 : : #include <sqlite3.h>
45 : : #include <string.h>
46 : : #include <stdio.h>
47 : : #include <stdbool.h>
48 : : #include <unistd.h>
49 : : #include <errno.h>
50 : : #include <math.h>
51 : : #include <pthread.h>
52 : : #include <fcntl.h>
53 : : #include <dirent.h>
54 : :
55 : : #include "tllist.h"
56 : : #include "pkg.h"
57 : : #include "private/event.h"
58 : : #include "private/utils.h"
59 : : #include "private/pkg.h"
60 : : #include "private/pkgdb.h"
61 : : #include "private/pkgsign.h"
62 : :
63 : : enum {
64 : : MSG_PKG_DONE=0,
65 : : MSG_PKG_READY,
66 : : };
67 : :
68 : : static int pkg_repo_pack_db(const char *name, const char *archive, char *path,
69 : : struct pkgsign_ctx *ctx, struct pkg_repo_create *prc);
70 : :
71 : : static int
72 : 0 : hash_file(struct pkg_repo_meta *meta, struct pkg *pkg, char *path)
73 : : {
74 : 0 : char tmp_repo[MAXPATHLEN] = { 0 };
75 : 0 : char tmp_name[MAXPATHLEN] = { 0 };
76 : 0 : char repo_name[MAXPATHLEN] = { 0 };
77 : 0 : char hash_name[MAXPATHLEN] = { 0 };
78 : 0 : char link_name[MAXPATHLEN] = { 0 };
79 : 0 : char *rel_repo = NULL;
80 : 0 : char *rel_dir = NULL;
81 : 0 : char *rel_link = NULL;
82 : 0 : char *ext = NULL;
83 : :
84 : : /* Don't rename symlinks */
85 [ # # ]: 0 : if (is_link(path))
86 : 0 : return (EPKG_OK);
87 : :
88 : 0 : ext = strrchr(path, '.');
89 : :
90 : 0 : strlcpy(tmp_name, path, sizeof(tmp_name));
91 : 0 : rel_dir = get_dirname(tmp_name);
92 [ # # ]: 0 : while (strstr(rel_dir, "/Hashed") != NULL) {
93 : 0 : rel_dir = get_dirname(rel_dir);
94 : : }
95 : 0 : strlcpy(tmp_name, rel_dir, sizeof(tmp_name));
96 : 0 : rel_dir = (char *)&tmp_name;
97 : :
98 : 0 : rel_repo = path;
99 [ # # ]: 0 : if (strncmp(rel_repo, meta->repopath, strlen(meta->repopath)) == 0) {
100 : 0 : rel_repo += strlen(meta->repopath);
101 [ # # ]: 0 : while (rel_repo[0] == '/')
102 : 0 : rel_repo++;
103 : 0 : }
104 : 0 : strlcpy(tmp_repo, rel_repo, sizeof(tmp_repo));
105 : 0 : rel_repo = get_dirname(tmp_repo);
106 [ # # ]: 0 : while (strstr(rel_repo, "/Hashed") != NULL) {
107 : 0 : rel_repo = get_dirname(rel_repo);
108 : : }
109 : 0 : strlcpy(tmp_repo, rel_repo, sizeof(tmp_repo));
110 : 0 : rel_repo = (char *)&tmp_repo;
111 : :
112 : 0 : pkg_snprintf(repo_name, sizeof(repo_name), "%S/%S/%n-%v%S%z%S",
113 : 0 : rel_repo, PKG_HASH_DIR, pkg, pkg, PKG_HASH_SEPSTR, pkg, ext);
114 : 0 : pkg_snprintf(link_name, sizeof(repo_name), "%S/%n-%v%S",
115 : 0 : rel_dir, pkg, pkg, ext);
116 : 0 : pkg_snprintf(hash_name, sizeof(hash_name), "%S/%S/%n-%v%S%z%S",
117 : 0 : rel_dir, PKG_HASH_DIR, pkg, pkg, PKG_HASH_SEPSTR, pkg, ext);
118 : 0 : rel_link = (char *)&hash_name;
119 : 0 : rel_link += strlen(rel_dir);
120 [ # # ]: 0 : while (rel_link[0] == '/')
121 : 0 : rel_link++;
122 : :
123 : 0 : snprintf(tmp_name, sizeof(tmp_name), "%s/%s", rel_dir, PKG_HASH_DIR);
124 : 0 : rel_dir = (char *)&tmp_name;
125 [ # # ]: 0 : if (!is_dir(rel_dir)) {
126 : 0 : pkg_debug(1, "Making directory: %s", rel_dir);
127 : 0 : (void)pkg_mkdirs(rel_dir);
128 : 0 : }
129 : :
130 [ # # ]: 0 : if (!STREQ(path, hash_name)) {
131 : 0 : pkg_debug(1, "Rename the pkg from: %s to: %s", path, hash_name);
132 [ # # ]: 0 : if (rename(path, hash_name) == -1) {
133 : 0 : pkg_emit_errno("rename", hash_name);
134 : 0 : return (EPKG_FATAL);
135 : : }
136 : 0 : }
137 [ # # ]: 0 : if (meta->hash_symlink) {
138 : 0 : pkg_debug(1, "Symlinking pkg file from: %s to: %s", rel_link,
139 : 0 : link_name);
140 : 0 : (void)unlink(link_name);
141 [ # # ]: 0 : if (symlink(rel_link, link_name) == -1) {
142 : 0 : pkg_emit_errno("symlink", link_name);
143 : 0 : return (EPKG_FATAL);
144 : : }
145 : 0 : }
146 : 0 : free(pkg->repopath);
147 : 0 : pkg->repopath = xstrdup(repo_name);
148 : :
149 : 0 : return (EPKG_OK);
150 : 0 : }
151 : :
152 : : struct pkg_fts_item {
153 : : char *fts_accpath;
154 : : char *pkg_path;
155 : : char *fts_name;
156 : : off_t fts_size;
157 : : int fts_info;
158 : : };
159 : : typedef tll(struct pkg_fts_item *) fts_item_t;
160 : :
161 : : static struct pkg_fts_item*
162 : 153 : pkg_create_repo_fts_new(FTSENT *fts, const char *root_path)
163 : : {
164 : : struct pkg_fts_item *item;
165 : : char *pkg_path;
166 : :
167 : 153 : item = xmalloc(sizeof(*item));
168 : 153 : item->fts_accpath = xstrdup(fts->fts_accpath);
169 : 153 : item->fts_name = xstrdup(fts->fts_name);
170 : 153 : item->fts_size = fts->fts_statp->st_size;
171 : 153 : item->fts_info = fts->fts_info;
172 : :
173 : 153 : pkg_path = fts->fts_path;
174 : 153 : pkg_path += strlen(root_path);
175 [ + + ]: 306 : while (pkg_path[0] == '/')
176 : 153 : pkg_path++;
177 : :
178 : 153 : item->pkg_path = xstrdup(pkg_path);
179 : :
180 : 153 : return (item);
181 : : }
182 : :
183 : : static void
184 : 153 : pkg_create_repo_fts_free(struct pkg_fts_item *item)
185 : : {
186 : 153 : free(item->fts_accpath);
187 : 153 : free(item->pkg_path);
188 : 153 : free(item->fts_name);
189 : 153 : free(item);
190 : 153 : }
191 : :
192 : : static int
193 : 75 : pkg_create_repo_read_fts(fts_item_t *items, FTS *fts,
194 : : const char *repopath, size_t *plen, struct pkg_repo_meta *meta)
195 : : {
196 : : FTSENT *fts_ent;
197 : : struct pkg_fts_item *fts_cur;
198 : : char *ext;
199 : 75 : int linklen = 0;
200 : 75 : char tmp_name[MAXPATHLEN] = { 0 };
201 : : char repo_path[MAXPATHLEN];
202 : : size_t repo_path_len;
203 : :
204 [ + - ]: 75 : if (realpath(repopath, repo_path) == NULL) {
205 : 0 : pkg_emit_errno("invalid repo path", repopath);
206 : 0 : return (EPKG_FATAL);
207 : : }
208 : 75 : repo_path_len = strlen(repo_path);
209 : 75 : errno = 0;
210 : :
211 [ + + ]: 1197 : while ((fts_ent = fts_read(fts)) != NULL) {
212 : : /*
213 : : * Skip directories starting with '.' to avoid Poudriere
214 : : * symlinks.
215 : : */
216 [ + + + - ]: 1458 : if ((fts_ent->fts_info == FTS_D ||
217 [ + + ]: 908 : fts_ent->fts_info == FTS_DP) &&
218 [ + + ]: 428 : fts_ent->fts_namelen > 2 &&
219 : 336 : fts_ent->fts_name[0] == '.') {
220 : 0 : fts_set(fts, fts_ent, FTS_SKIP);
221 : 0 : continue;
222 : : }
223 : : /*
224 : : * Ignore 'Latest' directory as it is just symlinks back to
225 : : * already-processed packages.
226 : : */
227 [ + + + + ]: 1356 : if ((fts_ent->fts_info == FTS_D ||
228 [ + + ]: 908 : fts_ent->fts_info == FTS_DP ||
229 [ + + ]: 694 : fts_ent->fts_info == FTS_SL) &&
230 : 448 : STREQ(fts_ent->fts_name, "Latest")) {
231 : 2 : fts_set(fts, fts_ent, FTS_SKIP);
232 : 2 : continue;
233 : : }
234 : : /* Follow symlinks. */
235 [ + + ]: 1120 : if (fts_ent->fts_info == FTS_SL) {
236 : : /*
237 : : * Skip symlinks pointing inside the repo
238 : : * and dead symlinks
239 : : */
240 [ + + ]: 20 : if (realpath(fts_ent->fts_path, tmp_name) == NULL)
241 : 6 : continue;
242 [ + + ]: 14 : if (strncmp(repo_path, tmp_name, repo_path_len) == 0)
243 : 13 : continue;
244 : : /* Skip symlinks to hashed packages */
245 [ + - ]: 1 : if (meta->hash) {
246 : 0 : linklen = readlink(fts_ent->fts_path,
247 : : (char *)&tmp_name, MAXPATHLEN);
248 [ # # ]: 0 : if (linklen < 0)
249 : 0 : continue;
250 : 0 : tmp_name[linklen] = '\0';
251 [ # # ]: 0 : if (strstr(tmp_name, PKG_HASH_DIR) != NULL)
252 : 0 : continue;
253 : 0 : }
254 : 1 : fts_set(fts, fts_ent, FTS_FOLLOW);
255 : : /* Restart. Next entry will be the resolved file. */
256 : 1 : continue;
257 : : }
258 : : /* Skip everything that is not a file */
259 [ + + ]: 1100 : if (fts_ent->fts_info != FTS_F)
260 : 426 : continue;
261 : :
262 : 674 : ext = strrchr(fts_ent->fts_name, '.');
263 : :
264 [ + + ]: 674 : if (ext == NULL)
265 : 236 : continue;
266 : :
267 [ + + ]: 438 : if (!packing_is_valid_format(ext + 1))
268 : 277 : continue;
269 : :
270 : : /* skip all files which are not .pkg */
271 [ + - + - ]: 161 : if (!ctx.repo_accept_legacy_pkg && !STREQ(ext + 1, "pkg"))
272 : 0 : continue;
273 : :
274 : :
275 : 161 : *ext = '\0';
276 : :
277 [ - + ]: 161 : if (pkg_repo_meta_is_old_file(fts_ent->fts_name, meta)) {
278 : 0 : unlink(fts_ent->fts_path);
279 : 0 : continue;
280 : : }
281 [ + - + + ]: 161 : if (STREQ(fts_ent->fts_name, "meta") ||
282 : 161 : pkg_repo_meta_is_special_file(fts_ent->fts_name, meta)) {
283 : 8 : *ext = '.';
284 : 8 : continue;
285 : : }
286 : :
287 : 153 : *ext = '.';
288 : 153 : fts_cur = pkg_create_repo_fts_new(fts_ent, repopath);
289 [ - + ]: 153 : if (fts_cur == NULL)
290 : 0 : return (EPKG_FATAL);
291 : :
292 [ + + + + : 153 : tll_push_front(*items, fts_cur);
+ - - + +
+ ]
293 : 153 : (*plen) ++;
294 : : }
295 : :
296 [ - + ]: 75 : if (errno != 0) {
297 : 0 : pkg_emit_errno("fts_read", "pkg_create_repo_read_fts");
298 : 0 : return (EPKG_FATAL);
299 : : }
300 : :
301 : 75 : return (EPKG_OK);
302 : 75 : }
303 : :
304 : : struct thr_env {
305 : : int ntask;
306 : : FILE *ffile;
307 : : FILE *mfile;
308 : : FILE *dfile;
309 : : struct ucl_emitter_context *ctx;
310 : : struct pkg_repo_meta *meta;
311 : : fts_item_t fts_items;
312 : : pthread_mutex_t nlock;
313 : : pthread_mutex_t llock;
314 : : pthread_mutex_t flock;
315 : : pthread_cond_t cond;
316 : : };
317 : :
318 : : static void *
319 : 152 : pkg_create_repo_thread(void *arg)
320 : : {
321 : 152 : struct thr_env *te = (struct thr_env *)arg;
322 : 152 : int flags, ret = EPKG_OK;
323 : 152 : struct pkg *pkg = NULL;
324 : : char *path;
325 : : const char *repopath;
326 : 152 : struct pkg_fts_item *items = NULL;
327 : :
328 : 152 : pkg_debug(1, "start worker to parse packages");
329 : :
330 [ + + ]: 152 : if (te->ffile != NULL)
331 : 4 : flags = PKG_OPEN_MANIFEST_ONLY;
332 : : else
333 : 148 : flags = PKG_OPEN_MANIFEST_ONLY | PKG_OPEN_MANIFEST_COMPACT;
334 : :
335 : 305 : for (;;) {
336 [ + + ]: 305 : if (items != NULL)
337 : 153 : pkg_create_repo_fts_free(items);
338 : 305 : pthread_mutex_lock(&te->llock);
339 [ + + ]: 305 : if (tll_length(te->fts_items) == 0) {
340 : 152 : pthread_mutex_unlock(&te->llock);
341 : 152 : goto cleanup;
342 : : }
343 [ + - - + : 153 : items = tll_pop_front(te->fts_items);
+ + ]
344 : 153 : pthread_mutex_unlock(&te->llock);
345 : 153 : path = items->fts_accpath;
346 : 153 : repopath = items->pkg_path;
347 [ - + ]: 153 : if (pkg_open(&pkg, path, flags) == EPKG_OK) {
348 : : struct stat st;
349 : :
350 : 153 : pkg->sum = pkg_checksum_file(path, PKG_HASH_TYPE_SHA256_HEX);
351 : 153 : stat(path, &st);
352 : 153 : pkg->pkgsize = st.st_size;
353 [ - + ]: 153 : if (te->meta->hash) {
354 : 0 : ret = hash_file(te->meta, pkg, path);
355 [ # # ]: 0 : if (ret != EPKG_OK)
356 : 0 : goto cleanup;
357 : 0 : } else {
358 : 153 : pkg->repopath = xstrdup(repopath);
359 : : }
360 : :
361 : : /*
362 : : * TODO: use pkg_checksum for new manifests
363 : : */
364 : 153 : pthread_mutex_lock(&te->flock);
365 : 153 : ucl_object_t *o = pkg_emit_object(pkg, 0);
366 : 153 : ucl_object_emit_streamline_add_object(te->ctx, o);
367 : 153 : ucl_object_emit_file(o, UCL_EMIT_JSON_COMPACT, te->mfile);
368 : 153 : fprintf(te->mfile, "\n");
369 : 153 : ucl_object_unref(o);
370 : :
371 [ + + ]: 153 : if (te->ffile != NULL) {
372 : 4 : pkg_emit_filelist(pkg, te->ffile);
373 : 4 : }
374 : :
375 : 153 : pthread_mutex_unlock(&te->flock);
376 : :
377 : 153 : pkg_free(pkg);
378 : 153 : }
379 : 153 : pthread_mutex_lock(&te->nlock);
380 : 153 : te->ntask++;
381 : 153 : pthread_cond_signal(&te->cond);
382 : 153 : pthread_mutex_unlock(&te->nlock);
383 : : }
384 : :
385 : : cleanup:
386 : 152 : pkg_debug(1, "worker done");
387 : 152 : return (NULL);
388 : : }
389 : :
390 : : #if defined (__linux__) || defined(_DARWIN_C_SOURCE) || defined (__APPLE__)
391 : : typedef const FTSENT *FTSENTP;
392 : : #else
393 : : typedef const FTSENT *const FTSENTP;
394 : : #endif
395 : :
396 : : static int
397 : 1727 : fts_compare(FTSENTP *a, FTSENTP *b)
398 : : {
399 : : /* Sort files before directories, then alpha order */
400 [ + + + + ]: 1727 : if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D)
401 : 69 : return -1;
402 [ + + + + ]: 1658 : if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D)
403 : 205 : return 1;
404 : 1453 : return (strcmp((*a)->fts_name, (*b)->fts_name));
405 : 1727 : }
406 : :
407 : : struct pkg_repo_create *
408 : 75 : pkg_repo_create_new(void)
409 : : {
410 : : struct pkg_repo_create *prc;
411 : :
412 : 75 : prc = xcalloc(1, sizeof(*prc));
413 : 75 : prc->ofd = -1;
414 : :
415 : 75 : return (prc);
416 : : }
417 : :
418 : : void
419 : 75 : pkg_repo_create_free(struct pkg_repo_create *prc)
420 : : {
421 [ + - ]: 75 : if (prc == NULL)
422 : 0 : return;
423 : 75 : pkg_repo_meta_free(prc->meta);
424 [ - + ]: 75 : if (prc->ofd != -1)
425 : 75 : close(prc->ofd);
426 : 75 : ucl_object_unref(prc->groups);
427 : 75 : free(prc);
428 : 75 : }
429 : :
430 : : static ucl_object_t*
431 : 0 : ucl_load(int dfd, const char *name, ucl_object_t *schema)
432 : : {
433 : : struct ucl_parser *p;
434 : 0 : ucl_object_t *obj = NULL;
435 : : int fd;
436 : : struct ucl_schema_error err;
437 : :
438 : 0 : fd = openat(dfd, name, O_RDONLY);
439 [ # # ]: 0 : if (fd == -1) {
440 : 0 : pkg_emit_error("Unable to open UCL file: %s", name);
441 : 0 : return (NULL);
442 : : }
443 : :
444 : 0 : p = ucl_parser_new(0);
445 [ # # ]: 0 : if (!ucl_parser_add_fd(p, fd)) {
446 : 0 : pkg_emit_error("Error parsing UCL file '%s': %s'",
447 : 0 : name, ucl_parser_get_error(p));
448 : 0 : ucl_parser_free(p);
449 : 0 : close(fd);
450 : 0 : return (NULL);
451 : : }
452 : 0 : close(fd);
453 : :
454 : 0 : obj = ucl_parser_get_object(p);
455 : 0 : ucl_parser_free(p);
456 [ # # ]: 0 : if (obj == NULL)
457 : 0 : return (NULL);
458 : :
459 [ # # ]: 0 : if (!ucl_object_validate(schema, obj, &err)) {
460 : 0 : pkg_emit_error("UCL definition %s cannot be validated: %s",
461 : 0 : name, err.msg);
462 : 0 : ucl_object_unref(obj);
463 : 0 : return (NULL);
464 : : }
465 : :
466 : 0 : return (obj);
467 : 0 : }
468 : :
469 : : static const char group_schema_str[] = ""
470 : : "{"
471 : : " type = object;"
472 : : " properties: {"
473 : : " name: { type = string };"
474 : : " requires: { "
475 : : " type = array;"
476 : : " item = { type = string };"
477 : : " };"
478 : : " depends: { "
479 : : " type = array;"
480 : : " item = { type = string };"
481 : : " };"
482 : : " comment: { type = string };"
483 : : " };"
484 : : " required = [ name, comment ];"
485 : : "};";
486 : :
487 : : static const char expired_schema_str[] = ""
488 : : "{"
489 : : " type = object;"
490 : : " properties: {"
491 : : " name: { type = string };"
492 : : " reason: { type = string };"
493 : : " replaced_by: { type = string };"
494 : : " };"
495 : : " required = [ name ];"
496 : : "};";
497 : :
498 : : static ucl_object_t *
499 : 0 : open_schema(const char* schema_str, size_t schema_str_len)
500 : : {
501 : : struct ucl_parser *parser;
502 : : ucl_object_t *schema;
503 : 0 : parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
504 [ # # # # ]: 0 : if (!ucl_parser_add_chunk(parser, schema_str,
505 : 0 : schema_str_len - 1)) {
506 : 0 : pkg_emit_error("Cannot parse schema string: %s",
507 : 0 : ucl_parser_get_error(parser));
508 : 0 : ucl_parser_free(parser);
509 : 0 : return (NULL);
510 : : }
511 : 0 : schema = ucl_parser_get_object(parser);
512 : 0 : ucl_parser_free(parser);
513 : 0 : return (schema);
514 : 0 : }
515 : :
516 : : static void
517 : 0 : read_ucl_dir(struct pkg_repo_create *prc, const char *path, ucl_object_t *schema, void (*callback)(struct pkg_repo_create *prc, ucl_object_t* parsed_obj))
518 : : {
519 : 0 : int dfd = open(path, O_DIRECTORY);
520 : : DIR *d;
521 : : struct dirent *e;
522 : : struct stat st;
523 : :
524 [ # # ]: 0 : if (dfd == -1) {
525 : 0 : pkg_emit_error("Unable to open directory '%s'", path);
526 : 0 : return;
527 : : }
528 : :
529 : 0 : d = fdopendir(dfd);
530 [ # # ]: 0 : if (d == NULL) {
531 : 0 : pkg_emit_error("Unable to open directory '%s'", path);
532 : 0 : close(dfd);
533 : 0 : return;
534 : : }
535 : :
536 [ # # ]: 0 : while ((e = readdir(d)) != NULL) {
537 : : const char *ext;
538 : : ucl_object_t* parsed_obj;
539 : : /* ignore all hidden files */
540 [ # # ]: 0 : if (e->d_name[0] == '.')
541 : 0 : continue;
542 : : /* only consider files ending with .ucl */
543 : 0 : ext = strrchr(e->d_name, '.');
544 [ # # ]: 0 : if (ext == NULL)
545 : 0 : continue;
546 [ # # ]: 0 : if (strcmp(ext, ".ucl") != 0)
547 : 0 : continue;
548 : : /* only regular files are considered */
549 [ # # ]: 0 : if (fstatat(dfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) {
550 : 0 : pkg_emit_errno("fstatat", e->d_name);
551 : 0 : goto cleanup;
552 : : }
553 [ # # ]: 0 : if (!S_ISREG(st.st_mode))
554 : 0 : continue;
555 : 0 : parsed_obj = ucl_load(dfd, e->d_name, schema);
556 [ # # ]: 0 : if (parsed_obj)
557 : 0 : callback (prc, parsed_obj);
558 : : }
559 : : cleanup:
560 : 0 : closedir(d);
561 : 0 : }
562 : :
563 : : static void
564 : 0 : append_groups(struct pkg_repo_create *prc, ucl_object_t* groups_obj)
565 : : {
566 [ # # ]: 0 : if (prc->groups == NULL)
567 : 0 : prc->groups = ucl_object_typed_new(UCL_ARRAY);
568 : 0 : ucl_array_append(prc->groups, groups_obj);
569 : 0 : }
570 : :
571 : : void
572 : 0 : pkg_repo_create_set_groups(struct pkg_repo_create *prc, const char *path)
573 : : {
574 : : ucl_object_t *schema;
575 : 0 : schema = open_schema(group_schema_str, sizeof(group_schema_str));
576 : :
577 : 0 : read_ucl_dir(prc, path, schema, append_groups);
578 : :
579 : 0 : ucl_object_unref(schema);
580 : 0 : }
581 : :
582 : : static void
583 : 0 : append_expired_packages(struct pkg_repo_create *prc, ucl_object_t* expired_packages_obj)
584 : : {
585 [ # # ]: 0 : if (prc->expired_packages == NULL)
586 : 0 : prc->expired_packages = ucl_object_typed_new(UCL_ARRAY);
587 : 0 : ucl_array_append(prc->expired_packages, expired_packages_obj);
588 : 0 : }
589 : :
590 : : void
591 : 0 : pkg_repo_create_set_expired_packages(struct pkg_repo_create *prc, const char *path)
592 : : {
593 : : ucl_object_t *schema;
594 : 0 : schema = open_schema(expired_schema_str, sizeof(expired_schema_str));
595 : :
596 : 0 : read_ucl_dir(prc, path, schema, append_expired_packages);
597 : :
598 : 0 : ucl_object_unref(schema);
599 : 0 : }
600 : :
601 : : void
602 : 1 : pkg_repo_create_set_create_filelist(struct pkg_repo_create *prc, bool val)
603 : : {
604 : 1 : prc->filelist = val;
605 : 1 : }
606 : :
607 : : void
608 : 75 : pkg_repo_create_set_hash(struct pkg_repo_create *prc, bool val)
609 : : {
610 : 75 : prc->hash = val;
611 : 75 : }
612 : :
613 : : void
614 : 75 : pkg_repo_create_set_hash_symlink(struct pkg_repo_create *prc, bool val)
615 : : {
616 : 75 : prc->hash_symlink = val;
617 : 75 : }
618 : :
619 : : void
620 : 7 : pkg_repo_create_set_output_dir(struct pkg_repo_create *prc, const char *out)
621 : : {
622 : 7 : prc->outdir = out;
623 : 7 : }
624 : :
625 : : void
626 : 2 : pkg_repo_create_set_metafile(struct pkg_repo_create *prc, const char *metafile)
627 : : {
628 : 2 : prc->metafile = metafile;
629 : 2 : }
630 : :
631 : : void
632 : 75 : pkg_repo_create_set_sign(struct pkg_repo_create *prc, char **argv, int argc, pkg_password_cb *cb)
633 : : {
634 : 75 : prc->sign.argc = argc;
635 : 75 : prc->sign.argv = argv;
636 : 75 : prc->sign.cb = cb;
637 : 75 : }
638 : :
639 : : static int
640 : 75 : pkg_repo_create_pack_and_sign(struct pkg_repo_create *prc)
641 : : {
642 : : char repo_path[MAXPATHLEN];
643 : : char repo_archive[MAXPATHLEN];
644 : : char *key_file;
645 : : const char *key_type;
646 : 75 : struct pkgsign_ctx *sctx = NULL;
647 : : struct stat st;
648 : 75 : int ret = EPKG_OK, nfile = 0;
649 : 75 : const int files_to_pack = 4;
650 : :
651 [ + + ]: 75 : if (prc->sign.argc == 1) {
652 : : char *cpos;
653 : :
654 : 4 : key_type = key_file = prc->sign.argv[0];
655 [ + + ]: 4 : if ((cpos = strchr(key_type, ':')) != NULL) {
656 : 3 : key_file = cpos + 1;
657 : 3 : *(key_file - 1) = '\0';
658 : 3 : } else {
659 : 1 : key_type = "rsa";
660 : : }
661 : :
662 : 4 : pkg_debug(1, "Loading %s key from '%s' for signing", key_type, key_file);
663 : 4 : ret = pkgsign_new_sign(key_type, &sctx);
664 [ - + ]: 4 : if (ret != EPKG_OK) {
665 : 0 : pkg_emit_error("'%s' signer not found", key_type);
666 : 0 : return (EPKG_FATAL);
667 : : }
668 : :
669 : 4 : pkgsign_set(sctx, prc->sign.cb, key_file);
670 : 4 : ret = EPKG_OK;
671 : 4 : }
672 : :
673 [ + + + - ]: 75 : if (prc->sign.argc > 1 && !STREQ(prc->sign.argv[0], "signing_command:"))
674 : 0 : return (EPKG_FATAL);
675 : :
676 [ + + ]: 75 : if (prc->sign.argc > 1) {
677 : 3 : prc->sign.argc--;
678 : 3 : prc->sign.argv++;
679 : 3 : }
680 : :
681 : 75 : pkg_emit_progress_start("Packing files for repository");
682 : 75 : pkg_emit_progress_tick(nfile++, files_to_pack);
683 : :
684 : 150 : snprintf(repo_path, sizeof(repo_path), "%s/%s", prc->outdir,
685 : 75 : prc->meta->manifests);
686 : 150 : snprintf(repo_archive, sizeof(repo_archive), "%s/%s", prc->outdir,
687 : 75 : prc->meta->manifests_archive);
688 [ - + ]: 75 : if (pkg_repo_pack_db(prc->meta->manifests, repo_archive, repo_path, sctx, prc) != EPKG_OK) {
689 : 0 : ret = EPKG_FATAL;
690 : 0 : goto cleanup;
691 : : }
692 : :
693 : 75 : pkg_emit_progress_tick(nfile++, files_to_pack);
694 : :
695 [ + + ]: 75 : if (prc->filelist) {
696 : 2 : snprintf(repo_path, sizeof(repo_path), "%s/%s", prc->outdir,
697 : 1 : prc->meta->filesite);
698 : 2 : snprintf(repo_archive, sizeof(repo_archive), "%s/%s",
699 : 1 : prc->outdir, prc->meta->filesite_archive);
700 [ - + ]: 1 : if (pkg_repo_pack_db(prc->meta->filesite, repo_archive, repo_path, sctx, prc) != EPKG_OK) {
701 : 0 : ret = EPKG_FATAL;
702 : 0 : goto cleanup;
703 : : }
704 : 1 : }
705 : :
706 : 75 : pkg_emit_progress_tick(nfile++, files_to_pack);
707 : 75 : snprintf(repo_path, sizeof(repo_path), "%s/%s", prc->outdir, prc->meta->data);
708 : 150 : snprintf(repo_archive, sizeof(repo_archive), "%s/%s", prc->outdir,
709 : 75 : prc->meta->data_archive);
710 [ - + ]: 75 : if (pkg_repo_pack_db(prc->meta->data, repo_archive, repo_path, sctx, prc) != EPKG_OK) {
711 : 0 : ret = EPKG_FATAL;
712 : 0 : goto cleanup;
713 : : }
714 : :
715 : 75 : pkg_emit_progress_tick(nfile++, files_to_pack);
716 : :
717 [ - + ]: 150 : if (fstatat(prc->ofd, "meta.conf", &st, 0) == 0) {
718 : 75 : struct timespec ts[2] = {
719 : 150 : {
720 : 75 : .tv_sec = st.st_mtime,
721 : : .tv_nsec = 0,
722 : : },
723 : 150 : {
724 : 75 : .tv_sec = st.st_mtime,
725 : : .tv_nsec = 0,
726 : : },
727 : : };
728 : 150 : snprintf(repo_archive, sizeof(repo_archive), "%s.pkg",
729 : 75 : prc->meta->manifests_archive);
730 : 75 : utimensat(prc->ofd, repo_archive, ts, 0);
731 [ + + ]: 75 : if (prc->filelist) {
732 : 2 : snprintf(repo_archive, sizeof(repo_archive),
733 : 1 : "%s.pkg", prc->meta->filesite_archive);
734 : 1 : utimensat(prc->ofd, repo_archive, ts, 0);
735 : 1 : }
736 : 150 : snprintf(repo_archive, sizeof(repo_archive), "%s.pkg",
737 : 75 : prc->meta->data_archive);
738 : 75 : utimensat(prc->ofd, repo_archive, ts, 0);
739 : 75 : }
740 : :
741 : : cleanup:
742 : 75 : pkg_emit_progress_tick(files_to_pack, files_to_pack);
743 : :
744 : 75 : pkgsign_free(sctx);
745 : :
746 : 75 : return (ret);
747 : 75 : }
748 : :
749 : : int
750 : 75 : pkg_repo_create(struct pkg_repo_create *prc, char *path)
751 : : {
752 : 75 : FTS *fts = NULL;
753 : : int num_workers;
754 : : pthread_t *threads;
755 : 75 : struct thr_env te = { 0 };
756 : : size_t len;
757 : : int fd;
758 : : int dfd, ffd, mfd;
759 : 75 : int retcode = EPKG_FATAL;
760 : : ucl_object_t *meta_dump;
761 : : char *repopath[2];
762 : :
763 [ + + ]: 75 : if (prc->outdir == NULL)
764 : 68 : prc->outdir = path;
765 : :
766 : 75 : te.dfile = te.ffile = te.mfile = NULL;
767 : :
768 [ + - ]: 75 : if (!is_dir(path)) {
769 : 0 : pkg_emit_error("%s is not a directory", path);
770 : 0 : return (EPKG_FATAL);
771 : : }
772 : :
773 : 75 : errno = 0;
774 [ + - ]: 75 : if (!is_dir(prc->outdir)) {
775 : : /* Try to create dir */
776 [ # # ]: 0 : if (errno == ENOENT) {
777 [ # # ]: 0 : if (mkdir(prc->outdir, 00755) == -1) {
778 : 0 : pkg_fatal_errno("cannot create output directory %s",
779 : : prc->outdir);
780 : 0 : }
781 : 0 : }
782 : : else {
783 : 0 : pkg_emit_error("%s is not a directory", prc->outdir);
784 : 0 : return (EPKG_FATAL);
785 : : }
786 : 0 : }
787 [ + - ]: 75 : if ((prc->ofd = open(prc->outdir, O_DIRECTORY)) == -1) {
788 : 0 : pkg_emit_error("Cannot open %s", prc->outdir);
789 : 0 : return (EPKG_FATAL);
790 : : }
791 : :
792 [ + + ]: 75 : if (prc->metafile != NULL) {
793 : 2 : fd = open(prc->metafile, O_RDONLY);
794 [ + - ]: 2 : if (fd == -1) {
795 : 0 : pkg_emit_error("meta loading error while trying %s", prc->metafile);
796 : 0 : return (EPKG_FATAL);
797 : : }
798 [ - + ]: 2 : if (pkg_repo_meta_load(fd, &prc->meta) != EPKG_OK) {
799 : 0 : pkg_emit_error("meta loading error while trying %s", prc->metafile);
800 : 0 : close(fd);
801 : 0 : return (EPKG_FATAL);
802 : : }
803 : 2 : close(fd);
804 : 2 : } else {
805 : 73 : prc->meta = pkg_repo_meta_default();
806 : : }
807 : 75 : prc->meta->repopath = path;
808 : 75 : prc->meta->hash = prc->hash;
809 : 75 : prc->meta->hash_symlink = prc->hash_symlink;
810 : :
811 : 75 : te.meta = prc->meta;
812 : :
813 : : /* initialize mutexes & conditions */
814 : 75 : pthread_mutex_init(&te.nlock, 0);
815 : 75 : pthread_mutex_init(&te.llock, 0);
816 : 75 : pthread_mutex_init(&te.flock, 0);
817 : 75 : pthread_cond_init(&te.cond, 0);
818 : :
819 : 75 : repopath[0] = path;
820 : 75 : repopath[1] = NULL;
821 : :
822 : 75 : num_workers = pkg_object_int(pkg_config_get("WORKERS_COUNT"));
823 [ - + ]: 75 : if (num_workers <= 0) {
824 : 75 : num_workers = (int)sysconf(_SC_NPROCESSORS_ONLN);
825 [ + - ]: 75 : if (num_workers == -1)
826 : 0 : num_workers = 6;
827 : 75 : }
828 : :
829 [ + - ]: 75 : if ((fts = fts_open(repopath, FTS_PHYSICAL|FTS_NOCHDIR, fts_compare)) == NULL) {
830 : 0 : pkg_emit_errno("fts_open", path);
831 : 0 : goto cleanup;
832 : : }
833 : :
834 [ + - + - ]: 150 : if ((mfd = openat(prc->ofd, prc->meta->manifests,
835 : 75 : O_CREAT|O_TRUNC|O_WRONLY, 00644)) == -1) {
836 : 0 : goto cleanup;
837 : : }
838 [ + - ]: 75 : if ((te.mfile = fdopen(mfd,"w")) == NULL) {
839 : 0 : goto cleanup;
840 : : }
841 : :
842 [ + - + - ]: 150 : if ((dfd = openat(prc->ofd, prc->meta->data,
843 : 75 : O_CREAT|O_TRUNC|O_WRONLY, 00644)) == -1) {
844 : 0 : goto cleanup;
845 : : }
846 [ + - ]: 75 : if ((te.dfile = fdopen(dfd,"w")) == NULL) {
847 : 0 : goto cleanup;
848 : : }
849 : :
850 [ + + ]: 75 : if (prc->filelist) {
851 [ + - + - ]: 2 : if ((ffd = openat(prc->ofd, prc->meta->filesite,
852 : 1 : O_CREAT|O_TRUNC|O_WRONLY, 00644)) == -1) {
853 : 0 : goto cleanup;
854 : : }
855 [ + - ]: 1 : if ((te.ffile = fdopen(ffd,"w")) == NULL) {
856 : 0 : goto cleanup;
857 : : }
858 : 1 : }
859 : :
860 : 75 : len = 0;
861 : :
862 : 75 : pkg_create_repo_read_fts(&te.fts_items, fts, path, &len, prc->meta);
863 : :
864 [ + - ]: 75 : if (len == 0) {
865 : : /* Nothing to do */
866 : 0 : pkg_emit_error("No package files have been found");
867 : 0 : goto cleanup;
868 : : }
869 : :
870 : : /* Split items over all workers */
871 [ - + ]: 75 : num_workers = MIN(num_workers, len);
872 : :
873 : : /* Launch workers */
874 : 75 : pkg_emit_progress_start("Creating repository in %s", prc->outdir);
875 : :
876 : 75 : threads = xcalloc(num_workers, sizeof(pthread_t));
877 : :
878 : : struct ucl_emitter_functions *f;
879 : 75 : ucl_object_t *obj = ucl_object_typed_new(UCL_OBJECT);
880 : : /*
881 : : * Work around a bug in the streamline exporter which creates an invalid
882 : : * json if there is nothing in the object, prior to the streamline to
883 : : * start. So always add at least an empty groups array
884 : : */
885 : 150 : ucl_object_insert_key(obj,
886 [ - + ]: 75 : prc->groups == NULL ? ucl_object_typed_new(UCL_ARRAY) : prc->groups,
887 : : "groups", 0, false);
888 : 150 : ucl_object_insert_key(obj,
889 [ - + ]: 75 : prc->expired_packages == NULL ? ucl_object_typed_new(UCL_ARRAY) : prc->expired_packages,
890 : : "expired_packages", 0, false);
891 : 75 : f = ucl_object_emit_file_funcs(te.dfile);
892 : 75 : te.ctx = ucl_object_emit_streamline_new(obj, UCL_EMIT_JSON_COMPACT, f);
893 : 75 : ucl_object_t *ar = ucl_object_typed_new(UCL_ARRAY);
894 : 75 : ar->key = "packages";
895 : 75 : ar->keylen = sizeof("packages") -1;
896 : :
897 : 75 : ucl_object_emit_streamline_start_container(te.ctx, ar);
898 : :
899 [ + + ]: 228 : for (int i = 0; i < num_workers; i++) {
900 : : /* Create new worker */
901 : 153 : pthread_create(&threads[i], NULL, &pkg_create_repo_thread, &te);
902 : 153 : }
903 : :
904 : 75 : pthread_mutex_lock(&te.nlock);
905 [ + + ]: 228 : while (te.ntask < len) {
906 : 153 : pthread_cond_wait(&te.cond, &te.nlock);
907 : 153 : pkg_emit_progress_tick(te.ntask, len);
908 : : }
909 : 75 : pthread_mutex_unlock(&te.nlock);
910 : :
911 [ + + ]: 228 : for (int i = 0; i < num_workers; i++)
912 : 153 : pthread_join(threads[i], NULL);
913 : 75 : free(threads);
914 : 75 : ucl_object_emit_streamline_end_container(te.ctx);
915 : 75 : pkg_emit_progress_tick(len, len);
916 : 75 : ucl_object_emit_streamline_finish(te.ctx);
917 : 75 : ucl_object_emit_funcs_free(f);
918 : 75 : ucl_object_unref(obj);
919 : 75 : ucl_object_unref(ar);
920 : :
921 : : /* Write metafile */
922 : :
923 : 75 : fd = openat(prc->ofd, "meta", O_CREAT|O_TRUNC|O_CLOEXEC|O_WRONLY,
924 : : 0644);
925 [ + - ]: 75 : if (fd != -1) {
926 : 75 : meta_dump = pkg_repo_meta_to_ucl(prc->meta);
927 : 75 : ucl_object_emit_fd(meta_dump, UCL_EMIT_CONFIG, fd);
928 : 75 : close(fd);
929 : 75 : fd = openat(prc->ofd, "meta.conf",
930 : : O_CREAT|O_TRUNC|O_CLOEXEC|O_WRONLY, 0644);
931 [ + - ]: 75 : if (fd != -1) {
932 : 75 : ucl_object_emit_fd(meta_dump, UCL_EMIT_CONFIG, fd);
933 : 75 : close(fd);;
934 : 75 : } else {
935 : 0 : pkg_emit_notice("cannot create metafile at 'meta.conf'");
936 : : }
937 : 75 : ucl_object_unref(meta_dump);
938 : 75 : }
939 : : else {
940 : 0 : pkg_emit_notice("cannot create metafile at 'meta'");
941 : : }
942 : 75 : retcode = EPKG_OK;
943 : : cleanup:
944 [ - + ]: 75 : if (te.mfile != NULL)
945 : 75 : fclose(te.mfile);
946 [ + + ]: 75 : if (te.ffile != NULL)
947 : 1 : fclose(te.ffile);
948 [ - + ]: 75 : if (te.dfile != NULL)
949 : 75 : fclose(te.dfile);
950 [ - + ]: 75 : if (fts != NULL)
951 : 75 : fts_close(fts);
952 : :
953 [ - + - + : 75 : tll_free_and_free(te.fts_items, pkg_create_repo_fts_free);
# # ]
954 : :
955 [ - + ]: 75 : if (retcode != EPKG_OK)
956 : 0 : return (retcode);
957 : :
958 : 75 : return (pkg_repo_create_pack_and_sign(prc));
959 : 75 : }
960 : :
961 : : static int
962 : 6 : pkg_repo_sign(const char *path, char **argv, int argc, char **sig, size_t *siglen,
963 : : char **sigtype, char **cert, size_t *certlen)
964 : : {
965 : : FILE *fps[2];
966 : : char *sha256;
967 : 6 : xstring *cmd = NULL;
968 : 6 : xstring *buf = NULL;
969 : 6 : xstring *sigstr = NULL;
970 : 6 : xstring *certstr = NULL;
971 : 6 : xstring *typestr = NULL;
972 : 6 : char *line = NULL;
973 : 6 : size_t linecap = 0;
974 : : ssize_t linelen;
975 : : pid_t spid;
976 : 6 : int i, pstatus, ret = EPKG_OK;
977 : 6 : bool end_seen = false;
978 : :
979 : 6 : sha256 = pkg_checksum_file(path, PKG_HASH_TYPE_SHA256_HEX);
980 [ + - ]: 6 : if (!sha256)
981 : 0 : return (EPKG_FATAL);
982 : :
983 : 6 : cmd = xstring_new();
984 : :
985 [ + + ]: 18 : for (i = 0; i < argc; i++) {
986 [ - + ]: 12 : if (strspn(argv[i], " \t\n") > 0)
987 : 0 : fprintf(cmd->fp, " \"%s\" ", argv[i]);
988 : : else
989 : 12 : fprintf(cmd->fp, " %s ", argv[i]);
990 : 12 : }
991 : :
992 : 6 : fflush(cmd->fp);
993 [ + - ]: 6 : if ((spid = process_spawn_pipe(fps, cmd->buf)) < 0) {
994 : 0 : ret = EPKG_FATAL;
995 : 0 : goto done;
996 : : }
997 : :
998 : 6 : fprintf(fps[1], "%s\n", sha256);
999 : 6 : fflush(fps[1]);
1000 : :
1001 : 6 : sigstr = xstring_new();
1002 : 6 : certstr = xstring_new();
1003 : 6 : typestr = xstring_new();
1004 : :
1005 [ + + ]: 73 : while ((linelen = getline(&line, &linecap, fps[0])) > 0 ) {
1006 [ + + ]: 71 : if (STREQ(line, "SIGNATURE\n")) {
1007 : 6 : buf = sigstr;
1008 : 6 : continue;
1009 [ + + ]: 65 : } else if (STREQ(line, "CERT\n")) {
1010 : 6 : buf = certstr;
1011 : 6 : continue;
1012 [ + + ]: 59 : } else if (STREQ(line, "TYPE\n")) {
1013 : 2 : buf = typestr;
1014 : 2 : continue;
1015 [ + + ]: 57 : } else if (STREQ(line, "END\n")) {
1016 : 4 : end_seen = true;
1017 : 4 : break;
1018 : : }
1019 [ + - ]: 53 : if (buf != NULL) {
1020 : 53 : fwrite(line, linelen, 1, buf->fp);
1021 : 53 : }
1022 : : }
1023 : 6 : free(line);
1024 : :
1025 : 6 : *sigtype = xstring_get(typestr);
1026 : 6 : *cert = xstring_get_binary(certstr, certlen);
1027 : 6 : *sig = xstring_get_binary(sigstr, siglen);
1028 : :
1029 : : /*
1030 : : * cert could be DER-encoded rather than PEM, so strip off any trailing
1031 : : * END marker if we ran over it.
1032 : : */
1033 [ + + - + : 6 : if (!end_seen && *certlen >= 4 &&
- + ]
1034 : 2 : STREQ(&(*cert)[*certlen - 4], "END\n"))
1035 : 2 : *certlen -= 4;
1036 : :
1037 : : /* remove the latest \n */
1038 : 6 : *siglen -= 1;
1039 : :
1040 : 6 : waitpid(spid, &pstatus, WNOHANG);
1041 : 6 : fclose(fps[0]);
1042 : 6 : fclose(fps[1]);
1043 : :
1044 : : done:
1045 : 6 : free(sha256);
1046 : 6 : xstring_free(cmd);
1047 : :
1048 : 6 : return (ret);
1049 : 6 : }
1050 : :
1051 : : static int
1052 : 8 : pack_sign(struct packing *pack, struct pkgsign_ctx *sctx, const char *path,
1053 : : const char *name)
1054 : : {
1055 : 8 : unsigned char *sigret = NULL;
1056 : : const char *sigtype;
1057 : 8 : size_t siglen = 0;
1058 : : struct iovec iov[2];
1059 : : char buf[32];
1060 : : int offset, size;
1061 : :
1062 [ + - ]: 8 : if (sctx == NULL)
1063 : 0 : return (EPKG_FATAL);
1064 : :
1065 [ - + ]: 8 : if (pkgsign_sign(sctx, path, &sigret, &siglen) != EPKG_OK) {
1066 : 0 : free(sigret);
1067 : 0 : return (EPKG_FATAL);
1068 : : }
1069 : :
1070 : 8 : offset = 0;
1071 : 8 : sigtype = pkgsign_impl_name(sctx);
1072 [ + + ]: 8 : if (!STREQ(sigtype, "rsa")) {
1073 : 4 : size = snprintf(buf, sizeof(buf), "%s%s$", PKGSIGN_HEAD, sigtype);
1074 [ - + ]: 4 : if (size >= sizeof(buf)) {
1075 : 0 : free(sigret);
1076 : 0 : return (EPKG_FATAL);
1077 : : }
1078 : :
1079 : 4 : iov[offset].iov_base = buf;
1080 : 4 : iov[offset++].iov_len = size;
1081 : 4 : }
1082 : :
1083 : 8 : iov[offset].iov_base = sigret;
1084 : 8 : iov[offset++].iov_len = siglen;
1085 : :
1086 [ - + ]: 8 : if (packing_append_iovec(pack, name, iov, offset) != EPKG_OK) {
1087 : 0 : free(sigret);
1088 : 0 : return (EPKG_FATAL);
1089 : : }
1090 : 8 : free(sigret);
1091 : :
1092 : 8 : return (EPKG_OK);
1093 : 8 : }
1094 : :
1095 : : static int
1096 : 6 : pack_command_sign(struct packing *pack, const char *path, char **argv, int argc,
1097 : : const char *name)
1098 : : {
1099 : 6 : size_t pub_len = 0, signature_len = 0;
1100 : : char fname[MAXPATHLEN];
1101 : : char *sig, *sigtype, *pub;
1102 : : char buf[32];
1103 : : struct iovec iov[2];
1104 : : int offset, size;
1105 : :
1106 : 6 : sig = NULL;
1107 : 6 : pub = NULL;
1108 : :
1109 [ - + - + ]: 12 : if (pkg_repo_sign(path, argv, argc, &sig, &signature_len, &sigtype, &pub,
1110 : 6 : &pub_len) != EPKG_OK) {
1111 : 0 : free(sig);
1112 : 0 : free(pub);
1113 : 0 : return (EPKG_FATAL);
1114 : : }
1115 : :
1116 : 6 : offset = 0;
1117 : 6 : snprintf(fname, sizeof(fname), "%s.sig", name);
1118 [ + + - + ]: 6 : if (*sigtype != '\0' && !STREQ(sigtype, "rsa")) {
1119 : : int typelen;
1120 : :
1121 : 2 : typelen = strlen(sigtype);
1122 [ - + ]: 2 : if (sigtype[typelen - 1] == '\n')
1123 : 2 : sigtype[--typelen] = '\0';
1124 : 2 : size = snprintf(buf, sizeof(buf), "%s%s$", PKGSIGN_HEAD, sigtype);
1125 : 2 : free(sigtype);
1126 [ - + ]: 2 : if (size >= sizeof(buf)) {
1127 : 0 : free(sig);
1128 : 0 : free(pub);
1129 : 0 : return (EPKG_FATAL);
1130 : : }
1131 : :
1132 : 2 : iov[offset].iov_base = buf;
1133 : 2 : iov[offset++].iov_len = size;
1134 : 2 : } else {
1135 : 4 : free(sigtype);
1136 : : }
1137 : :
1138 : 6 : iov[offset].iov_base = sig;
1139 : 6 : iov[offset].iov_len = signature_len;
1140 : :
1141 [ - + ]: 6 : if (packing_append_iovec(pack, fname, iov, offset + 1) != EPKG_OK) {
1142 : 0 : free(sig);
1143 : 0 : free(pub);
1144 : 0 : return (EPKG_FATAL);
1145 : : }
1146 : 6 : free(sig);
1147 : :
1148 : 6 : snprintf(fname, sizeof(fname), "%s.pub", name);
1149 : 6 : iov[offset].iov_base = pub;
1150 : 6 : iov[offset].iov_len = pub_len;
1151 [ - + ]: 6 : if (packing_append_iovec(pack, fname, iov, offset + 1) != EPKG_OK) {
1152 : 0 : free(pub);
1153 : 0 : return (EPKG_FATAL);
1154 : : }
1155 : 6 : free(pub);
1156 : :
1157 : 6 : return (EPKG_OK);
1158 : 6 : }
1159 : :
1160 : : static int
1161 : 151 : pkg_repo_pack_db(const char *name, const char *archive, char *path,
1162 : : struct pkgsign_ctx *sctx, struct pkg_repo_create *prc)
1163 : : {
1164 : : struct packing *pack;
1165 : 151 : int ret = EPKG_OK;
1166 : :
1167 [ - + ]: 151 : if (packing_init(&pack, archive, prc->meta->packing_format, 0, 0, (time_t)-1, true, true) != EPKG_OK)
1168 : 0 : return (EPKG_FATAL);
1169 : :
1170 [ + + ]: 151 : if (sctx != NULL) {
1171 : 8 : ret = pack_sign(pack, sctx, path, "signature");
1172 [ + + ]: 151 : } else if (prc->sign.argc >= 1) {
1173 : 6 : ret = pack_command_sign(pack, path, prc->sign.argv, prc->sign.argc, name);
1174 : 6 : }
1175 : 151 : packing_append_file_attr(pack, path, name, "root", "wheel", 0644, 0);
1176 : :
1177 : 151 : packing_finish(pack);
1178 : 151 : unlink(path);
1179 : :
1180 : 151 : return (ret);
1181 : 151 : }
|