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