Home | History | Annotate | Line # | Download | only in lib
      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 (at) 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
     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
     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
    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 *
    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 *
    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 *
    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
    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
    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
    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 *
    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 *
    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