1 /* $NetBSD: pkg_io.c,v 1.1.1.9 2010/04/23 20:54:11 joerg 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.1.1.9 2010/04/23 20:54:11 joerg 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 <fetch.h> 50 #include <stdlib.h> 51 52 #include "lib.h" 53 54 struct pkg_path { 55 TAILQ_ENTRY(pkg_path) pl_link; 56 char *pl_path; 57 }; 58 59 static char *orig_cwd, *last_toplevel; 60 static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path); 61 62 struct fetch_archive { 63 struct url *url; 64 fetchIO *fetch; 65 char buffer[32768]; 66 off_t size; 67 int restart; 68 }; 69 70 static int 71 fetch_archive_open(struct archive *a, void *client_data) 72 { 73 struct fetch_archive *f = client_data; 74 struct url_stat us; 75 76 f->fetch = fetchXGet(f->url, &us, fetch_flags); 77 if (f->fetch == NULL) 78 return ENOENT; 79 f->size = us.size; 80 f->restart = 1; 81 f->url->offset = 0; 82 return 0; 83 } 84 85 static ssize_t 86 fetch_archive_read(struct archive *a, void *client_data, 87 const void **buffer) 88 { 89 struct fetch_archive *f = client_data; 90 struct url_stat us; 91 ssize_t rv; 92 93 *buffer = f->buffer; 94 rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer)); 95 if (rv > 0) { 96 f->url->offset += rv; 97 return rv; 98 } 99 if (f->restart == 0) 100 return rv; 101 if (rv == 0) { 102 if (f->size == -1) 103 return 0; 104 if (f->url->offset == f->size) 105 return 0; 106 } 107 f->restart = 0; 108 if (1) { 109 char *url = fetchStringifyURL(f->url); 110 fprintf(stderr, "Trying to reconnect %s\n", url); 111 free(url); 112 } 113 fetchIO_close(f->fetch); 114 f->fetch = fetchXGet(f->url, &us, fetch_flags); 115 if (f->fetch == NULL) 116 return -1; 117 if (us.size != f->size) 118 return -1; 119 rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer)); 120 if (rv > 0) 121 f->url->offset += rv; 122 return rv; 123 } 124 125 static int 126 fetch_archive_close(struct archive *a, void *client_data) 127 { 128 struct fetch_archive *f = client_data; 129 130 if (f->fetch != NULL) 131 fetchIO_close(f->fetch); 132 fetchFreeURL(f->url); 133 free(f); 134 return 0; 135 } 136 137 static struct archive * 138 open_archive_by_url(struct url *url, char **archive_name) 139 { 140 struct fetch_archive *f; 141 struct archive *a; 142 143 f = xmalloc(sizeof(*f)); 144 f->url = fetchCopyURL(url); 145 146 *archive_name = fetchStringifyURL(url); 147 148 a = archive_read_new(); 149 archive_read_support_compression_all(a); 150 archive_read_support_format_all(a); 151 if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read, 152 fetch_archive_close)) { 153 free(*archive_name); 154 *archive_name = NULL; 155 archive_read_finish(a); 156 return NULL; 157 } 158 159 return a; 160 } 161 162 struct archive * 163 open_archive(const char *url, char **archive_name) 164 { 165 struct url *u; 166 struct archive *a; 167 168 *archive_name = NULL; 169 170 if (!IS_URL(url)) { 171 a = archive_read_new(); 172 archive_read_support_compression_all(a); 173 archive_read_support_format_all(a); 174 if (archive_read_open_filename(a, url, 1024)) { 175 archive_read_close(a); 176 return NULL; 177 } 178 *archive_name = xstrdup(url); 179 return a; 180 } 181 182 if ((u = fetchParseURL(url)) == NULL) 183 return NULL; 184 185 a = open_archive_by_url(u, archive_name); 186 187 fetchFreeURL(u); 188 return a; 189 } 190 191 static int 192 strip_suffix(char *filename) 193 { 194 size_t len; 195 196 len = strlen(filename); 197 if (len <= 4) 198 return 0; 199 if (strcmp(filename + len - 4, ".tgz") == 0 || 200 strcmp(filename + len - 4, ".tbz") == 0) { 201 filename[len - 4] = '\0'; 202 return 1; 203 } else 204 return 0; 205 } 206 207 static int 208 find_best_package_int(struct url *url, const char *pattern, 209 struct url **best_url) 210 { 211 char *cur_match, *url_pattern, *best_match = NULL; 212 struct url_list ue; 213 size_t i; 214 215 if (*best_url) { 216 if ((best_match = fetchUnquoteFilename(*best_url)) == NULL) 217 return -1; 218 } else 219 best_match = NULL; 220 221 if (best_match && strip_suffix(best_match) == 0) { 222 free(best_match); 223 return -1; 224 } 225 226 for (i = 0; pattern[i] != '\0'; ++i) { 227 if (!isalnum((unsigned char)(pattern[i])) && 228 (pattern[i]) != '-') 229 break; 230 } 231 url_pattern = xasprintf("%*.*s*", (int)i, (int)i, pattern); 232 233 fetchInitURLList(&ue); 234 if (fetchList(&ue, url, url_pattern, fetch_flags)) { 235 char *base_url; 236 base_url = fetchStringifyURL(url); 237 warnx("Can't process %s/%s: %s", base_url, url_pattern, 238 fetchLastErrString); 239 free(base_url); 240 free(url_pattern); 241 fetchFreeURLList(&ue); 242 return -1; 243 } 244 free(url_pattern); 245 246 for (i = 0; i < ue.length; ++i) { 247 cur_match = fetchUnquoteFilename(ue.urls + i); 248 249 if (cur_match == NULL) { 250 free(best_match); 251 fetchFreeURLList(&ue); 252 return -1; 253 } 254 if (strip_suffix(cur_match) == 0) { 255 free(cur_match); 256 continue; 257 } 258 if (pkg_order(pattern, cur_match, best_match) == 1) { 259 if (*best_url) 260 fetchFreeURL(*best_url); 261 *best_url = fetchCopyURL(ue.urls + i); 262 free(best_match); 263 best_match = cur_match; 264 cur_match = NULL; 265 if (*best_url == NULL) { 266 free(best_match); 267 return -1; 268 } 269 } 270 free(cur_match); 271 } 272 free(best_match); 273 fetchFreeURLList(&ue); 274 return 0; 275 } 276 277 void 278 process_pkg_path(void) 279 { 280 char cwd[PATH_MAX]; 281 int relative_path; 282 struct pkg_path *pl; 283 const char *start, *next; 284 size_t len; 285 286 if (getcwd(cwd, sizeof(cwd)) == NULL) 287 errx(EXIT_FAILURE, "getcwd failed"); 288 289 orig_cwd = xstrdup(cwd); 290 291 if (config_pkg_path == NULL) 292 return; 293 294 for (start = config_pkg_path; *start; start = next) { 295 len = strcspn(start, ";"); 296 if (*(next = start + len) != '\0') 297 ++next; 298 299 relative_path = !IS_FULLPATH(start) && !IS_URL(start); 300 pl = xmalloc(sizeof(*pl)); 301 pl->pl_path = xasprintf("%s%s%*.*s", 302 relative_path ? cwd : "", len && relative_path ? "/" : "", 303 (int)len, (int)len, start); 304 TAILQ_INSERT_TAIL(&pkg_path, pl, pl_link); 305 } 306 } 307 308 struct url * 309 find_best_package(const char *toplevel, const char *pattern, int do_path) 310 { 311 struct url *url, *best_match = NULL; 312 struct pkg_path *pl; 313 314 if (toplevel) { 315 url = fetchParseURL(last_toplevel); 316 if (url != NULL) { 317 find_best_package_int(url, pattern, &best_match); 318 /* XXX Check return value and complain */ 319 fetchFreeURL(url); 320 } 321 } 322 if (!do_path) 323 return best_match; 324 325 TAILQ_FOREACH(pl, &pkg_path, pl_link) { 326 url = fetchParseURL(pl->pl_path); 327 if (url != NULL) { 328 find_best_package_int(url, pattern, &best_match); 329 /* XXX Check return value and complain */ 330 fetchFreeURL(url); 331 } 332 } 333 334 return best_match; 335 } 336 337 struct archive * 338 find_archive(const char *fname, int top_level, char **archive_name) 339 { 340 struct archive *a; 341 struct url *best_match; 342 char *full_fname, *last_slash; 343 int search_path; 344 345 search_path = 0; 346 if (IS_FULLPATH(fname) || IS_URL(fname)) { 347 full_fname = xstrdup(fname); 348 } else { 349 if (strchr(fname, '/') == NULL) 350 search_path = 1; 351 full_fname = xasprintf("%s/%s", orig_cwd, fname); 352 } 353 354 last_slash = strrchr(full_fname, '/'); 355 if (top_level) { 356 free(last_toplevel); 357 *last_slash = '\0'; 358 last_toplevel = xstrdup(full_fname); 359 *last_slash = '/'; 360 } 361 362 a = open_archive(full_fname, archive_name); 363 if (a != NULL) { 364 free(full_fname); 365 return a; 366 } 367 368 fname = last_slash + 1; 369 *last_slash = '\0'; 370 371 best_match = find_best_package(full_fname, fname, 0); 372 373 if (search_path && best_match == NULL) 374 best_match = find_best_package(last_toplevel, fname, 1); 375 376 free(full_fname); 377 378 if (best_match == NULL) 379 return NULL; 380 a = open_archive_by_url(best_match, archive_name); 381 fetchFreeURL(best_match); 382 return a; 383 } 384