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