1/* $NetBSD: pkg_io.c,v 1.4 2021/04/10 19:49:59 nia 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.4 2021/04/10 19:49:59 nia 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#ifdef BOOTSTRAP 52#undef IS_URL 53#define IS_URL(x) 0 54#else 55#include <fetch.h> 56#endif 57 58#include "lib.h" 59 60struct pkg_path { 61 TAILQ_ENTRY(pkg_path) pl_link; 62 char *pl_path; 63}; 64 65static char *orig_cwd, *last_toplevel; 66static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path); 67 68#ifndef BOOTSTRAP 69struct fetch_archive { 70 struct url *url; 71 fetchIO *fetch; 72 char buffer[32768]; 73 off_t size; 74 int restart; 75}; 76 77static int 78fetch_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 92static ssize_t 93fetch_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 132static int 133fetch_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 144static struct archive * 145open_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 168struct archive * 169prepare_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 183struct archive * 184open_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 215static int 216strip_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 231static int 232find_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 301void 302process_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 332struct url * 333find_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 362struct archive * 363find_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