1 /* $NetBSD: file.c,v 1.1 2024/02/18 20:57:57 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /* 17 * Portions Copyright (c) 1987, 1993 18 * The Regents of the University of California. All rights reserved. 19 * 20 * Redistribution and use in source and binary forms, with or without 21 * modification, are permitted provided that the following conditions 22 * are met: 23 * 1. Redistributions of source code must retain the above copyright 24 * notice, this list of conditions and the following disclaimer. 25 * 2. Redistributions in binary form must reproduce the above copyright 26 * notice, this list of conditions and the following disclaimer in the 27 * documentation and/or other materials provided with the distribution. 28 * 3. Neither the name of the University nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 */ 44 45 /*! \file */ 46 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <inttypes.h> 50 #include <limits.h> 51 #include <stdbool.h> 52 #include <stdlib.h> 53 #include <sys/stat.h> 54 #include <sys/time.h> 55 #include <time.h> /* Required for utimes on some platforms. */ 56 #include <unistd.h> /* Required for mkstemp on NetBSD. */ 57 58 #ifdef HAVE_SYS_MMAN_H 59 #include <sys/mman.h> 60 #endif /* ifdef HAVE_SYS_MMAN_H */ 61 62 #include <isc/dir.h> 63 #include <isc/file.h> 64 #include <isc/log.h> 65 #include <isc/md.h> 66 #include <isc/mem.h> 67 #include <isc/platform.h> 68 #include <isc/print.h> 69 #include <isc/random.h> 70 #include <isc/string.h> 71 #include <isc/time.h> 72 #include <isc/util.h> 73 74 #include "errno2result.h" 75 76 /* 77 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded, 78 * it might be good to provide a mechanism that allows for the results 79 * of a previous stat() to be used again without having to do another stat, 80 * such as perl's mechanism of using "_" in place of a file name to indicate 81 * that the results of the last stat should be used. But then you get into 82 * annoying MP issues. BTW, Win32 has stat(). 83 */ 84 static isc_result_t 85 file_stats(const char *file, struct stat *stats) { 86 isc_result_t result = ISC_R_SUCCESS; 87 88 REQUIRE(file != NULL); 89 REQUIRE(stats != NULL); 90 91 if (stat(file, stats) != 0) { 92 result = isc__errno2result(errno); 93 } 94 95 return (result); 96 } 97 98 static isc_result_t 99 fd_stats(int fd, struct stat *stats) { 100 isc_result_t result = ISC_R_SUCCESS; 101 102 REQUIRE(stats != NULL); 103 104 if (fstat(fd, stats) != 0) { 105 result = isc__errno2result(errno); 106 } 107 108 return (result); 109 } 110 111 isc_result_t 112 isc_file_getsizefd(int fd, off_t *size) { 113 isc_result_t result; 114 struct stat stats; 115 116 REQUIRE(size != NULL); 117 118 result = fd_stats(fd, &stats); 119 120 if (result == ISC_R_SUCCESS) { 121 *size = stats.st_size; 122 } 123 124 return (result); 125 } 126 127 isc_result_t 128 isc_file_mode(const char *file, mode_t *modep) { 129 isc_result_t result; 130 struct stat stats; 131 132 REQUIRE(modep != NULL); 133 134 result = file_stats(file, &stats); 135 if (result == ISC_R_SUCCESS) { 136 *modep = (stats.st_mode & 07777); 137 } 138 139 return (result); 140 } 141 142 isc_result_t 143 isc_file_getmodtime(const char *file, isc_time_t *modtime) { 144 isc_result_t result; 145 struct stat stats; 146 147 REQUIRE(file != NULL); 148 REQUIRE(modtime != NULL); 149 150 result = file_stats(file, &stats); 151 152 if (result == ISC_R_SUCCESS) { 153 #if defined(HAVE_STAT_NSEC) 154 isc_time_set(modtime, stats.st_mtime, stats.st_mtim.tv_nsec); 155 #else /* if defined(HAVE_STAT_NSEC) */ 156 isc_time_set(modtime, stats.st_mtime, 0); 157 #endif /* if defined(HAVE_STAT_NSEC) */ 158 } 159 160 return (result); 161 } 162 163 isc_result_t 164 isc_file_getsize(const char *file, off_t *size) { 165 isc_result_t result; 166 struct stat stats; 167 168 REQUIRE(file != NULL); 169 REQUIRE(size != NULL); 170 171 result = file_stats(file, &stats); 172 173 if (result == ISC_R_SUCCESS) { 174 *size = stats.st_size; 175 } 176 177 return (result); 178 } 179 180 isc_result_t 181 isc_file_settime(const char *file, isc_time_t *when) { 182 struct timeval times[2]; 183 184 REQUIRE(file != NULL && when != NULL); 185 186 /* 187 * tv_sec is at least a 32 bit quantity on all platforms we're 188 * dealing with, but it is signed on most (all?) of them, 189 * so we need to make sure the high bit isn't set. This unfortunately 190 * loses when either: 191 * * tv_sec becomes a signed 64 bit integer but long is 32 bits 192 * and isc_time_seconds > LONG_MAX, or 193 * * isc_time_seconds is changed to be > 32 bits but long is 32 bits 194 * and isc_time_seconds has at least 33 significant bits. 195 */ 196 times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(when); 197 198 /* 199 * Here is the real check for the high bit being set. 200 */ 201 if ((times[0].tv_sec & 202 (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0) 203 { 204 return (ISC_R_RANGE); 205 } 206 207 /* 208 * isc_time_nanoseconds guarantees a value that divided by 1000 will 209 * fit into the minimum possible size tv_usec field. 210 */ 211 times[0].tv_usec = times[1].tv_usec = 212 (int32_t)(isc_time_nanoseconds(when) / 1000); 213 214 if (utimes(file, times) < 0) { 215 return (isc__errno2result(errno)); 216 } 217 218 return (ISC_R_SUCCESS); 219 } 220 221 #undef TEMPLATE 222 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */ 223 224 isc_result_t 225 isc_file_mktemplate(const char *path, char *buf, size_t buflen) { 226 return (isc_file_template(path, TEMPLATE, buf, buflen)); 227 } 228 229 isc_result_t 230 isc_file_template(const char *path, const char *templet, char *buf, 231 size_t buflen) { 232 const char *s; 233 234 REQUIRE(templet != NULL); 235 REQUIRE(buf != NULL); 236 237 if (path == NULL) { 238 path = ""; 239 } 240 241 s = strrchr(templet, '/'); 242 if (s != NULL) { 243 templet = s + 1; 244 } 245 246 s = strrchr(path, '/'); 247 248 if (s != NULL) { 249 size_t prefixlen = s - path + 1; 250 if ((prefixlen + strlen(templet) + 1) > buflen) { 251 return (ISC_R_NOSPACE); 252 } 253 254 /* Copy 'prefixlen' bytes and NUL terminate. */ 255 strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen)); 256 strlcat(buf, templet, buflen); 257 } else { 258 if ((strlen(templet) + 1) > buflen) { 259 return (ISC_R_NOSPACE); 260 } 261 262 strlcpy(buf, templet, buflen); 263 } 264 265 return (ISC_R_SUCCESS); 266 } 267 268 static const char alphnum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv" 269 "wxyz0123456789"; 270 271 isc_result_t 272 isc_file_renameunique(const char *file, char *templet) { 273 char *x; 274 char *cp; 275 276 REQUIRE(file != NULL); 277 REQUIRE(templet != NULL); 278 279 cp = templet; 280 while (*cp != '\0') { 281 cp++; 282 } 283 if (cp == templet) { 284 return (ISC_R_FAILURE); 285 } 286 287 x = cp--; 288 while (cp >= templet && *cp == 'X') { 289 *cp = alphnum[isc_random_uniform(sizeof(alphnum) - 1)]; 290 x = cp--; 291 } 292 while (link(file, templet) == -1) { 293 if (errno != EEXIST) { 294 return (isc__errno2result(errno)); 295 } 296 for (cp = x;;) { 297 const char *t; 298 if (*cp == '\0') { 299 return (ISC_R_FAILURE); 300 } 301 t = strchr(alphnum, *cp); 302 if (t == NULL || *++t == '\0') { 303 *cp++ = alphnum[0]; 304 } else { 305 *cp = *t; 306 break; 307 } 308 } 309 } 310 if (unlink(file) < 0) { 311 if (errno != ENOENT) { 312 return (isc__errno2result(errno)); 313 } 314 } 315 return (ISC_R_SUCCESS); 316 } 317 318 isc_result_t 319 isc_file_openunique(char *templet, FILE **fp) { 320 int mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 321 return (isc_file_openuniquemode(templet, mode, fp)); 322 } 323 324 isc_result_t 325 isc_file_openuniqueprivate(char *templet, FILE **fp) { 326 int mode = S_IWUSR | S_IRUSR; 327 return (isc_file_openuniquemode(templet, mode, fp)); 328 } 329 330 isc_result_t 331 isc_file_openuniquemode(char *templet, int mode, FILE **fp) { 332 int fd; 333 FILE *f; 334 isc_result_t result = ISC_R_SUCCESS; 335 char *x; 336 char *cp; 337 338 REQUIRE(templet != NULL); 339 REQUIRE(fp != NULL && *fp == NULL); 340 341 cp = templet; 342 while (*cp != '\0') { 343 cp++; 344 } 345 if (cp == templet) { 346 return (ISC_R_FAILURE); 347 } 348 349 x = cp--; 350 while (cp >= templet && *cp == 'X') { 351 *cp = alphnum[isc_random_uniform(sizeof(alphnum) - 1)]; 352 x = cp--; 353 } 354 355 while ((fd = open(templet, O_RDWR | O_CREAT | O_EXCL, mode)) == -1) { 356 if (errno != EEXIST) { 357 return (isc__errno2result(errno)); 358 } 359 for (cp = x;;) { 360 char *t; 361 if (*cp == '\0') { 362 return (ISC_R_FAILURE); 363 } 364 t = strchr(alphnum, *cp); 365 if (t == NULL || *++t == '\0') { 366 *cp++ = alphnum[0]; 367 } else { 368 *cp = *t; 369 break; 370 } 371 } 372 } 373 f = fdopen(fd, "w+"); 374 if (f == NULL) { 375 result = isc__errno2result(errno); 376 if (remove(templet) < 0) { 377 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 378 ISC_LOGMODULE_FILE, ISC_LOG_ERROR, 379 "remove '%s': failed", templet); 380 } 381 (void)close(fd); 382 } else { 383 *fp = f; 384 } 385 386 return (result); 387 } 388 389 isc_result_t 390 isc_file_bopenunique(char *templet, FILE **fp) { 391 int mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 392 return (isc_file_openuniquemode(templet, mode, fp)); 393 } 394 395 isc_result_t 396 isc_file_bopenuniqueprivate(char *templet, FILE **fp) { 397 int mode = S_IWUSR | S_IRUSR; 398 return (isc_file_openuniquemode(templet, mode, fp)); 399 } 400 401 isc_result_t 402 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) { 403 return (isc_file_openuniquemode(templet, mode, fp)); 404 } 405 406 isc_result_t 407 isc_file_remove(const char *filename) { 408 int r; 409 410 REQUIRE(filename != NULL); 411 412 r = unlink(filename); 413 if (r == 0) { 414 return (ISC_R_SUCCESS); 415 } else { 416 return (isc__errno2result(errno)); 417 } 418 } 419 420 isc_result_t 421 isc_file_rename(const char *oldname, const char *newname) { 422 int r; 423 424 REQUIRE(oldname != NULL); 425 REQUIRE(newname != NULL); 426 427 r = rename(oldname, newname); 428 if (r == 0) { 429 return (ISC_R_SUCCESS); 430 } else { 431 return (isc__errno2result(errno)); 432 } 433 } 434 435 bool 436 isc_file_exists(const char *pathname) { 437 struct stat stats; 438 439 REQUIRE(pathname != NULL); 440 441 return (file_stats(pathname, &stats) == ISC_R_SUCCESS); 442 } 443 444 isc_result_t 445 isc_file_isplainfile(const char *filename) { 446 /* 447 * This function returns success if filename is a plain file. 448 */ 449 struct stat filestat; 450 memset(&filestat, 0, sizeof(struct stat)); 451 452 if ((stat(filename, &filestat)) == -1) { 453 return (isc__errno2result(errno)); 454 } 455 456 if (!S_ISREG(filestat.st_mode)) { 457 return (ISC_R_INVALIDFILE); 458 } 459 460 return (ISC_R_SUCCESS); 461 } 462 463 isc_result_t 464 isc_file_isplainfilefd(int fd) { 465 /* 466 * This function returns success if filename is a plain file. 467 */ 468 struct stat filestat; 469 memset(&filestat, 0, sizeof(struct stat)); 470 471 if ((fstat(fd, &filestat)) == -1) { 472 return (isc__errno2result(errno)); 473 } 474 475 if (!S_ISREG(filestat.st_mode)) { 476 return (ISC_R_INVALIDFILE); 477 } 478 479 return (ISC_R_SUCCESS); 480 } 481 482 isc_result_t 483 isc_file_isdirectory(const char *filename) { 484 /* 485 * This function returns success if filename exists and is a 486 * directory. 487 */ 488 struct stat filestat; 489 memset(&filestat, 0, sizeof(struct stat)); 490 491 if ((stat(filename, &filestat)) == -1) { 492 return (isc__errno2result(errno)); 493 } 494 495 if (!S_ISDIR(filestat.st_mode)) { 496 return (ISC_R_INVALIDFILE); 497 } 498 499 return (ISC_R_SUCCESS); 500 } 501 502 bool 503 isc_file_isabsolute(const char *filename) { 504 REQUIRE(filename != NULL); 505 return (filename[0] == '/'); 506 } 507 508 bool 509 isc_file_iscurrentdir(const char *filename) { 510 REQUIRE(filename != NULL); 511 return (filename[0] == '.' && filename[1] == '\0'); 512 } 513 514 bool 515 isc_file_ischdiridempotent(const char *filename) { 516 REQUIRE(filename != NULL); 517 if (isc_file_isabsolute(filename)) { 518 return (true); 519 } 520 if (isc_file_iscurrentdir(filename)) { 521 return (true); 522 } 523 return (false); 524 } 525 526 const char * 527 isc_file_basename(const char *filename) { 528 const char *s; 529 530 REQUIRE(filename != NULL); 531 532 s = strrchr(filename, '/'); 533 if (s == NULL) { 534 return (filename); 535 } 536 537 return (s + 1); 538 } 539 540 isc_result_t 541 isc_file_progname(const char *filename, char *buf, size_t buflen) { 542 const char *base; 543 size_t len; 544 545 REQUIRE(filename != NULL); 546 REQUIRE(buf != NULL); 547 548 base = isc_file_basename(filename); 549 len = strlen(base) + 1; 550 551 if (len > buflen) { 552 return (ISC_R_NOSPACE); 553 } 554 memmove(buf, base, len); 555 556 return (ISC_R_SUCCESS); 557 } 558 559 /* 560 * Put the absolute name of the current directory into 'dirname', which is 561 * a buffer of at least 'length' characters. End the string with the 562 * appropriate path separator, such that the final product could be 563 * concatenated with a relative pathname to make a valid pathname string. 564 */ 565 static isc_result_t 566 dir_current(char *dirname, size_t length) { 567 char *cwd; 568 isc_result_t result = ISC_R_SUCCESS; 569 570 REQUIRE(dirname != NULL); 571 REQUIRE(length > 0U); 572 573 cwd = getcwd(dirname, length); 574 575 if (cwd == NULL) { 576 if (errno == ERANGE) { 577 result = ISC_R_NOSPACE; 578 } else { 579 result = isc__errno2result(errno); 580 } 581 } else { 582 if (strlen(dirname) + 1 == length) { 583 result = ISC_R_NOSPACE; 584 } else if (dirname[1] != '\0') { 585 strlcat(dirname, "/", length); 586 } 587 } 588 589 return (result); 590 } 591 592 isc_result_t 593 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { 594 isc_result_t result; 595 result = dir_current(path, pathlen); 596 if (result != ISC_R_SUCCESS) { 597 return (result); 598 } 599 if (strlen(path) + strlen(filename) + 1 > pathlen) { 600 return (ISC_R_NOSPACE); 601 } 602 strlcat(path, filename, pathlen); 603 return (ISC_R_SUCCESS); 604 } 605 606 isc_result_t 607 isc_file_truncate(const char *filename, isc_offset_t size) { 608 isc_result_t result = ISC_R_SUCCESS; 609 610 if (truncate(filename, size) < 0) { 611 result = isc__errno2result(errno); 612 } 613 return (result); 614 } 615 616 isc_result_t 617 isc_file_safecreate(const char *filename, FILE **fp) { 618 isc_result_t result; 619 int flags; 620 struct stat sb; 621 FILE *f; 622 int fd; 623 624 REQUIRE(filename != NULL); 625 REQUIRE(fp != NULL && *fp == NULL); 626 627 result = file_stats(filename, &sb); 628 if (result == ISC_R_SUCCESS) { 629 if ((sb.st_mode & S_IFREG) == 0) { 630 return (ISC_R_INVALIDFILE); 631 } 632 flags = O_WRONLY | O_TRUNC; 633 } else if (result == ISC_R_FILENOTFOUND) { 634 flags = O_WRONLY | O_CREAT | O_EXCL; 635 } else { 636 return (result); 637 } 638 639 fd = open(filename, flags, S_IRUSR | S_IWUSR); 640 if (fd == -1) { 641 return (isc__errno2result(errno)); 642 } 643 644 f = fdopen(fd, "w"); 645 if (f == NULL) { 646 result = isc__errno2result(errno); 647 close(fd); 648 return (result); 649 } 650 651 *fp = f; 652 return (ISC_R_SUCCESS); 653 } 654 655 isc_result_t 656 isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname, 657 char const **bname) { 658 char *dir; 659 const char *file, *slash; 660 661 if (path == NULL) { 662 return (ISC_R_INVALIDFILE); 663 } 664 665 slash = strrchr(path, '/'); 666 667 if (slash == path) { 668 file = ++slash; 669 dir = isc_mem_strdup(mctx, "/"); 670 } else if (slash != NULL) { 671 file = ++slash; 672 dir = isc_mem_allocate(mctx, slash - path); 673 strlcpy(dir, path, slash - path); 674 } else { 675 file = path; 676 dir = isc_mem_strdup(mctx, "."); 677 } 678 679 if (dir == NULL) { 680 return (ISC_R_NOMEMORY); 681 } 682 683 if (*file == '\0') { 684 isc_mem_free(mctx, dir); 685 return (ISC_R_INVALIDFILE); 686 } 687 688 *dirname = dir; 689 *bname = file; 690 691 return (ISC_R_SUCCESS); 692 } 693 694 void * 695 isc_file_mmap(void *addr, size_t len, int prot, int flags, int fd, 696 off_t offset) { 697 #ifdef HAVE_MMAP 698 return (mmap(addr, len, prot, flags, fd, offset)); 699 #else /* ifdef HAVE_MMAP */ 700 void *buf; 701 ssize_t ret; 702 off_t end; 703 704 UNUSED(addr); 705 UNUSED(prot); 706 UNUSED(flags); 707 708 end = lseek(fd, 0, SEEK_END); 709 lseek(fd, offset, SEEK_SET); 710 if (end - offset < (off_t)len) { 711 len = end - offset; 712 } 713 714 buf = malloc(len); 715 if (buf == NULL) { 716 return (NULL); 717 } 718 719 ret = read(fd, buf, len); 720 if (ret != (ssize_t)len) { 721 free(buf); 722 buf = NULL; 723 } 724 725 return (buf); 726 #endif /* ifdef HAVE_MMAP */ 727 } 728 729 int 730 isc_file_munmap(void *addr, size_t len) { 731 #ifdef HAVE_MMAP 732 return (munmap(addr, len)); 733 #else /* ifdef HAVE_MMAP */ 734 UNUSED(len); 735 736 free(addr); 737 return (0); 738 #endif /* ifdef HAVE_MMAP */ 739 } 740 741 #define DISALLOW "\\/ABCDEFGHIJKLMNOPQRSTUVWXYZ" 742 743 static isc_result_t 744 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash, 745 size_t hashlen) { 746 unsigned int i; 747 int ret; 748 for (i = 0; i < digestlen; i++) { 749 size_t left = hashlen - i * 2; 750 ret = snprintf(hash + i * 2, left, "%02x", digest[i]); 751 if (ret < 0 || (size_t)ret >= left) { 752 return (ISC_R_NOSPACE); 753 } 754 } 755 return (ISC_R_SUCCESS); 756 } 757 758 isc_result_t 759 isc_file_sanitize(const char *dir, const char *base, const char *ext, 760 char *path, size_t length) { 761 char buf[PATH_MAX]; 762 unsigned char digest[ISC_MAX_MD_SIZE]; 763 unsigned int digestlen; 764 char hash[ISC_MAX_MD_SIZE * 2 + 1]; 765 size_t l = 0; 766 isc_result_t err; 767 768 REQUIRE(base != NULL); 769 REQUIRE(path != NULL); 770 771 l = strlen(base) + 1; 772 773 /* 774 * allow room for a full sha256 hash (64 chars 775 * plus null terminator) 776 */ 777 if (l < 65U) { 778 l = 65; 779 } 780 781 if (dir != NULL) { 782 l += strlen(dir) + 1; 783 } 784 if (ext != NULL) { 785 l += strlen(ext) + 1; 786 } 787 788 if (l > length || l > (unsigned)PATH_MAX) { 789 return (ISC_R_NOSPACE); 790 } 791 792 /* Check whether the full-length SHA256 hash filename exists */ 793 err = isc_md(ISC_MD_SHA256, (const unsigned char *)base, strlen(base), 794 digest, &digestlen); 795 if (err != ISC_R_SUCCESS) { 796 return (err); 797 } 798 799 err = digest2hex(digest, digestlen, hash, sizeof(hash)); 800 if (err != ISC_R_SUCCESS) { 801 return (err); 802 } 803 804 snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "", 805 dir != NULL ? "/" : "", hash, ext != NULL ? "." : "", 806 ext != NULL ? ext : ""); 807 if (isc_file_exists(buf)) { 808 strlcpy(path, buf, length); 809 return (ISC_R_SUCCESS); 810 } 811 812 /* Check for a truncated SHA256 hash filename */ 813 hash[16] = '\0'; 814 snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "", 815 dir != NULL ? "/" : "", hash, ext != NULL ? "." : "", 816 ext != NULL ? ext : ""); 817 if (isc_file_exists(buf)) { 818 strlcpy(path, buf, length); 819 return (ISC_R_SUCCESS); 820 } 821 822 /* 823 * If neither hash filename already exists, then we'll use 824 * the original base name if it has no disallowed characters, 825 * or the truncated hash name if it does. 826 */ 827 if (strpbrk(base, DISALLOW) != NULL) { 828 strlcpy(path, buf, length); 829 return (ISC_R_SUCCESS); 830 } 831 832 snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "", 833 dir != NULL ? "/" : "", base, ext != NULL ? "." : "", 834 ext != NULL ? ext : ""); 835 strlcpy(path, buf, length); 836 return (ISC_R_SUCCESS); 837 } 838 839 bool 840 isc_file_isdirwritable(const char *path) { 841 return (access(path, W_OK | X_OK) == 0); 842 } 843