1 /* $NetBSD: file.c,v 1.9 2014/12/10 04:38:01 christos Exp $ */
4 * Copyright (C) 2004, 2007, 2009, 2011-2013 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.
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
, isc_boolean_t binary
, int *doopen
) {
54 int flags
= O_CREAT
|O_EXCL
|O_RDWR
;
59 trv
= strrchr(path
, 'X');
62 /* extra X's get set to 0's */
63 while (*--trv
== 'X') {
64 *trv
= (pid
% 10) + '0';
68 * check the target directory; if you have six X's and it
69 * doesn't exist this runs for a *very* long time.
71 for (start
= trv
+ 1;; --trv
) {
76 if (stat(path
, &sbuf
))
78 if (!S_ISDIR(sbuf
.st_mode
)) {
90 open(path
, flags
, _S_IREAD
| _S_IWRITE
)) >= 0)
94 } else if (stat(path
, &sbuf
))
95 return (errno
== ENOENT
? 1 : 0);
97 /* tricky little algorithm for backward compatibility */
116 mkstemp(char *path
, isc_boolean_t binary
) {
119 return (gettemp(path
, binary
, &fd
) ? fd
: -1);
123 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
124 * it might be good to provide a mechanism that allows for the results
125 * of a previous stat() to be used again without having to do another stat,
126 * such as perl's mechanism of using "_" in place of a file name to indicate
127 * that the results of the last stat should be used. But then you get into
128 * annoying MP issues. BTW, Win32 has stat().
131 file_stats(const char *file
, struct stat
*stats
) {
132 isc_result_t result
= ISC_R_SUCCESS
;
134 REQUIRE(file
!= NULL
);
135 REQUIRE(stats
!= NULL
);
137 if (stat(file
, stats
) != 0)
138 result
= isc__errno2result(errno
);
144 fd_stats(int fd
, struct stat
*stats
) {
145 isc_result_t result
= ISC_R_SUCCESS
;
147 REQUIRE(stats
!= NULL
);
149 if (fstat(fd
, stats
) != 0)
150 result
= isc__errno2result(errno
);
156 isc_file_getsizefd(int fd
, off_t
*size
) {
160 REQUIRE(size
!= NULL
);
162 result
= fd_stats(fd
, &stats
);
164 if (result
== ISC_R_SUCCESS
)
165 *size
= stats
.st_size
;
170 isc_file_mode(const char *file
, mode_t
*modep
) {
174 REQUIRE(modep
!= NULL
);
176 result
= file_stats(file
, &stats
);
177 if (result
== ISC_R_SUCCESS
)
178 *modep
= (stats
.st_mode
& 07777);
183 * isc_file_safemovefile is needed to be defined here to ensure that
184 * any file with the new name is renamed to a backup name and then the
185 * rename is done. If all goes well then the backup can be deleted,
186 * otherwise it gets renamed back.
190 isc_file_safemovefile(const char *oldname
, const char *newname
) {
198 * Make sure we have something to do
200 if (stat(oldname
, &sbuf
) != 0) {
206 * Rename to a backup the new file if it still exists
208 if (stat(newname
, &sbuf
) == 0) {
210 strcpy(buf
, newname
);
211 strcat(buf
, ".XXXXX");
212 tmpfd
= mkstemp(buf
, ISC_TRUE
);
215 (void)DeleteFile(buf
);
216 _chmod(newname
, _S_IREAD
| _S_IWRITE
);
218 filestatus
= MoveFile(newname
, buf
);
220 /* Now rename the file to the new name
222 _chmod(oldname
, _S_IREAD
| _S_IWRITE
);
224 filestatus
= MoveFile(oldname
, newname
);
225 if (filestatus
== 0) {
227 * Try to rename the backup back to the original name
228 * if the backup got created
230 if (exists
== TRUE
) {
231 filestatus
= MoveFile(buf
, newname
);
239 * Delete the backup file if it got created
242 (void)DeleteFile(buf
);
247 isc_file_getmodtime(const char *file
, isc_time_t
*time
) {
250 REQUIRE(file
!= NULL
);
251 REQUIRE(time
!= NULL
);
253 if ((fh
= open(file
, _O_RDONLY
| _O_BINARY
)) < 0)
254 return (isc__errno2result(errno
));
256 if (!GetFileTime((HANDLE
) _get_osfhandle(fh
),
263 return (isc__errno2result(errno
));
266 return (ISC_R_SUCCESS
);
270 isc_file_getsize(const char *file
, off_t
*size
) {
274 REQUIRE(file
!= NULL
);
275 REQUIRE(size
!= NULL
);
277 result
= file_stats(file
, &stats
);
279 if (result
== ISC_R_SUCCESS
)
280 *size
= stats
.st_size
;
286 isc_file_settime(const char *file
, isc_time_t
*time
) {
289 REQUIRE(file
!= NULL
&& time
!= NULL
);
291 if ((fh
= open(file
, _O_RDWR
| _O_BINARY
)) < 0)
292 return (isc__errno2result(errno
));
295 * Set the date via the filedate system call and return. Failing
296 * this call implies the new file times are not supported by the
297 * underlying file system.
299 if (!SetFileTime((HANDLE
) _get_osfhandle(fh
),
306 return (isc__errno2result(errno
));
310 return (ISC_R_SUCCESS
);
315 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
318 isc_file_mktemplate(const char *path
, char *buf
, size_t buflen
) {
319 return (isc_file_template(path
, TEMPLATE
, buf
, buflen
));
323 isc_file_template(const char *path
, const char *templet
, char *buf
,
327 REQUIRE(path
!= NULL
);
328 REQUIRE(templet
!= NULL
);
329 REQUIRE(buf
!= NULL
);
331 s
= strrchr(templet
, '\\');
335 s
= strrchr(path
, '\\');
338 if ((s
- path
+ 1 + strlen(templet
) + 1) > (ssize_t
)buflen
)
339 return (ISC_R_NOSPACE
);
341 strncpy(buf
, path
, s
- path
+ 1);
342 buf
[s
- path
+ 1] = '\0';
343 strcat(buf
, templet
);
345 if ((strlen(templet
) + 1) > buflen
)
346 return (ISC_R_NOSPACE
);
348 strcpy(buf
, templet
);
351 return (ISC_R_SUCCESS
);
355 isc_file_renameunique(const char *file
, char *templet
) {
358 isc_result_t result
= ISC_R_SUCCESS
;
360 REQUIRE(file
!= NULL
);
361 REQUIRE(templet
!= NULL
);
363 fd
= mkstemp(templet
, ISC_TRUE
);
365 result
= isc__errno2result(errno
);
369 if (result
== ISC_R_SUCCESS
) {
370 res
= isc_file_safemovefile(file
, templet
);
372 result
= isc__errno2result(errno
);
373 (void)unlink(templet
);
380 openuniquemode(char *templet
, int mode
, isc_boolean_t binary
, FILE **fp
) {
383 isc_result_t result
= ISC_R_SUCCESS
;
385 REQUIRE(templet
!= NULL
);
386 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
389 * Win32 does not have mkstemp. Using emulation above.
391 fd
= mkstemp(templet
, binary
);
394 result
= isc__errno2result(errno
);
395 if (result
== ISC_R_SUCCESS
) {
399 (void)fchmod(fd
, mode
);
401 f
= fdopen(fd
, binary
? "wb+" : "w+");
403 result
= isc__errno2result(errno
);
404 (void)remove(templet
);
414 isc_file_openuniqueprivate(char *templet
, FILE **fp
) {
415 int mode
= _S_IREAD
| _S_IWRITE
;
416 return (openuniquemode(templet
, mode
, ISC_FALSE
, fp
));
420 isc_file_openunique(char *templet
, FILE **fp
) {
421 int mode
= _S_IREAD
| _S_IWRITE
;
422 return (openuniquemode(templet
, mode
, ISC_FALSE
, fp
));
426 isc_file_openuniquemode(char *templet
, int mode
, FILE **fp
) {
427 return (openuniquemode(templet
, mode
, ISC_FALSE
, fp
));
431 isc_file_bopenuniqueprivate(char *templet
, FILE **fp
) {
432 int mode
= _S_IREAD
| _S_IWRITE
;
433 return (openuniquemode(templet
, mode
, ISC_TRUE
, fp
));
437 isc_file_bopenunique(char *templet
, FILE **fp
) {
438 int mode
= _S_IREAD
| _S_IWRITE
;
439 return (openuniquemode(templet
, mode
, ISC_TRUE
, fp
));
443 isc_file_bopenuniquemode(char *templet
, int mode
, FILE **fp
) {
444 return (openuniquemode(templet
, mode
, ISC_TRUE
, fp
));
448 isc_file_remove(const char *filename
) {
451 REQUIRE(filename
!= NULL
);
453 r
= unlink(filename
);
455 return (ISC_R_SUCCESS
);
457 return (isc__errno2result(errno
));
461 isc_file_rename(const char *oldname
, const char *newname
) {
464 REQUIRE(oldname
!= NULL
);
465 REQUIRE(newname
!= NULL
);
467 r
= isc_file_safemovefile(oldname
, newname
);
469 return (ISC_R_SUCCESS
);
471 return (isc__errno2result(errno
));
475 isc_file_exists(const char *pathname
) {
478 REQUIRE(pathname
!= NULL
);
480 return (ISC_TF(file_stats(pathname
, &stats
) == ISC_R_SUCCESS
));
484 isc_file_isplainfile(const char *filename
) {
486 * This function returns success if filename is a plain file.
488 struct stat filestat
;
489 memset(&filestat
,0,sizeof(struct stat
));
491 if ((stat(filename
, &filestat
)) == -1)
492 return(isc__errno2result(errno
));
494 if(! S_ISREG(filestat
.st_mode
))
495 return(ISC_R_INVALIDFILE
);
497 return(ISC_R_SUCCESS
);
501 isc_file_isplainfilefd(int fd
) {
503 * This function returns success if filename is a plain file.
505 struct stat filestat
;
506 memset(&filestat
,0,sizeof(struct stat
));
508 if ((fstat(fd
, &filestat
)) == -1)
509 return(isc__errno2result(errno
));
511 if(! S_ISREG(filestat
.st_mode
))
512 return(ISC_R_INVALIDFILE
);
514 return(ISC_R_SUCCESS
);
518 isc_file_isdirectory(const char *filename
) {
520 * This function returns success if filename is a directory.
522 struct stat filestat
;
523 memset(&filestat
,0,sizeof(struct stat
));
525 if ((stat(filename
, &filestat
)) == -1)
526 return(isc__errno2result(errno
));
528 if(! S_ISDIR(filestat
.st_mode
))
529 return(ISC_R_INVALIDFILE
);
531 return(ISC_R_SUCCESS
);
536 isc_file_isabsolute(const char *filename
) {
537 REQUIRE(filename
!= NULL
);
539 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
540 * the UNC style file specs
542 if ((filename
[0] == '\\') && (filename
[1] == '\\'))
544 if (isalpha(filename
[0]) && filename
[1] == ':' && filename
[2] == '\\')
546 if (isalpha(filename
[0]) && filename
[1] == ':' && filename
[2] == '/')
552 isc_file_iscurrentdir(const char *filename
) {
553 REQUIRE(filename
!= NULL
);
554 return (ISC_TF(filename
[0] == '.' && filename
[1] == '\0'));
558 isc_file_ischdiridempotent(const char *filename
) {
559 REQUIRE(filename
!= NULL
);
561 if (isc_file_isabsolute(filename
))
563 if (filename
[0] == '\\')
565 if (filename
[0] == '/')
567 if (isc_file_iscurrentdir(filename
))
573 isc_file_basename(const char *filename
) {
576 REQUIRE(filename
!= NULL
);
578 s
= strrchr(filename
, '\\');
585 isc_file_progname(const char *filename
, char *progname
, size_t namelen
) {
590 REQUIRE(filename
!= NULL
);
591 REQUIRE(progname
!= NULL
);
594 * Strip the path from the name
596 s
= isc_file_basename(filename
);
598 return (ISC_R_NOSPACE
);
602 * Strip any and all suffixes
606 if (namelen
<= strlen(s
))
607 return (ISC_R_NOSPACE
);
610 return (ISC_R_SUCCESS
);
614 * Copy the result to the buffer
618 return (ISC_R_NOSPACE
);
620 strncpy(progname
, s
, len
);
621 progname
[len
] = '\0';
622 return (ISC_R_SUCCESS
);
626 isc_file_absolutepath(const char *filename
, char *path
, size_t pathlen
) {
630 REQUIRE(filename
!= NULL
);
631 REQUIRE(path
!= NULL
);
633 retval
= GetFullPathName(filename
, (DWORD
) pathlen
, path
, &ptrname
);
635 /* Something went wrong in getting the path */
637 return (ISC_R_NOTFOUND
);
638 /* Caller needs to provide a larger buffer to contain the string */
639 if (retval
>= pathlen
)
640 return (ISC_R_NOSPACE
);
641 return (ISC_R_SUCCESS
);
645 isc_file_truncate(const char *filename
, isc_offset_t size
) {
648 REQUIRE(filename
!= NULL
&& size
>= 0);
650 if ((fh
= open(filename
, _O_RDWR
| _O_BINARY
)) < 0)
651 return (isc__errno2result(errno
));
653 if(_chsize(fh
, size
) != 0) {
655 return (isc__errno2result(errno
));
659 return (ISC_R_SUCCESS
);
663 isc_file_safecreate(const char *filename
, FILE **fp
) {
670 REQUIRE(filename
!= NULL
);
671 REQUIRE(fp
!= NULL
&& *fp
== NULL
);
673 result
= file_stats(filename
, &sb
);
674 if (result
== ISC_R_SUCCESS
) {
675 if ((sb
.st_mode
& S_IFREG
) == 0)
676 return (ISC_R_INVALIDFILE
);
677 flags
= O_WRONLY
| O_TRUNC
;
678 } else if (result
== ISC_R_FILENOTFOUND
) {
679 flags
= O_WRONLY
| O_CREAT
| O_EXCL
;
683 fd
= open(filename
, flags
, S_IRUSR
| S_IWUSR
);
685 return (isc__errno2result(errno
));
689 result
= isc__errno2result(errno
);
695 return (ISC_R_SUCCESS
);
699 isc_file_splitpath(isc_mem_t
*mctx
, char *path
, char **dirname
, char **basename
)
701 char *dir
, *file
, *slash
;
704 slash
= strrchr(path
, '/');
706 backslash
= strrchr(path
, '\\');
707 if ((slash
!= NULL
&& backslash
!= NULL
&& backslash
> slash
) ||
708 (slash
== NULL
&& backslash
!= NULL
))
713 dir
= isc_mem_strdup(mctx
, "/");
714 } else if (slash
!= NULL
) {
716 dir
= isc_mem_allocate(mctx
, slash
- path
);
718 strlcpy(dir
, path
, slash
- path
);
721 dir
= isc_mem_strdup(mctx
, ".");
725 return (ISC_R_NOMEMORY
);
728 isc_mem_free(mctx
, dir
);
729 return (ISC_R_INVALIDFILE
);
735 return (ISC_R_SUCCESS
);
739 isc_file_mmap(void *addr
, size_t len
, int prot
,
740 int flags
, int fd
, off_t offset
)
750 end
= lseek(fd
, 0, SEEK_END
);
751 lseek(fd
, offset
, SEEK_SET
);
752 if (end
- offset
< (off_t
) len
)
756 ret
= read(fd
, buf
, (unsigned int) len
);
757 if (ret
!= (ssize_t
) len
) {
766 isc_file_munmap(void *addr
, size_t len
) {