LCOV - code coverage report
Current view: top level - libpkg - fetch.c (source / functions) Hit Total Coverage
Test: plop Lines: 94 132 71.2 %
Date: 2024-12-28 18:40:32 Functions: 4 4 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 36 104 34.6 %

           Branch data     Line data    Source code
       1                 :            : /*-
       2                 :            :  * Copyright (c) 2012-2023 Baptiste Daroussin <bapt@FreeBSD.org>
       3                 :            :  * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
       4                 :            :  * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
       5                 :            :  * All rights reserved.
       6                 :            :  *
       7                 :            :  * Redistribution and use in source and binary forms, with or without
       8                 :            :  * modification, are permitted provided that the following conditions
       9                 :            :  * are met:
      10                 :            :  * 1. Redistributions of source code must retain the above copyright
      11                 :            :  *    notice, this list of conditions and the following disclaimer
      12                 :            :  *    in this position and unchanged.
      13                 :            :  * 2. Redistributions in binary form must reproduce the above copyright
      14                 :            :  *    notice, this list of conditions and the following disclaimer in the
      15                 :            :  *    documentation and/or other materials provided with the distribution.
      16                 :            :  *
      17                 :            :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
      18                 :            :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      19                 :            :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      20                 :            :  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
      21                 :            :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      22                 :            :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      23                 :            :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      24                 :            :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      25                 :            :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
      26                 :            :  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      27                 :            :  */
      28                 :            : 
      29                 :            : #include <sys/param.h>
      30                 :            : #include <sys/wait.h>
      31                 :            : #include <sys/socket.h>
      32                 :            : #include <sys/time.h>
      33                 :            : 
      34                 :            : #include <ctype.h>
      35                 :            : #include <fcntl.h>
      36                 :            : #include <errno.h>
      37                 :            : #include <stdio.h>
      38                 :            : #include <string.h>
      39                 :            : #include <paths.h>
      40                 :            : #include <poll.h>
      41                 :            : 
      42                 :            : #include <bsd_compat.h>
      43                 :            : 
      44                 :            : #include "pkg.h"
      45                 :            : #include "private/event.h"
      46                 :            : #include "private/pkg.h"
      47                 :            : #include "private/utils.h"
      48                 :            : #include "private/fetch.h"
      49                 :            : 
      50                 :            : static struct fetcher fetchers [] = {
      51                 :            :         {
      52                 :            :                 "tcp",
      53                 :            :                 0,
      54                 :            :                 tcp_open,
      55                 :            :                 NULL,
      56                 :            :                 fh_close,
      57                 :            :                 stdio_fetch,
      58                 :            :         },
      59                 :            :         {
      60                 :            :                 "ssh",
      61                 :            :                 0,
      62                 :            :                 ssh_open,
      63                 :            :                 NULL,
      64                 :            :                 fh_close,
      65                 :            :                 stdio_fetch,
      66                 :            :         },
      67                 :            :         {
      68                 :            :                 "pkg+https",
      69                 :            :                 0,
      70                 :            :                 curl_open,
      71                 :            :                 NULL,
      72                 :            :                 curl_cleanup,
      73                 :            :                 curl_fetch,
      74                 :            :         },
      75                 :            :         {
      76                 :            :                 "pkg+http",
      77                 :            :                 0,
      78                 :            :                 curl_open,
      79                 :            :                 NULL,
      80                 :            :                 curl_cleanup,
      81                 :            :                 curl_fetch,
      82                 :            :         },
      83                 :            :         {
      84                 :            :                 "https",
      85                 :            :                 0,
      86                 :            :                 curl_open,
      87                 :            :                 NULL,
      88                 :            :                 curl_cleanup,
      89                 :            :                 curl_fetch,
      90                 :            :         },
      91                 :            :         {
      92                 :            :                 "http",
      93                 :            :                 0,
      94                 :            :                 curl_open,
      95                 :            :                 NULL,
      96                 :            :                 curl_cleanup,
      97                 :            :                 curl_fetch,
      98                 :            :         },
      99                 :            :         {
     100                 :            :                 "file",
     101                 :            :                 0,
     102                 :            :                 file_open,
     103                 :            :                 fh_close,
     104                 :            :                 NULL,
     105                 :            :                 stdio_fetch,
     106                 :            :         },
     107                 :            : };
     108                 :            : 
     109                 :            : int
     110                 :          1 : pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
     111                 :            :         time_t t)
     112                 :            : {
     113                 :          1 :         int fd = -1;
     114                 :          1 :         int retcode = EPKG_FATAL;
     115                 :            :         struct fetch_item fi;
     116                 :            : 
     117                 :          1 :         memset(&fi, 0, sizeof(struct fetch_item));
     118                 :            : 
     119                 :          1 :         fd = mkstemp(dest);
     120                 :            : 
     121         [ +  - ]:          1 :         if (fd == -1) {
     122                 :          0 :                 pkg_emit_errno("mkstemp", dest);
     123                 :          0 :                 return(EPKG_FATAL);
     124                 :            :         }
     125                 :            : 
     126                 :          1 :         fi.url = url;
     127                 :          1 :         fi.mtime = t;
     128                 :          1 :         retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
     129                 :            : 
     130         [ -  + ]:          1 :         if (fi.mtime != 0) {
     131                 :          1 :                 struct timespec ts[2] = {
     132                 :          2 :                         {
     133                 :          1 :                         .tv_sec = fi.mtime,
     134                 :            :                         .tv_nsec = 0
     135                 :            :                         },
     136                 :          2 :                         {
     137                 :          1 :                         .tv_sec = fi.mtime,
     138                 :            :                         .tv_nsec = 0
     139                 :            :                         }
     140                 :            :                 };
     141                 :          1 :                 futimens(fd, ts);
     142                 :          1 :         }
     143                 :            : 
     144                 :          1 :         close(fd);
     145                 :            : 
     146                 :            :         /* Remove local file if fetch failed */
     147         [ +  - ]:          1 :         if (retcode != EPKG_OK)
     148                 :          0 :                 unlink(dest);
     149                 :            : 
     150                 :          1 :         return (retcode);
     151                 :          1 : }
     152                 :            : 
     153                 :            : int
     154                 :          2 : pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
     155                 :            :     ssize_t offset, int64_t size)
     156                 :            : {
     157                 :          2 :         int fd = -1;
     158                 :          2 :         int retcode = EPKG_FATAL;
     159                 :            :         struct fetch_item fi;
     160                 :          2 :         char *url_to_free = NULL;
     161                 :            : 
     162                 :          2 :         fd = open(dest, O_CREAT|O_APPEND|O_WRONLY, 00644);
     163         [ +  - ]:          2 :         if (fd == -1) {
     164                 :          0 :                 pkg_emit_errno("open", dest);
     165                 :          0 :                 return(EPKG_FATAL);
     166                 :            :         }
     167                 :            : 
     168         [ +  - ]:          2 :         if (repo != NULL) {
     169                 :          2 :                 xasprintf(&url_to_free, "%s/%s", repo->url, url);
     170                 :          2 :                 fi.url = url_to_free;
     171                 :          2 :         } else {
     172                 :          0 :                 fi.url = url;
     173                 :            :         }
     174                 :            : 
     175                 :          2 :         fi.offset = offset;
     176                 :          2 :         fi.size = size;
     177                 :          2 :         fi.mtime = t;
     178                 :            : 
     179                 :          2 :         retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
     180                 :          2 :         free(url_to_free);
     181                 :            : 
     182         [ +  - ]:          2 :         if (t != 0) {
     183                 :          0 :                 struct timespec ts[2] = {
     184                 :          0 :                         {
     185                 :          0 :                         .tv_sec = t,
     186                 :            :                         .tv_nsec = 0
     187                 :            :                         },
     188                 :          0 :                         {
     189                 :          0 :                         .tv_sec = t,
     190                 :            :                         .tv_nsec = 0
     191                 :            :                         }
     192                 :            :                 };
     193                 :          0 :                 futimens(fd, ts);
     194                 :          0 :         }
     195                 :          2 :         close(fd);
     196                 :            : 
     197                 :            :         /* Remove local file if fetch failed */
     198         [ +  - ]:          2 :         if (retcode != EPKG_OK)
     199                 :          0 :                 unlink(dest);
     200                 :            : 
     201                 :          2 :         return (retcode);
     202                 :          2 : }
     203                 :            : 
     204                 :            : #define URL_SCHEME_PREFIX       "pkg+"
     205                 :            : 
     206                 :            : static struct fetcher *
     207                 :        135 : select_fetcher(const char *url)
     208                 :            : {
     209                 :            :         struct fetcher *f;
     210                 :            :         size_t nsz;
     211                 :            : 
     212         [ +  - ]:        940 :         for (size_t i = 0; i < NELEM(fetchers); i++) {
     213                 :        940 :                 nsz = strlen(fetchers[i].scheme);
     214                 :            : 
     215   [ +  +  +  - ]:        940 :                 if ((strncasecmp(url, fetchers[i].scheme, nsz) == 0) &&
     216                 :        135 :                     url[nsz] == ':') {
     217                 :        135 :                         f = &fetchers[i];
     218                 :        135 :                         f->timeout =
     219                 :        135 :                                 pkg_object_int(pkg_config_get("FETCH_TIMEOUT"));
     220                 :        135 :                         return (f);
     221                 :            :                 }
     222                 :        805 :         }
     223                 :          0 :         return (NULL);
     224                 :            : 
     225                 :        135 : }
     226                 :            : int
     227                 :        300 : pkg_fetch_file_to_fd(struct pkg_repo *repo, int dest, struct fetch_item *fi,
     228                 :            :     bool silent)
     229                 :            : {
     230                 :            :         struct pkg_kv   *kv;
     231                 :        300 :         kvlist_t         envtorestore = tll_init();
     232                 :        300 :         stringlist_t     envtounset = tll_init();
     233                 :            :         char            *tmp;
     234                 :        300 :         int              retcode = EPKG_OK;
     235                 :        300 :         struct pkg_repo *fakerepo = NULL;
     236                 :            :         size_t           nsz;
     237                 :            : 
     238                 :            :         /* A URL of the form http://host.example.com/ where
     239                 :            :          * host.example.com does not resolve as a simple A record is
     240                 :            :          * not valid according to RFC 2616 Section 3.2.2.  Our usage
     241                 :            :          * with SRV records is incorrect.  However it is encoded into
     242                 :            :          * /usr/sbin/pkg in various releases so we can't just drop it.
     243                 :            :          *
     244                 :            :          * Instead, introduce new pkg+http://, pkg+https://,
     245                 :            :          * pkg+ssh://, pkg+file:// to support the
     246                 :            :          * SRV-style server discovery, and also to allow eg. Firefox
     247                 :            :          * to run pkg-related stuff given a pkg+foo:// URL.
     248                 :            :          *
     249                 :            :          * Error if using plain http://, https:// etc with SRV
     250                 :            :          */
     251                 :            : 
     252                 :        300 :         pkg_dbg(PKG_DBG_FETCH, 1, "Request to fetch %s", fi->url);
     253         [ +  + ]:        300 :         if (repo == NULL) {
     254                 :          1 :                 fakerepo = xcalloc(1, sizeof(struct pkg_repo));
     255                 :          1 :                 fakerepo->url = xstrdup(fi->url);
     256                 :          1 :                 fakerepo->mirror_type = NOMIRROR;
     257                 :          1 :                 repo = fakerepo;
     258                 :          1 :         }
     259                 :            : 
     260         [ +  + ]:        300 :         if (repo->fetcher == NULL)
     261                 :        135 :                 repo->fetcher = select_fetcher(fi->url);
     262         [ +  - ]:        300 :         if (repo->fetcher == NULL) {
     263                 :          0 :                 pkg_emit_error("Unknown scheme: %s", fi->url);
     264                 :          0 :                 return (EPKG_FATAL);
     265                 :            :         }
     266                 :            : 
     267                 :        300 :         nsz = strlen(URL_SCHEME_PREFIX);
     268         [ +  - ]:        300 :         if (strncasecmp(URL_SCHEME_PREFIX, fi->url, nsz) == 0) {
     269         [ #  # ]:          0 :                 if (repo->mirror_type != SRV) {
     270                 :          0 :                         pkg_emit_error("packagesite URL error for %s -- "
     271                 :            :                                         URL_SCHEME_PREFIX
     272                 :          0 :                                         ":// implies SRV mirror type", fi->url);
     273                 :            : 
     274                 :            :                         /* Too early for there to be anything to cleanup */
     275                 :          0 :                         return(EPKG_FATAL);
     276                 :            :                 }
     277                 :          0 :                 fi->url += nsz;
     278                 :          0 :         }
     279                 :            : 
     280                 :        300 :         repo->silent = silent;
     281   [ -  +  -  +  :        300 :         tll_foreach(repo->env, k) {
                   #  # ]
     282         [ #  # ]:          0 :                 if ((tmp = getenv(k->item->key)) != NULL) {
     283                 :          0 :                         kv = xcalloc(1, sizeof(*kv));
     284                 :          0 :                         kv->key = xstrdup(k->item->key);
     285                 :          0 :                         kv->value = xstrdup(tmp);
     286   [ #  #  #  #  :          0 :                         tll_push_back(envtorestore, kv);
          #  #  #  #  #  
                      # ]
     287                 :          0 :                 } else {
     288   [ #  #  #  #  :          0 :                         tll_push_back(envtounset, k->item->key);
          #  #  #  #  #  
                      # ]
     289                 :            :                 }
     290                 :          0 :                 setenv(k->item->key, k->item->value, 1);
     291                 :          0 :         }
     292                 :            : 
     293         [ +  + ]:        300 :         if ((retcode = repo->fetcher->open(repo, fi)) != EPKG_OK)
     294                 :        146 :                 goto cleanup;
     295                 :        154 :         pkg_dbg(PKG_DBG_FETCH, 1, "Fetch: fetcher used: %s", repo->fetcher->scheme);
     296                 :            : 
     297                 :        154 :         retcode = repo->fetcher->fetch(repo, dest, fi);
     298         [ +  + ]:        304 :         if (retcode == EPKG_OK)
     299                 :        150 :                 pkg_emit_fetch_finished(fi->url);
     300                 :            : 
     301                 :            : cleanup:
     302   [ -  +  -  +  :        300 :         tll_foreach(envtorestore, k) {
                   #  # ]
     303                 :          0 :                 setenv(k->item->key, k->item->value, 1);
     304   [ #  #  #  #  :          0 :                 tll_remove_and_free(envtorestore, k, pkg_kv_free);
                   #  # ]
     305                 :          0 :         }
     306   [ -  +  -  +  :        300 :         tll_free(envtorestore);
                   #  # ]
     307   [ -  +  -  +  :        300 :         tll_foreach(envtounset, k) {
                   #  # ]
     308                 :          0 :                 unsetenv(k->item);
     309   [ #  #  #  #  :          0 :                 tll_remove(envtounset, k);
                   #  # ]
     310                 :          0 :         }
     311   [ -  +  -  +  :        300 :         tll_free(envtounset);
                   #  # ]
     312                 :            : 
     313   [ +  -  +  + ]:        300 :         if (repo->fetcher != NULL && repo->fetcher->close != NULL)
     314                 :        289 :                 repo->fetcher->close(repo);
     315                 :        300 :         free(fakerepo);
     316                 :            : 
     317         [ +  + ]:        300 :         if (retcode == EPKG_OK) {
     318                 :        150 :                 struct timespec ts[2] = {
     319                 :        300 :                         {
     320                 :        150 :                         .tv_sec = fi->mtime,
     321                 :            :                         .tv_nsec = 0
     322                 :            :                         },
     323                 :        300 :                         {
     324                 :        150 :                         .tv_sec = fi->mtime,
     325                 :            :                         .tv_nsec = 0
     326                 :            :                         }
     327                 :            :                 };
     328                 :        150 :                 futimens(dest, ts);
     329                 :        150 :         }
     330                 :            : 
     331                 :        300 :         return (retcode);
     332                 :        300 : }

Generated by: LCOV version 1.15