LCOV - code coverage report
Current view: top level - libpkg - pkg_repo_create.c (source / functions) Hit Total Coverage
Test: plop Lines: 434 669 64.9 %
Date: 2024-12-28 18:40:32 Functions: 19 27 70.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 174 312 55.8 %

           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 : }

Generated by: LCOV version 1.15