Home | History | Annotate | Line # | Download | only in unix
      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