"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/lib/isc/unix/file.c" (4 Sep 2020, 18634 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "file.c" see the Fossies "Dox" file reference documentation.

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