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 : 1193 : 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 : 1193 : 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 : for (size_t i = 0; i < hl->len; i++) {
327 : 10 : h = hl->d[i];
328 : 10 : if (h->ino == st->st_ino &&
329 : 10 : h->dev == st->st_dev)
330 : 10 : return (true);
331 : 0 : }
332 : 10 : h = xcalloc(1, sizeof(*h));
333 : 10 : h->ino = st->st_ino;
334 : 10 : h->dev = st->st_dev;
335 [ + - - + ]: 10 : pkgvec_push(hl, h);
336 : :
337 : 10 : return (false);
338 : 20 : }
339 : :
340 : : /*
341 : : * ABI validation:
342 : : * - lowest match (case insensitive)
343 : : * - glob matching
344 : : *
345 : : * A package which is valid for installation on any FreeBSD now simply has
346 : : * to define itself as: abi: "FreeBSD" or "FreeBSD:*"
347 : : * A package which is valid for installation on any FreeBSD 15 regarless of
348 : : * the arch
349 : : * abi: "FreeBSD:15" or "FreeBSD:15:*"
350 : : *
351 : : */
352 : :
353 : : bool
354 : 167 : is_valid_abi(const char *testabi, bool emit_error)
355 : : {
356 : 167 : const char *abi = pkg_object_string(pkg_config_get("ABI"));
357 : :
358 : 167 : if (strncasecmp(testabi, abi, strlen(testabi)) != 0 &&
359 : 45 : fnmatch(testabi, abi, FNM_CASEFOLD) == FNM_NOMATCH) {
360 : 0 : if (emit_error)
361 : 0 : pkg_emit_error("wrong architecture: %s instead of %s",
362 : 0 : testabi, abi);
363 : 0 : return (false);
364 : : }
365 : :
366 : 167 : return (true);
367 : 167 : }
368 : :
369 : : bool
370 : 167 : is_valid_os_version(struct pkg *pkg)
371 : : {
372 [ - + ]: 167 : if (ctx.abi.os != PKG_OS_FREEBSD)
373 : 0 : return (true);
374 : : const char *fbsd_version;
375 : 167 : const char *errstr = NULL;
376 : : char query_buf[512];
377 : : /* -1: not checked, 0: not allowed, 1: allowed */
378 : : static int osver_mismatch_allowed = -1;
379 : : bool ret;
380 : :
381 [ - + ]: 167 : if (pkg_object_bool(pkg_config_get("IGNORE_OSVERSION")))
382 : 0 : return (true);
383 : 167 : if ((fbsd_version = pkg_kv_get(&pkg->annotations, "FreeBSD_version")) != NULL) {
384 : 122 : int pkg_osversion = strtonum(fbsd_version, 1, INT_MAX, &errstr);
385 [ - + ]: 122 : if (errstr != NULL) {
386 : 0 : pkg_emit_error("Invalid FreeBSD version %s for package %s",
387 : 0 : fbsd_version, pkg->name);
388 : 0 : return (false);
389 : : }
390 : 122 : int abi_osversion = pkg_abi_get_freebsd_osversion(&ctx.abi);
391 [ - + ]: 122 : if (pkg_osversion > abi_osversion) {
392 [ # # ]: 0 : if (pkg_osversion - abi_osversion < 100000) {
393 : : /* Negligible difference, ask user to enforce */
394 : 0 : if (osver_mismatch_allowed == -1) {
395 : 0 : snprintf(query_buf, sizeof(query_buf),
396 : : "Newer FreeBSD version for package %s:\n"
397 : : "To ignore this error set IGNORE_OSVERSION=yes\n"
398 : : "- package: %d\n"
399 : : "- running userland: %d\n"
400 : 0 : "Ignore the mismatch and continue? ", pkg->name,
401 : 0 : pkg_osversion, abi_osversion);
402 : 0 : ret = pkg_emit_query_yesno(false, query_buf);
403 : 0 : osver_mismatch_allowed = ret;
404 : 0 : }
405 : :
406 : 0 : return (osver_mismatch_allowed);
407 : : }
408 : : else {
409 : 0 : pkg_emit_error("Newer FreeBSD version for package %s:\n"
410 : : "To ignore this error set IGNORE_OSVERSION=yes\n"
411 : : "- package: %d\n"
412 : : "- running kernel: %d\n",
413 : 0 : pkg->name,
414 : 0 : pkg_osversion, abi_osversion);
415 : 0 : return (false);
416 : : }
417 : : }
418 : 122 : }
419 : 167 : return (true);
420 : 167 : }
421 : :
422 : : void
423 : 0 : set_nonblocking(int fd)
424 : : {
425 : : int flags;
426 : :
427 [ # # ]: 0 : if ((flags = fcntl(fd, F_GETFL)) == -1)
428 : 0 : return;
429 : 0 : if (!(flags & O_NONBLOCK)) {
430 : 0 : flags |= O_NONBLOCK;
431 : 0 : fcntl(fd, F_SETFL, flags);
432 : 0 : }
433 : 0 : }
434 : :
435 : : void
436 : 0 : set_blocking(int fd)
437 : : {
438 : : int flags;
439 : :
440 [ # # ]: 0 : if ((flags = fcntl(fd, F_GETFL)) == -1)
441 : 0 : return;
442 : 0 : if (flags & O_NONBLOCK) {
443 : 0 : flags &= ~O_NONBLOCK;
444 : 0 : fcntl(fd, F_SETFL, flags);
445 : 0 : }
446 : 0 : }
447 : :
448 : : /* Spawn a process from pfunc, returning it's pid. The fds array passed will
449 : : * be filled with two descriptors: fds[0] will read from the child process,
450 : : * and fds[1] will write to it.
451 : : * Similarly, the child process will receive a reading/writing fd set (in
452 : : * that same order) as arguments.
453 : : */
454 : : extern char **environ;
455 : : pid_t
456 : 6 : process_spawn_pipe(FILE *inout[2], const char *command)
457 : : {
458 : : pid_t pid;
459 : : int pipes[4];
460 : : char *argv[4];
461 : :
462 : : /* Parent read/child write pipe */
463 [ - + ]: 6 : if (pipe(&pipes[0]) == -1)
464 : 0 : return (-1);
465 : :
466 : : /* Child read/parent write pipe */
467 [ - + ]: 6 : if (pipe(&pipes[2]) == -1) {
468 : 0 : close(pipes[0]);
469 : 0 : close(pipes[1]);
470 : 0 : return (-1);
471 : : }
472 : :
473 : 6 : argv[0] = __DECONST(char *, "sh");
474 : 6 : argv[1] = __DECONST(char *, "-c");
475 : 6 : argv[2] = __DECONST(char *, command);
476 : 6 : argv[3] = NULL;
477 : :
478 : 6 : pid = fork();
479 [ + + ]: 6 : if (pid > 0) {
480 : : /* Parent process */
481 : 6 : inout[0] = fdopen(pipes[0], "r");
482 : 6 : inout[1] = fdopen(pipes[3], "w");
483 : :
484 : 6 : close(pipes[1]);
485 : 6 : close(pipes[2]);
486 : :
487 : 6 : return (pid);
488 : :
489 [ + - ]: 6 : } else if (pid == 0) {
490 : 6 : close(pipes[0]);
491 : 6 : close(pipes[3]);
492 : :
493 : 6 : if (pipes[1] != STDOUT_FILENO) {
494 : 6 : dup2(pipes[1], STDOUT_FILENO);
495 : 6 : close(pipes[1]);
496 : 6 : }
497 : 6 : if (pipes[2] != STDIN_FILENO) {
498 : 6 : dup2(pipes[2], STDIN_FILENO);
499 : 6 : close(pipes[2]);
500 : 6 : }
501 : 12 : closefrom(STDERR_FILENO + 1);
502 : :
503 : 12 : execve(_PATH_BSHELL, argv, environ);
504 : :
505 : 0 : _exit(127);
506 : : }
507 : :
508 : 0 : return (-1); /* ? */
509 : 6 : }
510 : :
511 : : static int
512 : 0 : ucl_buf_append_character(unsigned char c, size_t len, void *data)
513 : : {
514 : 0 : xstring *buf = data;
515 : : size_t i;
516 : :
517 [ # # ]: 0 : for (i = 0; i < len; i++)
518 : 0 : fprintf(buf->fp, "%c", c);
519 : :
520 : 0 : return (0);
521 : : }
522 : :
523 : : static int
524 : 0 : ucl_buf_append_len(const unsigned char *str, size_t len, void *data)
525 : : {
526 : 0 : xstring *buf = data;
527 : :
528 : 0 : fprintf(buf->fp, "%.*s", (int)len, str);
529 : :
530 : 0 : return (0);
531 : : }
532 : :
533 : : static int
534 : 0 : ucl_buf_append_int(int64_t val, void *data)
535 : : {
536 : 0 : xstring *buf = data;
537 : :
538 : 0 : fprintf(buf->fp, "%"PRId64, val);
539 : :
540 : 0 : return (0);
541 : : }
542 : :
543 : : static int
544 : 0 : ucl_buf_append_double(double val, void *data)
545 : : {
546 : 0 : xstring *buf = data;
547 : 0 : const double delta = 0.0000001;
548 : :
549 [ # # ]: 0 : if (val == (double)(int)val) {
550 : 0 : fprintf(buf->fp, "%.1lf", val);
551 [ # # ]: 0 : } else if (fabs(val - (double)(int)val) < delta) {
552 : 0 : fprintf(buf->fp, "%.*lg", DBL_DIG, val);
553 : 0 : } else {
554 : 0 : fprintf(buf->fp, "%lf", val);
555 : : }
556 : :
557 : 0 : return (0);
558 : : }
559 : :
560 : : bool
561 : 258 : ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,
562 : : FILE *out)
563 : : {
564 : 258 : struct ucl_emitter_functions *f = ucl_object_emit_file_funcs(out);
565 : 258 : bool ret = false;
566 : :
567 [ - + ]: 258 : if (obj == NULL)
568 : 0 : return (false);
569 : :
570 : 258 : ret = ucl_object_emit_full(obj, emit_type, f, NULL);
571 : 258 : ucl_object_emit_funcs_free(f);
572 : :
573 : 258 : return (ret);
574 : 258 : }
575 : :
576 : : bool
577 : 294 : ucl_object_emit_fd(const ucl_object_t *obj, enum ucl_emitter emit_type, int fd)
578 : : {
579 : 294 : struct ucl_emitter_functions *f = ucl_object_emit_fd_funcs(fd);
580 : 294 : bool ret = false;
581 : :
582 [ - + ]: 294 : if (obj == NULL)
583 : 0 : return (false);
584 : 294 : ret = ucl_object_emit_full(obj, emit_type, f, NULL);
585 : 294 : ucl_object_emit_funcs_free(f);
586 : :
587 : 294 : return (ret);
588 : 294 : }
589 : :
590 : :
591 : : bool
592 : 0 : ucl_object_emit_buf(const ucl_object_t *obj, enum ucl_emitter emit_type,
593 : : xstring **buf)
594 : : {
595 : 0 : bool ret = false;
596 : 0 : struct ucl_emitter_functions func = {
597 : : .ucl_emitter_append_character = ucl_buf_append_character,
598 : : .ucl_emitter_append_len = ucl_buf_append_len,
599 : : .ucl_emitter_append_int = ucl_buf_append_int,
600 : : .ucl_emitter_append_double = ucl_buf_append_double
601 : : };
602 : :
603 [ # # ]: 0 : xstring_renew(*buf);
604 : :
605 : 0 : func.ud = *buf;
606 : :
607 : 0 : ret = ucl_object_emit_full(obj, emit_type, &func, NULL);
608 : :
609 : 0 : return (ret);
610 : : }
611 : :
612 : : /* A bit like strsep(), except it accounts for "double" and 'single'
613 : : quotes. Unlike strsep(), returns the next arg string, trimmed of
614 : : whitespace or enclosing quotes, and updates **args to point at the
615 : : character after that. Sets *args to NULL when it has been
616 : : completely consumed. Quoted strings run from the first encountered
617 : : quotemark to the next one of the same type or the terminating NULL.
618 : : Quoted strings can contain the /other/ type of quote mark, which
619 : : loses any special significance. There isn't an escape
620 : : character. */
621 : :
622 : : enum parse_states {
623 : : START,
624 : : ORDINARY_TEXT,
625 : : OPEN_SINGLE_QUOTES,
626 : : IN_SINGLE_QUOTES,
627 : : OPEN_DOUBLE_QUOTES,
628 : : IN_DOUBLE_QUOTES,
629 : : };
630 : :
631 : : char *
632 : 41 : pkg_utils_tokenize(char **args)
633 : : {
634 : : char *p, *p_start;
635 : 41 : enum parse_states parse_state = START;
636 : :
637 [ + - ]: 41 : assert(*args != NULL);
638 : :
639 [ + + ]: 108 : for (p = p_start = *args; *p != '\0'; p++) {
640 [ + + - - : 91 : switch (parse_state) {
- - ]
641 : : case START:
642 [ + - ]: 41 : if (!isspace(*p)) {
643 [ - + ]: 41 : if (*p == '"')
644 : 0 : parse_state = OPEN_DOUBLE_QUOTES;
645 [ - + ]: 41 : else if (*p == '\'')
646 : 0 : parse_state = OPEN_SINGLE_QUOTES;
647 : : else {
648 : 41 : parse_state = ORDINARY_TEXT;
649 : 41 : p_start = p;
650 : : }
651 : 41 : } else
652 : 0 : p_start = p;
653 : 41 : break;
654 : : case ORDINARY_TEXT:
655 [ + + ]: 50 : if (isspace(*p))
656 : 24 : goto finish;
657 : 26 : break;
658 : : case OPEN_SINGLE_QUOTES:
659 : 0 : p_start = p;
660 [ # # ]: 0 : if (*p == '\'')
661 : 0 : goto finish;
662 : :
663 : 0 : parse_state = IN_SINGLE_QUOTES;
664 : 0 : break;
665 : : case IN_SINGLE_QUOTES:
666 [ # # ]: 0 : if (*p == '\'')
667 : 0 : goto finish;
668 : 0 : break;
669 : : case OPEN_DOUBLE_QUOTES:
670 : 0 : p_start = p;
671 [ # # ]: 0 : if (*p == '"')
672 : 0 : goto finish;
673 : 0 : parse_state = IN_DOUBLE_QUOTES;
674 : 0 : break;
675 : : case IN_DOUBLE_QUOTES:
676 [ # # ]: 0 : if (*p == '"')
677 : 0 : goto finish;
678 : 0 : break;
679 : : }
680 : 84 : }
681 : :
682 : : finish:
683 [ + + ]: 41 : if (*p == '\0')
684 : 17 : *args = NULL; /* All done */
685 : : else {
686 : 24 : *p = '\0';
687 : 24 : p++;
688 : 24 : if (*p == '\0' || parse_state == START)
689 : 0 : *args = NULL; /* whitespace or nothing left */
690 : : else
691 : 24 : *args = p;
692 : : }
693 : 41 : return (p_start);
694 : : }
695 : :
696 : : int
697 : 17 : pkg_utils_count_spaces(const char *args)
698 : : {
699 : : int spaces;
700 : : const char *p;
701 : :
702 [ + + ]: 108 : for (spaces = 0, p = args; *p != '\0'; p++)
703 : 115 : if (isspace(*p))
704 : 24 : spaces++;
705 : :
706 : 17 : return (spaces);
707 : : }
708 : :
709 : : /* unlike realpath(3), this routine does not expand symbolic links */
710 : : char *
711 : 1057 : pkg_absolutepath(const char *src, char *dest, size_t dest_size, bool fromroot) {
712 : : size_t dest_len, src_len, cur_len;
713 : : const char *cur, *next;
714 : :
715 : 1057 : src_len = strlen(src);
716 : 1057 : memset(dest, '\0', dest_size);
717 : :
718 : 1057 : if (src_len != 0 && src[0] != '/') {
719 [ # # ]: 0 : if (fromroot)
720 : 0 : *dest = '/';
721 : : /* relative path, we use cwd */
722 [ # # ]: 0 : else if (getcwd(dest, dest_size) == NULL)
723 : 0 : return (NULL);
724 : 0 : }
725 : 1057 : dest_len = strlen(dest);
726 : :
727 [ + + + + ]: 6867 : for (cur = next = src; next != NULL; cur = (next == NULL) ? NULL : next + 1) {
728 : 5810 : next = strchr(cur, '/');
729 [ + + ]: 5810 : if (next != NULL)
730 : 4753 : cur_len = next - cur;
731 : : else
732 : 1057 : cur_len = strlen(cur);
733 : :
734 : : /* check for special cases "", "." and ".." */
735 [ + + ]: 5810 : if (cur_len == 0)
736 : 1087 : continue;
737 : 4723 : else if (cur_len == 1 && cur[0] == '.')
738 : 0 : continue;
739 : 24 : else if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
740 : 0 : const char *slash = strrchr(dest, '/');
741 : 0 : if (slash != NULL) {
742 : 0 : dest_len = slash - dest;
743 : 0 : dest[dest_len] = '\0';
744 : 0 : }
745 : 0 : continue;
746 : : }
747 : :
748 [ - + ]: 4723 : if (dest_len + 1 + cur_len >= dest_size)
749 : 0 : return (NULL);
750 : 4723 : dest[dest_len++] = '/';
751 : 4723 : (void)memcpy(dest + dest_len, cur, cur_len);
752 : 4723 : dest_len += cur_len;
753 : 4723 : dest[dest_len] = '\0';
754 : 4723 : }
755 : :
756 : 1057 : if (dest_len == 0) {
757 [ # # ]: 0 : if (strlcpy(dest, "/", dest_size) >= dest_size)
758 : 0 : return (NULL);
759 : 0 : }
760 : :
761 : 1057 : return (dest);
762 : 1057 : }
763 : :
764 : : bool
765 : 35 : mkdirat_p(int fd, const char *path)
766 : : {
767 : : const char *next;
768 : : char *walk, *walkorig, pathdone[MAXPATHLEN];
769 : :
770 : 35 : walk = walkorig = xstrdup(path);
771 : 35 : pathdone[0] = '\0';
772 : :
773 [ + + ]: 148 : while ((next = strsep(&walk, "/")) != NULL) {
774 [ + + ]: 113 : if (*next == '\0')
775 : 1 : continue;
776 : 112 : strlcat(pathdone, next, sizeof(pathdone));
777 [ + + ]: 112 : if (mkdirat(fd, pathdone, 0755) == -1) {
778 [ + - ]: 19 : if (errno == EEXIST) {
779 : 19 : strlcat(pathdone, "/", sizeof(pathdone));
780 : 19 : continue;
781 : : }
782 : 0 : pkg_errno("Fail to create /%s", pathdone);
783 : 0 : free(walkorig);
784 : 0 : return (false);
785 : : }
786 : 93 : strlcat(pathdone, "/", sizeof(pathdone));
787 : : }
788 : 35 : free(walkorig);
789 : 35 : return (true);
790 : 35 : }
791 : :
792 : : int
793 : 58 : pkg_namecmp(struct pkg *a, struct pkg *b)
794 : : {
795 : :
796 : 58 : return (strcmp(a->name, b->name));
797 : : }
798 : :
799 : : int
800 : 46 : get_socketpair(int *pipe)
801 : : {
802 : : int r;
803 : :
804 : : #ifdef HAVE_DECL_SOCK_SEQPACKET
805 : 46 : r = socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, pipe);
806 : 46 : if (r == -1) {
807 : 0 : r = socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipe);
808 : 0 : }
809 : : #else
810 : : r = socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipe);
811 : : #endif
812 : :
813 : 46 : return (r);
814 : : }
815 : :
816 : : /*
817 : : * Modify the passed C-String by stripping off the last component delimited by '/'.
818 : : * Return the string. Return a constant "." when passed NULL or the empty string.
819 : : * FIXME: This routine corrupts memory when passed the empty string.
820 : : * FIXME: This routine should propagate NULL.
821 : : * TODO: Refactor at call sites.
822 : : */
823 : : char *
824 : 39 : get_dirname(char *d)
825 : : {
826 : : char *walk;
827 : :
828 [ - + ]: 39 : if (d == NULL)
829 : 0 : return (__DECONST(char *, "."));
830 : :
831 : 39 : walk = strrchr(d, '/');
832 [ - + ]: 39 : if (walk == NULL) {
833 : 0 : d[0] = '.';
834 : 0 : d[1] = '\0';
835 : 0 : } else {
836 : 39 : *walk = '\0';
837 : : }
838 : :
839 : 39 : return (d);
840 : 39 : }
841 : :
842 : : char *
843 : 25 : rtrimspace(char *buf)
844 : : {
845 : 25 : char *cp = buf + strlen(buf) -1;
846 : :
847 [ + + ]: 26 : while (cp > buf && isspace(*cp)) {
848 : 1 : *cp = 0;
849 : 1 : cp --;
850 : : }
851 : :
852 : 25 : return (buf);
853 : : }
854 : :
855 : : static int
856 : 0 : _copy_file(int from, int to)
857 : : {
858 : : char buf[BUFSIZ];
859 : 0 : ssize_t r, wresid, w = 0;
860 : : char *bufp;
861 : 0 : r = read(from, buf, BUFSIZ);
862 [ # # ]: 0 : if (r < 0)
863 : 0 : return (r);
864 : 0 : for (bufp = buf, wresid = r; ; bufp += w, wresid -= w) {
865 : 0 : w = write(to, bufp, wresid);
866 [ # # ]: 0 : if (w <= 0)
867 : 0 : break;
868 [ # # ]: 0 : if (w >= (ssize_t)wresid)
869 : 0 : break;
870 : 0 : }
871 [ # # ]: 0 : return (w < 0 ? w : r);
872 : 0 : }
873 : :
874 : : bool
875 : 1 : pkg_copy_file(int from, int to)
876 : : {
877 : : #ifdef HAVE_COPY_FILE_RANGE
878 : 1 : bool cfr = true;
879 : : #endif
880 : : int r;
881 : :
882 : 2 : do {
883 : : #ifdef HAVE_COPY_FILE_RANGE
884 : 2 : if (cfr) {
885 : 2 : r = copy_file_range(from, NULL, to, NULL, SSIZE_MAX,
886 : : 0);
887 : 2 : if (r < 0 && (errno == EINVAL || errno == EXDEV)) {
888 : : /* probably a non seekable FD */
889 : 0 : cfr = false;
890 : 0 : }
891 : 2 : }
892 : 4 : if (!cfr) {
893 : : #endif
894 : 0 : r = _copy_file(from, to);
895 : : #ifdef HAVE_COPY_FILE_RANGE
896 : 0 : }
897 : : #endif
898 [ + + ]: 2 : } while (r > 0);
899 : :
900 : 1 : return (r >= 0);
901 : : }
902 : :
903 : : static const unsigned char litchar[] =
904 : : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
905 : :
906 : : void
907 : 131 : append_random_suffix(char *buf, int buflen, int suflen)
908 : : {
909 : 131 : int nchars = strlen(buf);
910 : : char *pos;
911 : : int r;
912 : :
913 : : /* 2 being the "." and the \0 */
914 : 131 : if (nchars + suflen > buflen - 2) {
915 : 2 : suflen = buflen - nchars - 2;
916 [ - + ]: 2 : if (suflen <= 0)
917 : 0 : return;
918 : 2 : }
919 : :
920 : 131 : buf[nchars++] = '.';
921 : 131 : pos = buf + nchars;
922 : :
923 [ + + ]: 1692 : while(suflen --) {
924 : : #ifndef HAVE_ARC4RANDOM
925 : : r = rand() % (sizeof(litchar) - 1);
926 : : #else
927 : 1561 : r = arc4random_uniform(sizeof(litchar) - 1);
928 : : #endif
929 : 1561 : *pos++ = litchar[r];
930 : : }
931 : :
932 : 131 : *pos = '\0';
933 : 131 : }
934 : :
935 : : void
936 : 129 : hidden_tempfile(char *buf, int buflen, const char *path)
937 : : {
938 : : const char *fname;
939 : 129 : int suffixlen = 12;
940 : : int nbuflen;
941 : 129 : const char *prefix = ".pkgtemp.";
942 : :
943 : 129 : fname = strrchr(path, '/');
944 : 129 : if (fname != NULL)
945 : 127 : fname++;
946 : :
947 : : /*
948 : : * try to reduce the temporary name as much as possible to fit with very
949 : : * long file names if possible. by default
950 : : * .pkgtemp. fname . <suffix>
951 : : * otherwise
952 : : * . fname . <suffix>
953 : : * keep if suffix of at least 5 if possible
954 : : */
955 [ + + ]: 129 : if (fname != NULL) {
956 : 127 : if (strlen(fname) >= (NAME_MAX - 15))
957 : 1 : prefix = ".";
958 : 127 : snprintf(buf, buflen, "%.*s%s%s", (int)(fname - path), path, prefix, fname);
959 : 127 : nbuflen = buflen;
960 : 127 : } else {
961 : 2 : if (strlen(path) >= NAME_MAX - 15)
962 : 1 : prefix = ".";
963 : 2 : snprintf(buf, buflen, "%s%s", prefix, path);
964 : 2 : nbuflen = NAME_MAX;
965 : : }
966 : :
967 : :
968 : 129 : append_random_suffix(buf, nbuflen, suffixlen);
969 : 129 : }
970 : :
971 : : char *
972 : 1 : json_escape(const char *str)
973 : : {
974 : 1 : xstring *buf = xstring_new();
975 : :
976 [ + + ]: 11 : while (str != NULL && *str != '\0') {
977 : 10 : if (*str == '"' || *str == '\\')
978 : 3 : fputc('\\', buf->fp);
979 : 10 : fputc(*str, buf->fp);
980 : 10 : str++;
981 : : }
982 : :
983 : 1 : return (xstring_get(buf));
984 : : }
985 : :
986 : : struct tempdir *
987 : 131 : open_tempdir(int rootfd, const char *path, c_charv_t *symlinks_allowed)
988 : : {
989 : : struct stat st;
990 : : char walk[MAXPATHLEN];
991 : : char *dir;
992 : 131 : size_t cnt = 0, len;
993 : : struct tempdir *t;
994 : :
995 : 131 : strlcpy(walk, path, sizeof(walk));
996 : 257 : while ((dir = strrchr(walk, '/')) != NULL) {
997 : 257 : *dir = '\0';
998 : 257 : cnt++;
999 : : /* accept symlinks pointing to directories only for prefix */
1000 : 257 : len = strlen(walk);
1001 : 257 : if (len == 0 && cnt == 1)
1002 : 12 : break;
1003 : 215 : if (len > 0) {
1004 : 215 : int flag = AT_SYMLINK_NOFOLLOW;
1005 : 215 : if (symlinks_allowed != NULL) {
1006 [ + + ]: 418 : for (size_t i = 0; i < symlinks_allowed->len; i++) {
1007 : 209 : if (STREQ(RELATIVE_PATH(walk), RELATIVE_PATH(symlinks_allowed->d[i])))
1008 : 8 : flag = 0;
1009 : 209 : }
1010 : 209 : }
1011 [ + + ]: 215 : if (fstatat(rootfd, RELATIVE_PATH(walk), &st, flag) == -1)
1012 : 123 : continue;
1013 : 92 : if (S_ISDIR(st.st_mode) && cnt == 1)
1014 : 84 : break;
1015 [ + + ]: 8 : if (!S_ISDIR(st.st_mode))
1016 : 3 : continue;
1017 : 5 : }
1018 : 35 : *dir = '/';
1019 : 35 : t = xcalloc(1, sizeof(*t));
1020 : 35 : hidden_tempfile(t->temp, sizeof(t->temp), walk);
1021 [ - + ]: 35 : if (mkdirat(rootfd, RELATIVE_PATH(t->temp), 0755) == -1) {
1022 : 0 : pkg_errno("Fail to create temporary directory: %s", t->temp);
1023 : 0 : free(t);
1024 : 0 : return (NULL);
1025 : : }
1026 : :
1027 : 35 : strlcpy(t->name, walk, sizeof(t->name));
1028 : 35 : t->len = strlen(t->name);
1029 : 35 : t->fd = openat(rootfd, RELATIVE_PATH(t->temp), O_DIRECTORY);
1030 [ - + ]: 35 : if (t->fd == -1) {
1031 : 0 : pkg_errno("Fail to open directory %s", t->temp);
1032 : 0 : free(t);
1033 : 0 : return (NULL);
1034 : : }
1035 : 35 : return (t);
1036 : : }
1037 : 96 : errno = 0;
1038 : 96 : return (NULL);
1039 : 131 : }
1040 : :
1041 : : const char *
1042 : 16 : get_http_auth(void)
1043 : : {
1044 : 16 : const char *str = getenv("HTTP_AUTH");
1045 [ + + ]: 16 : if (str == NULL)
1046 : 12 : return (NULL);
1047 [ + + ]: 4 : if ((str = strchr(str, ':')) == NULL) {
1048 : 1 : pkg_emit_error("malformed HTTP_AUTH");
1049 : 1 : return (NULL);
1050 : : }
1051 [ + + ]: 3 : if ((str = strchr(++str, ':')) == NULL) {
1052 : 1 : pkg_emit_error("malformed HTTP_AUTH");
1053 : 1 : return (NULL);
1054 : : }
1055 [ + + ]: 2 : if (strchr(++str, ':') == NULL) {
1056 : 1 : pkg_emit_error("malformed HTTP_AUTH");
1057 : 1 : return (NULL);
1058 : : }
1059 : 1 : return (str);
1060 : 16 : }
1061 : :
1062 : : bool
1063 : 33 : c_charv_contains(c_charv_t *v, const char *el, bool casesensitive)
1064 : : {
1065 [ + + ]: 43 : for (size_t i = 0; i < v->len; i ++) {
1066 [ + + ]: 39 : if (casesensitive) {
1067 [ + + ]: 33 : if (STREQ(v->d[i], el))
1068 : 28 : return (true);
1069 : 5 : } else {
1070 [ + + ]: 6 : if (STRIEQ(v->d[i], el)) {
1071 : 1 : return (true);
1072 : : }
1073 : : }
1074 : 10 : }
1075 : 4 : return (false);
1076 : 33 : }
|