2 * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002 Internet Software Consortium.
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.
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.
19 * Portions Copyright (c) 1987, 1993
20 * The Regents of the University of California. All rights reserved.
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
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.
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
51 /* $Id: file.c,v 1.51.128.2 2009/02/16 23:46:44 tbox Exp $ */
61 #include <time.h> /* Required for utimes on some platforms. */
62 #include <unistd.h> /* Required for mkstemp on NetBSD. */
71 #include <isc/random.h>
72 #include <isc/string.h>
76 #include "errno2result.h"
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().
87 file_stats(const char *file
, struct stat
*stats
) {
88 isc_result_t result
= ISC_R_SUCCESS
;
90 REQUIRE(file
!= NULL
);
91 REQUIRE(stats
!= NULL
);
93 if (stat(file
, stats
) != 0)
94 result
= isc__errno2result(errno
);
100 isc_file_getmodtime(const char *file
, isc_time_t
*time
) {
104 REQUIRE(file
!= NULL
);
105 REQUIRE(time
!= NULL
);
107 result
= file_stats(file
, &stats
);
109 if (result
== ISC_R_SUCCESS
)
111 * XXXDCL some operating systems provide nanoseconds, too,
112 * such as BSD/OS via st_mtimespec.
114 isc_time_set(time
, stats
.st_mtime
, 0);
120 isc_file_settime(const char *file
, isc_time_t
*time
) {
121 struct timeval times
[2];
123 REQUIRE(file
!= NULL
&& time
!= NULL
);
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
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.
135 times
[0].tv_sec
= times
[1].tv_sec
= (long)isc_time_seconds(time
);
138 * Here is the real check for the high bit being set.
140 if ((times
[0].tv_sec
&
141 (1ULL << (sizeof(times
[0].tv_sec
) * CHAR_BIT
- 1))) != 0)
142 return (ISC_R_RANGE
);
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.
150 times
[0].tv_usec
= times
[1].tv_usec
=
151 (isc_int32_t
)(isc_time_nanoseconds(time
) / 1000);
153 if (utimes(file
, times
) < 0)
154 return (isc__errno2result(errno
));
156 return (ISC_R_SUCCESS
);
160 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
163 isc_file_mktemplate(const char *path
, char *buf
, size_t buflen
) {
164 return (isc_file_template(path
, TEMPLATE
, buf
, buflen
));
168 isc_file_template(const char *path
, const char *templet
, char *buf
,
172 REQUIRE(path
!= NULL
);
173 REQUIRE(templet
!= NULL
);
174 REQUIRE(buf
!= NULL
);
176 s
= strrchr(templet
, '/');
180 s
= strrchr(path
, '/');
183 if ((s
- path
+ 1 + strlen(templet
) + 1) > buflen
)
184 return (ISC_R_NOSPACE
);
186 strncpy(buf
, path
, s
- path
+ 1);
187 buf
[s
- path
+ 1] = '\0';
188 strcat(buf
, templet
);
190 if ((strlen(templet
) + 1) > buflen
)
191 return (ISC_R_NOSPACE
);
193 strcpy(buf
, templet
);
196 return (ISC_R_SUCCESS
);
199 static char alphnum
[] =
200 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
203 isc_file_renameunique(const char *file
, char *templet
) {
208 REQUIRE(file
!= NULL
);
209 REQUIRE(templet
!= NULL
);
215 return (ISC_R_FAILURE
);
218 while (cp
>= templet
&& *cp
== 'X') {
219 isc_random_get(&which
);
220 *cp
= alphnum
[which
% (sizeof(alphnum
) - 1)];
223 while (link(file
, templet
) == -1) {
225 return (isc__errno2result(errno
));
229 return (ISC_R_FAILURE
);
230 t
= strchr(alphnum
, *cp
);
231 if (t
== NULL
|| *++t
== '\0')
239 if (unlink(file
) < 0)
241 return (isc__errno2result(errno
));
242 return (ISC_R_SUCCESS
);
247 isc_file_openunique(char *templet
, FILE **fp
) {
250 isc_result_t result
= ISC_R_SUCCESS
;
256 REQUIRE(templet
!= NULL
);
257 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
263 return (ISC_R_FAILURE
);
266 while (cp
>= templet
&& *cp
== 'X') {
267 isc_random_get(&which
);
268 *cp
= alphnum
[which
% (sizeof(alphnum
) - 1)];
272 mode
= S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
;
274 while ((fd
= open(templet
, O_RDWR
|O_CREAT
|O_EXCL
, mode
)) == -1) {
276 return (isc__errno2result(errno
));
280 return (ISC_R_FAILURE
);
281 t
= strchr(alphnum
, *cp
);
282 if (t
== NULL
|| *++t
== '\0')
290 f
= fdopen(fd
, "w+");
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
);
306 isc_file_remove(const char *filename
) {
309 REQUIRE(filename
!= NULL
);
311 r
= unlink(filename
);
313 return (ISC_R_SUCCESS
);
315 return (isc__errno2result(errno
));
319 isc_file_rename(const char *oldname
, const char *newname
) {
322 REQUIRE(oldname
!= NULL
);
323 REQUIRE(newname
!= NULL
);
325 r
= rename(oldname
, newname
);
327 return (ISC_R_SUCCESS
);
329 return (isc__errno2result(errno
));
333 isc_file_exists(const char *pathname
) {
336 REQUIRE(pathname
!= NULL
);
338 return (ISC_TF(file_stats(pathname
, &stats
) == ISC_R_SUCCESS
));
342 isc_file_isabsolute(const char *filename
) {
343 REQUIRE(filename
!= NULL
);
344 return (ISC_TF(filename
[0] == '/'));
348 isc_file_iscurrentdir(const char *filename
) {
349 REQUIRE(filename
!= NULL
);
350 return (ISC_TF(filename
[0] == '.' && filename
[1] == '\0'));
354 isc_file_ischdiridempotent(const char *filename
) {
355 REQUIRE(filename
!= NULL
);
356 if (isc_file_isabsolute(filename
))
358 if (isc_file_iscurrentdir(filename
))
364 isc_file_basename(const char *filename
) {
367 REQUIRE(filename
!= NULL
);
369 s
= strrchr(filename
, '/');
377 isc_file_progname(const char *filename
, char *buf
, size_t buflen
) {
381 REQUIRE(filename
!= NULL
);
382 REQUIRE(buf
!= NULL
);
384 base
= isc_file_basename(filename
);
385 len
= strlen(base
) + 1;
388 return (ISC_R_NOSPACE
);
389 memcpy(buf
, base
, len
);
391 return (ISC_R_SUCCESS
);
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.
401 dir_current(char *dirname
, size_t length
) {
403 isc_result_t result
= ISC_R_SUCCESS
;
405 REQUIRE(dirname
!= NULL
);
406 REQUIRE(length
> 0U);
408 cwd
= getcwd(dirname
, length
);
412 result
= ISC_R_NOSPACE
;
414 result
= isc__errno2result(errno
);
416 if (strlen(dirname
) + 1 == length
)
417 result
= ISC_R_NOSPACE
;
418 else if (dirname
[1] != '\0')
419 strcat(dirname
, "/");
426 isc_file_absolutepath(const char *filename
, char *path
, size_t pathlen
) {
428 result
= dir_current(path
, pathlen
);
429 if (result
!= ISC_R_SUCCESS
)
431 if (strlen(path
) + strlen(filename
) + 1 > pathlen
)
432 return (ISC_R_NOSPACE
);
433 strcat(path
, filename
);
434 return (ISC_R_SUCCESS
);
438 isc_file_truncate(const char *filename
, isc_offset_t size
) {
439 isc_result_t result
= ISC_R_SUCCESS
;
441 if (truncate(filename
, size
) < 0)
442 result
= isc__errno2result(errno
);