4 * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2002 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
21 * Portions Copyright (c) 1987, 1993
22 * The Regents of the University of California. All rights reserved.
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 * 3. All advertising materials mentioning features or use of this software
33 * must display the following acknowledgement:
34 * This product includes software developed by the University of
35 * California, Berkeley and its contributors.
36 * 4. Neither the name of the University nor the names of its contributors
37 * may be used to endorse or promote products derived from this software
38 * without specific prior written permission.
40 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 /* Id: file.c,v 1.55 2009/08/28 03:13:08 each Exp */
63 #include <time.h> /* Required for utimes on some platforms. */
64 #include <unistd.h> /* Required for mkstemp on NetBSD. */
74 #include <isc/random.h>
75 #include <isc/string.h>
79 #include "errno2result.h"
82 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
83 * it might be good to provide a mechanism that allows for the results
84 * of a previous stat() to be used again without having to do another stat,
85 * such as perl's mechanism of using "_" in place of a file name to indicate
86 * that the results of the last stat should be used. But then you get into
87 * annoying MP issues. BTW, Win32 has stat().
90 file_stats(const char *file
, struct stat
*stats
) {
91 isc_result_t result
= ISC_R_SUCCESS
;
93 REQUIRE(file
!= NULL
);
94 REQUIRE(stats
!= NULL
);
96 if (stat(file
, stats
) != 0)
97 result
= isc__errno2result(errno
);
103 isc_file_getmodtime(const char *file
, isc_time_t
*time
) {
107 REQUIRE(file
!= NULL
);
108 REQUIRE(time
!= NULL
);
110 result
= file_stats(file
, &stats
);
112 if (result
== ISC_R_SUCCESS
)
114 * XXXDCL some operating systems provide nanoseconds, too,
115 * such as BSD/OS via st_mtimespec.
117 isc_time_set(time
, stats
.st_mtime
, 0);
123 isc_file_settime(const char *file
, isc_time_t
*time
) {
124 struct timeval times
[2];
126 REQUIRE(file
!= NULL
&& time
!= NULL
);
129 * tv_sec is at least a 32 bit quantity on all platforms we're
130 * dealing with, but it is signed on most (all?) of them,
131 * so we need to make sure the high bit isn't set. This unfortunately
133 * * tv_sec becomes a signed 64 bit integer but long is 32 bits
134 * and isc_time_seconds > LONG_MAX, or
135 * * isc_time_seconds is changed to be > 32 bits but long is 32 bits
136 * and isc_time_seconds has at least 33 significant bits.
138 times
[0].tv_sec
= times
[1].tv_sec
= (long)isc_time_seconds(time
);
141 * Here is the real check for the high bit being set.
143 if ((times
[0].tv_sec
&
144 (1ULL << (sizeof(times
[0].tv_sec
) * CHAR_BIT
- 1))) != 0)
145 return (ISC_R_RANGE
);
148 * isc_time_nanoseconds guarantees a value that divided by 1000 will
149 * fit into the minimum possible size tv_usec field. Unfortunately,
150 * we don't know what that type is so can't cast directly ... but
151 * we can at least cast to signed so the IRIX compiler shuts up.
153 times
[0].tv_usec
= times
[1].tv_usec
=
154 (isc_int32_t
)(isc_time_nanoseconds(time
) / 1000);
156 if (utimes(file
, times
) < 0)
157 return (isc__errno2result(errno
));
159 return (ISC_R_SUCCESS
);
163 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
166 isc_file_mktemplate(const char *path
, char *buf
, size_t buflen
) {
167 return (isc_file_template(path
, TEMPLATE
, buf
, buflen
));
171 isc_file_template(const char *path
, const char *templet
, char *buf
,
175 REQUIRE(path
!= NULL
);
176 REQUIRE(templet
!= NULL
);
177 REQUIRE(buf
!= NULL
);
179 s
= strrchr(templet
, '/');
183 s
= strrchr(path
, '/');
186 if ((s
- path
+ 1 + strlen(templet
) + 1) > buflen
)
187 return (ISC_R_NOSPACE
);
189 strncpy(buf
, path
, s
- path
+ 1);
190 buf
[s
- path
+ 1] = '\0';
191 strcat(buf
, templet
);
193 if ((strlen(templet
) + 1) > buflen
)
194 return (ISC_R_NOSPACE
);
196 strcpy(buf
, templet
);
199 return (ISC_R_SUCCESS
);
202 static char alphnum
[] =
203 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
206 isc_file_renameunique(const char *file
, char *templet
) {
211 REQUIRE(file
!= NULL
);
212 REQUIRE(templet
!= NULL
);
218 return (ISC_R_FAILURE
);
221 while (cp
>= templet
&& *cp
== 'X') {
222 isc_random_get(&which
);
223 *cp
= alphnum
[which
% (sizeof(alphnum
) - 1)];
226 while (link(file
, templet
) == -1) {
228 return (isc__errno2result(errno
));
232 return (ISC_R_FAILURE
);
233 t
= strchr(alphnum
, *cp
);
234 if (t
== NULL
|| *++t
== '\0')
242 if (unlink(file
) < 0)
244 return (isc__errno2result(errno
));
245 return (ISC_R_SUCCESS
);
250 isc_file_openunique(char *templet
, FILE **fp
) {
253 isc_result_t result
= ISC_R_SUCCESS
;
259 REQUIRE(templet
!= NULL
);
260 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
266 return (ISC_R_FAILURE
);
269 while (cp
>= templet
&& *cp
== 'X') {
270 isc_random_get(&which
);
271 *cp
= alphnum
[which
% (sizeof(alphnum
) - 1)];
275 mode
= S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
;
277 while ((fd
= open(templet
, O_RDWR
|O_CREAT
|O_EXCL
, mode
)) == -1) {
279 return (isc__errno2result(errno
));
283 return (ISC_R_FAILURE
);
284 t
= strchr(alphnum
, *cp
);
285 if (t
== NULL
|| *++t
== '\0')
293 f
= fdopen(fd
, "w+");
295 result
= isc__errno2result(errno
);
296 if (remove(templet
) < 0) {
297 isc_log_write(isc_lctx
, ISC_LOGCATEGORY_GENERAL
,
298 ISC_LOGMODULE_FILE
, ISC_LOG_ERROR
,
299 "remove '%s': failed", templet
);
309 isc_file_remove(const char *filename
) {
312 REQUIRE(filename
!= NULL
);
314 r
= unlink(filename
);
316 return (ISC_R_SUCCESS
);
318 return (isc__errno2result(errno
));
322 isc_file_rename(const char *oldname
, const char *newname
) {
325 REQUIRE(oldname
!= NULL
);
326 REQUIRE(newname
!= NULL
);
328 r
= rename(oldname
, newname
);
330 return (ISC_R_SUCCESS
);
332 return (isc__errno2result(errno
));
336 isc_file_exists(const char *pathname
) {
339 REQUIRE(pathname
!= NULL
);
341 return (ISC_TF(file_stats(pathname
, &stats
) == ISC_R_SUCCESS
));
345 isc_file_isabsolute(const char *filename
) {
346 REQUIRE(filename
!= NULL
);
347 return (ISC_TF(filename
[0] == '/'));
351 isc_file_iscurrentdir(const char *filename
) {
352 REQUIRE(filename
!= NULL
);
353 return (ISC_TF(filename
[0] == '.' && filename
[1] == '\0'));
357 isc_file_ischdiridempotent(const char *filename
) {
358 REQUIRE(filename
!= NULL
);
359 if (isc_file_isabsolute(filename
))
361 if (isc_file_iscurrentdir(filename
))
367 isc_file_basename(const char *filename
) {
370 REQUIRE(filename
!= NULL
);
372 s
= strrchr(filename
, '/');
380 isc_file_progname(const char *filename
, char *buf
, size_t buflen
) {
384 REQUIRE(filename
!= NULL
);
385 REQUIRE(buf
!= NULL
);
387 base
= isc_file_basename(filename
);
388 len
= strlen(base
) + 1;
391 return (ISC_R_NOSPACE
);
392 memcpy(buf
, base
, len
);
394 return (ISC_R_SUCCESS
);
398 * Put the absolute name of the current directory into 'dirname', which is
399 * a buffer of at least 'length' characters. End the string with the
400 * appropriate path separator, such that the final product could be
401 * concatenated with a relative pathname to make a valid pathname string.
404 dir_current(char *dirname
, size_t length
) {
406 isc_result_t result
= ISC_R_SUCCESS
;
408 REQUIRE(dirname
!= NULL
);
409 REQUIRE(length
> 0U);
411 cwd
= getcwd(dirname
, length
);
415 result
= ISC_R_NOSPACE
;
417 result
= isc__errno2result(errno
);
419 if (strlen(dirname
) + 1 == length
)
420 result
= ISC_R_NOSPACE
;
421 else if (dirname
[1] != '\0')
422 strcat(dirname
, "/");
429 isc_file_absolutepath(const char *filename
, char *path
, size_t pathlen
) {
431 result
= dir_current(path
, pathlen
);
432 if (result
!= ISC_R_SUCCESS
)
434 if (strlen(path
) + strlen(filename
) + 1 > pathlen
)
435 return (ISC_R_NOSPACE
);
436 strcat(path
, filename
);
437 return (ISC_R_SUCCESS
);
441 isc_file_truncate(const char *filename
, isc_offset_t size
) {
442 isc_result_t result
= ISC_R_SUCCESS
;
444 if (truncate(filename
, size
) < 0)
445 result
= isc__errno2result(errno
);
450 isc_file_safecreate(const char *filename
, FILE **fp
) {
457 REQUIRE(filename
!= NULL
);
458 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
460 result
= file_stats(filename
, &sb
);
461 if (result
== ISC_R_SUCCESS
) {
462 if ((sb
.st_mode
& S_IFREG
) == 0)
463 return (ISC_R_INVALIDFILE
);
464 flags
= O_WRONLY
| O_TRUNC
;
465 } else if (result
== ISC_R_FILENOTFOUND
) {
466 flags
= O_WRONLY
| O_CREAT
| O_EXCL
;
470 fd
= open(filename
, flags
, S_IRUSR
| S_IWUSR
);
472 return (isc__errno2result(errno
));
476 result
= isc__errno2result(errno
);
482 return (ISC_R_SUCCESS
);
486 isc_file_splitpath(isc_mem_t
*mctx
, char *path
, char **dirname
, char **basename
)
488 char *dir
, *file
, *slash
;
490 slash
= strrchr(path
, '/');
494 dir
= isc_mem_strdup(mctx
, "/");
495 } else if (slash
!= NULL
) {
497 dir
= isc_mem_allocate(mctx
, slash
- path
);
499 strlcpy(dir
, path
, slash
- path
);
502 dir
= isc_mem_strdup(mctx
, ".");
506 return (ISC_R_NOMEMORY
);
509 isc_mem_free(mctx
, dir
);
510 return (ISC_R_INVALIDFILE
);
516 return (ISC_R_SUCCESS
);