fetch_file.c
1 /*- 2 * Copyright (c) 2020-2023 Baptiste Daroussin <bapt@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer 9 * in this position and unchanged. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/stat.h> 27 #include <sys/param.h> 28 29 #include <stdio.h> 30 #include <errno.h> 31 32 #include "pkg.h" 33 #include "private/pkg.h" 34 #include "private/event.h" 35 #include "private/utils.h" 36 #include "private/fetch.h" 37 38 int 39 file_open(struct pkg_repo *repo, struct fetch_item *fi) 40 { 41 struct stat st; 42 const char *u = fi->url; 43 size_t len = strlen(u); 44 45 if (len > 5) 46 u += 5; /* file: */ 47 if (len < 8) { 48 pkg_emit_error("Invalid URL: '%s', " 49 "file://<absolutepath> expected", fi->url); 50 return (EPKG_FATAL); 51 } 52 if (strncmp(u, "//", 2) != 0) { 53 pkg_emit_error("Invalid URL: '%s'", fi->url); 54 return (EPKG_FATAL); 55 } 56 u+=2; 57 /* if we don't have a '/' it means we have a host FQDN component, otherwise just proceed */ 58 /* we can fetch local files only, so we accept the localhost FQDN */ 59 /* TODO: consider accepting gethostname/getdomainname and combinations of these. */ 60 if (*u != '/') { 61 char fqdn[256]=""; 62 char *path = strchr(u+1, '/'); 63 if (path == NULL) { 64 pkg_emit_error("Invalid URL: '%s',\n" 65 "file:///<path> or file://localhost/<path> expected.", fi->url); 66 return (EPKG_FATAL); 67 } 68 strncat(fqdn, u, MIN(255, path-u)); 69 if (0 != strncmp("localhost", fqdn, sizeof(fqdn))) { 70 pkg_emit_error("Invalid URL: '%s'\n" 71 "file:///<path> or file://localhost/<path> expected.", fi->url); 72 return (EPKG_FATAL); 73 } 74 u = path; 75 } 76 if (stat(u, &st) == -1) { 77 if (!repo->silent) 78 pkg_emit_error("%s: %s", fi->url, 79 strerror(errno)); 80 return (EPKG_FATAL); 81 } 82 fi->size = st.st_size; 83 if (st.st_mtime <= fi->mtime) 84 return (EPKG_UPTODATE); 85 86 pkg_dbg(PKG_DBG_FETCH, 1, "mtime: local %ld, remote %ld", st.st_mtime, fi->mtime); 87 repo->fh = fopen(u, "re"); 88 if (repo->fh == NULL) 89 return (EPKG_FATAL); 90 fi->mtime = st.st_mtime; 91 return (EPKG_OK); 92 } 93 94 void 95 fh_close(struct pkg_repo *repo) 96 { 97 if (repo->fh != NULL) 98 fclose(repo->fh); 99 repo->fh = NULL; 100 } 101 102 int 103 stdio_fetch(struct pkg_repo *repo, int dest, struct fetch_item *fi) 104 { 105 char buf[8192]; 106 size_t buflen = 0, left = 0; 107 off_t done = 0, r; 108 109 pkg_emit_fetch_begin(fi->url); 110 pkg_emit_progress_start(NULL); 111 if (fi->offset > 0) 112 done += fi->offset; 113 buflen = sizeof(buf); 114 left = sizeof(buf); 115 if (fi->size > 0) 116 left = fi->size - done; 117 118 while ((r = fread(buf, 1, left < buflen ? left : buflen, repo->fh)) > 0) { 119 if (write(dest, buf, r) != r) { 120 pkg_emit_errno("write", ""); 121 return (EPKG_FATAL); 122 } 123 done += r; 124 if (fi->size > 0) { 125 left -= r; 126 pkg_dbg(PKG_DBG_FETCH, 1, "Read status: %jd over %jd", (intmax_t)done, (intmax_t)fi->size); 127 } else 128 pkg_dbg(PKG_DBG_FETCH, 1, "Read status: %jd", (intmax_t)done); 129 if (fi->size > 0) 130 pkg_emit_progress_tick(done, fi->size); 131 } 132 if (ferror(repo->fh)) { 133 pkg_emit_error("An error occurred while fetching package"); 134 return(EPKG_FATAL); 135 } 136 return (EPKG_OK); 137 }