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