xref: /minix/external/bsd/bind/dist/lib/isc/unix/file.c (revision bb9622b5)
1 /*	$NetBSD: file.c,v 1.9 2014/12/10 04:38:01 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2005, 2007, 2009, 2011-2014  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2000-2002  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * Portions Copyright (c) 1987, 1993
22  *      The Regents of the University of California.  All rights reserved.
23  *
24  * Redistribution and use in source and binary forms, with or without
25  * modification, are permitted provided that the following conditions
26  * are met:
27  * 1. Redistributions of source code must retain the above copyright
28  *    notice, this list of conditions and the following disclaimer.
29  * 2. Redistributions in binary form must reproduce the above copyright
30  *    notice, this list of conditions and the following disclaimer in the
31  *    documentation and/or other materials provided with the distribution.
32  * 3. Neither the name of the University nor the names of its contributors
33  *    may be used to endorse or promote products derived from this software
34  *    without specific prior written permission.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
37  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
40  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  */
48 
49 /* Id */
50 
51 /*! \file */
52 
53 #include <config.h>
54 
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <limits.h>
58 #include <stdlib.h>
59 #include <time.h>		/* Required for utimes on some platforms. */
60 #include <unistd.h>		/* Required for mkstemp on NetBSD. */
61 
62 
63 #include <sys/stat.h>
64 #include <sys/time.h>
65 
66 #ifdef HAVE_SYS_MMAN_H
67 #include <sys/mman.h>
68 #endif
69 
70 #include <isc/dir.h>
71 #include <isc/file.h>
72 #include <isc/log.h>
73 #include <isc/mem.h>
74 #include <isc/random.h>
75 #include <isc/string.h>
76 #include <isc/time.h>
77 #include <isc/util.h>
78 
79 #include "errno2result.h"
80 
81 /*
82  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
83  * it might be good to provide a mechanism that allows for the results
84  * of a previous stat() to be used again without having to do another stat,
85  * such as perl's mechanism of using "_" in place of a file name to indicate
86  * that the results of the last stat should be used.  But then you get into
87  * annoying MP issues.   BTW, Win32 has stat().
88  */
89 static isc_result_t
90 file_stats(const char *file, struct stat *stats) {
91 	isc_result_t result = ISC_R_SUCCESS;
92 
93 	REQUIRE(file != NULL);
94 	REQUIRE(stats != NULL);
95 
96 	if (stat(file, stats) != 0)
97 		result = isc__errno2result(errno);
98 
99 	return (result);
100 }
101 
102 static isc_result_t
103 fd_stats(int fd, struct stat *stats) {
104 	isc_result_t result = ISC_R_SUCCESS;
105 
106 	REQUIRE(stats != NULL);
107 
108 	if (fstat(fd, stats) != 0)
109 		result = isc__errno2result(errno);
110 
111 	return (result);
112 }
113 
114 isc_result_t
115 isc_file_getsizefd(int fd, off_t *size) {
116 	isc_result_t result;
117 	struct stat stats;
118 
119 	REQUIRE(size != NULL);
120 
121 	result = fd_stats(fd, &stats);
122 
123 	if (result == ISC_R_SUCCESS)
124 		*size = stats.st_size;
125 
126 	return (result);
127 }
128 
129 isc_result_t
130 isc_file_mode(const char *file, mode_t *modep) {
131 	isc_result_t result;
132 	struct stat stats;
133 
134 	REQUIRE(modep != NULL);
135 
136 	result = file_stats(file, &stats);
137 	if (result == ISC_R_SUCCESS)
138 		*modep = (stats.st_mode & 07777);
139 
140 	return (result);
141 }
142 
143 isc_result_t
144 isc_file_getmodtime(const char *file, isc_time_t *time) {
145 	isc_result_t result;
146 	struct stat stats;
147 
148 	REQUIRE(file != NULL);
149 	REQUIRE(time != NULL);
150 
151 	result = file_stats(file, &stats);
152 
153 	if (result == ISC_R_SUCCESS)
154 		/*
155 		 * XXXDCL some operating systems provide nanoseconds, too,
156 		 * such as BSD/OS via st_mtimespec.
157 		 */
158 		isc_time_set(time, stats.st_mtime, 0);
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 	return (result);
177 }
178 
179 isc_result_t
180 isc_file_settime(const char *file, isc_time_t *time) {
181 	struct timeval times[2];
182 
183 	REQUIRE(file != NULL && time != NULL);
184 
185 	/*
186 	 * tv_sec is at least a 32 bit quantity on all platforms we're
187 	 * dealing with, but it is signed on most (all?) of them,
188 	 * so we need to make sure the high bit isn't set.  This unfortunately
189 	 * loses when either:
190 	 *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
191 	 *	and isc_time_seconds > LONG_MAX, or
192 	 *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
193 	 *      and isc_time_seconds has at least 33 significant bits.
194 	 */
195 	times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
196 
197 	/*
198 	 * Here is the real check for the high bit being set.
199 	 */
200 	if ((times[0].tv_sec &
201 	     (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
202 		return (ISC_R_RANGE);
203 
204 	/*
205 	 * isc_time_nanoseconds guarantees a value that divided by 1000 will
206 	 * fit into the minimum possible size tv_usec field.  Unfortunately,
207 	 * we don't know what that type is so can't cast directly ... but
208 	 * we can at least cast to signed so the IRIX compiler shuts up.
209 	 */
210 	times[0].tv_usec = times[1].tv_usec =
211 		(isc_int32_t)(isc_time_nanoseconds(time) / 1000);
212 
213 	if (utimes(file, times) < 0)
214 		return (isc__errno2result(errno));
215 
216 	return (ISC_R_SUCCESS);
217 }
218 
219 #undef TEMPLATE
220 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
221 
222 isc_result_t
223 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
224 	return (isc_file_template(path, TEMPLATE, buf, buflen));
225 }
226 
227 isc_result_t
228 isc_file_template(const char *path, const char *templet, char *buf,
229 			size_t buflen) {
230 	char *s;
231 
232 	REQUIRE(path != NULL);
233 	REQUIRE(templet != NULL);
234 	REQUIRE(buf != NULL);
235 
236 	s = strrchr(templet, '/');
237 	if (s != NULL)
238 		templet = s + 1;
239 
240 	s = strrchr(path, '/');
241 
242 	if (s != NULL) {
243 		if ((s - path + 1 + strlen(templet) + 1) > buflen)
244 			return (ISC_R_NOSPACE);
245 
246 		strncpy(buf, path, s - path + 1);
247 		buf[s - path + 1] = '\0';
248 		strcat(buf, templet);
249 	} else {
250 		if ((strlen(templet) + 1) > buflen)
251 			return (ISC_R_NOSPACE);
252 
253 		strcpy(buf, templet);
254 	}
255 
256 	return (ISC_R_SUCCESS);
257 }
258 
259 static char alphnum[] =
260 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
261 
262 isc_result_t
263 isc_file_renameunique(const char *file, char *templet) {
264 	char *x;
265 	char *cp;
266 	isc_uint32_t which;
267 
268 	REQUIRE(file != NULL);
269 	REQUIRE(templet != NULL);
270 
271 	cp = templet;
272 	while (*cp != '\0')
273 		cp++;
274 	if (cp == templet)
275 		return (ISC_R_FAILURE);
276 
277 	x = cp--;
278 	while (cp >= templet && *cp == 'X') {
279 		isc_random_get(&which);
280 		*cp = alphnum[which % (sizeof(alphnum) - 1)];
281 		x = cp--;
282 	}
283 	while (link(file, templet) == -1) {
284 		if (errno != EEXIST)
285 			return (isc__errno2result(errno));
286 		for (cp = x;;) {
287 			char *t;
288 			if (*cp == '\0')
289 				return (ISC_R_FAILURE);
290 			t = strchr(alphnum, *cp);
291 			if (t == NULL || *++t == '\0')
292 				*cp++ = alphnum[0];
293 			else {
294 				*cp = *t;
295 				break;
296 			}
297 		}
298 	}
299 	if (unlink(file) < 0)
300 		if (errno != ENOENT)
301 			return (isc__errno2result(errno));
302 	return (ISC_R_SUCCESS);
303 }
304 
305 isc_result_t
306 isc_file_openunique(char *templet, FILE **fp) {
307 	int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
308 	return (isc_file_openuniquemode(templet, mode, fp));
309 }
310 
311 isc_result_t
312 isc_file_openuniqueprivate(char *templet, FILE **fp) {
313 	int mode = S_IWUSR|S_IRUSR;
314 	return (isc_file_openuniquemode(templet, mode, fp));
315 }
316 
317 isc_result_t
318 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
319 	int fd;
320 	FILE *f;
321 	isc_result_t result = ISC_R_SUCCESS;
322 	char *x;
323 	char *cp;
324 	isc_uint32_t which;
325 
326 	REQUIRE(templet != NULL);
327 	REQUIRE(fp != NULL && *fp == NULL);
328 
329 	cp = templet;
330 	while (*cp != '\0')
331 		cp++;
332 	if (cp == templet)
333 		return (ISC_R_FAILURE);
334 
335 	x = cp--;
336 	while (cp >= templet && *cp == 'X') {
337 		isc_random_get(&which);
338 		*cp = alphnum[which % (sizeof(alphnum) - 1)];
339 		x = cp--;
340 	}
341 
342 
343 	while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
344 		if (errno != EEXIST)
345 			return (isc__errno2result(errno));
346 		for (cp = x;;) {
347 			char *t;
348 			if (*cp == '\0')
349 				return (ISC_R_FAILURE);
350 			t = strchr(alphnum, *cp);
351 			if (t == NULL || *++t == '\0')
352 				*cp++ = alphnum[0];
353 			else {
354 				*cp = *t;
355 				break;
356 			}
357 		}
358 	}
359 	f = fdopen(fd, "w+");
360 	if (f == NULL) {
361 		result = isc__errno2result(errno);
362 		if (remove(templet) < 0) {
363 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
364 				      ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
365 				      "remove '%s': failed", templet);
366 		}
367 		(void)close(fd);
368 	} else
369 		*fp = f;
370 
371 	return (result);
372 }
373 
374 isc_result_t
375 isc_file_bopenunique(char *templet, FILE **fp) {
376 	int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
377 	return (isc_file_openuniquemode(templet, mode, fp));
378 }
379 
380 isc_result_t
381 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
382 	int mode = S_IWUSR|S_IRUSR;
383 	return (isc_file_openuniquemode(templet, mode, fp));
384 }
385 
386 isc_result_t
387 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
388 	return (isc_file_openuniquemode(templet, mode, fp));
389 }
390 
391 isc_result_t
392 isc_file_remove(const char *filename) {
393 	int r;
394 
395 	REQUIRE(filename != NULL);
396 
397 	r = unlink(filename);
398 	if (r == 0)
399 		return (ISC_R_SUCCESS);
400 	else
401 		return (isc__errno2result(errno));
402 }
403 
404 isc_result_t
405 isc_file_rename(const char *oldname, const char *newname) {
406 	int r;
407 
408 	REQUIRE(oldname != NULL);
409 	REQUIRE(newname != NULL);
410 
411 	r = rename(oldname, newname);
412 	if (r == 0)
413 		return (ISC_R_SUCCESS);
414 	else
415 		return (isc__errno2result(errno));
416 }
417 
418 isc_boolean_t
419 isc_file_exists(const char *pathname) {
420 	struct stat stats;
421 
422 	REQUIRE(pathname != NULL);
423 
424 	return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
425 }
426 
427 isc_result_t
428 isc_file_isplainfile(const char *filename) {
429 	/*
430 	 * This function returns success if filename is a plain file.
431 	 */
432 	struct stat filestat;
433 	memset(&filestat,0,sizeof(struct stat));
434 
435 	if ((stat(filename, &filestat)) == -1)
436 		return(isc__errno2result(errno));
437 
438 	if(! S_ISREG(filestat.st_mode))
439 		return(ISC_R_INVALIDFILE);
440 
441 	return(ISC_R_SUCCESS);
442 }
443 
444 isc_result_t
445 isc_file_isplainfilefd(int fd) {
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 ((fstat(fd, &filestat)) == -1)
453 		return(isc__errno2result(errno));
454 
455 	if(! S_ISREG(filestat.st_mode))
456 		return(ISC_R_INVALIDFILE);
457 
458 	return(ISC_R_SUCCESS);
459 }
460 
461 isc_result_t
462 isc_file_isdirectory(const char *filename) {
463 	/*
464 	 * This function returns success if filename exists and is a
465 	 * directory.
466 	 */
467 	struct stat filestat;
468 	memset(&filestat,0,sizeof(struct stat));
469 
470 	if ((stat(filename, &filestat)) == -1)
471 		return(isc__errno2result(errno));
472 
473 	if(! S_ISDIR(filestat.st_mode))
474 		return(ISC_R_INVALIDFILE);
475 
476 	return(ISC_R_SUCCESS);
477 }
478 
479 
480 isc_boolean_t
481 isc_file_isabsolute(const char *filename) {
482 	REQUIRE(filename != NULL);
483 	return (ISC_TF(filename[0] == '/'));
484 }
485 
486 isc_boolean_t
487 isc_file_iscurrentdir(const char *filename) {
488 	REQUIRE(filename != NULL);
489 	return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
490 }
491 
492 isc_boolean_t
493 isc_file_ischdiridempotent(const char *filename) {
494 	REQUIRE(filename != NULL);
495 	if (isc_file_isabsolute(filename))
496 		return (ISC_TRUE);
497 	if (isc_file_iscurrentdir(filename))
498 		return (ISC_TRUE);
499 	return (ISC_FALSE);
500 }
501 
502 const char *
503 isc_file_basename(const char *filename) {
504 	char *s;
505 
506 	REQUIRE(filename != NULL);
507 
508 	s = strrchr(filename, '/');
509 	if (s == NULL)
510 		return (filename);
511 
512 	return (s + 1);
513 }
514 
515 isc_result_t
516 isc_file_progname(const char *filename, char *buf, size_t buflen) {
517 	const char *base;
518 	size_t len;
519 
520 	REQUIRE(filename != NULL);
521 	REQUIRE(buf != NULL);
522 
523 	base = isc_file_basename(filename);
524 	len = strlen(base) + 1;
525 
526 	if (len > buflen)
527 		return (ISC_R_NOSPACE);
528 	memmove(buf, base, len);
529 
530 	return (ISC_R_SUCCESS);
531 }
532 
533 /*
534  * Put the absolute name of the current directory into 'dirname', which is
535  * a buffer of at least 'length' characters.  End the string with the
536  * appropriate path separator, such that the final product could be
537  * concatenated with a relative pathname to make a valid pathname string.
538  */
539 static isc_result_t
540 dir_current(char *dirname, size_t length) {
541 	char *cwd;
542 	isc_result_t result = ISC_R_SUCCESS;
543 
544 	REQUIRE(dirname != NULL);
545 	REQUIRE(length > 0U);
546 
547 	cwd = getcwd(dirname, length);
548 
549 	if (cwd == NULL) {
550 		if (errno == ERANGE)
551 			result = ISC_R_NOSPACE;
552 		else
553 			result = isc__errno2result(errno);
554 	} else {
555 		if (strlen(dirname) + 1 == length)
556 			result = ISC_R_NOSPACE;
557 		else if (dirname[1] != '\0')
558 			strcat(dirname, "/");
559 	}
560 
561 	return (result);
562 }
563 
564 isc_result_t
565 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
566 	isc_result_t result;
567 	result = dir_current(path, pathlen);
568 	if (result != ISC_R_SUCCESS)
569 		return (result);
570 	if (strlen(path) + strlen(filename) + 1 > pathlen)
571 		return (ISC_R_NOSPACE);
572 	strcat(path, filename);
573 	return (ISC_R_SUCCESS);
574 }
575 
576 isc_result_t
577 isc_file_truncate(const char *filename, isc_offset_t size) {
578 	isc_result_t result = ISC_R_SUCCESS;
579 
580 	if (truncate(filename, size) < 0)
581 		result = isc__errno2result(errno);
582 	return (result);
583 }
584 
585 isc_result_t
586 isc_file_safecreate(const char *filename, FILE **fp) {
587 	isc_result_t result;
588 	int flags;
589 	struct stat sb;
590 	FILE *f;
591 	int fd;
592 
593 	REQUIRE(filename != NULL);
594 	REQUIRE(fp != NULL && *fp == NULL);
595 
596 	result = file_stats(filename, &sb);
597 	if (result == ISC_R_SUCCESS) {
598 		if ((sb.st_mode & S_IFREG) == 0)
599 			return (ISC_R_INVALIDFILE);
600 		flags = O_WRONLY | O_TRUNC;
601 	} else if (result == ISC_R_FILENOTFOUND) {
602 		flags = O_WRONLY | O_CREAT | O_EXCL;
603 	} else
604 		return (result);
605 
606 	fd = open(filename, flags, S_IRUSR | S_IWUSR);
607 	if (fd == -1)
608 		return (isc__errno2result(errno));
609 
610 	f = fdopen(fd, "w");
611 	if (f == NULL) {
612 		result = isc__errno2result(errno);
613 		close(fd);
614 		return (result);
615 	}
616 
617 	*fp = f;
618 	return (ISC_R_SUCCESS);
619 }
620 
621 isc_result_t
622 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
623 {
624 	char *dir, *file, *slash;
625 
626 	if (path == NULL)
627 		return (ISC_R_INVALIDFILE);
628 
629 	slash = strrchr(path, '/');
630 
631 	if (slash == path) {
632 		file = ++slash;
633 		dir = isc_mem_strdup(mctx, "/");
634 	} else if (slash != NULL) {
635 		file = ++slash;
636 		dir = isc_mem_allocate(mctx, slash - path);
637 		if (dir != NULL)
638 			strlcpy(dir, path, slash - path);
639 	} else {
640 		file = path;
641 		dir = isc_mem_strdup(mctx, ".");
642 	}
643 
644 	if (dir == NULL)
645 		return (ISC_R_NOMEMORY);
646 
647 	if (*file == '\0') {
648 		isc_mem_free(mctx, dir);
649 		return (ISC_R_INVALIDFILE);
650 	}
651 
652 	*dirname = dir;
653 	*basename = file;
654 
655 	return (ISC_R_SUCCESS);
656 }
657 
658 void *
659 isc_file_mmap(void *addr, size_t len, int prot,
660 	      int flags, int fd, off_t offset)
661 {
662 #ifdef HAVE_MMAP
663 	return (mmap(addr, len, prot, flags, fd, offset));
664 #else
665 	void *buf;
666 	ssize_t ret;
667 	off_t end;
668 
669 	UNUSED(addr);
670 	UNUSED(prot);
671 	UNUSED(flags);
672 
673 	end = lseek(fd, 0, SEEK_END);
674 	lseek(fd, offset, SEEK_SET);
675 	if (end - offset < (off_t) len)
676 		len = end - offset;
677 
678 	buf = malloc(len);
679 	ret = read(fd, buf, len);
680 	if (ret != (ssize_t) len) {
681 		free(buf);
682 		buf = NULL;
683 	}
684 
685 	return (buf);
686 #endif
687 }
688 
689 int
690 isc_file_munmap(void *addr, size_t len) {
691 #ifdef HAVE_MMAP
692 	return (munmap(addr, len));
693 #else
694 	UNUSED(len);
695 
696 	free(addr);
697 	return (0);
698 #endif
699 }
700