utils.c
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 #include <pwd.h> 53 #include <grp.h> 54 55 #include <bsd_compat.h> 56 57 #include "pkg.h" 58 #include "pkg/vec.h" 59 #include "private/event.h" 60 #include "private/pkg_abi.h" 61 #include "private/utils.h" 62 #include "private/pkg.h" 63 #include "xmalloc.h" 64 65 extern struct pkg_ctx ctx; 66 67 bool 68 match_ucl_lists(const char *buf, const ucl_object_t *globs, const ucl_object_t *regexes) 69 { 70 const ucl_object_t *cur; 71 ucl_object_iter_t it; 72 73 if (globs == NULL && regexes == NULL) 74 return (false); 75 76 if (globs != NULL) { 77 it = NULL; 78 while ((cur = ucl_iterate_object(globs, &it, true))) { 79 if (fnmatch(ucl_object_tostring(cur), buf, 0) == 0) 80 return (true); 81 } 82 } 83 84 if (regexes != NULL) { 85 it = NULL; 86 while ((cur = ucl_iterate_object(regexes, &it, true))) { 87 regex_t re; 88 regcomp(&re, ucl_object_tostring(cur), 89 REG_EXTENDED|REG_NOSUB); 90 if (regexec(&re, buf, 0, NULL, 0) == 0) { 91 regfree(&re); 92 return (true); 93 } 94 regfree(&re); 95 } 96 } 97 98 return (false); 99 } 100 101 /* Check if two absolute directory paths are equal, collapsing consecutive 102 * path separators and ignoring trailing path separators. */ 103 static bool 104 dir_paths_equal(const char *a, const char *b) 105 { 106 assert(a != NULL); 107 assert(b != NULL); 108 assert(*a == '/'); 109 assert(*b == '/'); 110 111 while (*a == *b) { 112 if (*a == '\0') { 113 return (true); 114 } 115 116 /* Skip over consecutive path separators */ 117 if (*a == '/') { 118 while (*a == '/') a++; 119 while (*b == '/') b++; 120 } else { 121 a++; 122 b++; 123 } 124 } 125 126 /* There may be trailing path separators on one path but not the other */ 127 while (*a == '/') a++; 128 while (*b == '/') b++; 129 130 return (*a == *b); 131 } 132 133 /* 134 * Given a ucl list of directory paths, check if the file is in one of the 135 * directories in the list (subdirectories not included). 136 * 137 * Asserts that file is an absolute path that does not end in /. */ 138 bool 139 pkg_match_paths_list(const ucl_object_t *paths, const char *file) 140 { 141 assert(file != NULL); 142 assert(file[0] == '/'); 143 144 char *copy = xstrdup(file); 145 char *final_slash = strrchr(copy, '/'); 146 assert(final_slash != NULL); 147 assert(*(final_slash + 1) != '\0'); 148 if (final_slash == copy) { 149 *(final_slash + 1) = '\0'; 150 } else { 151 *final_slash = '\0'; 152 } 153 const char *dirname = copy; 154 155 bool found = false; 156 const ucl_object_t *cur; 157 ucl_object_iter_t it = NULL; 158 while ((cur = ucl_object_iterate(paths, &it, true))) { 159 if (dir_paths_equal(dirname, ucl_object_tostring(cur))) { 160 found = true; 161 break; 162 } 163 } 164 165 free(copy); 166 167 return (found); 168 } 169 170 int 171 pkg_mkdirs(const char *_path) 172 { 173 char path[MAXPATHLEN]; 174 char *p; 175 int dirfd; 176 177 dirfd = open(_path, O_RDONLY|O_DIRECTORY); 178 if (dirfd >= 0) { 179 close(dirfd); 180 return EPKG_OK; 181 } 182 183 strlcpy(path, _path, sizeof(path)); 184 p = path; 185 while (*p == '/') 186 p++; 187 188 for (;;) { 189 if ((p = strchr(p, '/')) != NULL) 190 *p = '\0'; 191 192 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) 193 if (errno != EEXIST && errno != EISDIR) { 194 pkg_emit_errno("mkdir", path); 195 return (EPKG_FATAL); 196 } 197 198 /* that was the last element of the path */ 199 if (p == NULL) 200 break; 201 202 *p = '/'; 203 p++; 204 } 205 206 return (EPKG_OK); 207 } 208 int 209 file_to_bufferat(int dfd, const char *path, char **buffer, off_t *sz) 210 { 211 int fd = -1; 212 struct stat st; 213 int retcode = EPKG_OK; 214 215 assert(path != NULL && path[0] != '\0'); 216 assert(buffer != NULL); 217 assert(sz != NULL); 218 219 if ((fd = openat(dfd, path, O_RDONLY)) == -1) { 220 pkg_emit_errno("openat", path); 221 retcode = EPKG_FATAL; 222 goto cleanup; 223 } 224 225 if (fstat(fd, &st) == -1) { 226 pkg_emit_errno("fstatat", path); 227 retcode = EPKG_FATAL; 228 goto cleanup; 229 } 230 231 *buffer = xmalloc(st.st_size + 1); 232 233 if (read(fd, *buffer, st.st_size) == -1) { 234 pkg_emit_errno("read", path); 235 retcode = EPKG_FATAL; 236 goto cleanup; 237 } 238 239 cleanup: 240 if (fd >= 0) 241 close(fd); 242 243 if (retcode == EPKG_OK) { 244 (*buffer)[st.st_size] = '\0'; 245 *sz = st.st_size; 246 } else { 247 free(*buffer); 248 *buffer = NULL; 249 *sz = -1; 250 } 251 return (retcode); 252 } 253 254 int 255 file_to_buffer(const char *path, char **buffer, off_t *sz) 256 { 257 return file_to_bufferat(AT_FDCWD, path, buffer, sz); 258 } 259 260 int 261 format_exec_cmd(char **dest, const char *in, const char *prefix, 262 const char *plist_file, const char *line, int argc, char **argv, bool lua) 263 { 264 xstring *buf; 265 char path[MAXPATHLEN]; 266 char *cp; 267 const char *ptr; 268 size_t sz; 269 270 buf = xstring_new(); 271 cp = NULL; 272 273 if (line != NULL && argv != NULL) { 274 if (lua) { 275 fprintf(buf->fp, "-- args: %s\n", line); 276 } else { 277 fprintf(buf->fp, "# args: %s\n", line); 278 } 279 } 280 281 while (in[0] != '\0') { 282 if (in[0] != '%') { 283 fputc(in[0], buf->fp); 284 in++; 285 continue; 286 } 287 in++; 288 switch(in[0]) { 289 case 'D': 290 fprintf(buf->fp, "%s", prefix); 291 break; 292 case 'F': 293 if (plist_file == NULL || plist_file[0] == '\0') { 294 pkg_emit_error("No files defined %%F couldn't " 295 "be expanded, ignoring %s", in); 296 xstring_free(buf); 297 return (EPKG_FATAL); 298 } 299 fprintf(buf->fp, "%s", plist_file); 300 break; 301 case 'f': 302 if (plist_file == NULL || plist_file[0] == '\0') { 303 pkg_emit_error("No files defined %%f couldn't " 304 "be expanded, ignoring %s", in); 305 xstring_free(buf); 306 return (EPKG_FATAL); 307 } 308 ptr = strrchr(plist_file, '/'); 309 if (ptr != NULL) 310 ptr++; 311 else 312 ptr = plist_file; 313 fprintf(buf->fp, "%s", ptr); 314 break; 315 case 'B': 316 if (plist_file == NULL || plist_file[0] == '\0') { 317 pkg_emit_error("No files defined %%B couldn't " 318 "be expanded, ignoring %s", in); 319 xstring_free(buf); 320 return (EPKG_FATAL); 321 } 322 if (prefix[strlen(prefix) - 1] == '/') 323 snprintf(path, sizeof(path), "%s%s", prefix, 324 plist_file); 325 else 326 snprintf(path, sizeof(path), "%s/%s", prefix, 327 plist_file); 328 cp = strrchr(path, '/'); 329 cp[0] = '\0'; 330 fprintf(buf->fp, "%s", path); 331 break; 332 case '%': 333 fputc('%', buf->fp); 334 break; 335 case '@': 336 if (line != NULL) { 337 fprintf(buf->fp, "%s", line); 338 break; 339 } 340 341 /* 342 * no break here because if line is not 343 * given (default exec) %@ does not 344 * exists 345 */ 346 /* FALLTHRU */ 347 case '#': 348 fprintf(buf->fp, "%d", argc); 349 break; 350 default: 351 if ((sz = strspn(in, "0123456789")) > 0) { 352 int pos = strtol(in, NULL, 10); 353 if (pos > argc) { 354 pkg_emit_error("Requesting argument " 355 "%%%d while only %d arguments are" 356 " available", pos, argc); 357 xstring_free(buf); 358 return (EPKG_FATAL); 359 } 360 fprintf(buf->fp, "%s", argv[pos -1]); 361 in += sz -1; 362 break; 363 } 364 fprintf(buf->fp, "%c%c", '%', in[0]); 365 break; 366 } 367 368 in++; 369 } 370 371 *dest = xstring_get(buf); 372 373 return (EPKG_OK); 374 } 375 376 int 377 is_dir(const char *path) 378 { 379 struct stat st; 380 381 return (stat(path, &st) == 0 && S_ISDIR(st.st_mode)); 382 } 383 384 int 385 is_link(const char *path) 386 { 387 struct stat st; 388 389 return (lstat(path, &st) == 0 && S_ISLNK(st.st_mode)); 390 } 391 392 bool 393 check_for_hardlink(hardlinks_t *hl, struct stat *st) 394 { 395 struct hardlink *h; 396 397 vec_foreach(*hl, i) { 398 h = hl->d[i]; 399 if (h->ino == st->st_ino && 400 h->dev == st->st_dev) 401 return (true); 402 } 403 h = xcalloc(1, sizeof(*h)); 404 h->ino = st->st_ino; 405 h->dev = st->st_dev; 406 vec_push(hl, h); 407 408 return (false); 409 } 410 411 /* 412 * ABI validation: 413 * - lowest match (case insensitive) 414 * - glob matching 415 * 416 * A package which is valid for installation on any FreeBSD now simply has 417 * to define itself as: abi: "FreeBSD" or "FreeBSD:*" 418 * A package which is valid for installation on any FreeBSD 15 regarless of 419 * the arch 420 * abi: "FreeBSD:15" or "FreeBSD:15:*" 421 * 422 */ 423 424 bool 425 is_valid_abi(const char *testabi, bool emit_error) 426 { 427 const char *abi = pkg_object_string(pkg_config_get("ABI")); 428 429 if (strncasecmp(testabi, abi, strlen(testabi)) != 0 && 430 fnmatch(testabi, abi, FNM_CASEFOLD) == FNM_NOMATCH) { 431 if (emit_error) 432 pkg_emit_error("wrong architecture: %s instead of %s", 433 testabi, abi); 434 return (false); 435 } 436 437 return (true); 438 } 439 440 bool 441 is_valid_os_version(struct pkg *pkg) 442 { 443 if (ctx.abi.os != PKG_OS_FREEBSD) 444 return (true); 445 const char *fbsd_version; 446 const char *errstr = NULL; 447 char query_buf[512]; 448 /* -1: not checked, 0: not allowed, 1: allowed */ 449 static int osver_mismatch_allowed = -1; 450 bool ret; 451 452 if (pkg_object_bool(pkg_config_get("IGNORE_OSVERSION"))) 453 return (true); 454 if ((fbsd_version = pkg_kv_get(&pkg->annotations, "FreeBSD_version")) != NULL) { 455 int pkg_osversion = strtonum(fbsd_version, 1, INT_MAX, &errstr); 456 if (errstr != NULL) { 457 pkg_emit_error("Invalid FreeBSD version %s for package %s", 458 fbsd_version, pkg->name); 459 return (false); 460 } 461 int abi_osversion = pkg_abi_get_freebsd_osversion(&ctx.abi); 462 if (pkg_osversion > abi_osversion) { 463 if (pkg_osversion - abi_osversion < 100000) { 464 /* Negligible difference, ask user to enforce */ 465 if (osver_mismatch_allowed == -1) { 466 snprintf(query_buf, sizeof(query_buf), 467 "Newer FreeBSD version for package %s:\n" 468 "To ignore this error set IGNORE_OSVERSION=yes\n" 469 "- package: %d\n" 470 "- running userland: %d\n" 471 "Ignore the mismatch and continue? ", pkg->name, 472 pkg_osversion, abi_osversion); 473 ret = pkg_emit_query_yesno(false, query_buf); 474 osver_mismatch_allowed = ret; 475 } 476 477 return (osver_mismatch_allowed); 478 } 479 else { 480 pkg_emit_error("Newer FreeBSD version for package %s:\n" 481 "To ignore this error set IGNORE_OSVERSION=yes\n" 482 "- package: %d\n" 483 "- running kernel: %d\n", 484 pkg->name, 485 pkg_osversion, abi_osversion); 486 return (false); 487 } 488 } 489 } 490 return (true); 491 } 492 493 void 494 set_nonblocking(int fd) 495 { 496 int flags; 497 498 if ((flags = fcntl(fd, F_GETFL)) == -1) 499 return; 500 if (!(flags & O_NONBLOCK)) { 501 flags |= O_NONBLOCK; 502 fcntl(fd, F_SETFL, flags); 503 } 504 } 505 506 void 507 set_blocking(int fd) 508 { 509 int flags; 510 511 if ((flags = fcntl(fd, F_GETFL)) == -1) 512 return; 513 if (flags & O_NONBLOCK) { 514 flags &= ~O_NONBLOCK; 515 fcntl(fd, F_SETFL, flags); 516 } 517 } 518 519 /* Spawn a process from pfunc, returning it's pid. The fds array passed will 520 * be filled with two descriptors: fds[0] will read from the child process, 521 * and fds[1] will write to it. 522 * Similarly, the child process will receive a reading/writing fd set (in 523 * that same order) as arguments. 524 */ 525 extern char **environ; 526 pid_t 527 process_spawn_pipe(FILE *inout[2], const char *command) 528 { 529 pid_t pid; 530 int pipes[4]; 531 char *argv[4]; 532 533 /* Parent read/child write pipe */ 534 if (pipe(&pipes[0]) == -1) 535 return (-1); 536 537 /* Child read/parent write pipe */ 538 if (pipe(&pipes[2]) == -1) { 539 close(pipes[0]); 540 close(pipes[1]); 541 return (-1); 542 } 543 544 argv[0] = __DECONST(char *, "sh"); 545 argv[1] = __DECONST(char *, "-c"); 546 argv[2] = __DECONST(char *, command); 547 argv[3] = NULL; 548 549 pid = fork(); 550 if (pid > 0) { 551 /* Parent process */ 552 inout[0] = fdopen(pipes[0], "r"); 553 inout[1] = fdopen(pipes[3], "w"); 554 555 close(pipes[1]); 556 close(pipes[2]); 557 558 return (pid); 559 560 } else if (pid == 0) { 561 close(pipes[0]); 562 close(pipes[3]); 563 564 if (pipes[1] != STDOUT_FILENO) { 565 dup2(pipes[1], STDOUT_FILENO); 566 close(pipes[1]); 567 } 568 if (pipes[2] != STDIN_FILENO) { 569 dup2(pipes[2], STDIN_FILENO); 570 close(pipes[2]); 571 } 572 closefrom(STDERR_FILENO + 1); 573 574 execve(_PATH_BSHELL, argv, environ); 575 576 _exit(127); 577 } 578 579 return (-1); /* ? */ 580 } 581 582 static int 583 ucl_buf_append_character(unsigned char c, size_t len, void *data) 584 { 585 xstring *buf = data; 586 size_t i; 587 588 for (i = 0; i < len; i++) 589 fprintf(buf->fp, "%c", c); 590 591 return (0); 592 } 593 594 static int 595 ucl_buf_append_len(const unsigned char *str, size_t len, void *data) 596 { 597 xstring *buf = data; 598 599 fprintf(buf->fp, "%.*s", (int)len, str); 600 601 return (0); 602 } 603 604 static int 605 ucl_buf_append_int(int64_t val, void *data) 606 { 607 xstring *buf = data; 608 609 fprintf(buf->fp, "%"PRId64, val); 610 611 return (0); 612 } 613 614 static int 615 ucl_buf_append_double(double val, void *data) 616 { 617 xstring *buf = data; 618 const double delta = 0.0000001; 619 620 if (val == (double)(int)val) { 621 fprintf(buf->fp, "%.1lf", val); 622 } else if (fabs(val - (double)(int)val) < delta) { 623 fprintf(buf->fp, "%.*lg", DBL_DIG, val); 624 } else { 625 fprintf(buf->fp, "%lf", val); 626 } 627 628 return (0); 629 } 630 631 bool 632 ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type, 633 FILE *out) 634 { 635 struct ucl_emitter_functions *f = ucl_object_emit_file_funcs(out); 636 bool ret = false; 637 638 if (obj == NULL) 639 return (false); 640 641 ret = ucl_object_emit_full(obj, emit_type, f, NULL); 642 ucl_object_emit_funcs_free(f); 643 644 return (ret); 645 } 646 647 bool 648 ucl_object_emit_fd(const ucl_object_t *obj, enum ucl_emitter emit_type, int fd) 649 { 650 struct ucl_emitter_functions *f = ucl_object_emit_fd_funcs(fd); 651 bool ret = false; 652 653 if (obj == NULL) 654 return (false); 655 ret = ucl_object_emit_full(obj, emit_type, f, NULL); 656 ucl_object_emit_funcs_free(f); 657 658 return (ret); 659 } 660 661 662 bool 663 ucl_object_emit_buf(const ucl_object_t *obj, enum ucl_emitter emit_type, 664 xstring **buf) 665 { 666 bool ret = false; 667 struct ucl_emitter_functions func = { 668 .ucl_emitter_append_character = ucl_buf_append_character, 669 .ucl_emitter_append_len = ucl_buf_append_len, 670 .ucl_emitter_append_int = ucl_buf_append_int, 671 .ucl_emitter_append_double = ucl_buf_append_double 672 }; 673 674 xstring_renew(*buf); 675 676 func.ud = *buf; 677 678 ret = ucl_object_emit_full(obj, emit_type, &func, NULL); 679 680 return (ret); 681 } 682 683 /* A bit like strsep(), except it accounts for "double" and 'single' 684 quotes. Unlike strsep(), returns the next arg string, trimmed of 685 whitespace or enclosing quotes, and updates **args to point at the 686 character after that. Sets *args to NULL when it has been 687 completely consumed. Quoted strings run from the first encountered 688 quotemark to the next one of the same type or the terminating NULL. 689 Quoted strings can contain the /other/ type of quote mark, which 690 loses any special significance. There isn't an escape 691 character. */ 692 693 enum parse_states { 694 START, 695 ORDINARY_TEXT, 696 OPEN_SINGLE_QUOTES, 697 IN_SINGLE_QUOTES, 698 OPEN_DOUBLE_QUOTES, 699 IN_DOUBLE_QUOTES, 700 }; 701 702 char * 703 pkg_utils_tokenize(char **args) 704 { 705 char *p, *p_start; 706 enum parse_states parse_state = START; 707 708 assert(*args != NULL); 709 710 for (p = p_start = *args; *p != '\0'; p++) { 711 switch (parse_state) { 712 case START: 713 if (!isspace(*p)) { 714 if (*p == '"') 715 parse_state = OPEN_DOUBLE_QUOTES; 716 else if (*p == '\'') 717 parse_state = OPEN_SINGLE_QUOTES; 718 else { 719 parse_state = ORDINARY_TEXT; 720 p_start = p; 721 } 722 } else 723 p_start = p; 724 break; 725 case ORDINARY_TEXT: 726 if (isspace(*p)) 727 goto finish; 728 break; 729 case OPEN_SINGLE_QUOTES: 730 p_start = p; 731 if (*p == '\'') 732 goto finish; 733 734 parse_state = IN_SINGLE_QUOTES; 735 break; 736 case IN_SINGLE_QUOTES: 737 if (*p == '\'') 738 goto finish; 739 break; 740 case OPEN_DOUBLE_QUOTES: 741 p_start = p; 742 if (*p == '"') 743 goto finish; 744 parse_state = IN_DOUBLE_QUOTES; 745 break; 746 case IN_DOUBLE_QUOTES: 747 if (*p == '"') 748 goto finish; 749 break; 750 } 751 } 752 753 finish: 754 if (*p == '\0') 755 *args = NULL; /* All done */ 756 else { 757 *p = '\0'; 758 p++; 759 if (*p == '\0' || parse_state == START) 760 *args = NULL; /* whitespace or nothing left */ 761 else 762 *args = p; 763 } 764 return (p_start); 765 } 766 767 int 768 pkg_utils_count_spaces(const char *args) 769 { 770 int spaces; 771 const char *p; 772 773 for (spaces = 0, p = args; *p != '\0'; p++) 774 if (isspace(*p)) 775 spaces++; 776 777 return (spaces); 778 } 779 780 /* unlike realpath(3), this routine does not expand symbolic links */ 781 char * 782 pkg_absolutepath(const char *src, char *dest, size_t dest_size, bool fromroot) { 783 size_t dest_len, src_len, cur_len; 784 const char *cur, *next; 785 786 src_len = strlen(src); 787 memset(dest, '\0', dest_size); 788 789 if (src_len != 0 && src[0] != '/') { 790 if (fromroot) 791 *dest = '/'; 792 /* relative path, we use cwd */ 793 else if (getcwd(dest, dest_size) == NULL) 794 return (NULL); 795 } 796 dest_len = strlen(dest); 797 798 for (cur = next = src; next != NULL; cur = (next == NULL) ? NULL : next + 1) { 799 next = strchr(cur, '/'); 800 if (next != NULL) 801 cur_len = next - cur; 802 else 803 cur_len = strlen(cur); 804 805 /* check for special cases "", "." and ".." */ 806 if (cur_len == 0) 807 continue; 808 else if (cur_len == 1 && cur[0] == '.') 809 continue; 810 else if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') { 811 const char *slash = strrchr(dest, '/'); 812 if (slash != NULL) { 813 dest_len = slash - dest; 814 dest[dest_len] = '\0'; 815 } 816 continue; 817 } 818 819 if (dest_len + 1 + cur_len >= dest_size) 820 return (NULL); 821 dest[dest_len++] = '/'; 822 (void)memcpy(dest + dest_len, cur, cur_len); 823 dest_len += cur_len; 824 dest[dest_len] = '\0'; 825 } 826 827 if (dest_len == 0) { 828 if (strlcpy(dest, "/", dest_size) >= dest_size) 829 return (NULL); 830 } 831 832 return (dest); 833 } 834 835 bool 836 mkdirat_p(int fd, const char *path) 837 { 838 const char *next; 839 char pathdone[MAXPATHLEN], walkbuf[MAXPATHLEN], *walk; 840 841 pathdone[0] = '\0'; 842 strlcpy(walkbuf, path, sizeof(walkbuf)); 843 walk = walkbuf; 844 while ((next = strsep(&walk, "/")) != NULL) { 845 if (*next == '\0') 846 continue; 847 strlcat(pathdone, next, sizeof(pathdone)); 848 if (mkdirat(fd, pathdone, 0755) == -1) { 849 if (errno == EEXIST) { 850 strlcat(pathdone, "/", sizeof(pathdone)); 851 continue; 852 } 853 pkg_errno("Failed to create /%s", pathdone); 854 return (false); 855 } 856 strlcat(pathdone, "/", sizeof(pathdone)); 857 } 858 return (true); 859 } 860 861 int 862 pkg_namecmp(struct pkg *a, struct pkg *b) 863 { 864 865 return (strcmp(a->name, b->name)); 866 } 867 868 int 869 get_socketpair(int *pipe) 870 { 871 int r; 872 873 #ifdef SOCK_SEQPACKET 874 r = socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, pipe); 875 if (r == -1) { 876 r = socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipe); 877 } 878 #else 879 r = socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipe); 880 #endif 881 882 return (r); 883 } 884 885 /* 886 * Modify the passed C-String by stripping off the last component delimited by '/'. 887 * Return the string. Return a constant "." when passed NULL or the empty string. 888 * FIXME: This routine corrupts memory when passed the empty string. 889 * FIXME: This routine should propagate NULL. 890 * TODO: Refactor at call sites. 891 */ 892 char * 893 get_dirname(char *d) 894 { 895 char *walk; 896 897 if (d == NULL) 898 return (__DECONST(char *, ".")); 899 900 walk = strrchr(d, '/'); 901 if (walk == NULL) { 902 d[0] = '.'; 903 d[1] = '\0'; 904 } else { 905 *walk = '\0'; 906 } 907 908 return (d); 909 } 910 911 char * 912 rtrimspace(char *buf) 913 { 914 char *cp = buf + strlen(buf) -1; 915 916 while (cp > buf && isspace(*cp)) { 917 *cp = 0; 918 cp --; 919 } 920 921 return (buf); 922 } 923 924 static ssize_t 925 _copy_file(int from, int to) 926 { 927 char buf[BUFSIZ]; 928 ssize_t r, wresid, w = 0; 929 char *bufp; 930 931 r = read(from, buf, sizeof(buf)); 932 if (r < 0) 933 return (r); 934 for (bufp = buf, wresid = r; ; bufp += w, wresid -= w) { 935 w = write(to, bufp, wresid); 936 if (w <= 0) 937 break; 938 if (w >= (ssize_t)wresid) 939 break; 940 } 941 return (w < 0 ? w : r); 942 } 943 944 bool 945 pkg_copy_file(int from, int to) 946 { 947 #ifdef HAVE_COPY_FILE_RANGE 948 bool cfr = true; 949 #endif 950 ssize_t r; 951 952 do { 953 #ifdef HAVE_COPY_FILE_RANGE 954 if (cfr) { 955 r = copy_file_range(from, NULL, to, NULL, SSIZE_MAX, 0); 956 if (r < 0 && (errno == EINVAL || errno == EXDEV)) { 957 /* probably a non seekable FD */ 958 cfr = false; 959 } 960 } 961 if (!cfr) { 962 #endif 963 r = _copy_file(from, to); 964 #ifdef HAVE_COPY_FILE_RANGE 965 } 966 #endif 967 } while (r > 0); 968 969 return (r >= 0); 970 } 971 972 static const unsigned char litchar[] = 973 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 974 975 void 976 append_random_suffix(char *buf, int buflen, int suflen) 977 { 978 int nchars = strlen(buf); 979 char *pos; 980 int r; 981 982 /* 2 being the "." and the \0 */ 983 if (nchars + suflen > buflen - 2) { 984 suflen = buflen - nchars - 2; 985 if (suflen <= 0) 986 return; 987 } 988 989 buf[nchars++] = '.'; 990 pos = buf + nchars; 991 992 while(suflen --) { 993 #ifndef HAVE_ARC4RANDOM 994 r = rand() % (sizeof(litchar) - 1); 995 #else 996 r = arc4random_uniform(sizeof(litchar) - 1); 997 #endif 998 *pos++ = litchar[r]; 999 } 1000 1001 *pos = '\0'; 1002 } 1003 1004 void 1005 hidden_tempfile(char *buf, int buflen, const char *path) 1006 { 1007 const char *fname; 1008 int suffixlen = 12; 1009 int nbuflen; 1010 const char *prefix = ".pkgtemp."; 1011 1012 fname = strrchr(path, '/'); 1013 if (fname != NULL) 1014 fname++; 1015 1016 /* 1017 * try to reduce the temporary name as much as possible to fit with very 1018 * long file names if possible. by default 1019 * .pkgtemp. fname . <suffix> 1020 * otherwise 1021 * . fname . <suffix> 1022 * keep if suffix of at least 5 if possible 1023 */ 1024 if (fname != NULL) { 1025 if (strlen(fname) >= (NAME_MAX - 15)) 1026 prefix = "."; 1027 snprintf(buf, buflen, "%.*s%s%s", (int)(fname - path), path, prefix, fname); 1028 nbuflen = buflen; 1029 } else { 1030 if (strlen(path) >= NAME_MAX - 15) 1031 prefix = "."; 1032 snprintf(buf, buflen, "%s%s", prefix, path); 1033 nbuflen = NAME_MAX; 1034 } 1035 1036 1037 append_random_suffix(buf, nbuflen, suffixlen); 1038 } 1039 1040 char * 1041 json_escape(const char *str) 1042 { 1043 xstring *buf = xstring_new(); 1044 1045 while (str != NULL && *str != '\0') { 1046 if (*str == '"' || *str == '\\') 1047 fputc('\\', buf->fp); 1048 fputc(*str, buf->fp); 1049 str++; 1050 } 1051 1052 return (xstring_get(buf)); 1053 } 1054 1055 const char * 1056 get_http_auth(void) 1057 { 1058 const char *str = getenv("HTTP_AUTH"); 1059 if (str == NULL) 1060 return (NULL); 1061 if ((str = strchr(str, ':')) == NULL) { 1062 pkg_emit_error("malformed HTTP_AUTH"); 1063 return (NULL); 1064 } 1065 if ((str = strchr(++str, ':')) == NULL) { 1066 pkg_emit_error("malformed HTTP_AUTH"); 1067 return (NULL); 1068 } 1069 if (strchr(++str, ':') == NULL) { 1070 pkg_emit_error("malformed HTTP_AUTH"); 1071 return (NULL); 1072 } 1073 return (str); 1074 } 1075 1076 bool 1077 charv_contains(charv_t *v, const char *el, bool casesensitive) 1078 { 1079 vec_foreach(*v, i) { 1080 if (casesensitive) { 1081 if (STREQ(v->d[i], el)) 1082 return (true); 1083 } else { 1084 if (STRIEQ(v->d[i], el)) { 1085 return (true); 1086 } 1087 } 1088 } 1089 return (false); 1090 } 1091 1092 bool 1093 c_charv_contains(c_charv_t *v, const char *el, bool casesensitive) 1094 { 1095 vec_foreach(*v, i) { 1096 if (casesensitive) { 1097 if (STREQ(v->d[i], el)) 1098 return (true); 1099 } else { 1100 if (STRIEQ(v->d[i], el)) { 1101 return (true); 1102 } 1103 } 1104 } 1105 return (false); 1106 } 1107 1108 bool 1109 str_ends_with(const char *str, const char *end) 1110 { 1111 size_t el, sl; 1112 1113 if (end == NULL) 1114 return (true); 1115 if (str == NULL) 1116 return (false); 1117 1118 sl = strlen(str); 1119 el = strlen(end); 1120 if (sl < el) 1121 return (false); 1122 return (strncmp(str + (sl - el), end, el) == 0); 1123 } 1124 1125 int 1126 char_cmp(const void *a, const void *b) { 1127 return strcmp(*(char **)a, *(char **)b); 1128 } 1129 1130 DEFINE_VEC_INSERT_SORTED_FUNC(charv_t, charv, char *, char_cmp); 1131 1132 const char * 1133 charv_search(charv_t *v, const char *el) 1134 { 1135 if (v->len == 0) 1136 return (NULL); 1137 const char **res = bsearch(&el, v->d, v->len, sizeof(char *), char_cmp); 1138 if (res == NULL) 1139 return (NULL); 1140 return *res; 1141 } 1142 1143 uid_t 1144 get_uid_from_uname(const char *uname) 1145 { 1146 static char user_buffer[1024]; 1147 static struct passwd pwent; 1148 struct passwd *result; 1149 int err; 1150 const char *testuname = uname ? uname : ""; 1151 1152 if (pwent.pw_name != NULL && STREQ(testuname, pwent.pw_name)) 1153 goto out; 1154 pwent.pw_name = NULL; 1155 err = getpwnam_r(testuname, &pwent, user_buffer, sizeof(user_buffer), 1156 &result); 1157 if (err != 0) { 1158 pkg_emit_error("getpwnam_r(%s): %s", testuname, strerror(err)); 1159 return (0); 1160 } 1161 if (result == NULL) 1162 return (0); 1163 out: 1164 return (pwent.pw_uid); 1165 } 1166 1167 gid_t 1168 get_gid_from_gname(const char *gname) 1169 { 1170 static char group_buffer[1024]; 1171 static struct group grent; 1172 struct group *result; 1173 int err; 1174 const char *testgname = gname ? gname : ""; 1175 1176 if (grent.gr_name != NULL && STREQ(testgname, grent.gr_name)) 1177 goto out; 1178 grent.gr_name = NULL; 1179 err = getgrnam_r(testgname, &grent, group_buffer, sizeof(group_buffer), 1180 &result); 1181 if (err != 0) { 1182 pkg_emit_error("getgrnam_r(%s): %s", testgname, strerror(err)); 1183 return (0); 1184 } 1185 if (result == NULL) 1186 return (0); 1187 out: 1188 return (grent.gr_gid); 1189 } 1190 1191 const char * 1192 pkg_meta_attribute_tostring(enum pkg_meta_attribute attrib) 1193 { 1194 switch (attrib) { 1195 case PKG_META_ATTR_TYPE: return "type"; 1196 case PKG_META_ATTR_UNAME: return "uname"; 1197 case PKG_META_ATTR_GNAME: return "gname"; 1198 case PKG_META_ATTR_PERM: return "perm"; 1199 case PKG_META_ATTR_FFLAGS: return "fflags"; 1200 case PKG_META_ATTR_MTIME: return "mtime"; 1201 case PKG_META_ATTR_SYMLINK: return "symlink"; 1202 } 1203 1204 return "???"; 1205 }