LCOV - code coverage report
Current view: top level - libpkg - fetch.c (source / functions) Hit Total Coverage
Test: rapport Lines: 128 171 74.9 %
Date: 2021-12-10 16:22:55 Functions: 3 3 100.0 %
Branches: 54 108 50.0 %

           Branch data     Line data    Source code
       1                 :            : /*-
       2                 :            :  * Copyright (c) 2012-2020 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 <fetch.h>
      40                 :            : #include <paths.h>
      41                 :            : #include <poll.h>
      42                 :            : 
      43                 :            : #include <bsd_compat.h>
      44                 :            : 
      45                 :            : #include "pkg.h"
      46                 :            : #include "private/event.h"
      47                 :            : #include "private/pkg.h"
      48                 :            : #include "private/utils.h"
      49                 :            : #include "private/fetch.h"
      50                 :            : 
      51                 :            : static struct fetcher {
      52                 :            :         const char *scheme;
      53                 :            :         int (*open)(struct pkg_repo *, struct url *, off_t *);
      54                 :            : } fetchers [] = {
      55                 :            :         {
      56                 :            :                 "ssh",
      57                 :            :                 ssh_open,
      58                 :            :         },
      59                 :            :         {
      60                 :            :                 "pkg+https",
      61                 :            :                 fetch_open,
      62                 :            :         },
      63                 :            :         {
      64                 :            :                 "pkg+http",
      65                 :            :                 fetch_open,
      66                 :            :         },
      67                 :            :         {
      68                 :            :                 "https",
      69                 :            :                 fetch_open,
      70                 :            :         },
      71                 :            :         {
      72                 :            :                 "http",
      73                 :            :                 fetch_open,
      74                 :            :         },
      75                 :            :         {
      76                 :            :                 "pkg+ftps",
      77                 :            :                 fetch_open,
      78                 :            :         },
      79                 :            :         {
      80                 :            :                 "pkg+ftp",
      81                 :            :                 fetch_open,
      82                 :            :         },
      83                 :            :         {
      84                 :            :                 "ftps",
      85                 :            :                 fetch_open,
      86                 :            :         },
      87                 :            :         {
      88                 :            :                 "ftp",
      89                 :            :                 fetch_open,
      90                 :            :         },
      91                 :            :         {
      92                 :            :                 "file",
      93                 :            :                 file_open,
      94                 :            :         },
      95                 :            : };
      96                 :            : 
      97                 :            : 
      98                 :            : int
      99                 :          4 : pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
     100                 :            :         time_t t)
     101                 :            : {
     102                 :          4 :         int fd = -1;
     103                 :          4 :         int retcode = EPKG_FATAL;
     104                 :            : 
     105                 :          4 :         fd = mkstemp(dest);
     106                 :            : 
     107         [ -  + ]:          4 :         if (fd == -1) {
     108                 :          0 :                 pkg_emit_errno("mkstemp", dest);
     109                 :          0 :                 return(EPKG_FATAL);
     110                 :            :         }
     111                 :            : 
     112                 :          4 :         retcode = pkg_fetch_file_to_fd(repo, url, fd, &t, 0, -1, false);
     113                 :            : 
     114         [ -  + ]:          4 :         if (t != 0) {
     115                 :          8 :                 struct timeval ftimes[2] = {
     116                 :          8 :                         {
     117                 :          4 :                         .tv_sec = t,
     118                 :            :                         .tv_usec = 0
     119                 :            :                         },
     120                 :          8 :                         {
     121                 :          4 :                         .tv_sec = t,
     122                 :            :                         .tv_usec = 0
     123                 :            :                         }
     124                 :            :                 };
     125                 :          4 :                 futimes(fd, ftimes);
     126                 :          4 :         }
     127                 :            : 
     128                 :          4 :         close(fd);
     129                 :            : 
     130                 :            :         /* Remove local file if fetch failed */
     131         [ +  - ]:          4 :         if (retcode != EPKG_OK)
     132                 :          0 :                 unlink(dest);
     133                 :            : 
     134                 :          4 :         return (retcode);
     135                 :          4 : }
     136                 :            : 
     137                 :            : int
     138                 :          4 : pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
     139                 :            :     ssize_t offset, int64_t size)
     140                 :            : {
     141                 :          4 :         int fd = -1;
     142                 :          4 :         int retcode = EPKG_FATAL;
     143                 :            : 
     144                 :          4 :         fd = open(dest, O_CREAT|O_APPEND|O_WRONLY, 00644);
     145         [ -  + ]:          4 :         if (fd == -1) {
     146                 :          0 :                 pkg_emit_errno("open", dest);
     147                 :          0 :                 return(EPKG_FATAL);
     148                 :            :         }
     149                 :            : 
     150                 :          4 :         retcode = pkg_fetch_file_to_fd(repo, url, fd, &t, offset, size, false);
     151                 :            : 
     152         [ -  + ]:          4 :         if (t != 0) {
     153                 :          8 :                 struct timeval ftimes[2] = {
     154                 :          8 :                         {
     155                 :          4 :                         .tv_sec = t,
     156                 :            :                         .tv_usec = 0
     157                 :            :                         },
     158                 :          8 :                         {
     159                 :          4 :                         .tv_sec = t,
     160                 :            :                         .tv_usec = 0
     161                 :            :                         }
     162                 :            :                 };
     163                 :          4 :                 futimes(fd, ftimes);
     164                 :          4 :         }
     165                 :            : 
     166                 :          4 :         close(fd);
     167                 :            : 
     168                 :            :         /* Remove local file if fetch failed */
     169         [ +  - ]:          4 :         if (retcode != EPKG_OK)
     170                 :          0 :                 unlink(dest);
     171                 :            : 
     172                 :          4 :         return (retcode);
     173                 :          4 : }
     174                 :            : 
     175                 :            : #define URL_SCHEME_PREFIX       "pkg+"
     176                 :            : 
     177                 :            : int
     178                 :       1011 : pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
     179                 :            :     time_t *t, ssize_t offset, int64_t size, bool silent)
     180                 :            : {
     181                 :       1011 :         struct url      *u = NULL;
     182                 :            :         struct pkg_kv   *kv, *kvtmp;
     183                 :       1011 :         struct pkg_kv   *envtorestore = NULL;
     184                 :       1011 :         struct pkg_kv   *envtounset = NULL;
     185                 :            :         char            *tmp;
     186                 :       1011 :         off_t            done = 0;
     187                 :            :         off_t            r;
     188                 :            :         char             buf[8192];
     189                 :       1011 :         int              retcode = EPKG_OK;
     190                 :       1011 :         off_t            sz = 0;
     191                 :       1011 :         size_t           buflen = 0;
     192                 :       1011 :         size_t           left = 0;
     193                 :       1011 :         struct fetcher  *fetcher = NULL;
     194                 :       1011 :         struct pkg_repo *fakerepo = NULL;
     195                 :            : 
     196                 :       1011 :         FILE *remote = NULL;
     197                 :            : 
     198                 :            :         /* A URL of the form http://host.example.com/ where
     199                 :            :          * host.example.com does not resolve as a simple A record is
     200                 :            :          * not valid according to RFC 2616 Section 3.2.2.  Our usage
     201                 :            :          * with SRV records is incorrect.  However it is encoded into
     202                 :            :          * /usr/sbin/pkg in various releases so we can't just drop it.
     203                 :            :          *
     204                 :            :          * Instead, introduce new pkg+http://, pkg+https://,
     205                 :            :          * pkg+ssh://, pkg+ftp://, pkg+file:// to support the
     206                 :            :          * SRV-style server discovery, and also to allow eg. Firefox
     207                 :            :          * to run pkg-related stuff given a pkg+foo:// URL.
     208                 :            :          *
     209                 :            :          * Error if using plain http://, https:// etc with SRV
     210                 :            :          */
     211                 :            : 
     212                 :       1011 :         pkg_debug(1, "Request to fetch %s", url);
     213   [ +  +  +  - ]:       1011 :         if (repo != NULL &&
     214                 :       1007 :                 strncmp(URL_SCHEME_PREFIX, url, strlen(URL_SCHEME_PREFIX)) == 0) {
     215         [ #  # ]:          0 :                 if (repo->mirror_type != SRV) {
     216                 :          0 :                         pkg_emit_error("packagesite URL error for %s -- "
     217                 :            :                                        URL_SCHEME_PREFIX
     218                 :          0 :                                        ":// implies SRV mirror type", url);
     219                 :            : 
     220                 :            :                         /* Too early for there to be anything to cleanup */
     221                 :          0 :                         return(EPKG_FATAL);
     222                 :            :                 }
     223                 :            : 
     224                 :          0 :                 url += strlen(URL_SCHEME_PREFIX);
     225                 :          0 :                 u = fetchParseURL(url);
     226                 :          0 :         }
     227                 :            : 
     228         [ -  + ]:       1011 :         if (u == NULL)
     229                 :       1011 :                 u = fetchParseURL(url);
     230                 :            : 
     231         [ +  - ]:       1011 :         if (offset > 0)
     232                 :          0 :                 u->offset = offset;
     233                 :            : 
     234         [ +  + ]:       1011 :         if (repo != NULL) {
     235                 :       1007 :                 repo->silent = silent;
     236         [ -  + ]:       1007 :                 LL_FOREACH(repo->env, kv) {
     237                 :          0 :                         kvtmp = xcalloc(1, sizeof(*kvtmp));
     238                 :          0 :                         kvtmp->key = xstrdup(kv->key);
     239         [ #  # ]:          0 :                         if ((tmp = getenv(kv->key)) != NULL) {
     240                 :          0 :                                 kvtmp->value = xstrdup(tmp);
     241         [ #  # ]:          0 :                                 DL_APPEND(envtorestore, kvtmp);
     242                 :          0 :                         } else {
     243         [ #  # ]:          0 :                                 DL_APPEND(envtounset, kvtmp);
     244                 :            :                         }
     245                 :          0 :                         setenv(kv->key, kv->value, 1);
     246                 :          0 :                 }
     247                 :       1007 :         } else {
     248                 :          4 :                 fakerepo = xcalloc(1, sizeof(struct pkg_repo));
     249                 :          4 :                 fakerepo->url = xstrdup(url);
     250                 :          4 :                 repo = fakerepo;
     251                 :            :         }
     252                 :            : 
     253         [ +  - ]:       1011 :         if (u == NULL) {
     254                 :          0 :                 pkg_emit_error("%s: parse error", url);
     255                 :            :                 /* Too early for there to be anything to cleanup */
     256                 :          0 :                 return(EPKG_FATAL);
     257                 :            :         }
     258                 :            : 
     259         [ -  + ]:       1011 :         if (t != NULL)
     260                 :       1011 :                 u->ims_time = *t;
     261                 :            : 
     262         [ -  + ]:       9970 :         for (int i = 0; i < nitems(fetchers); i++) {
     263         [ +  + ]:       9970 :                 if (strcmp(u->scheme, fetchers[i].scheme) == 0) {
     264                 :       1011 :                         fetcher = &fetchers[i];
     265         [ +  + ]:       1011 :                         if ((retcode = fetcher->open(repo, u, &sz)) != EPKG_OK)
     266                 :         28 :                                 goto cleanup;
     267         [ -  + ]:        983 :                         remote = repo->ssh ? repo->ssh : repo->fh;
     268                 :        983 :                         break;
     269                 :            :                 }
     270                 :       8959 :         }
     271         [ +  - ]:        983 :         if (fetcher == NULL) {
     272                 :          0 :                 pkg_emit_error("Unknown scheme: %s", u->scheme);
     273                 :          0 :                 return (EPKG_FATAL);
     274                 :            :         }
     275                 :        983 :         pkg_debug(1, "Fetch: fetcher chosen: %s", fetcher->scheme);
     276                 :            : 
     277         [ -  + ]:        983 :         if (strcmp(u->scheme, "ssh") != 0) {
     278   [ +  -  -  + ]:        983 :                 if (t != NULL && u->ims_time != 0) {
     279         [ +  + ]:        983 :                         if (u->ims_time <= *t) {
     280                 :        489 :                                 retcode = EPKG_UPTODATE;
     281                 :        489 :                                 goto cleanup;
     282                 :            :                         } else
     283                 :        494 :                                 *t = u->ims_time;
     284                 :        494 :                 }
     285                 :        494 :         }
     286                 :            : 
     287   [ -  +  #  # ]:        494 :         if (sz <= 0 && size > 0)
     288                 :          0 :                 sz = size;
     289                 :            : 
     290                 :        494 :         pkg_emit_fetch_begin(url);
     291                 :        494 :         pkg_emit_progress_start(NULL);
     292         [ -  + ]:        494 :         if (offset > 0)
     293                 :          0 :                 done += offset;
     294                 :        494 :         buflen = sizeof(buf);
     295                 :        494 :         left = sizeof(buf);
     296         [ +  - ]:        494 :         if (sz > 0)
     297                 :        494 :                 left = sz - done;
     298   [ +  -  +  + ]:        988 :         while ((r = fread(buf, 1, left < buflen ? left : buflen, remote)) > 0) {
     299         [ +  - ]:        494 :                 if (write(dest, buf, r) != r) {
     300                 :          0 :                         pkg_emit_errno("write", "");
     301                 :          0 :                         retcode = EPKG_FATAL;
     302                 :          0 :                         goto cleanup;
     303                 :            :                 }
     304                 :        494 :                 done += r;
     305         [ -  + ]:        494 :                 if (sz > 0) {
     306                 :        494 :                         left -= r;
     307                 :        494 :                         pkg_debug(4, "Read status: %jd over %jd", (intmax_t)done, (intmax_t)sz);
     308                 :        494 :                 } else
     309                 :          0 :                         pkg_debug(4, "Read status: %jd", (intmax_t)done);
     310         [ -  + ]:        494 :                 if (sz > 0)
     311                 :        494 :                         pkg_emit_progress_tick(done, sz);
     312                 :            :         }
     313                 :            : 
     314         [ -  + ]:        494 :         if (r != 0) {
     315                 :          0 :                 pkg_emit_error("An error occurred while fetching package");
     316                 :          0 :                 retcode = EPKG_FATAL;
     317                 :          0 :                 goto cleanup;
     318                 :            :         } else {
     319                 :        494 :                 pkg_emit_progress_tick(done, done);
     320                 :            :         }
     321                 :        494 :         pkg_emit_fetch_finished(url);
     322                 :            : 
     323   [ -  +  +  -  :        494 :         if (strcmp(u->scheme, "ssh") != 0 && ferror(remote)) {
                   +  + ]
     324                 :        988 :                 pkg_emit_error("%s: %s", url, fetchLastErrString);
     325                 :        988 :                 retcode = EPKG_FATAL;
     326                 :        988 :                 goto cleanup;
     327                 :            :         }
     328                 :            : 
     329                 :            : cleanup:
     330         [ -  + ]:       1011 :         if (repo != NULL) {
     331   [ +  -  -  + ]:       1011 :                 LL_FOREACH_SAFE(envtorestore, kv, kvtmp) {
     332                 :          0 :                         setenv(kv->key, kv->value, 1);
     333   [ #  #  #  #  :          0 :                         LL_DELETE(envtorestore, kv);
             #  #  #  # ]
     334                 :          0 :                         pkg_kv_free(kv);
     335                 :          0 :                 }
     336   [ +  -  -  + ]:       1011 :                 LL_FOREACH_SAFE(envtounset, kv, kvtmp) {
     337                 :          0 :                         unsetenv(kv->key);
     338                 :          0 :                         pkg_kv_free(kv);
     339                 :          0 :                 }
     340                 :       1011 :         }
     341                 :            : 
     342         [ -  + ]:       1011 :         if (u != NULL) {
     343   [ +  +  +  -  :       1011 :                 if (remote != NULL &&  repo != NULL && remote != repo->ssh) {
                   -  + ]
     344                 :        983 :                         fclose(remote);
     345                 :        983 :                         repo->fh = NULL;
     346                 :        983 :                 }
     347                 :       1011 :         }
     348                 :       1011 :         free(fakerepo);
     349                 :            : 
     350         [ +  + ]:       1011 :         if (retcode == EPKG_OK) {
     351                 :        988 :                 struct timeval ftimes[2] = {
     352                 :        988 :                         {
     353                 :        494 :                         .tv_sec = *t,
     354                 :            :                         .tv_usec = 0
     355                 :            :                         },
     356                 :        988 :                         {
     357                 :        494 :                         .tv_sec = *t,
     358                 :            :                         .tv_usec = 0
     359                 :            :                         }
     360                 :            :                 };
     361                 :        494 :                 futimes(dest, ftimes);
     362                 :        494 :         }
     363                 :            : 
     364                 :            :         /* restore original doc */
     365                 :       1011 :         fetchFreeURL(u);
     366                 :            : 
     367                 :       1011 :         return (retcode);
     368                 :       1011 : }

Generated by: LCOV version 1.15