"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/lib/isc/win32/file.c" (4 Sep 2020, 20488 Bytes) of package /linux/misc/dns/bind9/9.16.7/bind-9.16.7.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 #undef rename
   13 #include <errno.h>
   14 #include <fcntl.h>
   15 #include <inttypes.h>
   16 #include <io.h>
   17 #include <limits.h>
   18 #include <process.h>
   19 #include <stdbool.h>
   20 #include <stdlib.h>
   21 #include <sys/stat.h>
   22 #include <sys/utime.h>
   23 
   24 #include <isc/file.h>
   25 #include <isc/md.h>
   26 #include <isc/mem.h>
   27 #include <isc/platform.h>
   28 #include <isc/print.h>
   29 #include <isc/random.h>
   30 #include <isc/result.h>
   31 #include <isc/stat.h>
   32 #include <isc/string.h>
   33 #include <isc/time.h>
   34 #include <isc/util.h>
   35 
   36 #include "errno2result.h"
   37 
   38 static const char alphnum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
   39                   "wxyz0123456789";
   40 
   41 /*
   42  * Emulate UNIX mkstemp, which returns an open FD to the new file
   43  *
   44  */
   45 static int
   46 gettemp(char *path, bool binary, int *doopen) {
   47     char *start, *trv;
   48     struct stat sbuf;
   49     int flags = O_CREAT | O_EXCL | O_RDWR;
   50 
   51     if (binary) {
   52         flags |= _O_BINARY;
   53     }
   54 
   55     trv = strrchr(path, 'X');
   56     trv++;
   57     /* extra X's get set to 0's */
   58     while (*--trv == 'X') {
   59         uint32_t which = isc_random_uniform(sizeof(alphnum) - 1);
   60         *trv = alphnum[which];
   61     }
   62     /*
   63      * check the target directory; if you have six X's and it
   64      * doesn't exist this runs for a *very* long time.
   65      */
   66     for (start = trv + 1;; --trv) {
   67         if (trv <= path) {
   68             break;
   69         }
   70         if (*trv == '\\') {
   71             *trv = '\0';
   72             if (stat(path, &sbuf)) {
   73                 return (0);
   74             }
   75             if (!S_ISDIR(sbuf.st_mode)) {
   76                 errno = ENOTDIR;
   77                 return (0);
   78             }
   79             *trv = '\\';
   80             break;
   81         }
   82     }
   83 
   84     for (;;) {
   85         if (doopen) {
   86             if ((*doopen = open(path, flags,
   87                         _S_IREAD | _S_IWRITE)) >= 0) {
   88                 return (1);
   89             }
   90             if (errno != EEXIST) {
   91                 return (0);
   92             }
   93         } else if (stat(path, &sbuf)) {
   94             return (errno == ENOENT ? 1 : 0);
   95         }
   96 
   97         /* tricky little algorithm for backward compatibility */
   98         for (trv = start;;) {
   99             if (!*trv) {
  100                 return (0);
  101             }
  102             if (*trv == 'z') {
  103                 *trv++ = 'a';
  104             } else {
  105                 if (isdigit(*trv)) {
  106                     *trv = 'a';
  107                 } else {
  108                     ++*trv;
  109                 }
  110                 break;
  111             }
  112         }
  113     }
  114     /*NOTREACHED*/
  115 }
  116 
  117 static int
  118 mkstemp(char *path, bool binary) {
  119     int fd;
  120 
  121     return (gettemp(path, binary, &fd) ? fd : -1);
  122 }
  123 
  124 /*
  125  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
  126  * it might be good to provide a mechanism that allows for the results
  127  * of a previous stat() to be used again without having to do another stat,
  128  * such as perl's mechanism of using "_" in place of a file name to indicate
  129  * that the results of the last stat should be used.  But then you get into
  130  * annoying MP issues.   BTW, Win32 has stat().
  131  */
  132 static isc_result_t
  133 file_stats(const char *file, struct stat *stats) {
  134     isc_result_t result = ISC_R_SUCCESS;
  135 
  136     REQUIRE(file != NULL);
  137     REQUIRE(stats != NULL);
  138 
  139     if (stat(file, stats) != 0) {
  140         result = isc__errno2result(errno);
  141     }
  142 
  143     return (result);
  144 }
  145 
  146 static isc_result_t
  147 fd_stats(int fd, struct stat *stats) {
  148     isc_result_t result = ISC_R_SUCCESS;
  149 
  150     REQUIRE(stats != NULL);
  151 
  152     if (fstat(fd, stats) != 0) {
  153         result = isc__errno2result(errno);
  154     }
  155 
  156     return (result);
  157 }
  158 
  159 isc_result_t
  160 isc_file_getsizefd(int fd, off_t *size) {
  161     isc_result_t result;
  162     struct stat stats;
  163 
  164     REQUIRE(size != NULL);
  165 
  166     result = fd_stats(fd, &stats);
  167 
  168     if (result == ISC_R_SUCCESS) {
  169         *size = stats.st_size;
  170     }
  171     return (result);
  172 }
  173 
  174 isc_result_t
  175 isc_file_mode(const char *file, mode_t *modep) {
  176     isc_result_t result;
  177     struct stat stats;
  178 
  179     REQUIRE(modep != NULL);
  180 
  181     result = file_stats(file, &stats);
  182     if (result == ISC_R_SUCCESS) {
  183         *modep = (stats.st_mode & 07777);
  184     }
  185     return (result);
  186 }
  187 
  188 /*
  189  * isc_file_safemovefile is needed to be defined here to ensure that
  190  * any file with the new name is renamed to a backup name and then the
  191  * rename is done. If all goes well then the backup can be deleted,
  192  * otherwise it gets renamed back.
  193  */
  194 
  195 int
  196 isc_file_safemovefile(const char *oldname, const char *newname) {
  197     BOOL filestatus;
  198     char buf[512];
  199     struct stat sbuf;
  200     BOOL exists = FALSE;
  201     int tmpfd;
  202 
  203     /*
  204      * Make sure we have something to do
  205      */
  206     if (stat(oldname, &sbuf) != 0) {
  207         errno = ENOENT;
  208         return (-1);
  209     }
  210 
  211     /*
  212      * Rename to a backup the new file if it still exists
  213      */
  214     if (stat(newname, &sbuf) == 0) {
  215         exists = TRUE;
  216         strlcpy(buf, newname, sizeof(buf));
  217         strlcat(buf, ".XXXXX", sizeof(buf));
  218         tmpfd = mkstemp(buf, true);
  219         if (tmpfd > 0) {
  220             _close(tmpfd);
  221         }
  222         (void)DeleteFile(buf);
  223         _chmod(newname, _S_IREAD | _S_IWRITE);
  224 
  225         filestatus = MoveFile(newname, buf);
  226     }
  227     /* Now rename the file to the new name
  228      */
  229     _chmod(oldname, _S_IREAD | _S_IWRITE);
  230 
  231     filestatus = MoveFile(oldname, newname);
  232     if (filestatus == 0) {
  233         /*
  234          * Try to rename the backup back to the original name
  235          * if the backup got created
  236          */
  237         if (exists == TRUE) {
  238             filestatus = MoveFile(buf, newname);
  239             if (filestatus == 0) {
  240                 errno = EACCES;
  241             }
  242         }
  243         return (-1);
  244     }
  245 
  246     /*
  247      * Delete the backup file if it got created
  248      */
  249     if (exists == TRUE) {
  250         (void)DeleteFile(buf);
  251     }
  252     return (0);
  253 }
  254 
  255 isc_result_t
  256 isc_file_getmodtime(const char *file, isc_time_t *time) {
  257     int fh;
  258 
  259     REQUIRE(file != NULL);
  260     REQUIRE(time != NULL);
  261 
  262     if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0) {
  263         return (isc__errno2result(errno));
  264     }
  265 
  266     if (!GetFileTime((HANDLE)_get_osfhandle(fh), NULL, NULL,
  267              &time->absolute)) {
  268         close(fh);
  269         errno = EINVAL;
  270         return (isc__errno2result(errno));
  271     }
  272     close(fh);
  273     return (ISC_R_SUCCESS);
  274 }
  275 
  276 isc_result_t
  277 isc_file_getsize(const char *file, off_t *size) {
  278     isc_result_t result;
  279     struct stat stats;
  280 
  281     REQUIRE(file != NULL);
  282     REQUIRE(size != NULL);
  283 
  284     result = file_stats(file, &stats);
  285 
  286     if (result == ISC_R_SUCCESS) {
  287         *size = stats.st_size;
  288     }
  289 
  290     return (result);
  291 }
  292 
  293 isc_result_t
  294 isc_file_settime(const char *file, isc_time_t *time) {
  295     int fh;
  296 
  297     REQUIRE(file != NULL && time != NULL);
  298 
  299     if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0) {
  300         return (isc__errno2result(errno));
  301     }
  302 
  303     /*
  304      * Set the date via the filedate system call and return.  Failing
  305      * this call implies the new file times are not supported by the
  306      * underlying file system.
  307      */
  308     if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &time->absolute,
  309              &time->absolute))
  310     {
  311         close(fh);
  312         errno = EINVAL;
  313         return (isc__errno2result(errno));
  314     }
  315 
  316     close(fh);
  317     return (ISC_R_SUCCESS);
  318 }
  319 
  320 #undef TEMPLATE
  321 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
  322 
  323 isc_result_t
  324 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
  325     return (isc_file_template(path, TEMPLATE, buf, buflen));
  326 }
  327 
  328 isc_result_t
  329 isc_file_template(const char *path, const char *templet, char *buf,
  330           size_t buflen) {
  331     char *s;
  332 
  333     REQUIRE(templet != NULL);
  334     REQUIRE(buf != NULL);
  335 
  336     if (path == NULL) {
  337         path = "";
  338     }
  339 
  340     s = strrchr(templet, '\\');
  341     if (s != NULL) {
  342         templet = s + 1;
  343     }
  344 
  345     s = strrchr(path, '\\');
  346 
  347     if (s != NULL) {
  348         size_t prefixlen = s - path + 1;
  349         if ((prefixlen + strlen(templet) + 1) > buflen) {
  350             return (ISC_R_NOSPACE);
  351         }
  352 
  353         /* Copy 'prefixlen' bytes and NUL terminate. */
  354         strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen));
  355         strlcat(buf, templet, buflen);
  356     } else {
  357         if ((strlen(templet) + 1) > buflen) {
  358             return (ISC_R_NOSPACE);
  359         }
  360 
  361         strlcpy(buf, templet, buflen);
  362     }
  363 
  364     return (ISC_R_SUCCESS);
  365 }
  366 
  367 isc_result_t
  368 isc_file_renameunique(const char *file, char *templet) {
  369     int fd;
  370     isc_result_t result = ISC_R_SUCCESS;
  371 
  372     REQUIRE(file != NULL);
  373     REQUIRE(templet != NULL);
  374 
  375     fd = mkstemp(templet, true);
  376     if (fd == -1) {
  377         result = isc__errno2result(errno);
  378     } else {
  379         close(fd);
  380     }
  381 
  382     if (result == ISC_R_SUCCESS) {
  383         int res;
  384         res = isc_file_safemovefile(file, templet);
  385         if (res != 0) {
  386             result = isc__errno2result(errno);
  387             (void)unlink(templet);
  388         }
  389     }
  390     return (result);
  391 }
  392 
  393 static isc_result_t
  394 openuniquemode(char *templet, int mode, bool binary, FILE **fp) {
  395     int fd;
  396     FILE *f;
  397     isc_result_t result = ISC_R_SUCCESS;
  398 
  399     REQUIRE(templet != NULL);
  400     REQUIRE(fp != NULL && *fp == NULL);
  401 
  402     /*
  403      * Win32 does not have mkstemp. Using emulation above.
  404      */
  405     fd = mkstemp(templet, binary);
  406 
  407     if (fd == -1) {
  408         result = isc__errno2result(errno);
  409     }
  410     if (result == ISC_R_SUCCESS) {
  411 #if 1
  412         UNUSED(mode);
  413 #else  /* if 1 */
  414         (void)fchmod(fd, mode);
  415 #endif /* if 1 */
  416         f = fdopen(fd, binary ? "wb+" : "w+");
  417         if (f == NULL) {
  418             result = isc__errno2result(errno);
  419             (void)remove(templet);
  420             (void)close(fd);
  421         } else {
  422             *fp = f;
  423         }
  424     }
  425 
  426     return (result);
  427 }
  428 
  429 isc_result_t
  430 isc_file_openuniqueprivate(char *templet, FILE **fp) {
  431     int mode = _S_IREAD | _S_IWRITE;
  432     return (openuniquemode(templet, mode, false, fp));
  433 }
  434 
  435 isc_result_t
  436 isc_file_openunique(char *templet, FILE **fp) {
  437     int mode = _S_IREAD | _S_IWRITE;
  438     return (openuniquemode(templet, mode, false, fp));
  439 }
  440 
  441 isc_result_t
  442 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
  443     return (openuniquemode(templet, mode, false, fp));
  444 }
  445 
  446 isc_result_t
  447 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
  448     int mode = _S_IREAD | _S_IWRITE;
  449     return (openuniquemode(templet, mode, true, fp));
  450 }
  451 
  452 isc_result_t
  453 isc_file_bopenunique(char *templet, FILE **fp) {
  454     int mode = _S_IREAD | _S_IWRITE;
  455     return (openuniquemode(templet, mode, true, fp));
  456 }
  457 
  458 isc_result_t
  459 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
  460     return (openuniquemode(templet, mode, true, fp));
  461 }
  462 
  463 isc_result_t
  464 isc_file_remove(const char *filename) {
  465     int r;
  466 
  467     REQUIRE(filename != NULL);
  468 
  469     r = unlink(filename);
  470     if (r == 0) {
  471         return (ISC_R_SUCCESS);
  472     } else {
  473         return (isc__errno2result(errno));
  474     }
  475 }
  476 
  477 isc_result_t
  478 isc_file_rename(const char *oldname, const char *newname) {
  479     int r;
  480 
  481     REQUIRE(oldname != NULL);
  482     REQUIRE(newname != NULL);
  483 
  484     r = isc_file_safemovefile(oldname, newname);
  485     if (r == 0) {
  486         return (ISC_R_SUCCESS);
  487     } else {
  488         return (isc__errno2result(errno));
  489     }
  490 }
  491 
  492 bool
  493 isc_file_exists(const char *pathname) {
  494     struct stat stats;
  495 
  496     REQUIRE(pathname != NULL);
  497 
  498     return (file_stats(pathname, &stats) == ISC_R_SUCCESS);
  499 }
  500 
  501 isc_result_t
  502 isc_file_isplainfile(const char *filename) {
  503     /*
  504      * This function returns success if filename is a plain file.
  505      */
  506     struct stat filestat;
  507     memset(&filestat, 0, sizeof(struct stat));
  508 
  509     if ((stat(filename, &filestat)) == -1) {
  510         return (isc__errno2result(errno));
  511     }
  512 
  513     if (!S_ISREG(filestat.st_mode)) {
  514         return (ISC_R_INVALIDFILE);
  515     }
  516 
  517     return (ISC_R_SUCCESS);
  518 }
  519 
  520 isc_result_t
  521 isc_file_isplainfilefd(int fd) {
  522     /*
  523      * This function returns success if filename is a plain file.
  524      */
  525     struct stat filestat;
  526     memset(&filestat, 0, sizeof(struct stat));
  527 
  528     if ((fstat(fd, &filestat)) == -1) {
  529         return (isc__errno2result(errno));
  530     }
  531 
  532     if (!S_ISREG(filestat.st_mode)) {
  533         return (ISC_R_INVALIDFILE);
  534     }
  535 
  536     return (ISC_R_SUCCESS);
  537 }
  538 
  539 isc_result_t
  540 isc_file_isdirectory(const char *filename) {
  541     /*
  542      * This function returns success if filename is a directory.
  543      */
  544     struct stat filestat;
  545     memset(&filestat, 0, sizeof(struct stat));
  546 
  547     if ((stat(filename, &filestat)) == -1) {
  548         return (isc__errno2result(errno));
  549     }
  550 
  551     if (!S_ISDIR(filestat.st_mode)) {
  552         return (ISC_R_INVALIDFILE);
  553     }
  554 
  555     return (ISC_R_SUCCESS);
  556 }
  557 
  558 bool
  559 isc_file_isabsolute(const char *filename) {
  560     REQUIRE(filename != NULL);
  561     /*
  562      * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
  563      * the UNC style file specs
  564      */
  565     if ((filename[0] == '\\') && (filename[1] == '\\')) {
  566         return (true);
  567     }
  568     if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\') {
  569         return (true);
  570     }
  571     if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/') {
  572         return (true);
  573     }
  574     return (false);
  575 }
  576 
  577 bool
  578 isc_file_iscurrentdir(const char *filename) {
  579     REQUIRE(filename != NULL);
  580     return (filename[0] == '.' && filename[1] == '\0');
  581 }
  582 
  583 bool
  584 isc_file_ischdiridempotent(const char *filename) {
  585     REQUIRE(filename != NULL);
  586 
  587     if (isc_file_isabsolute(filename)) {
  588         return (true);
  589     }
  590     if (filename[0] == '\\') {
  591         return (true);
  592     }
  593     if (filename[0] == '/') {
  594         return (true);
  595     }
  596     if (isc_file_iscurrentdir(filename)) {
  597         return (true);
  598     }
  599     return (false);
  600 }
  601 
  602 const char *
  603 isc_file_basename(const char *filename) {
  604     char *s;
  605 
  606     REQUIRE(filename != NULL);
  607 
  608     s = strrchr(filename, '\\');
  609     if (s == NULL) {
  610         return (filename);
  611     }
  612     return (s + 1);
  613 }
  614 
  615 isc_result_t
  616 isc_file_progname(const char *filename, char *progname, size_t namelen) {
  617     const char *s;
  618     const char *p;
  619     size_t len;
  620 
  621     REQUIRE(filename != NULL);
  622     REQUIRE(progname != NULL);
  623 
  624     /*
  625      * Strip the path from the name
  626      */
  627     s = isc_file_basename(filename);
  628     if (s == NULL) {
  629         return (ISC_R_NOSPACE);
  630     }
  631 
  632     /*
  633      * Strip any and all suffixes
  634      */
  635     p = strchr(s, '.');
  636     if (p == NULL) {
  637         if (namelen <= strlen(s)) {
  638             return (ISC_R_NOSPACE);
  639         }
  640 
  641         strlcpy(progname, s, namelen);
  642         return (ISC_R_SUCCESS);
  643     }
  644 
  645     /*
  646      * Copy the result to the buffer
  647      */
  648     len = p - s;
  649     if (len >= namelen) {
  650         return (ISC_R_NOSPACE);
  651     }
  652 
  653     /* Copy up to 'len' bytes and NUL terminate. */
  654     strlcpy(progname, s, ISC_MIN(len + 1, namelen));
  655     return (ISC_R_SUCCESS);
  656 }
  657 
  658 isc_result_t
  659 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
  660     char *ptrname;
  661     DWORD retval;
  662 
  663     REQUIRE(filename != NULL);
  664     REQUIRE(path != NULL);
  665 
  666     retval = GetFullPathName(filename, (DWORD)pathlen, path, &ptrname);
  667 
  668     /* Something went wrong in getting the path */
  669     if (retval == 0) {
  670         return (ISC_R_NOTFOUND);
  671     }
  672     /* Caller needs to provide a larger buffer to contain the string */
  673     if (retval >= pathlen) {
  674         return (ISC_R_NOSPACE);
  675     }
  676     return (ISC_R_SUCCESS);
  677 }
  678 
  679 isc_result_t
  680 isc_file_truncate(const char *filename, isc_offset_t size) {
  681     int fh;
  682 
  683     REQUIRE(filename != NULL && size >= 0);
  684 
  685     if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0) {
  686         return (isc__errno2result(errno));
  687     }
  688 
  689     if (_chsize(fh, size) != 0) {
  690         close(fh);
  691         return (isc__errno2result(errno));
  692     }
  693     close(fh);
  694 
  695     return (ISC_R_SUCCESS);
  696 }
  697 
  698 isc_result_t
  699 isc_file_safecreate(const char *filename, FILE **fp) {
  700     isc_result_t result;
  701     int flags;
  702     struct stat sb;
  703     FILE *f;
  704     int fd;
  705 
  706     REQUIRE(filename != NULL);
  707     REQUIRE(fp != NULL && *fp == NULL);
  708 
  709     result = file_stats(filename, &sb);
  710     if (result == ISC_R_SUCCESS) {
  711         if ((sb.st_mode & S_IFREG) == 0) {
  712             return (ISC_R_INVALIDFILE);
  713         }
  714         flags = O_WRONLY | O_TRUNC;
  715     } else if (result == ISC_R_FILENOTFOUND) {
  716         flags = O_WRONLY | O_CREAT | O_EXCL;
  717     } else {
  718         return (result);
  719     }
  720 
  721     fd = open(filename, flags, S_IRUSR | S_IWUSR);
  722     if (fd == -1) {
  723         return (isc__errno2result(errno));
  724     }
  725 
  726     f = fdopen(fd, "w");
  727     if (f == NULL) {
  728         result = isc__errno2result(errno);
  729         close(fd);
  730         return (result);
  731     }
  732 
  733     *fp = f;
  734     return (ISC_R_SUCCESS);
  735 }
  736 
  737 isc_result_t
  738 isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
  739            char const **basename) {
  740     char *dir;
  741     const char *file, *slash;
  742     char *backslash;
  743 
  744     slash = strrchr(path, '/');
  745 
  746     backslash = strrchr(path, '\\');
  747     if ((slash != NULL && backslash != NULL && backslash > slash) ||
  748         (slash == NULL && backslash != NULL))
  749     {
  750         slash = backslash;
  751     }
  752 
  753     if (slash == path) {
  754         file = ++slash;
  755         dir = isc_mem_strdup(mctx, "/");
  756     } else if (slash != NULL) {
  757         file = ++slash;
  758         dir = isc_mem_allocate(mctx, slash - path);
  759         strlcpy(dir, path, slash - path);
  760     } else {
  761         file = path;
  762         dir = isc_mem_strdup(mctx, ".");
  763     }
  764 
  765     if (dir == NULL) {
  766         return (ISC_R_NOMEMORY);
  767     }
  768 
  769     if (*file == '\0') {
  770         isc_mem_free(mctx, dir);
  771         return (ISC_R_INVALIDFILE);
  772     }
  773 
  774     *dirname = dir;
  775     *basename = file;
  776 
  777     return (ISC_R_SUCCESS);
  778 }
  779 
  780 void *
  781 isc_file_mmap(void *addr, size_t len, int prot, int flags, int fd,
  782           off_t offset) {
  783     void *buf;
  784     ssize_t ret;
  785     off_t end;
  786 
  787     UNUSED(addr);
  788     UNUSED(prot);
  789     UNUSED(flags);
  790 
  791     end = lseek(fd, 0, SEEK_END);
  792     lseek(fd, offset, SEEK_SET);
  793     if (end - offset < (off_t)len) {
  794         len = end - offset;
  795     }
  796 
  797     buf = malloc(len);
  798     if (buf == NULL) {
  799         return (NULL);
  800     }
  801 
  802     ret = read(fd, buf, (unsigned int)len);
  803     if (ret != (ssize_t)len) {
  804         free(buf);
  805         buf = NULL;
  806     }
  807 
  808     return (buf);
  809 }
  810 
  811 int
  812 isc_file_munmap(void *addr, size_t len) {
  813     UNUSED(len);
  814     free(addr);
  815     return (0);
  816 }
  817 
  818 #define DISALLOW "\\/:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  819 
  820 static isc_result_t
  821 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
  822        size_t hashlen) {
  823     unsigned int i;
  824     int ret;
  825     for (i = 0; i < digestlen; i++) {
  826         size_t left = hashlen - i * 2;
  827         ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
  828         if (ret < 0 || (size_t)ret >= left) {
  829             return (ISC_R_NOSPACE);
  830         }
  831     }
  832     return (ISC_R_SUCCESS);
  833 }
  834 
  835 isc_result_t
  836 isc_file_sanitize(const char *dir, const char *base, const char *ext,
  837           char *path, size_t length) {
  838     char buf[PATH_MAX];
  839     unsigned char digest[ISC_MAX_MD_SIZE];
  840     unsigned int digestlen;
  841     char hash[ISC_MAX_MD_SIZE * 2 + 1];
  842     size_t l = 0;
  843     isc_result_t err;
  844 
  845     REQUIRE(base != NULL);
  846     REQUIRE(path != NULL);
  847 
  848     l = strlen(base) + 1;
  849 
  850     /*
  851      * allow room for a full sha256 hash (64 chars
  852      * plus null terminator)
  853      */
  854     if (l < 65) {
  855         l = 65;
  856     }
  857 
  858     if (dir != NULL) {
  859         l += strlen(dir) + 1;
  860     }
  861     if (ext != NULL) {
  862         l += strlen(ext) + 1;
  863     }
  864 
  865     if (l > length || l > PATH_MAX) {
  866         return (ISC_R_NOSPACE);
  867     }
  868 
  869     /* Check whether the full-length SHA256 hash filename exists */
  870     err = isc_md(ISC_MD_SHA256, (const unsigned char *)base, strlen(base),
  871              digest, &digestlen);
  872     if (err != ISC_R_SUCCESS) {
  873         return (err);
  874     }
  875 
  876     err = digest2hex(digest, digestlen, hash, sizeof(hash));
  877     if (err != ISC_R_SUCCESS) {
  878         return (err);
  879     }
  880 
  881     snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
  882          dir != NULL ? "/" : "", hash, ext != NULL ? "." : "",
  883          ext != NULL ? ext : "");
  884     if (isc_file_exists(buf)) {
  885         strlcpy(path, buf, length);
  886         return (ISC_R_SUCCESS);
  887     }
  888 
  889     /* Check for a truncated SHA256 hash filename */
  890     hash[16] = '\0';
  891     snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
  892          dir != NULL ? "/" : "", hash, ext != NULL ? "." : "",
  893          ext != NULL ? ext : "");
  894     if (isc_file_exists(buf)) {
  895         strlcpy(path, buf, length);
  896         return (ISC_R_SUCCESS);
  897     }
  898 
  899     /*
  900      * If neither hash filename already exists, then we'll use
  901      * the original base name if it has no disallowed characters,
  902      * or the truncated hash name if it does.
  903      */
  904     if (strpbrk(base, DISALLOW) != NULL) {
  905         strlcpy(path, buf, length);
  906         return (ISC_R_SUCCESS);
  907     }
  908 
  909     snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
  910          dir != NULL ? "/" : "", base, ext != NULL ? "." : "",
  911          ext != NULL ? ext : "");
  912     strlcpy(path, buf, length);
  913     return (ISC_R_SUCCESS);
  914 }
  915 
  916 /*
  917  * Based on http://blog.aaronballman.com/2011/08/how-to-check-access-rights/
  918  */
  919 bool
  920 isc_file_isdirwritable(const char *path) {
  921     DWORD length = 0;
  922     HANDLE hToken = NULL;
  923     PSECURITY_DESCRIPTOR security = NULL;
  924     bool answer = false;
  925 
  926     if (isc_file_isdirectory(path) != ISC_R_SUCCESS) {
  927         return (answer);
  928     }
  929 
  930     /*
  931      * Figure out buffer size. GetFileSecurity() should not succeed.
  932      */
  933     if (GetFileSecurity(path,
  934                 OWNER_SECURITY_INFORMATION |
  935                     GROUP_SECURITY_INFORMATION |
  936                     DACL_SECURITY_INFORMATION,
  937                 NULL, 0, &length))
  938     {
  939         return (answer);
  940     }
  941 
  942     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  943         return (answer);
  944     }
  945 
  946     security = malloc(length);
  947     if (security == NULL) {
  948         return (answer);
  949     }
  950 
  951     /*
  952      * GetFileSecurity() should succeed.
  953      */
  954     if (!GetFileSecurity(path,
  955                  OWNER_SECURITY_INFORMATION |
  956                      GROUP_SECURITY_INFORMATION |
  957                      DACL_SECURITY_INFORMATION,
  958                  security, length, &length))
  959     {
  960         return (answer);
  961     }
  962 
  963     if (OpenProcessToken(GetCurrentProcess(),
  964                  TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE |
  965                      STANDARD_RIGHTS_READ,
  966                  &hToken))
  967     {
  968         HANDLE hImpersonatedToken = NULL;
  969 
  970         if (DuplicateToken(hToken, SecurityImpersonation,
  971                    &hImpersonatedToken)) {
  972             GENERIC_MAPPING mapping;
  973             PRIVILEGE_SET privileges = { 0 };
  974             DWORD grantedAccess = 0;
  975             DWORD privilegesLength = sizeof(privileges);
  976             BOOL result = FALSE;
  977             DWORD genericAccessRights = GENERIC_WRITE;
  978 
  979             mapping.GenericRead = FILE_GENERIC_READ;
  980             mapping.GenericWrite = FILE_GENERIC_WRITE;
  981             mapping.GenericExecute = FILE_GENERIC_EXECUTE;
  982             mapping.GenericAll = FILE_ALL_ACCESS;
  983 
  984             MapGenericMask(&genericAccessRights, &mapping);
  985             if (AccessCheck(security, hImpersonatedToken,
  986                     genericAccessRights, &mapping,
  987                     &privileges, &privilegesLength,
  988                     &grantedAccess, &result))
  989             {
  990                 answer = result;
  991             }
  992             CloseHandle(hImpersonatedToken);
  993         }
  994         CloseHandle(hToken);
  995     }
  996     free(security);
  997     return (answer);
  998 }