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