file.c revision 135446
1/*
2 * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and 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: file.c,v 1.38.12.8 2004/03/16 05:50:25 marka Exp $ */
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#include <isc/dir.h>
67#include <isc/file.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
96isc_result_t
97isc_file_getmodtime(const char *file, isc_time_t *time) {
98	isc_result_t result;
99	struct stat stats;
100
101	REQUIRE(file != NULL);
102	REQUIRE(time != NULL);
103
104	result = file_stats(file, &stats);
105
106	if (result == ISC_R_SUCCESS)
107		/*
108		 * XXXDCL some operating systems provide nanoseconds, too,
109		 * such as BSD/OS via st_mtimespec.
110		 */
111		isc_time_set(time, stats.st_mtime, 0);
112
113	return (result);
114}
115
116isc_result_t
117isc_file_settime(const char *file, isc_time_t *time) {
118	struct timeval times[2];
119
120	REQUIRE(file != NULL && time != NULL);
121
122	/*
123	 * tv_sec is at least a 32 bit quantity on all platforms we're
124	 * dealing with, but it is signed on most (all?) of them,
125	 * so we need to make sure the high bit isn't set.  This unfortunately
126	 * loses when either:
127	 *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
128	 *	and isc_time_seconds > LONG_MAX, or
129	 *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
130	 *      and isc_time_seconds has at least 33 significant bits.
131	 */
132	times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
133
134	/*
135	 * Here is the real check for the high bit being set.
136	 */
137	if ((times[0].tv_sec &
138	     (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
139		return (ISC_R_RANGE);
140
141	/*
142	 * isc_time_nanoseconds guarantees a value that divided by 1000 will
143	 * fit into the minimum possible size tv_usec field.  Unfortunately,
144	 * we don't know what that type is so can't cast directly ... but
145	 * we can at least cast to signed so the IRIX compiler shuts up.
146	 */
147	times[0].tv_usec = times[1].tv_usec =
148		(isc_int32_t)(isc_time_nanoseconds(time) / 1000);
149
150	if (utimes(file, times) < 0)
151		return (isc__errno2result(errno));
152
153	return (ISC_R_SUCCESS);
154}
155
156#undef TEMPLATE
157#define TEMPLATE "tmp-XXXXXXXXXX" /* 14 characters. */
158
159isc_result_t
160isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
161	return (isc_file_template(path, TEMPLATE, buf, buflen));
162}
163
164isc_result_t
165isc_file_template(const char *path, const char *templet, char *buf,
166			size_t buflen) {
167	char *s;
168
169	REQUIRE(path != NULL);
170	REQUIRE(templet != NULL);
171	REQUIRE(buf != NULL);
172
173	s = strrchr(templet, '/');
174	if (s != NULL)
175		templet = s + 1;
176
177	s = strrchr(path, '/');
178
179	if (s != NULL) {
180		if ((s - path + 1 + strlen(templet) + 1) > buflen)
181			return (ISC_R_NOSPACE);
182
183		strncpy(buf, path, s - path + 1);
184		buf[s - path + 1] = '\0';
185		strcat(buf, templet);
186	} else {
187		if ((strlen(templet) + 1) > buflen)
188			return (ISC_R_NOSPACE);
189
190		strcpy(buf, templet);
191	}
192
193	return (ISC_R_SUCCESS);
194}
195
196static char alphnum[] =
197	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
198
199isc_result_t
200isc_file_renameunique(const char *file, char *templet) {
201	char *x;
202	char *cp;
203	isc_uint32_t which;
204
205	REQUIRE(file != NULL);
206	REQUIRE(templet != NULL);
207
208	cp = templet;
209	while (*cp != '\0')
210		cp++;
211	if (cp == templet)
212		return (ISC_R_FAILURE);
213
214	x = cp--;
215	while (cp >= templet && *cp == 'X') {
216		isc_random_get(&which);
217		*cp = alphnum[which % (sizeof(alphnum) - 1)];
218		x = cp--;
219	}
220	while (link(file, templet) == -1) {
221		if (errno != EEXIST)
222			return (isc__errno2result(errno));
223		for (cp = x;;) {
224			char *t;
225			if (*cp == '\0')
226				return (ISC_R_FAILURE);
227			t = strchr(alphnum, *cp);
228			if (t == NULL || *++t == '\0')
229				*cp++ = alphnum[0];
230			else {
231				*cp = *t;
232				break;
233			}
234		}
235	}
236	(void)unlink(file);
237	return (ISC_R_SUCCESS);
238}
239
240
241isc_result_t
242isc_file_openunique(char *templet, FILE **fp) {
243	int fd;
244	FILE *f;
245	isc_result_t result = ISC_R_SUCCESS;
246	char *x;
247	char *cp;
248	isc_uint32_t which;
249	int mode;
250
251	REQUIRE(templet != NULL);
252	REQUIRE(fp != NULL && *fp == NULL);
253
254	cp = templet;
255	while (*cp != '\0')
256		cp++;
257	if (cp == templet)
258		return (ISC_R_FAILURE);
259
260	x = cp--;
261	while (cp >= templet && *cp == 'X') {
262		isc_random_get(&which);
263		*cp = alphnum[which % (sizeof(alphnum) - 1)];
264		x = cp--;
265	}
266
267	mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
268
269	while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
270		if (errno != EEXIST)
271			return (isc__errno2result(errno));
272		for (cp = x;;) {
273			char *t;
274			if (*cp == '\0')
275				return (ISC_R_FAILURE);
276			t = strchr(alphnum, *cp);
277			if (t == NULL || *++t == '\0')
278				*cp++ = alphnum[0];
279			else {
280				*cp = *t;
281				break;
282			}
283		}
284	}
285	f = fdopen(fd, "w+");
286	if (f == NULL) {
287		result = isc__errno2result(errno);
288		(void)remove(templet);
289		(void)close(fd);
290	} else
291		*fp = f;
292
293	return (result);
294}
295
296isc_result_t
297isc_file_remove(const char *filename) {
298	int r;
299
300	REQUIRE(filename != NULL);
301
302	r = unlink(filename);
303	if (r == 0)
304		return (ISC_R_SUCCESS);
305	else
306		return (isc__errno2result(errno));
307}
308
309isc_result_t
310isc_file_rename(const char *oldname, const char *newname) {
311	int r;
312
313	REQUIRE(oldname != NULL);
314	REQUIRE(newname != NULL);
315
316	r = rename(oldname, newname);
317	if (r == 0)
318		return (ISC_R_SUCCESS);
319	else
320		return (isc__errno2result(errno));
321}
322
323isc_boolean_t
324isc_file_exists(const char *pathname) {
325	struct stat stats;
326
327	REQUIRE(pathname != NULL);
328
329	return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
330}
331
332isc_boolean_t
333isc_file_isabsolute(const char *filename) {
334	REQUIRE(filename != NULL);
335	return (ISC_TF(filename[0] == '/'));
336}
337
338isc_boolean_t
339isc_file_iscurrentdir(const char *filename) {
340	REQUIRE(filename != NULL);
341	return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
342}
343
344isc_boolean_t
345isc_file_ischdiridempotent(const char *filename) {
346	REQUIRE(filename != NULL);
347	if (isc_file_isabsolute(filename))
348		return (ISC_TRUE);
349	if (isc_file_iscurrentdir(filename))
350		return (ISC_TRUE);
351	return (ISC_FALSE);
352}
353
354const char *
355isc_file_basename(const char *filename) {
356	char *s;
357
358	REQUIRE(filename != NULL);
359
360	s = strrchr(filename, '/');
361	if (s == NULL)
362		return (filename);
363
364	return (s + 1);
365}
366
367isc_result_t
368isc_file_progname(const char *filename, char *buf, size_t buflen) {
369	const char *base;
370	size_t len;
371
372	REQUIRE(filename != NULL);
373	REQUIRE(buf != NULL);
374
375	base = isc_file_basename(filename);
376	len = strlen(base) + 1;
377
378	if (len > buflen)
379		return (ISC_R_NOSPACE);
380	memcpy(buf, base, len);
381
382	return (ISC_R_SUCCESS);
383}
384
385/*
386 * Put the absolute name of the current directory into 'dirname', which is
387 * a buffer of at least 'length' characters.  End the string with the
388 * appropriate path separator, such that the final product could be
389 * concatenated with a relative pathname to make a valid pathname string.
390 */
391static isc_result_t
392dir_current(char *dirname, size_t length) {
393	char *cwd;
394	isc_result_t result = ISC_R_SUCCESS;
395
396	REQUIRE(dirname != NULL);
397	REQUIRE(length > 0U);
398
399	cwd = getcwd(dirname, length);
400
401	if (cwd == NULL) {
402		if (errno == ERANGE)
403			result = ISC_R_NOSPACE;
404		else
405			result = isc__errno2result(errno);
406	} else {
407		if (strlen(dirname) + 1 == length)
408			result = ISC_R_NOSPACE;
409		else if (dirname[1] != '\0')
410			strcat(dirname, "/");
411	}
412
413	return (result);
414}
415
416isc_result_t
417isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
418	isc_result_t result;
419	result = dir_current(path, pathlen);
420	if (result != ISC_R_SUCCESS)
421		return (result);
422	if (strlen(path) + strlen(filename) + 1 > pathlen)
423		return (ISC_R_NOSPACE);
424	strcat(path, filename);
425	return (ISC_R_SUCCESS);
426}
427
428isc_result_t
429isc_file_truncate(const char *filename, isc_offset_t size) {
430	isc_result_t result = ISC_R_SUCCESS;
431
432	if (truncate(filename, size) < 0)
433		result = isc__errno2result(errno);
434	return (result);
435}
436