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.51.332.2 2009/02/16 23:47:15 tbox Exp */
63 #include <time.h> /* Required for utimes on some platforms. */
64 #include <unistd.h> /* Required for mkstemp on NetBSD. */
73 #include <isc/random.h>
74 #include <isc/string.h>
78 #include "errno2result.h"
81 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
82 * it might be good to provide a mechanism that allows for the results
83 * of a previous stat() to be used again without having to do another stat,
84 * such as perl's mechanism of using "_" in place of a file name to indicate
85 * that the results of the last stat should be used. But then you get into
86 * annoying MP issues. BTW, Win32 has stat().
89 file_stats(const char *file
, struct stat
*stats
) {
90 isc_result_t result
= ISC_R_SUCCESS
;
92 REQUIRE(file
!= NULL
);
93 REQUIRE(stats
!= NULL
);
95 if (stat(file
, stats
) != 0)
96 result
= isc__errno2result(errno
);
102 isc_file_getmodtime(const char *file
, isc_time_t
*time
) {
106 REQUIRE(file
!= NULL
);
107 REQUIRE(time
!= NULL
);
109 result
= file_stats(file
, &stats
);
111 if (result
== ISC_R_SUCCESS
)
113 * XXXDCL some operating systems provide nanoseconds, too,
114 * such as BSD/OS via st_mtimespec.
116 isc_time_set(time
, stats
.st_mtime
, 0);
122 isc_file_settime(const char *file
, isc_time_t
*time
) {
123 struct timeval times
[2];
125 REQUIRE(file
!= NULL
&& time
!= NULL
);
128 * tv_sec is at least a 32 bit quantity on all platforms we're
129 * dealing with, but it is signed on most (all?) of them,
130 * so we need to make sure the high bit isn't set. This unfortunately
132 * * tv_sec becomes a signed 64 bit integer but long is 32 bits
133 * and isc_time_seconds > LONG_MAX, or
134 * * isc_time_seconds is changed to be > 32 bits but long is 32 bits
135 * and isc_time_seconds has at least 33 significant bits.
137 times
[0].tv_sec
= times
[1].tv_sec
= (long)isc_time_seconds(time
);
140 * Here is the real check for the high bit being set.
142 if ((times
[0].tv_sec
&
143 (1ULL << (sizeof(times
[0].tv_sec
) * CHAR_BIT
- 1))) != 0)
144 return (ISC_R_RANGE
);
147 * isc_time_nanoseconds guarantees a value that divided by 1000 will
148 * fit into the minimum possible size tv_usec field. Unfortunately,
149 * we don't know what that type is so can't cast directly ... but
150 * we can at least cast to signed so the IRIX compiler shuts up.
152 times
[0].tv_usec
= times
[1].tv_usec
=
153 (isc_int32_t
)(isc_time_nanoseconds(time
) / 1000);
155 if (utimes(file
, times
) < 0)
156 return (isc__errno2result(errno
));
158 return (ISC_R_SUCCESS
);
162 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
165 isc_file_mktemplate(const char *path
, char *buf
, size_t buflen
) {
166 return (isc_file_template(path
, TEMPLATE
, buf
, buflen
));
170 isc_file_template(const char *path
, const char *templet
, char *buf
,
174 REQUIRE(path
!= NULL
);
175 REQUIRE(templet
!= NULL
);
176 REQUIRE(buf
!= NULL
);
178 s
= strrchr(templet
, '/');
182 s
= strrchr(path
, '/');
185 if ((s
- path
+ 1 + strlen(templet
) + 1) > buflen
)
186 return (ISC_R_NOSPACE
);
188 strncpy(buf
, path
, s
- path
+ 1);
189 buf
[s
- path
+ 1] = '\0';
190 strcat(buf
, templet
);
192 if ((strlen(templet
) + 1) > buflen
)
193 return (ISC_R_NOSPACE
);
195 strcpy(buf
, templet
);
198 return (ISC_R_SUCCESS
);
201 static char alphnum
[] =
202 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
205 isc_file_renameunique(const char *file
, char *templet
) {
210 REQUIRE(file
!= NULL
);
211 REQUIRE(templet
!= NULL
);
217 return (ISC_R_FAILURE
);
220 while (cp
>= templet
&& *cp
== 'X') {
221 isc_random_get(&which
);
222 *cp
= alphnum
[which
% (sizeof(alphnum
) - 1)];
225 while (link(file
, templet
) == -1) {
227 return (isc__errno2result(errno
));
231 return (ISC_R_FAILURE
);
232 t
= strchr(alphnum
, *cp
);
233 if (t
== NULL
|| *++t
== '\0')
241 if (unlink(file
) < 0)
243 return (isc__errno2result(errno
));
244 return (ISC_R_SUCCESS
);
249 isc_file_openunique(char *templet
, FILE **fp
) {
252 isc_result_t result
= ISC_R_SUCCESS
;
258 REQUIRE(templet
!= NULL
);
259 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
265 return (ISC_R_FAILURE
);
268 while (cp
>= templet
&& *cp
== 'X') {
269 isc_random_get(&which
);
270 *cp
= alphnum
[which
% (sizeof(alphnum
) - 1)];
274 mode
= S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
;
276 while ((fd
= open(templet
, O_RDWR
|O_CREAT
|O_EXCL
, mode
)) == -1) {
278 return (isc__errno2result(errno
));
282 return (ISC_R_FAILURE
);
283 t
= strchr(alphnum
, *cp
);
284 if (t
== NULL
|| *++t
== '\0')
292 f
= fdopen(fd
, "w+");
294 result
= isc__errno2result(errno
);
295 if (remove(templet
) < 0) {
296 isc_log_write(isc_lctx
, ISC_LOGCATEGORY_GENERAL
,
297 ISC_LOGMODULE_FILE
, ISC_LOG_ERROR
,
298 "remove '%s': failed", templet
);
308 isc_file_remove(const char *filename
) {
311 REQUIRE(filename
!= NULL
);
313 r
= unlink(filename
);
315 return (ISC_R_SUCCESS
);
317 return (isc__errno2result(errno
));
321 isc_file_rename(const char *oldname
, const char *newname
) {
324 REQUIRE(oldname
!= NULL
);
325 REQUIRE(newname
!= NULL
);
327 r
= rename(oldname
, newname
);
329 return (ISC_R_SUCCESS
);
331 return (isc__errno2result(errno
));
335 isc_file_exists(const char *pathname
) {
338 REQUIRE(pathname
!= NULL
);
340 return (ISC_TF(file_stats(pathname
, &stats
) == ISC_R_SUCCESS
));
344 isc_file_isabsolute(const char *filename
) {
345 REQUIRE(filename
!= NULL
);
346 return (ISC_TF(filename
[0] == '/'));
350 isc_file_iscurrentdir(const char *filename
) {
351 REQUIRE(filename
!= NULL
);
352 return (ISC_TF(filename
[0] == '.' && filename
[1] == '\0'));
356 isc_file_ischdiridempotent(const char *filename
) {
357 REQUIRE(filename
!= NULL
);
358 if (isc_file_isabsolute(filename
))
360 if (isc_file_iscurrentdir(filename
))
366 isc_file_basename(const char *filename
) {
369 REQUIRE(filename
!= NULL
);
371 s
= strrchr(filename
, '/');
379 isc_file_progname(const char *filename
, char *buf
, size_t buflen
) {
383 REQUIRE(filename
!= NULL
);
384 REQUIRE(buf
!= NULL
);
386 base
= isc_file_basename(filename
);
387 len
= strlen(base
) + 1;
390 return (ISC_R_NOSPACE
);
391 memcpy(buf
, base
, len
);
393 return (ISC_R_SUCCESS
);
397 * Put the absolute name of the current directory into 'dirname', which is
398 * a buffer of at least 'length' characters. End the string with the
399 * appropriate path separator, such that the final product could be
400 * concatenated with a relative pathname to make a valid pathname string.
403 dir_current(char *dirname
, size_t length
) {
405 isc_result_t result
= ISC_R_SUCCESS
;
407 REQUIRE(dirname
!= NULL
);
408 REQUIRE(length
> 0U);
410 cwd
= getcwd(dirname
, length
);
414 result
= ISC_R_NOSPACE
;
416 result
= isc__errno2result(errno
);
418 if (strlen(dirname
) + 1 == length
)
419 result
= ISC_R_NOSPACE
;
420 else if (dirname
[1] != '\0')
421 strcat(dirname
, "/");
428 isc_file_absolutepath(const char *filename
, char *path
, size_t pathlen
) {
430 result
= dir_current(path
, pathlen
);
431 if (result
!= ISC_R_SUCCESS
)
433 if (strlen(path
) + strlen(filename
) + 1 > pathlen
)
434 return (ISC_R_NOSPACE
);
435 strcat(path
, filename
);
436 return (ISC_R_SUCCESS
);
440 isc_file_truncate(const char *filename
, isc_offset_t size
) {
441 isc_result_t result
= ISC_R_SUCCESS
;
443 if (truncate(filename
, size
) < 0)
444 result
= isc__errno2result(errno
);