4 * Copyright (C) 2004, 2007 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.31 2007/06/19 23:47:19 tbox Exp */
33 #include <sys/utime.h>
36 #include <isc/result.h>
41 #include "errno2result.h"
44 * Emulate UNIX mkstemp, which returns an open FD to the new file
48 gettemp(char *path
, int *doopen
) {
53 trv
= strrchr(path
, 'X');
56 /* extra X's get set to 0's */
57 while (*--trv
== 'X') {
58 *trv
= (pid
% 10) + '0';
62 * check the target directory; if you have six X's and it
63 * doesn't exist this runs for a *very* long time.
65 for (start
= trv
+ 1;; --trv
) {
70 if (stat(path
, &sbuf
))
72 if (!S_ISDIR(sbuf
.st_mode
)) {
84 open(path
, O_CREAT
|O_EXCL
|O_RDWR
,
85 _S_IREAD
| _S_IWRITE
)) >= 0)
89 } else if (stat(path
, &sbuf
))
90 return (errno
== ENOENT
? 1 : 0);
92 /* tricky little algorithm for backward compatibility */
111 mkstemp(char *path
) {
114 return (gettemp(path
, &fd
) ? fd
: -1);
118 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
119 * it might be good to provide a mechanism that allows for the results
120 * of a previous stat() to be used again without having to do another stat,
121 * such as perl's mechanism of using "_" in place of a file name to indicate
122 * that the results of the last stat should be used. But then you get into
123 * annoying MP issues. BTW, Win32 has stat().
126 file_stats(const char *file
, struct stat
*stats
) {
127 isc_result_t result
= ISC_R_SUCCESS
;
129 REQUIRE(file
!= NULL
);
130 REQUIRE(stats
!= NULL
);
132 if (stat(file
, stats
) != 0)
133 result
= isc__errno2result(errno
);
139 * isc_file_safemovefile is needed to be defined here to ensure that
140 * any file with the new name is renamed to a backup name and then the
141 * rename is done. If all goes well then the backup can be deleted,
142 * otherwise it gets renamed back.
146 isc_file_safemovefile(const char *oldname
, const char *newname
) {
154 * Make sure we have something to do
156 if (stat(oldname
, &sbuf
) != 0) {
162 * Rename to a backup the new file if it still exists
164 if (stat(newname
, &sbuf
) == 0) {
166 strcpy(buf
, newname
);
167 strcat(buf
, ".XXXXX");
168 tmpfd
= mkstemp(buf
);
172 _chmod(newname
, _S_IREAD
| _S_IWRITE
);
174 filestatus
= MoveFile(newname
, buf
);
176 /* Now rename the file to the new name
178 _chmod(oldname
, _S_IREAD
| _S_IWRITE
);
180 filestatus
= MoveFile(oldname
, newname
);
181 if (filestatus
== 0) {
183 * Try to rename the backup back to the original name
184 * if the backup got created
186 if (exists
== TRUE
) {
187 filestatus
= MoveFile(buf
, newname
);
195 * Delete the backup file if it got created
198 filestatus
= DeleteFile(buf
);
203 isc_file_getmodtime(const char *file
, isc_time_t
*time
) {
206 REQUIRE(file
!= NULL
);
207 REQUIRE(time
!= NULL
);
209 if ((fh
= open(file
, _O_RDONLY
| _O_BINARY
)) < 0)
210 return (isc__errno2result(errno
));
212 if (!GetFileTime((HANDLE
) _get_osfhandle(fh
),
219 return (isc__errno2result(errno
));
222 return (ISC_R_SUCCESS
);
226 isc_file_settime(const char *file
, isc_time_t
*time
) {
229 REQUIRE(file
!= NULL
&& time
!= NULL
);
231 if ((fh
= open(file
, _O_RDWR
| _O_BINARY
)) < 0)
232 return (isc__errno2result(errno
));
235 * Set the date via the filedate system call and return. Failing
236 * this call implies the new file times are not supported by the
237 * underlying file system.
239 if (!SetFileTime((HANDLE
) _get_osfhandle(fh
),
246 return (isc__errno2result(errno
));
250 return (ISC_R_SUCCESS
);
255 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
258 isc_file_mktemplate(const char *path
, char *buf
, size_t buflen
) {
259 return (isc_file_template(path
, TEMPLATE
, buf
, buflen
));
263 isc_file_template(const char *path
, const char *templet
, char *buf
,
267 REQUIRE(path
!= NULL
);
268 REQUIRE(templet
!= NULL
);
269 REQUIRE(buf
!= NULL
);
271 s
= strrchr(templet
, '\\');
275 s
= strrchr(path
, '\\');
278 if ((s
- path
+ 1 + strlen(templet
) + 1) > buflen
)
279 return (ISC_R_NOSPACE
);
281 strncpy(buf
, path
, s
- path
+ 1);
282 buf
[s
- path
+ 1] = '\0';
283 strcat(buf
, templet
);
285 if ((strlen(templet
) + 1) > buflen
)
286 return (ISC_R_NOSPACE
);
288 strcpy(buf
, templet
);
291 return (ISC_R_SUCCESS
);
295 isc_file_renameunique(const char *file
, char *templet
) {
298 isc_result_t result
= ISC_R_SUCCESS
;
300 REQUIRE(file
!= NULL
);
301 REQUIRE(templet
!= NULL
);
303 fd
= mkstemp(templet
);
305 result
= isc__errno2result(errno
);
309 if (result
== ISC_R_SUCCESS
) {
310 res
= isc_file_safemovefile(file
, templet
);
312 result
= isc__errno2result(errno
);
313 (void)unlink(templet
);
320 isc_file_openunique(char *templet
, FILE **fp
) {
323 isc_result_t result
= ISC_R_SUCCESS
;
325 REQUIRE(templet
!= NULL
);
326 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
329 * Win32 does not have mkstemp. Using emulation above.
331 fd
= mkstemp(templet
);
334 result
= isc__errno2result(errno
);
335 if (result
== ISC_R_SUCCESS
) {
336 f
= fdopen(fd
, "w+");
338 result
= isc__errno2result(errno
);
339 (void)remove(templet
);
349 isc_file_remove(const char *filename
) {
352 REQUIRE(filename
!= NULL
);
354 r
= unlink(filename
);
356 return (ISC_R_SUCCESS
);
358 return (isc__errno2result(errno
));
362 isc_file_rename(const char *oldname
, const char *newname
) {
365 REQUIRE(oldname
!= NULL
);
366 REQUIRE(newname
!= NULL
);
368 r
= isc_file_safemovefile(oldname
, newname
);
370 return (ISC_R_SUCCESS
);
372 return (isc__errno2result(errno
));
376 isc_file_exists(const char *pathname
) {
379 REQUIRE(pathname
!= NULL
);
381 return (ISC_TF(file_stats(pathname
, &stats
) == ISC_R_SUCCESS
));
385 isc_file_isabsolute(const char *filename
) {
386 REQUIRE(filename
!= NULL
);
388 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
389 * the UNC style file specs
391 if ((filename
[0] == '\\') && (filename
[1] == '\\'))
393 if (isalpha(filename
[0]) && filename
[1] == ':' && filename
[2] == '\\')
395 if (isalpha(filename
[0]) && filename
[1] == ':' && filename
[2] == '/')
401 isc_file_iscurrentdir(const char *filename
) {
402 REQUIRE(filename
!= NULL
);
403 return (ISC_TF(filename
[0] == '.' && filename
[1] == '\0'));
407 isc_file_ischdiridempotent(const char *filename
) {
408 REQUIRE(filename
!= NULL
);
410 if (isc_file_isabsolute(filename
))
412 if (filename
[0] == '\\')
414 if (filename
[0] == '/')
416 if (isc_file_iscurrentdir(filename
))
422 isc_file_basename(const char *filename
) {
425 REQUIRE(filename
!= NULL
);
427 s
= strrchr(filename
, '\\');
434 isc_file_progname(const char *filename
, char *progname
, size_t namelen
) {
439 REQUIRE(filename
!= NULL
);
440 REQUIRE(progname
!= NULL
);
443 * Strip the path from the name
445 s
= isc_file_basename(filename
);
447 return (ISC_R_NOSPACE
);
451 * Strip any and all suffixes
455 if (namelen
<= strlen(s
))
456 return (ISC_R_NOSPACE
);
459 return (ISC_R_SUCCESS
);
463 * Copy the result to the buffer
467 return (ISC_R_NOSPACE
);
469 strncpy(progname
, s
, len
);
470 progname
[len
] = '\0';
471 return (ISC_R_SUCCESS
);
475 isc_file_absolutepath(const char *filename
, char *path
, size_t pathlen
) {
479 REQUIRE(filename
!= NULL
);
480 REQUIRE(path
!= NULL
);
482 retval
= GetFullPathName(filename
, pathlen
, path
, &ptrname
);
484 /* Something went wrong in getting the path */
486 return (ISC_R_NOTFOUND
);
487 /* Caller needs to provide a larger buffer to contain the string */
488 if (retval
>= pathlen
)
489 return (ISC_R_NOSPACE
);
490 return (ISC_R_SUCCESS
);
494 isc_file_truncate(const char *filename
, isc_offset_t size
) {
497 REQUIRE(filename
!= NULL
&& size
>= 0);
499 if ((fh
= open(filename
, _O_RDWR
| _O_BINARY
)) < 0)
500 return (isc__errno2result(errno
));
502 if(_chsize(fh
, size
) != 0) {
504 return (isc__errno2result(errno
));
508 return (ISC_R_SUCCESS
);