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