4 * Copyright (C) 2004, 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.
20 /* Id: file.c,v 1.35 2009/09/02 17:58:06 each Exp */
33 #include <sys/utime.h>
37 #include <isc/result.h>
41 #include <isc/string.h>
43 #include "errno2result.h"
46 * Emulate UNIX mkstemp, which returns an open FD to the new file
50 gettemp(char *path
, int *doopen
) {
55 trv
= strrchr(path
, 'X');
58 /* extra X's get set to 0's */
59 while (*--trv
== 'X') {
60 *trv
= (pid
% 10) + '0';
64 * check the target directory; if you have six X's and it
65 * doesn't exist this runs for a *very* long time.
67 for (start
= trv
+ 1;; --trv
) {
72 if (stat(path
, &sbuf
))
74 if (!S_ISDIR(sbuf
.st_mode
)) {
86 open(path
, O_CREAT
|O_EXCL
|O_RDWR
,
87 _S_IREAD
| _S_IWRITE
)) >= 0)
91 } else if (stat(path
, &sbuf
))
92 return (errno
== ENOENT
? 1 : 0);
94 /* tricky little algorithm for backward compatibility */
113 mkstemp(char *path
) {
116 return (gettemp(path
, &fd
) ? fd
: -1);
120 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
121 * it might be good to provide a mechanism that allows for the results
122 * of a previous stat() to be used again without having to do another stat,
123 * such as perl's mechanism of using "_" in place of a file name to indicate
124 * that the results of the last stat should be used. But then you get into
125 * annoying MP issues. BTW, Win32 has stat().
128 file_stats(const char *file
, struct stat
*stats
) {
129 isc_result_t result
= ISC_R_SUCCESS
;
131 REQUIRE(file
!= NULL
);
132 REQUIRE(stats
!= NULL
);
134 if (stat(file
, stats
) != 0)
135 result
= isc__errno2result(errno
);
141 * isc_file_safemovefile is needed to be defined here to ensure that
142 * any file with the new name is renamed to a backup name and then the
143 * rename is done. If all goes well then the backup can be deleted,
144 * otherwise it gets renamed back.
148 isc_file_safemovefile(const char *oldname
, const char *newname
) {
156 * Make sure we have something to do
158 if (stat(oldname
, &sbuf
) != 0) {
164 * Rename to a backup the new file if it still exists
166 if (stat(newname
, &sbuf
) == 0) {
168 strcpy(buf
, newname
);
169 strcat(buf
, ".XXXXX");
170 tmpfd
= mkstemp(buf
);
174 _chmod(newname
, _S_IREAD
| _S_IWRITE
);
176 filestatus
= MoveFile(newname
, buf
);
178 /* Now rename the file to the new name
180 _chmod(oldname
, _S_IREAD
| _S_IWRITE
);
182 filestatus
= MoveFile(oldname
, newname
);
183 if (filestatus
== 0) {
185 * Try to rename the backup back to the original name
186 * if the backup got created
188 if (exists
== TRUE
) {
189 filestatus
= MoveFile(buf
, newname
);
197 * Delete the backup file if it got created
200 filestatus
= DeleteFile(buf
);
205 isc_file_getmodtime(const char *file
, isc_time_t
*time
) {
208 REQUIRE(file
!= NULL
);
209 REQUIRE(time
!= NULL
);
211 if ((fh
= open(file
, _O_RDONLY
| _O_BINARY
)) < 0)
212 return (isc__errno2result(errno
));
214 if (!GetFileTime((HANDLE
) _get_osfhandle(fh
),
221 return (isc__errno2result(errno
));
224 return (ISC_R_SUCCESS
);
228 isc_file_settime(const char *file
, isc_time_t
*time
) {
231 REQUIRE(file
!= NULL
&& time
!= NULL
);
233 if ((fh
= open(file
, _O_RDWR
| _O_BINARY
)) < 0)
234 return (isc__errno2result(errno
));
237 * Set the date via the filedate system call and return. Failing
238 * this call implies the new file times are not supported by the
239 * underlying file system.
241 if (!SetFileTime((HANDLE
) _get_osfhandle(fh
),
248 return (isc__errno2result(errno
));
252 return (ISC_R_SUCCESS
);
257 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
260 isc_file_mktemplate(const char *path
, char *buf
, size_t buflen
) {
261 return (isc_file_template(path
, TEMPLATE
, buf
, buflen
));
265 isc_file_template(const char *path
, const char *templet
, char *buf
,
269 REQUIRE(path
!= NULL
);
270 REQUIRE(templet
!= NULL
);
271 REQUIRE(buf
!= NULL
);
273 s
= strrchr(templet
, '\\');
277 s
= strrchr(path
, '\\');
280 if ((s
- path
+ 1 + strlen(templet
) + 1) > buflen
)
281 return (ISC_R_NOSPACE
);
283 strncpy(buf
, path
, s
- path
+ 1);
284 buf
[s
- path
+ 1] = '\0';
285 strcat(buf
, templet
);
287 if ((strlen(templet
) + 1) > buflen
)
288 return (ISC_R_NOSPACE
);
290 strcpy(buf
, templet
);
293 return (ISC_R_SUCCESS
);
297 isc_file_renameunique(const char *file
, char *templet
) {
300 isc_result_t result
= ISC_R_SUCCESS
;
302 REQUIRE(file
!= NULL
);
303 REQUIRE(templet
!= NULL
);
305 fd
= mkstemp(templet
);
307 result
= isc__errno2result(errno
);
311 if (result
== ISC_R_SUCCESS
) {
312 res
= isc_file_safemovefile(file
, templet
);
314 result
= isc__errno2result(errno
);
315 (void)unlink(templet
);
322 isc_file_openunique(char *templet
, FILE **fp
) {
325 isc_result_t result
= ISC_R_SUCCESS
;
327 REQUIRE(templet
!= NULL
);
328 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
331 * Win32 does not have mkstemp. Using emulation above.
333 fd
= mkstemp(templet
);
336 result
= isc__errno2result(errno
);
337 if (result
== ISC_R_SUCCESS
) {
338 f
= fdopen(fd
, "w+");
340 result
= isc__errno2result(errno
);
341 (void)remove(templet
);
351 isc_file_remove(const char *filename
) {
354 REQUIRE(filename
!= NULL
);
356 r
= unlink(filename
);
358 return (ISC_R_SUCCESS
);
360 return (isc__errno2result(errno
));
364 isc_file_rename(const char *oldname
, const char *newname
) {
367 REQUIRE(oldname
!= NULL
);
368 REQUIRE(newname
!= NULL
);
370 r
= isc_file_safemovefile(oldname
, newname
);
372 return (ISC_R_SUCCESS
);
374 return (isc__errno2result(errno
));
378 isc_file_exists(const char *pathname
) {
381 REQUIRE(pathname
!= NULL
);
383 return (ISC_TF(file_stats(pathname
, &stats
) == ISC_R_SUCCESS
));
387 isc_file_isabsolute(const char *filename
) {
388 REQUIRE(filename
!= NULL
);
390 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
391 * the UNC style file specs
393 if ((filename
[0] == '\\') && (filename
[1] == '\\'))
395 if (isalpha(filename
[0]) && filename
[1] == ':' && filename
[2] == '\\')
397 if (isalpha(filename
[0]) && filename
[1] == ':' && filename
[2] == '/')
403 isc_file_iscurrentdir(const char *filename
) {
404 REQUIRE(filename
!= NULL
);
405 return (ISC_TF(filename
[0] == '.' && filename
[1] == '\0'));
409 isc_file_ischdiridempotent(const char *filename
) {
410 REQUIRE(filename
!= NULL
);
412 if (isc_file_isabsolute(filename
))
414 if (filename
[0] == '\\')
416 if (filename
[0] == '/')
418 if (isc_file_iscurrentdir(filename
))
424 isc_file_basename(const char *filename
) {
427 REQUIRE(filename
!= NULL
);
429 s
= strrchr(filename
, '\\');
436 isc_file_progname(const char *filename
, char *progname
, size_t namelen
) {
441 REQUIRE(filename
!= NULL
);
442 REQUIRE(progname
!= NULL
);
445 * Strip the path from the name
447 s
= isc_file_basename(filename
);
449 return (ISC_R_NOSPACE
);
453 * Strip any and all suffixes
457 if (namelen
<= strlen(s
))
458 return (ISC_R_NOSPACE
);
461 return (ISC_R_SUCCESS
);
465 * Copy the result to the buffer
469 return (ISC_R_NOSPACE
);
471 strncpy(progname
, s
, len
);
472 progname
[len
] = '\0';
473 return (ISC_R_SUCCESS
);
477 isc_file_absolutepath(const char *filename
, char *path
, size_t pathlen
) {
481 REQUIRE(filename
!= NULL
);
482 REQUIRE(path
!= NULL
);
484 retval
= GetFullPathName(filename
, pathlen
, path
, &ptrname
);
486 /* Something went wrong in getting the path */
488 return (ISC_R_NOTFOUND
);
489 /* Caller needs to provide a larger buffer to contain the string */
490 if (retval
>= pathlen
)
491 return (ISC_R_NOSPACE
);
492 return (ISC_R_SUCCESS
);
496 isc_file_truncate(const char *filename
, isc_offset_t size
) {
499 REQUIRE(filename
!= NULL
&& size
>= 0);
501 if ((fh
= open(filename
, _O_RDWR
| _O_BINARY
)) < 0)
502 return (isc__errno2result(errno
));
504 if(_chsize(fh
, size
) != 0) {
506 return (isc__errno2result(errno
));
510 return (ISC_R_SUCCESS
);
514 isc_file_safecreate(const char *filename
, FILE **fp
) {
521 REQUIRE(filename
!= NULL
);
522 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
524 result
= file_stats(filename
, &sb
);
525 if (result
== ISC_R_SUCCESS
) {
526 if ((sb
.st_mode
& S_IFREG
) == 0)
527 return (ISC_R_INVALIDFILE
);
528 flags
= O_WRONLY
| O_TRUNC
;
529 } else if (result
== ISC_R_FILENOTFOUND
) {
530 flags
= O_WRONLY
| O_CREAT
| O_EXCL
;
534 fd
= open(filename
, flags
, S_IRUSR
| S_IWUSR
);
536 return (isc__errno2result(errno
));
540 result
= isc__errno2result(errno
);
546 return (ISC_R_SUCCESS
);
550 isc_file_splitpath(isc_mem_t
*mctx
, char *path
, char **dirname
, char **basename
)
552 char *dir
, *file
, *slash
;
555 slash
= strrchr(path
, '/');
557 backslash
= strrchr(path
, '\\');
558 if ((slash
!= NULL
&& backslash
!= NULL
&& backslash
> slash
) ||
559 (slash
== NULL
&& backslash
!= NULL
))
564 dir
= isc_mem_strdup(mctx
, "/");
565 } else if (slash
!= NULL
) {
567 dir
= isc_mem_allocate(mctx
, slash
- path
);
569 strlcpy(dir
, path
, slash
- path
);
572 dir
= isc_mem_strdup(mctx
, ".");
576 return (ISC_R_NOMEMORY
);
579 isc_mem_free(mctx
, dir
);
580 return (ISC_R_INVALIDFILE
);
586 return (ISC_R_SUCCESS
);