XBPS Library API 20240111
The X Binary Package System
download.c
1/*-
2 * Copyright (c) 2009-2013 Juan Romero Pardines
3 * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * From FreeBSD fetch(8):
30 * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.84.2.1 2009/08/03 08:13:06 kensmith Exp $
31 */
32#include <sys/param.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/types.h>
36#include <sys/wait.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <libgen.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <time.h>
45#include <unistd.h>
46
47#include <openssl/sha.h>
48
49#include "xbps_api_impl.h"
50#include "fetch.h"
51#include "compat.h"
52
53/**
54 * @file lib/download.c
55 * @brief Download routines
56 * @defgroup download Download functions
57 *
58 * XBPS download related functions, frontend for NetBSD's libfetch.
59 */
60static const char *
61print_time(time_t *t)
62{
63 struct tm tm;
64 static char buf[255];
65
66 gmtime_r(t, &tm);
67 strftime(buf, sizeof(buf), "%d %b %Y %H:%M", &tm);
68 return buf;
69}
70
71void HIDDEN
72xbps_fetch_set_cache_connection(int global, int per_host)
73{
74 if (global == 0)
75 global = XBPS_FETCH_CACHECONN;
76 if (per_host == 0)
77 per_host = XBPS_FETCH_CACHECONN_HOST;
78
79 fetchConnectionCacheInit(global, per_host);
80}
81
82void HIDDEN
83xbps_fetch_unset_cache_connection(void)
84{
85 fetchConnectionCacheClose();
86}
87
88const char *
90{
91 if (fetchLastErrCode == 0 || fetchLastErrCode == FETCH_OK)
92 return NULL;
93
94 return fetchLastErrString;
95}
96
97int
98xbps_fetch_file_dest_sha256(struct xbps_handle *xhp, const char *uri, const char *filename, const char *flags, unsigned char *digest, size_t digestlen)
99{
100 struct stat st, st_tmpfile, *stp;
101 struct url *url = NULL;
102 struct url_stat url_st;
103 struct fetchIO *fio = NULL;
104 struct timespec ts[2];
105 off_t bytes_dload = 0;
106 ssize_t bytes_read = 0, bytes_written = 0;
107 char buf[4096], *tempfile = NULL;
108 char fetch_flags[8];
109 int fd = -1, rv = 0;
110 bool refetch = false, restart = false;
111 SHA256_CTX sha256;
112
113 assert(xhp);
114 assert(uri);
115
116 if (digest != NULL) {
117 assert(digestlen >= XBPS_SHA256_DIGEST_SIZE);
118 if (digestlen < XBPS_SHA256_DIGEST_SIZE) {
119 errno = ENOBUFS;
120 return -1;
121 }
122 SHA256_Init(&sha256);
123 }
124
125 /* Extern vars declared in libfetch */
126 fetchLastErrCode = 0;
127
128 if (!filename || (url = fetchParseURL(uri)) == NULL)
129 return -1;
130
131 memset(&fetch_flags, 0, sizeof(fetch_flags));
132 if (flags != NULL)
133 xbps_strlcpy(fetch_flags, flags, 7);
134
135 tempfile = xbps_xasprintf("%s.part", filename);
136 /*
137 * Check if we have to resume a transfer.
138 */
139 memset(&st_tmpfile, 0, sizeof(st_tmpfile));
140 if (stat(tempfile, &st_tmpfile) == 0) {
141 if (st_tmpfile.st_size > 0)
142 restart = true;
143 } else {
144 if (errno != ENOENT) {
145 rv = -1;
146 goto fetch_file_out;
147 }
148 }
149 /*
150 * Check if we have to refetch a transfer.
151 */
152 memset(&st, 0, sizeof(st));
153 if (stat(filename, &st) == 0) {
154 refetch = true;
155 url->last_modified = st.st_mtime;
156 xbps_strlcat(fetch_flags, "i", sizeof(fetch_flags));
157 } else {
158 if (errno != ENOENT) {
159 rv = -1;
160 goto fetch_file_out;
161 }
162 }
163 if (refetch && !restart) {
164 /* fetch the whole file, filename available */
165 stp = &st;
166 } else {
167 /* resume transfer, partial file found */
168 stp = &st_tmpfile;
169 url->offset = stp->st_size;
170 }
171 /*
172 * Issue a GET request.
173 */
174 fio = fetchXGet(url, &url_st, fetch_flags);
175
176 /* debug stuff */
177 xbps_dbg_printf("st.st_size: %zd\n", (ssize_t)stp->st_size);
178 xbps_dbg_printf("st.st_atime: %s\n", print_time(&stp->st_atime));
179 xbps_dbg_printf("st.st_mtime: %s\n", print_time(&stp->st_mtime));
180 xbps_dbg_printf("url_stat.size: %zd\n", (ssize_t)url_st.size);
181 xbps_dbg_printf("url_stat.atime: %s\n", print_time(&url_st.atime));
182 xbps_dbg_printf("url_stat.mtime: %s\n", print_time(&url_st.mtime));
183
184 if (fio == NULL) {
185 if (fetchLastErrCode == FETCH_UNCHANGED) {
186 /* Last-Modified matched */
187 goto fetch_file_out;
188 } else if (fetchLastErrCode == FETCH_PROTO && url_st.size == stp->st_size) {
189 /* 413, requested offset == length */
190 goto rename_file;
191 }
192 rv = -1;
193 goto fetch_file_out;
194 }
195 if (url_st.size == -1) {
196 xbps_dbg_printf("Remote file size is unknown, resume "
197 "not possible...\n");
198 restart = false;
199 } else if (stp->st_size > url_st.size) {
200 /*
201 * Remove local file if bigger than remote, and refetch the
202 * whole shit again.
203 */
204 xbps_dbg_printf("Local file %s is greater than remote, "
205 "removing local file and refetching...\n", filename);
206 (void)remove(tempfile);
207 restart = false;
208 }
209 xbps_dbg_printf("url->scheme: %s\n", url->scheme);
210 xbps_dbg_printf("url->host: %s\n", url->host);
211 xbps_dbg_printf("url->port: %d\n", url->port);
212 xbps_dbg_printf("url->doc: %s\n", url->doc);
213 xbps_dbg_printf("url->offset: %zd\n", (ssize_t)url->offset);
214 xbps_dbg_printf("url->length: %zu\n", url->length);
215 xbps_dbg_printf("url->last_modified: %s\n",
216 print_time(&url->last_modified));
217 /*
218 * If restarting, open the file for appending otherwise create it.
219 */
220 if (restart)
221 fd = open(tempfile, O_RDWR|O_CLOEXEC);
222 else
223 fd = open(tempfile, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC, 0644);
224
225 if (fd == -1) {
226 rv = -1;
227 goto fetch_file_out;
228 }
229
230 /*
231 * If restarting and digest was requested, read the current data
232 * and feed into sha256 hash.
233 */
234 if (restart) {
235 if (digest) {
236 while ((bytes_read = read(fd, buf, sizeof(buf))) > 0) {
237 SHA256_Update(&sha256, buf, bytes_read);
238 }
239 if (bytes_read == -1) {
240 xbps_dbg_printf("IO error while reading %s: %s\n",
241 tempfile, strerror(errno));
242 errno = EIO;
243 rv = -1;
244 goto fetch_file_out;
245 }
246 }
247 lseek(fd, 0, SEEK_END);
248 }
249
250 /*
251 * Initialize data for the fetch progress function callback
252 * and let the user know that the transfer is going to start
253 * immediately.
254 */
255 xbps_set_cb_fetch(xhp, url_st.size, url->offset, url->offset,
256 filename, true, false, false);
257 /*
258 * Start fetching requested file.
259 */
260 while ((bytes_read = fetchIO_read(fio, buf, sizeof(buf))) > 0) {
261 if (digest)
262 SHA256_Update(&sha256, buf, bytes_read);
263 bytes_written = write(fd, buf, (size_t)bytes_read);
264 if (bytes_written != bytes_read) {
265 xbps_dbg_printf("Couldn't write to %s!\n", tempfile);
266 rv = -1;
267 goto fetch_file_out;
268 }
269 bytes_dload += bytes_read;
270 /*
271 * Let the fetch progress callback know that
272 * we are sucking more bytes from it.
273 */
274 xbps_set_cb_fetch(xhp, url_st.size, url->offset,
275 url->offset + bytes_dload,
276 filename, false, true, false);
277 }
278 if (bytes_read == -1) {
279 xbps_dbg_printf("IO error while fetching %s: %s\n",
280 filename, fetchLastErrString);
281 errno = EIO;
282 rv = -1;
283 goto fetch_file_out;
284 } else if (url_st.size > 0 && ((bytes_dload + url->offset) != url_st.size)) {
285 xbps_dbg_printf("file %s is truncated\n", filename);
286 errno = EIO;
287 rv = -1;
288 goto fetch_file_out;
289 }
290
291 /*
292 * Let the fetch progress callback know that the file
293 * has been fetched.
294 */
295 xbps_set_cb_fetch(xhp, url_st.size, url->offset, bytes_dload,
296 filename, false, false, true);
297
298 /*
299 * Update mtime in local file to match remote file if transfer
300 * was successful.
301 */
302 ts[0].tv_sec = url_st.atime ? url_st.atime : url_st.mtime;
303 ts[1].tv_sec = url_st.mtime;
304 ts[0].tv_nsec = ts[1].tv_nsec = 0;
305 if (futimens(fd, ts) == -1) {
306 rv = -1;
307 goto fetch_file_out;
308 }
309 (void)close(fd);
310 fd = -1;
311
312rename_file:
313 /* File downloaded successfully, rename to destfile */
314 if (rename(tempfile, filename) == -1) {
315 xbps_dbg_printf("failed to rename %s to %s: %s",
316 tempfile, filename, strerror(errno));
317 rv = -1;
318 goto fetch_file_out;
319 }
320 rv = 1;
321
322 if (digest)
323 SHA256_Final(digest, &sha256);
324
325fetch_file_out:
326 if (fio != NULL)
327 fetchIO_close(fio);
328 if (fd != -1)
329 (void)close(fd);
330 if (url != NULL)
331 fetchFreeURL(url);
332
333 free(tempfile);
334
335 return rv;
336}
337
338int
339xbps_fetch_file_dest(struct xbps_handle *xhp, const char *uri,
340 const char *filename, const char *flags)
341{
342 return xbps_fetch_file_dest_sha256(xhp, uri, filename, flags, NULL, 0);
343}
344
345int
346xbps_fetch_file_sha256(struct xbps_handle *xhp, const char *uri,
347 const char *flags, unsigned char *digest, size_t digestlen)
348{
349 const char *filename;
350 /*
351 * Get the filename specified in URI argument.
352 */
353 if ((filename = strrchr(uri, '/')) == NULL)
354 return -1;
355
356 filename++;
357 return xbps_fetch_file_dest_sha256(xhp, uri, filename, flags,
358 digest, digestlen);
359}
360
361int
362xbps_fetch_file(struct xbps_handle *xhp, const char *uri, const char *flags)
363{
364 return xbps_fetch_file_sha256(xhp, uri, flags, NULL, 0);
365}
int xbps_fetch_file_sha256(struct xbps_handle *xhp, const char *uri, const char *flags, unsigned char *digest, size_t digestlen)
Definition download.c:346
const char * xbps_fetch_error_string(void)
Definition download.c:89
int xbps_fetch_file_dest(struct xbps_handle *xhp, const char *uri, const char *filename, const char *flags)
Definition download.c:339
int xbps_fetch_file(struct xbps_handle *xhp, const char *uri, const char *flags)
Definition download.c:362
int xbps_fetch_file_dest_sha256(struct xbps_handle *xhp, const char *uri, const char *filename, const char *flags, unsigned char *digest, size_t digestlen)
Definition download.c:98
Generic XBPS structure handler for initialization.
Definition xbps.h:550
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
size_t xbps_strlcpy(char *dst, const char *src, size_t dstsize)
Definition util.c:575
size_t xbps_strlcat(char *dst, const char *src, size_t dstsize)
Definition util.c:566