xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/isc/unix/file.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
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
file_stats(const char * file,struct stat * stats)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
fd_stats(int fd,struct stat * stats)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
isc_file_getsizefd(int fd,off_t * size)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
isc_file_mode(const char * file,mode_t * modep)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
isc_file_getmodtime(const char * file,isc_time_t * modtime)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
isc_file_getsize(const char * file,off_t * size)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
isc_file_settime(const char * file,isc_time_t * when)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
isc_file_mktemplate(const char * path,char * buf,size_t buflen)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
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)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
isc_file_renameunique(const char * file,char * templet)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
isc_file_openunique(char * templet,FILE ** fp)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
isc_file_openuniqueprivate(char * templet,FILE ** fp)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
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)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
isc_file_bopenunique(char * templet,FILE ** fp)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
isc_file_bopenuniqueprivate(char * templet,FILE ** fp)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
isc_file_bopenuniquemode(char * templet,int mode,FILE ** fp)402 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
403 	return (isc_file_openuniquemode(templet, mode, fp));
404 }
405 
406 isc_result_t
isc_file_remove(const char * filename)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
isc_file_rename(const char * oldname,const char * newname)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
isc_file_exists(const char * pathname)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
isc_file_isplainfile(const char * filename)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
isc_file_isplainfilefd(int fd)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
isc_file_isdirectory(const char * filename)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
isc_file_isabsolute(const char * filename)503 isc_file_isabsolute(const char *filename) {
504 	REQUIRE(filename != NULL);
505 	return (filename[0] == '/');
506 }
507 
508 bool
isc_file_iscurrentdir(const char * filename)509 isc_file_iscurrentdir(const char *filename) {
510 	REQUIRE(filename != NULL);
511 	return (filename[0] == '.' && filename[1] == '\0');
512 }
513 
514 bool
isc_file_ischdiridempotent(const char * filename)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 *
isc_file_basename(const char * filename)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
isc_file_progname(const char * filename,char * buf,size_t buflen)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
dir_current(char * dirname,size_t length)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
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)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
isc_file_truncate(const char * filename,isc_offset_t size)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
isc_file_safecreate(const char * filename,FILE ** fp)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
isc_file_splitpath(isc_mem_t * mctx,const char * path,char ** dirname,char const ** bname)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 *
isc_file_mmap(void * addr,size_t len,int prot,int flags,int fd,off_t offset)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
isc_file_munmap(void * addr,size_t len)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
digest2hex(unsigned char * digest,unsigned int digestlen,char * hash,size_t hashlen)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
isc_file_sanitize(const char * dir,const char * base,const char * ext,char * path,size_t length)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
isc_file_isdirwritable(const char * path)840 isc_file_isdirwritable(const char *path) {
841 	return (access(path, W_OK | X_OK) == 0);
842 }
843