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 : }
|