Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : : * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : : * Copyright (c) 2023 Serenity Cyber Security, LLC
6 : : * Author: Gleb Popov <arrowd@FreeBSD.org>
7 : : * All rights reserved.
8 : : *
9 : : * Redistribution and use in source and binary forms, with or without
10 : : * modification, are permitted provided that the following conditions
11 : : * are met:
12 : : * 1. Redistributions of source code must retain the above copyright
13 : : * notice, this list of conditions and the following disclaimer
14 : : * in this position and unchanged.
15 : : * 2. Redistributions in binary form must reproduce the above copyright
16 : : * notice, this list of conditions and the following disclaimer in the
17 : : * documentation and/or other materials provided with the distribution.
18 : : *
19 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
20 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
23 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #include <pkg_config.h>
32 : :
33 : : #include <sys/socket.h>
34 : : #include <sys/stat.h>
35 : : #include <sys/param.h>
36 : : #include <stdio.h>
37 : :
38 : : #include <assert.h>
39 : : #include <errno.h>
40 : : #include <fcntl.h>
41 : : #include <stdlib.h>
42 : : #include <unistd.h>
43 : : #include <string.h>
44 : : #include <ucl.h>
45 : : #include <utlist.h>
46 : : #include <ctype.h>
47 : : #include <fnmatch.h>
48 : : #include <paths.h>
49 : : #include <float.h>
50 : : #include <math.h>
51 : : #include <regex.h>
52 : :
53 : : #include <bsd_compat.h>
54 : :
55 : : #include "pkg.h"
56 : : #include "pkgvec.h"
57 : : #include "private/event.h"
58 : : #include "private/pkg_abi.h"
59 : : #include "private/utils.h"
60 : : #include "private/pkg.h"
61 : : #include "xmalloc.h"
62 : : #include "tllist.h"
63 : :
64 : : extern struct pkg_ctx ctx;
65 : :
66 : : bool
67 : 1192 : match_ucl_lists(const char *buf, const ucl_object_t *globs, const ucl_object_t *regexes)
68 : : {
69 : : const ucl_object_t *cur;
70 : : ucl_object_iter_t it;
71 : :
72 : 1192 : if (globs == NULL && regexes == NULL)
73 : 270 : return (false);
74 : :
75 : 922 : if (globs != NULL) {
76 : 922 : it = NULL;
77 [ + + ]: 998 : while ((cur = ucl_iterate_object(globs, &it, true))) {
78 [ + + ]: 81 : if (fnmatch(ucl_object_tostring(cur), buf, 0) == 0)
79 : 5 : return (true);
80 : : }
81 : 917 : }
82 : :
83 : 992 : if (regexes != NULL) {
84 : 842 : it = NULL;
85 [ + + ]: 842 : while ((cur = ucl_iterate_object(regexes, &it, true))) {
86 : : regex_t re;
87 : 1 : regcomp(&re, ucl_object_tostring(cur),
88 : : REG_EXTENDED|REG_NOSUB);
89 [ + - ]: 1 : if (regexec(&re, buf, 0, NULL, 0) == 0) {
90 : 1 : regfree(&re);
91 : 1 : return (true);
92 : : }
93 : 0 : regfree(&re);
94 : : }
95 : 841 : }
96 : :
97 : 917 : return (false);
98 : 1193 : }
99 : :
100 : : int
101 : 578 : pkg_mkdirs(const char *_path)
102 : : {
103 : : char path[MAXPATHLEN];
104 : : char *p;
105 : : int dirfd;
106 : :
107 : 578 : dirfd = open(_path, O_RDONLY|O_DIRECTORY);
108 [ + + ]: 578 : if (dirfd >= 0) {
109 : 557 : close(dirfd);
110 : 557 : return EPKG_OK;
111 : : }
112 : :
113 : 21 : strlcpy(path, _path, sizeof(path));
114 : 21 : p = path;
115 [ + + ]: 38 : while (*p == '/')
116 : 17 : p++;
117 : :
118 : 121 : for (;;) {
119 : 121 : if ((p = strchr(p, '/')) != NULL)
120 : 100 : *p = '\0';
121 : :
122 : 116 : if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
123 : 84 : if (errno != EEXIST && errno != EISDIR) {
124 : 0 : pkg_emit_errno("mkdir", path);
125 : 0 : return (EPKG_FATAL);
126 : : }
127 : :
128 : : /* that was the last element of the path */
129 [ + + ]: 121 : if (p == NULL)
130 : 21 : break;
131 : :
132 : 100 : *p = '/';
133 : 100 : p++;
134 : : }
135 : :
136 : 21 : return (EPKG_OK);
137 : 578 : }
138 : : int
139 : 385 : file_to_bufferat(int dfd, const char *path, char **buffer, off_t *sz)
140 : : {
141 : 385 : int fd = -1;
142 : : struct stat st;
143 : 385 : int retcode = EPKG_OK;
144 : :
145 : 385 : assert(path != NULL && path[0] != '\0');
146 [ + - ]: 385 : assert(buffer != NULL);
147 [ + - ]: 385 : assert(sz != NULL);
148 : :
149 [ - + ]: 385 : if ((fd = openat(dfd, path, O_RDONLY)) == -1) {
150 : 0 : pkg_emit_errno("openat", path);
151 : 0 : retcode = EPKG_FATAL;
152 : 0 : goto cleanup;
153 : : }
154 : :
155 [ - + ]: 385 : if (fstat(fd, &st) == -1) {
156 : 0 : pkg_emit_errno("fstatat", path);
157 : 0 : retcode = EPKG_FATAL;
158 : 0 : goto cleanup;
159 : : }
160 : :
161 : 385 : *buffer = xmalloc(st.st_size + 1);
162 : :
163 [ - + ]: 385 : if (read(fd, *buffer, st.st_size) == -1) {
164 : 0 : pkg_emit_errno("read", path);
165 : 0 : retcode = EPKG_FATAL;
166 : 0 : goto cleanup;
167 : : }
168 : :
169 : : cleanup:
170 : 385 : if (fd >= 0)
171 : 385 : close(fd);
172 : :
173 [ + - ]: 385 : if (retcode == EPKG_OK) {
174 : 385 : (*buffer)[st.st_size] = '\0';
175 : 385 : *sz = st.st_size;
176 : 385 : } else {
177 : 0 : *buffer = NULL;
178 : 0 : *sz = -1;
179 : : }
180 : 385 : return (retcode);
181 : : }
182 : :
183 : : int
184 : 4 : file_to_buffer(const char *path, char **buffer, off_t *sz)
185 : : {
186 : 4 : return file_to_bufferat(AT_FDCWD, path, buffer, sz);
187 : : }
188 : :
189 : : int
190 : 6 : format_exec_cmd(char **dest, const char *in, const char *prefix,
191 : : const char *plist_file, const char *line, int argc, char **argv, bool lua)
192 : : {
193 : : xstring *buf;
194 : : char path[MAXPATHLEN];
195 : : char *cp;
196 : : const char *ptr;
197 : : size_t sz;
198 : :
199 : 6 : buf = xstring_new();
200 : 6 : cp = NULL;
201 : :
202 : 6 : if (line != NULL && argv != NULL) {
203 [ + + ]: 6 : if (lua) {
204 : 2 : fprintf(buf->fp, "-- args: %s\n", line);
205 : 2 : } else {
206 : 4 : fprintf(buf->fp, "# args: %s\n", line);
207 : : }
208 : 6 : }
209 : :
210 [ + + ]: 185 : while (in[0] != '\0') {
211 [ + + ]: 180 : if (in[0] != '%') {
212 : 172 : fputc(in[0], buf->fp);
213 : 172 : in++;
214 : 172 : continue;
215 : : }
216 : 8 : in++;
217 [ - - - - : 8 : switch(in[0]) {
- - - + ]
218 : : case 'D':
219 : 0 : fprintf(buf->fp, "%s", prefix);
220 : 0 : break;
221 : : case 'F':
222 : 0 : if (plist_file == NULL || plist_file[0] == '\0') {
223 : 0 : pkg_emit_error("No files defined %%F couldn't "
224 : 0 : "be expanded, ignoring %s", in);
225 : 0 : xstring_free(buf);
226 : 0 : return (EPKG_FATAL);
227 : : }
228 : 0 : fprintf(buf->fp, "%s", plist_file);
229 : 0 : break;
230 : : case 'f':
231 : 0 : if (plist_file == NULL || plist_file[0] == '\0') {
232 : 0 : pkg_emit_error("No files defined %%f couldn't "
233 : 0 : "be expanded, ignoring %s", in);
234 : 0 : xstring_free(buf);
235 : 0 : return (EPKG_FATAL);
236 : : }
237 : 0 : ptr = strrchr(plist_file, '/');
238 [ # # ]: 0 : if (ptr != NULL)
239 : 0 : ptr++;
240 : : else
241 : 0 : ptr = plist_file;
242 : 0 : fprintf(buf->fp, "%s", ptr);
243 : 0 : break;
244 : : case 'B':
245 : 0 : if (plist_file == NULL || plist_file[0] == '\0') {
246 : 0 : pkg_emit_error("No files defined %%B couldn't "
247 : 0 : "be expanded, ignoring %s", in);
248 : 0 : xstring_free(buf);
249 : 0 : return (EPKG_FATAL);
250 : : }
251 [ # # ]: 0 : if (prefix[strlen(prefix) - 1] == '/')
252 : 0 : snprintf(path, sizeof(path), "%s%s", prefix,
253 : 0 : plist_file);
254 : : else
255 : 0 : snprintf(path, sizeof(path), "%s/%s", prefix,
256 : 0 : plist_file);
257 : 0 : cp = strrchr(path, '/');
258 : 0 : cp[0] = '\0';
259 : 0 : fprintf(buf->fp, "%s", path);
260 : 0 : break;
261 : : case '%':
262 : 0 : fputc('%', buf->fp);
263 : 0 : break;
264 : : case '@':
265 [ # # ]: 0 : if (line != NULL) {
266 : 0 : fprintf(buf->fp, "%s", line);
267 : 0 : break;
268 : : }
269 : :
270 : : /*
271 : : * no break here because if line is not
272 : : * given (default exec) %@ does not
273 : : * exists
274 : : */
275 : : /* FALLTHRU */
276 : : case '#':
277 : 0 : fprintf(buf->fp, "%d", argc);
278 : 0 : break;
279 : : default:
280 [ + - ]: 8 : if ((sz = strspn(in, "0123456789")) > 0) {
281 : 8 : int pos = strtol(in, NULL, 10);
282 [ + + ]: 8 : if (pos > argc) {
283 : 1 : pkg_emit_error("Requesting argument "
284 : : "%%%d while only %d arguments are"
285 : 1 : " available", pos, argc);
286 : 1 : xstring_free(buf);
287 : 1 : return (EPKG_FATAL);
288 : : }
289 : 7 : fprintf(buf->fp, "%s", argv[pos -1]);
290 : 7 : in += sz -1;
291 : 7 : break;
292 : : }
293 : 0 : fprintf(buf->fp, "%c%c", '%', in[0]);
294 : 0 : break;
295 : : }
296 : :
297 : 7 : in++;
298 : : }
299 : :
300 : 5 : *dest = xstring_get(buf);
301 : :
302 : 5 : return (EPKG_OK);
303 : 6 : }
304 : :
305 : : int
306 : 150 : is_dir(const char *path)
307 : : {
308 : : struct stat st;
309 : :
310 : 150 : return (stat(path, &st) == 0 && S_ISDIR(st.st_mode));
311 : : }
312 : :
313 : : int
314 : 0 : is_link(const char *path)
315 : : {
316 : : struct stat st;
317 : :
318 : 0 : return (lstat(path, &st) == 0 && S_ISLNK(st.st_mode));
319 : : }
320 : :
321 : : bool
322 : 20 : check_for_hardlink(hardlinks_t *hl, struct stat *st)
323 : : {
324 : : struct hardlink *h;
325 : :
326 [ + + + + : 20 : tll_foreach(*hl, it) {
# # ]
327 : 10 : if (it->item->ino == st->st_ino &&
328 : 10 : it->item->dev == st->st_dev)
329 : 10 : return (true);
330 : 0 : }
331 : 10 : h = xcalloc(1, sizeof(*h));
332 : 10 : h->ino = st->st_ino;
333 : 10 : h->dev = st->st_dev;
334 [ - + ]: 10 : tll_push_back(*hl, h);
335 : :
336 : 10 : return (false);
337 : 20 : }
338 : :
339 : : /*
340 : : * ABI validation:
341 : : * - lowest match (case insensitive)
342 : : * - glob matching
343 : : *
344 : : * A package which is valid for installation on any FreeBSD now simply has
345 : : * to define itself as: abi: "FreeBSD" or "FreeBSD:*"
346 : : * A package which is valid for installation on any FreeBSD 15 regarless of
347 : : * the arch
348 : : * abi: "FreeBSD:15" or "FreeBSD:15:*"
349 : : *
350 : : */
351 : :
352 : : bool
353 : 167 : is_valid_abi(const char *testabi, bool emit_error)
354 : : {
355 : 167 : const char *abi = pkg_object_string(pkg_config_get("ABI"));
356 : :
357 : 167 : if (strncasecmp(testabi, abi, strlen(testabi)) != 0 &&
358 : 45 : fnmatch(testabi, abi, FNM_CASEFOLD) == FNM_NOMATCH) {
359 : 0 : if (emit_error)
360 : 0 : pkg_emit_error("wrong architecture: %s instead of %s",
361 : 0 : testabi, abi);
362 : 0 : return (false);
363 : : }
364 : :
365 : 167 : return (true);
366 : 167 : }
367 : :
368 : : bool
369 : 167 : is_valid_os_version(struct pkg *pkg)
370 : : {
371 [ - + ]: 167 : if (ctx.abi.os != PKG_OS_FREEBSD)
372 : 0 : return (true);
373 : : const char *fbsd_version;
374 : 167 : const char *errstr = NULL;
375 : : char query_buf[512];
376 : : /* -1: not checked, 0: not allowed, 1: allowed */
377 : : static int osver_mismatch_allowed = -1;
378 : : bool ret;
379 : :
380 [ - + ]: 167 : if (pkg_object_bool(pkg_config_get("IGNORE_OSVERSION")))
381 : 0 : return (true);
382 : 167 : if ((fbsd_version = pkg_kv_get(&pkg->annotations, "FreeBSD_version")) != NULL) {
383 : 122 : int pkg_osversion = strtonum(fbsd_version, 1, INT_MAX, &errstr);
384 [ - + ]: 122 : if (errstr != NULL) {
385 : 0 : pkg_emit_error("Invalid FreeBSD version %s for package %s",
386 : 0 : fbsd_version, pkg->name);
387 : 0 : return (false);
388 : : }
389 : 122 : int abi_osversion = pkg_abi_get_freebsd_osversion(&ctx.abi);
390 [ - + ]: 122 : if (pkg_osversion > abi_osversion) {
391 [ # # ]: 0 : if (pkg_osversion - abi_osversion < 100000) {
392 : : /* Negligible difference, ask user to enforce */
393 : 0 : if (osver_mismatch_allowed == -1) {
394 : 0 : snprintf(query_buf, sizeof(query_buf),
395 : : "Newer FreeBSD version for package %s:\n"
396 : : "To ignore this error set IGNORE_OSVERSION=yes\n"
397 : : "- package: %d\n"
398 : : "- running userland: %d\n"
399 : 0 : "Ignore the mismatch and continue? ", pkg->name,
400 : 0 : pkg_osversion, abi_osversion);
401 : 0 : ret = pkg_emit_query_yesno(false, query_buf);
402 : 0 : osver_mismatch_allowed = ret;
403 : 0 : }
404 : :
405 : 0 : return (osver_mismatch_allowed);
406 : : }
407 : : else {
408 : 0 : pkg_emit_error("Newer FreeBSD version for package %s:\n"
409 : : "To ignore this error set IGNORE_OSVERSION=yes\n"
410 : : "- package: %d\n"
411 : : "- running kernel: %d\n",
412 : 0 : pkg->name,
413 : 0 : pkg_osversion, abi_osversion);
414 : 0 : return (false);
415 : : }
416 : : }
417 : 122 : }
418 : 167 : return (true);
419 : 167 : }
420 : :
421 : : void
422 : 0 : set_nonblocking(int fd)
423 : : {
424 : : int flags;
425 : :
426 [ # # ]: 0 : if ((flags = fcntl(fd, F_GETFL)) == -1)
427 : 0 : return;
428 : 0 : if (!(flags & O_NONBLOCK)) {
429 : 0 : flags |= O_NONBLOCK;
430 : 0 : fcntl(fd, F_SETFL, flags);
431 : 0 : }
432 : 0 : }
433 : :
434 : : void
435 : 0 : set_blocking(int fd)
436 : : {
437 : : int flags;
438 : :
439 [ # # ]: 0 : if ((flags = fcntl(fd, F_GETFL)) == -1)
440 : 0 : return;
441 : 0 : if (flags & O_NONBLOCK) {
442 : 0 : flags &= ~O_NONBLOCK;
443 : 0 : fcntl(fd, F_SETFL, flags);
444 : 0 : }
445 : 0 : }
446 : :
447 : : /* Spawn a process from pfunc, returning it's pid. The fds array passed will
448 : : * be filled with two descriptors: fds[0] will read from the child process,
449 : : * and fds[1] will write to it.
450 : : * Similarly, the child process will receive a reading/writing fd set (in
451 : : * that same order) as arguments.
452 : : */
453 : : extern char **environ;
454 : : pid_t
455 : 6 : process_spawn_pipe(FILE *inout[2], const char *command)
456 : : {
457 : : pid_t pid;
458 : : int pipes[4];
459 : : char *argv[4];
460 : :
461 : : /* Parent read/child write pipe */
462 [ - + ]: 6 : if (pipe(&pipes[0]) == -1)
463 : 0 : return (-1);
464 : :
465 : : /* Child read/parent write pipe */
466 [ - + ]: 6 : if (pipe(&pipes[2]) == -1) {
467 : 0 : close(pipes[0]);
468 : 0 : close(pipes[1]);
469 : 0 : return (-1);
470 : : }
471 : :
472 : 6 : argv[0] = __DECONST(char *, "sh");
473 : 6 : argv[1] = __DECONST(char *, "-c");
474 : 6 : argv[2] = __DECONST(char *, command);
475 : 6 : argv[3] = NULL;
476 : :
477 : 6 : pid = fork();
478 [ + + ]: 6 : if (pid > 0) {
479 : : /* Parent process */
480 : 6 : inout[0] = fdopen(pipes[0], "r");
481 : 6 : inout[1] = fdopen(pipes[3], "w");
482 : :
483 : 6 : close(pipes[1]);
484 : 6 : close(pipes[2]);
485 : :
486 : 6 : return (pid);
487 : :
488 [ + - ]: 6 : } else if (pid == 0) {
489 : 6 : close(pipes[0]);
490 : 6 : close(pipes[3]);
491 : :
492 : 6 : if (pipes[1] != STDOUT_FILENO) {
493 : 6 : dup2(pipes[1], STDOUT_FILENO);
494 : 6 : close(pipes[1]);
495 : 6 : }
496 : 6 : if (pipes[2] != STDIN_FILENO) {
497 : 6 : dup2(pipes[2], STDIN_FILENO);
498 : 6 : close(pipes[2]);
499 : 6 : }
500 : 12 : closefrom(STDERR_FILENO + 1);
501 : :
502 : 12 : execve(_PATH_BSHELL, argv, environ);
503 : :
504 : 0 : _exit(127);
505 : : }
506 : :
507 : 0 : return (-1); /* ? */
508 : 6 : }
509 : :
510 : : static int
511 : 0 : ucl_buf_append_character(unsigned char c, size_t len, void *data)
512 : : {
513 : 0 : xstring *buf = data;
514 : : size_t i;
515 : :
516 [ # # ]: 0 : for (i = 0; i < len; i++)
517 : 0 : fprintf(buf->fp, "%c", c);
518 : :
519 : 0 : return (0);
520 : : }
521 : :
522 : : static int
523 : 0 : ucl_buf_append_len(const unsigned char *str, size_t len, void *data)
524 : : {
525 : 0 : xstring *buf = data;
526 : :
527 : 0 : fprintf(buf->fp, "%.*s", (int)len, str);
528 : :
529 : 0 : return (0);
530 : : }
531 : :
532 : : static int
533 : 0 : ucl_buf_append_int(int64_t val, void *data)
534 : : {
535 : 0 : xstring *buf = data;
536 : :
537 : 0 : fprintf(buf->fp, "%"PRId64, val);
538 : :
539 : 0 : return (0);
540 : : }
541 : :
542 : : static int
543 : 0 : ucl_buf_append_double(double val, void *data)
544 : : {
545 : 0 : xstring *buf = data;
546 : 0 : const double delta = 0.0000001;
547 : :
548 [ # # ]: 0 : if (val == (double)(int)val) {
549 : 0 : fprintf(buf->fp, "%.1lf", val);
550 [ # # ]: 0 : } else if (fabs(val - (double)(int)val) < delta) {
551 : 0 : fprintf(buf->fp, "%.*lg", DBL_DIG, val);
552 : 0 : } else {
553 : 0 : fprintf(buf->fp, "%lf", val);
554 : : }
555 : :
556 : 0 : return (0);
557 : : }
558 : :
559 : : bool
560 : 258 : ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,
561 : : FILE *out)
562 : : {
563 : 258 : struct ucl_emitter_functions *f = ucl_object_emit_file_funcs(out);
564 : 258 : bool ret = false;
565 : :
566 [ - + ]: 258 : if (obj == NULL)
567 : 0 : return (false);
568 : :
569 : 258 : ret = ucl_object_emit_full(obj, emit_type, f, NULL);
570 : 258 : ucl_object_emit_funcs_free(f);
571 : :
572 : 258 : return (ret);
573 : 258 : }
574 : :
575 : : bool
576 : 294 : ucl_object_emit_fd(const ucl_object_t *obj, enum ucl_emitter emit_type, int fd)
577 : : {
578 : 294 : struct ucl_emitter_functions *f = ucl_object_emit_fd_funcs(fd);
579 : 294 : bool ret = false;
580 : :
581 [ - + ]: 294 : if (obj == NULL)
582 : 0 : return (false);
583 : 294 : ret = ucl_object_emit_full(obj, emit_type, f, NULL);
584 : 294 : ucl_object_emit_funcs_free(f);
585 : :
586 : 294 : return (ret);
587 : 294 : }
588 : :
589 : :
590 : : bool
591 : 0 : ucl_object_emit_buf(const ucl_object_t *obj, enum ucl_emitter emit_type,
592 : : xstring **buf)
593 : : {
594 : 0 : bool ret = false;
595 : 0 : struct ucl_emitter_functions func = {
596 : : .ucl_emitter_append_character = ucl_buf_append_character,
597 : : .ucl_emitter_append_len = ucl_buf_append_len,
598 : : .ucl_emitter_append_int = ucl_buf_append_int,
599 : : .ucl_emitter_append_double = ucl_buf_append_double
600 : : };
601 : :
602 [ # # ]: 0 : xstring_renew(*buf);
603 : :
604 : 0 : func.ud = *buf;
605 : :
606 : 0 : ret = ucl_object_emit_full(obj, emit_type, &func, NULL);
607 : :
608 : 0 : return (ret);
609 : : }
610 : :
611 : : /* A bit like strsep(), except it accounts for "double" and 'single'
612 : : quotes. Unlike strsep(), returns the next arg string, trimmed of
613 : : whitespace or enclosing quotes, and updates **args to point at the
614 : : character after that. Sets *args to NULL when it has been
615 : : completely consumed. Quoted strings run from the first encountered
616 : : quotemark to the next one of the same type or the terminating NULL.
617 : : Quoted strings can contain the /other/ type of quote mark, which
618 : : loses any special significance. There isn't an escape
619 : : character. */
620 : :
621 : : enum parse_states {
622 : : START,
623 : : ORDINARY_TEXT,
624 : : OPEN_SINGLE_QUOTES,
625 : : IN_SINGLE_QUOTES,
626 : : OPEN_DOUBLE_QUOTES,
627 : : IN_DOUBLE_QUOTES,
628 : : };
629 : :
630 : : char *
631 : 41 : pkg_utils_tokenize(char **args)
632 : : {
633 : : char *p, *p_start;
634 : 41 : enum parse_states parse_state = START;
635 : :
636 [ + - ]: 41 : assert(*args != NULL);
637 : :
638 [ + + ]: 108 : for (p = p_start = *args; *p != '\0'; p++) {
639 [ + + - - : 91 : switch (parse_state) {
- - ]
640 : : case START:
641 [ + - ]: 41 : if (!isspace(*p)) {
642 [ - + ]: 41 : if (*p == '"')
643 : 0 : parse_state = OPEN_DOUBLE_QUOTES;
644 [ - + ]: 41 : else if (*p == '\'')
645 : 0 : parse_state = OPEN_SINGLE_QUOTES;
646 : : else {
647 : 41 : parse_state = ORDINARY_TEXT;
648 : 41 : p_start = p;
649 : : }
650 : 41 : } else
651 : 0 : p_start = p;
652 : 41 : break;
653 : : case ORDINARY_TEXT:
654 [ + + ]: 50 : if (isspace(*p))
655 : 24 : goto finish;
656 : 26 : break;
657 : : case OPEN_SINGLE_QUOTES:
658 : 0 : p_start = p;
659 [ # # ]: 0 : if (*p == '\'')
660 : 0 : goto finish;
661 : :
662 : 0 : parse_state = IN_SINGLE_QUOTES;
663 : 0 : break;
664 : : case IN_SINGLE_QUOTES:
665 [ # # ]: 0 : if (*p == '\'')
666 : 0 : goto finish;
667 : 0 : break;
668 : : case OPEN_DOUBLE_QUOTES:
669 : 0 : p_start = p;
670 [ # # ]: 0 : if (*p == '"')
671 : 0 : goto finish;
672 : 0 : parse_state = IN_DOUBLE_QUOTES;
673 : 0 : break;
674 : : case IN_DOUBLE_QUOTES:
675 [ # # ]: 0 : if (*p == '"')
676 : 0 : goto finish;
677 : 0 : break;
678 : : }
679 : 84 : }
680 : :
681 : : finish:
682 [ + + ]: 41 : if (*p == '\0')
683 : 17 : *args = NULL; /* All done */
684 : : else {
685 : 24 : *p = '\0';
686 : 24 : p++;
687 : 24 : if (*p == '\0' || parse_state == START)
688 : 0 : *args = NULL; /* whitespace or nothing left */
689 : : else
690 : 24 : *args = p;
691 : : }
692 : 41 : return (p_start);
693 : : }
694 : :
695 : : int
696 : 17 : pkg_utils_count_spaces(const char *args)
697 : : {
698 : : int spaces;
699 : : const char *p;
700 : :
701 [ + + ]: 108 : for (spaces = 0, p = args; *p != '\0'; p++)
702 : 115 : if (isspace(*p))
703 : 24 : spaces++;
704 : :
705 : 17 : return (spaces);
706 : : }
707 : :
708 : : /* unlike realpath(3), this routine does not expand symbolic links */
709 : : char *
710 : 1057 : pkg_absolutepath(const char *src, char *dest, size_t dest_size, bool fromroot) {
711 : : size_t dest_len, src_len, cur_len;
712 : : const char *cur, *next;
713 : :
714 : 1057 : src_len = strlen(src);
715 : 1057 : memset(dest, '\0', dest_size);
716 : :
717 : 1057 : if (src_len != 0 && src[0] != '/') {
718 [ # # ]: 0 : if (fromroot)
719 : 0 : *dest = '/';
720 : : /* relative path, we use cwd */
721 [ # # ]: 0 : else if (getcwd(dest, dest_size) == NULL)
722 : 0 : return (NULL);
723 : 0 : }
724 : 1057 : dest_len = strlen(dest);
725 : :
726 [ + + + + ]: 6867 : for (cur = next = src; next != NULL; cur = (next == NULL) ? NULL : next + 1) {
727 : 5810 : next = strchr(cur, '/');
728 [ + + ]: 5810 : if (next != NULL)
729 : 4753 : cur_len = next - cur;
730 : : else
731 : 1057 : cur_len = strlen(cur);
732 : :
733 : : /* check for special cases "", "." and ".." */
734 [ + + ]: 5810 : if (cur_len == 0)
735 : 1087 : continue;
736 : 4723 : else if (cur_len == 1 && cur[0] == '.')
737 : 0 : continue;
738 : 24 : else if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
739 : 0 : const char *slash = strrchr(dest, '/');
740 : 0 : if (slash != NULL) {
741 : 0 : dest_len = slash - dest;
742 : 0 : dest[dest_len] = '\0';
743 : 0 : }
744 : 0 : continue;
745 : : }
746 : :
747 [ - + ]: 4723 : if (dest_len + 1 + cur_len >= dest_size)
748 : 0 : return (NULL);
749 : 4723 : dest[dest_len++] = '/';
750 : 4723 : (void)memcpy(dest + dest_len, cur, cur_len);
751 : 4723 : dest_len += cur_len;
752 : 4723 : dest[dest_len] = '\0';
753 : 4723 : }
754 : :
755 : 1057 : if (dest_len == 0) {
756 [ # # ]: 0 : if (strlcpy(dest, "/", dest_size) >= dest_size)
757 : 0 : return (NULL);
758 : 0 : }
759 : :
760 : 1057 : return (dest);
761 : 1057 : }
762 : :
763 : : bool
764 : 35 : mkdirat_p(int fd, const char *path)
765 : : {
766 : : const char *next;
767 : : char *walk, *walkorig, pathdone[MAXPATHLEN];
768 : :
769 : 35 : walk = walkorig = xstrdup(path);
770 : 35 : pathdone[0] = '\0';
771 : :
772 [ + + ]: 148 : while ((next = strsep(&walk, "/")) != NULL) {
773 [ + + ]: 113 : if (*next == '\0')
774 : 1 : continue;
775 : 112 : strlcat(pathdone, next, sizeof(pathdone));
776 [ + + ]: 112 : if (mkdirat(fd, pathdone, 0755) == -1) {
777 [ + - ]: 19 : if (errno == EEXIST) {
778 : 19 : strlcat(pathdone, "/", sizeof(pathdone));
779 : 19 : continue;
780 : : }
781 : 0 : pkg_errno("Fail to create /%s", pathdone);
782 : 0 : free(walkorig);
783 : 0 : return (false);
784 : : }
785 : 93 : strlcat(pathdone, "/", sizeof(pathdone));
786 : : }
787 : 35 : free(walkorig);
788 : 35 : return (true);
789 : 35 : }
790 : :
791 : : int
792 : 58 : pkg_namecmp(struct pkg *a, struct pkg *b)
793 : : {
794 : :
795 : 58 : return (strcmp(a->name, b->name));
796 : : }
797 : :
798 : : int
799 : 46 : get_socketpair(int *pipe)
800 : : {
801 : : int r;
802 : :
803 : : #ifdef HAVE_DECL_SOCK_SEQPACKET
804 : 46 : r = socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, pipe);
805 : 46 : if (r == -1) {
806 : 0 : r = socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipe);
807 : 0 : }
808 : : #else
809 : : r = socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipe);
810 : : #endif
811 : :
812 : 46 : return (r);
813 : : }
814 : :
815 : : /*
816 : : * Modify the passed C-String by stripping off the last component delimited by '/'.
817 : : * Return the string. Return a constant "." when passed NULL or the empty string.
818 : : * FIXME: This routine corrupts memory when passed the empty string.
819 : : * FIXME: This routine should propagate NULL.
820 : : * TODO: Refactor at call sites.
821 : : */
822 : : char *
823 : 39 : get_dirname(char *d)
824 : : {
825 : : char *walk;
826 : :
827 [ - + ]: 39 : if (d == NULL)
828 : 0 : return (__DECONST(char *, "."));
829 : :
830 : 39 : walk = strrchr(d, '/');
831 [ - + ]: 39 : if (walk == NULL) {
832 : 0 : d[0] = '.';
833 : 0 : d[1] = '\0';
834 : 0 : } else {
835 : 39 : *walk = '\0';
836 : : }
837 : :
838 : 39 : return (d);
839 : 39 : }
840 : :
841 : : char *
842 : 25 : rtrimspace(char *buf)
843 : : {
844 : 25 : char *cp = buf + strlen(buf) -1;
845 : :
846 [ + + ]: 26 : while (cp > buf && isspace(*cp)) {
847 : 1 : *cp = 0;
848 : 1 : cp --;
849 : : }
850 : :
851 : 25 : return (buf);
852 : : }
853 : :
854 : : static int
855 : 0 : _copy_file(int from, int to)
856 : : {
857 : : char buf[BUFSIZ];
858 : 0 : ssize_t r, wresid, w = 0;
859 : : char *bufp;
860 : 0 : r = read(from, buf, BUFSIZ);
861 [ # # ]: 0 : if (r < 0)
862 : 0 : return (r);
863 : 0 : for (bufp = buf, wresid = r; ; bufp += w, wresid -= w) {
864 : 0 : w = write(to, bufp, wresid);
865 [ # # ]: 0 : if (w <= 0)
866 : 0 : break;
867 [ # # ]: 0 : if (w >= (ssize_t)wresid)
868 : 0 : break;
869 : 0 : }
870 [ # # ]: 0 : return (w < 0 ? w : r);
871 : 0 : }
872 : :
873 : : bool
874 : 1 : pkg_copy_file(int from, int to)
875 : : {
876 : : #ifdef HAVE_COPY_FILE_RANGE
877 : 1 : bool cfr = true;
878 : : #endif
879 : : int r;
880 : :
881 : 2 : do {
882 : : #ifdef HAVE_COPY_FILE_RANGE
883 : 2 : if (cfr) {
884 : 2 : r = copy_file_range(from, NULL, to, NULL, SSIZE_MAX,
885 : : 0);
886 : 2 : if (r < 0 && (errno == EINVAL || errno == EXDEV)) {
887 : : /* probably a non seekable FD */
888 : 0 : cfr = false;
889 : 0 : }
890 : 2 : }
891 : 4 : if (!cfr) {
892 : : #endif
893 : 0 : r = _copy_file(from, to);
894 : : #ifdef HAVE_COPY_FILE_RANGE
895 : 0 : }
896 : : #endif
897 [ + + ]: 2 : } while (r > 0);
898 : :
899 : 1 : return (r >= 0);
900 : : }
901 : :
902 : : static const unsigned char litchar[] =
903 : : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
904 : :
905 : : void
906 : 131 : append_random_suffix(char *buf, int buflen, int suflen)
907 : : {
908 : 131 : int nchars = strlen(buf);
909 : : char *pos;
910 : : int r;
911 : :
912 : : /* 2 being the "." and the \0 */
913 : 131 : if (nchars + suflen > buflen - 2) {
914 : 2 : suflen = buflen - nchars - 2;
915 [ - + ]: 2 : if (suflen <= 0)
916 : 0 : return;
917 : 2 : }
918 : :
919 : 131 : buf[nchars++] = '.';
920 : 131 : pos = buf + nchars;
921 : :
922 [ + + ]: 1692 : while(suflen --) {
923 : : #ifndef HAVE_ARC4RANDOM
924 : : r = rand() % (sizeof(litchar) - 1);
925 : : #else
926 : 1561 : r = arc4random_uniform(sizeof(litchar) - 1);
927 : : #endif
928 : 1561 : *pos++ = litchar[r];
929 : : }
930 : :
931 : 131 : *pos = '\0';
932 : 131 : }
933 : :
934 : : void
935 : 129 : hidden_tempfile(char *buf, int buflen, const char *path)
936 : : {
937 : : const char *fname;
938 : 129 : int suffixlen = 12;
939 : : int nbuflen;
940 : 129 : const char *prefix = ".pkgtemp.";
941 : :
942 : 129 : fname = strrchr(path, '/');
943 : 129 : if (fname != NULL)
944 : 127 : fname++;
945 : :
946 : : /*
947 : : * try to reduce the temporary name as much as possible to fit with very
948 : : * long file names if possible. by default
949 : : * .pkgtemp. fname . <suffix>
950 : : * otherwise
951 : : * . fname . <suffix>
952 : : * keep if suffix of at least 5 if possible
953 : : */
954 [ + + ]: 129 : if (fname != NULL) {
955 : 127 : if (strlen(fname) >= (NAME_MAX - 15))
956 : 1 : prefix = ".";
957 : 127 : snprintf(buf, buflen, "%.*s%s%s", (int)(fname - path), path, prefix, fname);
958 : 127 : nbuflen = buflen;
959 : 127 : } else {
960 : 2 : if (strlen(path) >= NAME_MAX - 15)
961 : 1 : prefix = ".";
962 : 2 : snprintf(buf, buflen, "%s%s", prefix, path);
963 : 2 : nbuflen = NAME_MAX;
964 : : }
965 : :
966 : :
967 : 129 : append_random_suffix(buf, nbuflen, suffixlen);
968 : 129 : }
969 : :
970 : : char *
971 : 1 : json_escape(const char *str)
972 : : {
973 : 1 : xstring *buf = xstring_new();
974 : :
975 [ + + ]: 11 : while (str != NULL && *str != '\0') {
976 : 10 : if (*str == '"' || *str == '\\')
977 : 3 : fputc('\\', buf->fp);
978 : 10 : fputc(*str, buf->fp);
979 : 10 : str++;
980 : : }
981 : :
982 : 1 : return (xstring_get(buf));
983 : : }
984 : :
985 : : struct tempdir *
986 : 131 : open_tempdir(int rootfd, const char *path, stringlist_t *symlinks_allowed)
987 : : {
988 : : struct stat st;
989 : : char walk[MAXPATHLEN];
990 : : char *dir;
991 : 131 : size_t cnt = 0, len;
992 : : struct tempdir *t;
993 : :
994 : 131 : strlcpy(walk, path, sizeof(walk));
995 : 257 : while ((dir = strrchr(walk, '/')) != NULL) {
996 : 257 : *dir = '\0';
997 : 257 : cnt++;
998 : : /* accept symlinks pointing to directories only for prefix */
999 : 257 : len = strlen(walk);
1000 : 257 : if (len == 0 && cnt == 1)
1001 : 12 : break;
1002 : 215 : if (len > 0) {
1003 : 215 : int flag = AT_SYMLINK_NOFOLLOW;
1004 : 215 : if (symlinks_allowed != NULL) {
1005 [ + - + + : 418 : tll_foreach(*symlinks_allowed, t) {
- + ]
1006 : 209 : if (STREQ(RELATIVE_PATH(walk), RELATIVE_PATH(t->item)))
1007 : 8 : flag = 0;
1008 : 209 : }
1009 : 209 : }
1010 [ + + ]: 215 : if (fstatat(rootfd, RELATIVE_PATH(walk), &st, flag) == -1)
1011 : 123 : continue;
1012 : 92 : if (S_ISDIR(st.st_mode) && cnt == 1)
1013 : 84 : break;
1014 [ + + ]: 8 : if (!S_ISDIR(st.st_mode))
1015 : 3 : continue;
1016 : 5 : }
1017 : 35 : *dir = '/';
1018 : 35 : t = xcalloc(1, sizeof(*t));
1019 : 35 : hidden_tempfile(t->temp, sizeof(t->temp), walk);
1020 [ - + ]: 35 : if (mkdirat(rootfd, RELATIVE_PATH(t->temp), 0755) == -1) {
1021 : 0 : pkg_errno("Fail to create temporary directory: %s", t->temp);
1022 : 0 : free(t);
1023 : 0 : return (NULL);
1024 : : }
1025 : :
1026 : 35 : strlcpy(t->name, walk, sizeof(t->name));
1027 : 35 : t->len = strlen(t->name);
1028 : 35 : t->fd = openat(rootfd, RELATIVE_PATH(t->temp), O_DIRECTORY);
1029 [ - + ]: 35 : if (t->fd == -1) {
1030 : 0 : pkg_errno("Fail to open directory %s", t->temp);
1031 : 0 : free(t);
1032 : 0 : return (NULL);
1033 : : }
1034 : 35 : return (t);
1035 : : }
1036 : 96 : errno = 0;
1037 : 96 : return (NULL);
1038 : 131 : }
1039 : :
1040 : : const char *
1041 : 16 : get_http_auth(void)
1042 : : {
1043 : 16 : const char *str = getenv("HTTP_AUTH");
1044 [ + + ]: 16 : if (str == NULL)
1045 : 12 : return (NULL);
1046 [ + + ]: 4 : if ((str = strchr(str, ':')) == NULL) {
1047 : 1 : pkg_emit_error("malformed HTTP_AUTH");
1048 : 1 : return (NULL);
1049 : : }
1050 [ + + ]: 3 : if ((str = strchr(++str, ':')) == NULL) {
1051 : 1 : pkg_emit_error("malformed HTTP_AUTH");
1052 : 1 : return (NULL);
1053 : : }
1054 [ + + ]: 2 : if (strchr(++str, ':') == NULL) {
1055 : 1 : pkg_emit_error("malformed HTTP_AUTH");
1056 : 1 : return (NULL);
1057 : : }
1058 : 1 : return (str);
1059 : 16 : }
1060 : :
1061 : : bool
1062 : 33 : c_charv_contains(c_charv_t *v, const char *el, bool casesensitive)
1063 : : {
1064 [ + + ]: 43 : for (size_t i = 0; i < v->len; i ++) {
1065 [ + + ]: 39 : if (casesensitive) {
1066 [ + + ]: 33 : if (STREQ(v->d[i], el))
1067 : 28 : return (true);
1068 : 5 : } else {
1069 [ + + ]: 6 : if (STRIEQ(v->d[i], el)) {
1070 : 1 : return (true);
1071 : : }
1072 : : }
1073 : 10 : }
1074 : 4 : return (false);
1075 : 33 : }
|