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