/ libpkg / fetch_file.c
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  }