file.c revision 290001
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#include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
79
80/*
81 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
82 * it might be good to provide a mechanism that allows for the results
83 * of a previous stat() to be used again without having to do another stat,
84 * such as perl's mechanism of using "_" in place of a file name to indicate
85 * that the results of the last stat should be used.  But then you get into
86 * annoying MP issues.   BTW, Win32 has stat().
87 */
88static isc_result_t
89file_stats(const char *file, struct stat *stats) {
90	isc_result_t result = ISC_R_SUCCESS;
91
92	REQUIRE(file != NULL);
93	REQUIRE(stats != NULL);
94
95	if (stat(file, stats) != 0)
96		result = isc__errno2result(errno);
97
98	return (result);
99}
100
101isc_result_t
102isc_file_getmodtime(const char *file, isc_time_t *itime) {
103	isc_result_t result;
104	struct stat stats;
105
106	REQUIRE(file != NULL);
107	REQUIRE(itime != NULL);
108
109	result = file_stats(file, &stats);
110
111	if (result == ISC_R_SUCCESS)
112		/*
113		 * XXXDCL some operating systems provide nanoseconds, too,
114		 * such as BSD/OS via st_mtimespec.
115		 */
116		isc_time_set(itime, stats.st_mtime, 0);
117
118	return (result);
119}
120
121isc_result_t
122isc_file_settime(const char *file, isc_time_t *itime) {
123	struct timeval times[2];
124
125	REQUIRE(file != NULL && itime != NULL);
126
127	/*
128	 * tv_sec is at least a 32 bit quantity on all platforms we're
129	 * dealing with, but it is signed on most (all?) of them,
130	 * so we need to make sure the high bit isn't set.  This unfortunately
131	 * loses when either:
132	 *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
133	 *	and isc_time_seconds > LONG_MAX, or
134	 *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
135	 *      and isc_time_seconds has at least 33 significant bits.
136	 */
137	times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(itime);
138
139	/*
140	 * Here is the real check for the high bit being set.
141	 */
142	if ((times[0].tv_sec &
143	     (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
144		return (ISC_R_RANGE);
145
146	/*
147	 * isc_time_nanoseconds guarantees a value that divided by 1000 will
148	 * fit into the minimum possible size tv_usec field.  Unfortunately,
149	 * we don't know what that type is so can't cast directly ... but
150	 * we can at least cast to signed so the IRIX compiler shuts up.
151	 */
152	times[0].tv_usec = times[1].tv_usec =
153		(isc_int32_t)(isc_time_nanoseconds(itime) / 1000);
154
155	if (utimes(file, times) < 0)
156		return (isc__errno2result(errno));
157
158	return (ISC_R_SUCCESS);
159}
160
161#undef TEMPLATE
162#define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
163
164isc_result_t
165isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
166	return (isc_file_template(path, TEMPLATE, buf, buflen));
167}
168
169isc_result_t
170isc_file_template(const char *path, const char *templet, char *buf,
171			size_t buflen) {
172	char *s;
173
174	REQUIRE(path != NULL);
175	REQUIRE(templet != NULL);
176	REQUIRE(buf != NULL);
177
178	s = strrchr(templet, '/');
179	if (s != NULL)
180		templet = s + 1;
181
182	s = strrchr(path, '/');
183
184	if (s != NULL) {
185		if ((s - path + 1 + strlen(templet) + 1) > buflen)
186			return (ISC_R_NOSPACE);
187
188		strlcpy(buf, path, buflen);
189		buf[s - path + 1] = '\0';
190		strlcat(buf, templet, buflen);
191	} else {
192		if ((strlen(templet) + 1) > buflen)
193			return (ISC_R_NOSPACE);
194
195		strlcpy(buf, templet, buflen);
196	}
197
198	return (ISC_R_SUCCESS);
199}
200
201static char alphnum[] =
202	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
203
204isc_result_t
205isc_file_renameunique(const char *file, char *templet) {
206	char *x;
207	char *cp;
208	isc_uint32_t which;
209
210	REQUIRE(file != NULL);
211	REQUIRE(templet != NULL);
212
213	cp = templet;
214	while (*cp != '\0')
215		cp++;
216	if (cp == templet)
217		return (ISC_R_FAILURE);
218
219	x = cp--;
220	while (cp >= templet && *cp == 'X') {
221		isc_random_get(&which);
222		*cp = alphnum[which % (sizeof(alphnum) - 1)];
223		x = cp--;
224	}
225	while (link(file, templet) == -1) {
226		if (errno != EEXIST)
227			return (isc__errno2result(errno));
228		for (cp = x;;) {
229			char *t;
230			if (*cp == '\0')
231				return (ISC_R_FAILURE);
232			t = strchr(alphnum, *cp);
233			if (t == NULL || *++t == '\0')
234				*cp++ = alphnum[0];
235			else {
236				*cp = *t;
237				break;
238			}
239		}
240	}
241	if (unlink(file) < 0)
242		if (errno != ENOENT)
243			return (isc__errno2result(errno));
244	return (ISC_R_SUCCESS);
245}
246
247isc_result_t
248isc_file_openunique(char *templet, FILE **fp) {
249	int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
250	return (isc_file_openuniquemode(templet, mode, fp));
251}
252
253isc_result_t
254isc_file_openuniqueprivate(char *templet, FILE **fp) {
255	int mode = S_IWUSR|S_IRUSR;
256	return (isc_file_openuniquemode(templet, mode, fp));
257}
258
259isc_result_t
260isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
261	int fd;
262	FILE *f;
263	isc_result_t result = ISC_R_SUCCESS;
264	char *x;
265	char *cp;
266	isc_uint32_t which;
267
268	REQUIRE(templet != NULL);
269	REQUIRE(fp != NULL && *fp == 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
284
285	while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
286		if (errno != EEXIST)
287			return (isc__errno2result(errno));
288		for (cp = x;;) {
289			char *t;
290			if (*cp == '\0')
291				return (ISC_R_FAILURE);
292			t = strchr(alphnum, *cp);
293			if (t == NULL || *++t == '\0')
294				*cp++ = alphnum[0];
295			else {
296				*cp = *t;
297				break;
298			}
299		}
300	}
301	f = fdopen(fd, "w+");
302	if (f == NULL) {
303		result = isc__errno2result(errno);
304		if (remove(templet) < 0) {
305			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
306				      ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
307				      "remove '%s': failed", templet);
308		}
309		(void)close(fd);
310	} else
311		*fp = f;
312
313	return (result);
314}
315
316isc_result_t
317isc_file_remove(const char *filename) {
318	int r;
319
320	REQUIRE(filename != NULL);
321
322	r = unlink(filename);
323	if (r == 0)
324		return (ISC_R_SUCCESS);
325	else
326		return (isc__errno2result(errno));
327}
328
329isc_result_t
330isc_file_rename(const char *oldname, const char *newname) {
331	int r;
332
333	REQUIRE(oldname != NULL);
334	REQUIRE(newname != NULL);
335
336	r = rename(oldname, newname);
337	if (r == 0)
338		return (ISC_R_SUCCESS);
339	else
340		return (isc__errno2result(errno));
341}
342
343isc_boolean_t
344isc_file_exists(const char *pathname) {
345	struct stat stats;
346
347	REQUIRE(pathname != NULL);
348
349	return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
350}
351
352isc_result_t
353isc_file_isplainfile(const char *filename) {
354	/*
355	 * This function returns success if filename is a plain file.
356	 */
357	struct stat filestat;
358	memset(&filestat,0,sizeof(struct stat));
359
360	if ((stat(filename, &filestat)) == -1)
361		return(isc__errno2result(errno));
362
363	if(! S_ISREG(filestat.st_mode))
364		return(ISC_R_INVALIDFILE);
365
366	return(ISC_R_SUCCESS);
367}
368
369isc_boolean_t
370isc_file_isabsolute(const char *filename) {
371	REQUIRE(filename != NULL);
372	return (ISC_TF(filename[0] == '/'));
373}
374
375isc_boolean_t
376isc_file_iscurrentdir(const char *filename) {
377	REQUIRE(filename != NULL);
378	return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
379}
380
381isc_boolean_t
382isc_file_ischdiridempotent(const char *filename) {
383	REQUIRE(filename != NULL);
384	if (isc_file_isabsolute(filename))
385		return (ISC_TRUE);
386	if (isc_file_iscurrentdir(filename))
387		return (ISC_TRUE);
388	return (ISC_FALSE);
389}
390
391const char *
392isc_file_basename(const char *filename) {
393	char *s;
394
395	REQUIRE(filename != NULL);
396
397	s = strrchr(filename, '/');
398	if (s == NULL)
399		return (filename);
400
401	return (s + 1);
402}
403
404isc_result_t
405isc_file_progname(const char *filename, char *buf, size_t buflen) {
406	const char *base;
407	size_t len;
408
409	REQUIRE(filename != NULL);
410	REQUIRE(buf != NULL);
411
412	base = isc_file_basename(filename);
413	len = strlen(base) + 1;
414
415	if (len > buflen)
416		return (ISC_R_NOSPACE);
417	memcpy(buf, base, len);
418
419	return (ISC_R_SUCCESS);
420}
421
422/*
423 * Put the absolute name of the current directory into 'dirname', which is
424 * a buffer of at least 'length' characters.  End the string with the
425 * appropriate path separator, such that the final product could be
426 * concatenated with a relative pathname to make a valid pathname string.
427 */
428static isc_result_t
429dir_current(char *dirname, size_t length) {
430	char *cwd;
431	isc_result_t result = ISC_R_SUCCESS;
432
433	REQUIRE(dirname != NULL);
434	REQUIRE(length > 0U);
435
436	cwd = getcwd(dirname, length);
437
438	if (cwd == NULL) {
439		if (errno == ERANGE)
440			result = ISC_R_NOSPACE;
441		else
442			result = isc__errno2result(errno);
443	} else {
444		if (strlen(dirname) + 1 == length)
445			result = ISC_R_NOSPACE;
446		else if (dirname[1] != '\0')
447			strlcat(dirname, "/", length);
448	}
449
450	return (result);
451}
452
453isc_result_t
454isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
455	isc_result_t result;
456	result = dir_current(path, pathlen);
457	if (result != ISC_R_SUCCESS)
458		return (result);
459	if (strlen(path) + strlen(filename) + 1 > pathlen)
460		return (ISC_R_NOSPACE);
461	strlcat(path, filename, pathlen);
462	return (ISC_R_SUCCESS);
463}
464
465isc_result_t
466isc_file_truncate(const char *filename, isc_offset_t size) {
467	isc_result_t result = ISC_R_SUCCESS;
468
469	if (truncate(filename, size) < 0)
470		result = isc__errno2result(errno);
471	return (result);
472}
473
474isc_result_t
475isc_file_safecreate(const char *filename, FILE **fp) {
476	isc_result_t result;
477	int flags;
478	struct stat sb;
479	FILE *f;
480	int fd;
481
482	REQUIRE(filename != NULL);
483	REQUIRE(fp != NULL && *fp == NULL);
484
485	result = file_stats(filename, &sb);
486	if (result == ISC_R_SUCCESS) {
487		if ((sb.st_mode & S_IFREG) == 0)
488			return (ISC_R_INVALIDFILE);
489		flags = O_WRONLY | O_TRUNC;
490	} else if (result == ISC_R_FILENOTFOUND) {
491		flags = O_WRONLY | O_CREAT | O_EXCL;
492	} else
493		return (result);
494
495	fd = open(filename, flags, S_IRUSR | S_IWUSR);
496	if (fd == -1)
497		return (isc__errno2result(errno));
498
499	f = fdopen(fd, "w");
500	if (f == NULL) {
501		result = isc__errno2result(errno);
502		close(fd);
503		return (result);
504	}
505
506	*fp = f;
507	return (ISC_R_SUCCESS);
508}
509
510isc_result_t
511isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirnam, char **basenam)
512{
513	char *dir, *file, *slash;
514
515	REQUIRE(path != NULL);
516
517	slash = strrchr(path, '/');
518
519	if (slash == path) {
520		file = ++slash;
521		dir = isc_mem_strdup(mctx, "/");
522	} else if (slash != NULL) {
523		file = ++slash;
524		dir = isc_mem_allocate(mctx, slash - path);
525		if (dir != NULL)
526			strlcpy(dir, path, slash - path);
527	} else {
528		file = path;
529		dir = isc_mem_strdup(mctx, ".");
530	}
531
532	if (dir == NULL)
533		return (ISC_R_NOMEMORY);
534
535	if (*file == '\0') {
536		isc_mem_free(mctx, dir);
537		return (ISC_R_INVALIDFILE);
538	}
539
540	*dirnam = dir;
541	*basenam = file;
542
543	return (ISC_R_SUCCESS);
544}
545