xref: /minix/external/bsd/pkg_install/dist/lib/pkg_io.c (revision a824f5a1)
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
fetch_archive_open(struct archive * a,void * client_data)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
fetch_archive_read(struct archive * a,void * client_data,const void ** buffer)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
fetch_archive_close(struct archive * a,void * client_data)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 *
open_archive_by_url(struct url * url,char ** archive_name)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 *
open_archive(const char * url,char ** archive_name)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
strip_suffix(char * filename)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
find_best_package_int(struct url * url,const char * pattern,struct url ** best_url)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
process_pkg_path(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 *
find_best_package(const char * toplevel,const char * pattern,int do_path)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 *
find_archive(const char * fname,int top_level,char ** archive_name)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