xref: /netbsd-src/external/bsd/pkg_install/dist/lib/pkg_io.c (revision 1b2611421270f128b12bc59de682905e00d5645e)
1 /*	$NetBSD: pkg_io.c,v 1.5 2024/06/11 09:26:57 wiz Exp $	*/
2 /*-
3  * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
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  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #if HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 #include <nbcompat.h>
35 #if HAVE_SYS_CDEFS_H
36 #include <sys/cdefs.h>
37 #endif
38 
39 __RCSID("$NetBSD: pkg_io.c,v 1.5 2024/06/11 09:26:57 wiz Exp $");
40 
41 #include <archive.h>
42 #include <archive_entry.h>
43 #if HAVE_ERR_H
44 #include <err.h>
45 #endif
46 #if HAVE_ERRNO_H
47 #include <errno.h>
48 #endif
49 #include <stdlib.h>
50 
51 #include "lib.h"
52 
53 #ifdef BOOTSTRAP
54 #undef IS_URL
55 #define IS_URL(x) 0
56 #else
57 #include <fetch.h>
58 #endif
59 
60 struct pkg_path {
61 	TAILQ_ENTRY(pkg_path) pl_link;
62 	char *pl_path;
63 };
64 
65 static char *orig_cwd, *last_toplevel;
66 static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path);
67 
68 #ifndef BOOTSTRAP
69 struct fetch_archive {
70 	struct url *url;
71 	fetchIO *fetch;
72 	char buffer[32768];
73 	off_t size;
74 	int restart;
75 };
76 
77 static int
fetch_archive_open(struct archive * a,void * client_data)78 fetch_archive_open(struct archive *a, void *client_data)
79 {
80 	struct fetch_archive *f = client_data;
81 	struct url_stat us;
82 
83 	f->fetch = fetchXGet(f->url, &us, fetch_flags);
84 	if (f->fetch == NULL)
85 		return ENOENT;
86 	f->size = us.size;
87 	f->restart = 1;
88 	f->url->offset = 0;
89 	return 0;
90 }
91 
92 static ssize_t
fetch_archive_read(struct archive * a,void * client_data,const void ** buffer)93 fetch_archive_read(struct archive *a, void *client_data,
94     const void **buffer)
95 {
96 	struct fetch_archive *f = client_data;
97 	struct url_stat us;
98 	ssize_t rv;
99 
100 	*buffer = f->buffer;
101 	rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
102 	if (rv > 0) {
103 		f->url->offset += rv;
104 		return rv;
105 	}
106 	if (f->restart == 0)
107 		return rv;
108 	if (rv == 0) {
109 		if (f->size == -1)
110 			return 0;
111 		if (f->url->offset == f->size)
112 			return 0;
113 	}
114 	f->restart = 0;
115 	if (1) {
116 		char *url = fetchStringifyURL(f->url);
117 		fprintf(stderr, "Trying to reconnect %s\n", url);
118 		free(url);
119 	}
120 	fetchIO_close(f->fetch);
121 	f->fetch = fetchXGet(f->url, &us, fetch_flags);
122 	if (f->fetch == NULL)
123 		return -1;
124 	if (us.size != f->size)
125 		return -1;
126 	rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
127 	if (rv > 0)
128 		f->url->offset += rv;
129 	return rv;
130 }
131 
132 static int
fetch_archive_close(struct archive * a,void * client_data)133 fetch_archive_close(struct archive *a, void *client_data)
134 {
135 	struct fetch_archive *f = client_data;
136 
137 	if (f->fetch != NULL)
138 		fetchIO_close(f->fetch);
139 	fetchFreeURL(f->url);
140 	free(f);
141 	return 0;
142 }
143 
144 static struct archive *
open_archive_by_url(struct url * url,char ** archive_name)145 open_archive_by_url(struct url *url, char **archive_name)
146 {
147 	struct fetch_archive *f;
148 	struct archive *a;
149 
150 	f = xmalloc(sizeof(*f));
151 	f->url = fetchCopyURL(url);
152 
153 	*archive_name = fetchStringifyURL(url);
154 
155 	a = prepare_archive();
156 	if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read,
157 	    fetch_archive_close)) {
158 		free(*archive_name);
159 		*archive_name = NULL;
160 		archive_read_free(a);
161 		return NULL;
162 	}
163 
164 	return a;
165 }
166 #endif /* !BOOTSTRAP */
167 
168 struct archive *
prepare_archive(void)169 prepare_archive(void)
170 {
171 	struct archive *a = archive_read_new();
172 	if (a == NULL)
173 		errx(EXIT_FAILURE, "memory allocation failed");
174 	archive_read_support_filter_gzip(a);
175 	archive_read_support_filter_bzip2(a);
176 	archive_read_support_filter_xz(a);
177 	archive_read_support_format_ar(a);
178 	archive_read_support_format_tar(a);
179 	archive_read_set_options(a, "hdrcharset=BINARY");
180 	return a;
181 }
182 
183 struct archive *
open_archive(const char * url,char ** archive_name)184 open_archive(const char *url, char **archive_name)
185 {
186 	struct url *u;
187 	struct archive *a;
188 
189 	*archive_name = NULL;
190 
191 	if (!IS_URL(url)) {
192 		a = prepare_archive();
193 		if (archive_read_open_filename(a, url, 1024)) {
194 			archive_read_close(a);
195 			return NULL;
196 		}
197 		*archive_name = xstrdup(url);
198 		return a;
199 	}
200 
201 #ifdef BOOTSTRAP
202 	return NULL;
203 #else
204 	if ((u = fetchParseURL(url)) == NULL)
205 		return NULL;
206 
207 	a = open_archive_by_url(u, archive_name);
208 
209 	fetchFreeURL(u);
210 	return a;
211 #endif
212 }
213 
214 #ifndef BOOTSTRAP
215 static int
strip_suffix(char * filename)216 strip_suffix(char *filename)
217 {
218 	size_t len;
219 
220 	len = strlen(filename);
221 	if (len <= 4)
222 		return 0;
223 	if (strcmp(filename + len - 4, ".tgz") == 0 ||
224 	    strcmp(filename + len - 4, ".tbz") == 0) {
225 		filename[len - 4] = '\0';
226 		return 1;
227 	} else
228 		return 0;
229 }
230 
231 static int
find_best_package_int(struct url * url,const char * pattern,struct url ** best_url)232 find_best_package_int(struct url *url, const char *pattern,
233     struct url **best_url)
234 {
235 	char *cur_match, *url_pattern, *best_match = NULL;
236 	struct url_list ue;
237 	size_t i;
238 
239 	if (*best_url) {
240 		if ((best_match = fetchUnquoteFilename(*best_url)) == NULL)
241 			return -1;
242 	} else
243 		best_match = NULL;
244 
245 	if (best_match && strip_suffix(best_match) == 0) {
246 		free(best_match);
247 		return -1;
248 	}
249 
250 	for (i = 0; pattern[i] != '\0'; ++i) {
251 		if (!isalnum((unsigned char)(pattern[i])) &&
252 		    (pattern[i]) != '-')
253 			break;
254 	}
255 	url_pattern = xasprintf("%*.*s*", (int)i, (int)i, pattern);
256 
257 	fetchInitURLList(&ue);
258 	if (fetchList(&ue, url, url_pattern, fetch_flags)) {
259 		char *base_url;
260 		base_url = fetchStringifyURL(url);
261 		warnx("Can't process %s/%s: %s", base_url, url_pattern,
262 		    fetchLastErrString);
263 		free(base_url);
264 		free(url_pattern);
265 		fetchFreeURLList(&ue);
266 		return -1;
267 	}
268 	free(url_pattern);
269 
270 	for (i = 0; i < ue.length; ++i) {
271 		cur_match = fetchUnquoteFilename(ue.urls + i);
272 
273 		if (cur_match == NULL) {
274 			free(best_match);
275 			fetchFreeURLList(&ue);
276 			return -1;
277 		}
278 		if (strip_suffix(cur_match) == 0) {
279 			free(cur_match);
280 			continue;
281 		}
282 		if (pkg_order(pattern, cur_match, best_match) == 1) {
283 			if (*best_url)
284 				fetchFreeURL(*best_url);
285 			*best_url = fetchCopyURL(ue.urls + i);
286 			free(best_match);
287 			best_match = cur_match;
288 			cur_match = NULL;
289 			if (*best_url == NULL) {
290 				free(best_match);
291 				return -1;
292 			}
293 		}
294 		free(cur_match);
295 	}
296 	free(best_match);
297 	fetchFreeURLList(&ue);
298 	return 0;
299 }
300 
301 void
process_pkg_path(void)302 process_pkg_path(void)
303 {
304 	char cwd[PATH_MAX];
305 	int relative_path;
306 	struct pkg_path *pl;
307 	const char *start, *next;
308 	size_t len;
309 
310 	if (getcwd(cwd, sizeof(cwd)) == NULL)
311 		errx(EXIT_FAILURE, "getcwd failed");
312 
313 	orig_cwd = xstrdup(cwd);
314 
315 	if (config_pkg_path == NULL)
316 		return;
317 
318 	for (start = config_pkg_path; *start; start = next) {
319 		len = strcspn(start, ";");
320 		if (*(next = start + len) != '\0')
321 			++next;
322 
323 		relative_path = !IS_FULLPATH(start) && !IS_URL(start);
324 		pl = xmalloc(sizeof(*pl));
325 		pl->pl_path = xasprintf("%s%s%*.*s",
326 		    relative_path ? cwd : "", len && relative_path ? "/" : "",
327 		    (int)len, (int)len, start);
328 		TAILQ_INSERT_TAIL(&pkg_path, pl, pl_link);
329 	}
330 }
331 
332 struct url *
find_best_package(const char * toplevel,const char * pattern,int do_path)333 find_best_package(const char *toplevel, const char *pattern, int do_path)
334 {
335 	struct url *url, *best_match = NULL;
336 	struct pkg_path *pl;
337 
338 	if (toplevel) {
339 		url = fetchParseURL(last_toplevel);
340 		if (url != NULL) {
341 			find_best_package_int(url, pattern, &best_match);
342 			/* XXX Check return value and complain */
343 			fetchFreeURL(url);
344 		}
345 	}
346 	if (!do_path)
347 		return best_match;
348 
349 	TAILQ_FOREACH(pl, &pkg_path, pl_link) {
350 		url = fetchParseURL(pl->pl_path);
351 		if (url != NULL) {
352 			find_best_package_int(url, pattern, &best_match);
353 			/* XXX Check return value and complain */
354 			fetchFreeURL(url);
355 		}
356 	}
357 
358 	return best_match;
359 }
360 #endif /* !BOOTSTRAP */
361 
362 struct archive *
find_archive(const char * fname,int top_level,char ** archive_name)363 find_archive(const char *fname, int top_level, char **archive_name)
364 {
365 	struct archive *a;
366 	struct url *best_match;
367 	char *full_fname, *last_slash;
368 	int search_path;
369 
370 	search_path = 0;
371 	if (IS_FULLPATH(fname) || IS_URL(fname)) {
372 		full_fname = xstrdup(fname);
373 	} else {
374 		if (strchr(fname, '/') == NULL)
375 			search_path = 1;
376 		full_fname = xasprintf("%s/%s", orig_cwd, fname);
377 	}
378 
379 	last_slash = strrchr(full_fname, '/');
380 	if (top_level) {
381 		free(last_toplevel);
382 		*last_slash = '\0';
383 		last_toplevel = xstrdup(full_fname);
384 		*last_slash = '/';
385 	}
386 
387 	a = open_archive(full_fname, archive_name);
388 	if (a != NULL) {
389 		free(full_fname);
390 		return a;
391 	}
392 #ifndef BOOTSTRAP
393 	fname = last_slash + 1;
394 	*last_slash = '\0';
395 
396 	best_match = find_best_package(full_fname, fname, 0);
397 
398 	if (search_path && best_match == NULL)
399 		best_match = find_best_package(last_toplevel, fname, 1);
400 
401 	free(full_fname);
402 
403 	if (best_match == NULL)
404 		return NULL;
405 	a = open_archive_by_url(best_match, archive_name);
406 	fetchFreeURL(best_match);
407 #endif /* !BOOTSTRAP */
408 	return a;
409 }
410