1 /* $NetBSD: file.c,v 1.9 2014/12/10 04:38:01 christos Exp $ */
2
3 /*
4 * Copyright (C) 2004, 2007, 2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2002 Internet Software Consortium.
6 *
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.
10 *
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.
18 */
19
20 /* Id */
21
22 #include <config.h>
23
24 #undef rename
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <io.h>
29 #include <process.h>
30
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <sys/utime.h>
34
35 #include <isc/file.h>
36 #include <isc/mem.h>
37 #include <isc/result.h>
38 #include <isc/time.h>
39 #include <isc/util.h>
40 #include <isc/stat.h>
41 #include <isc/string.h>
42
43 #include "errno2result.h"
44
45 /*
46 * Emulate UNIX mkstemp, which returns an open FD to the new file
47 *
48 */
49 static int
gettemp(char * path,isc_boolean_t binary,int * doopen)50 gettemp(char *path, isc_boolean_t binary, int *doopen) {
51 char *start, *trv;
52 struct stat sbuf;
53 int pid;
54 int flags = O_CREAT|O_EXCL|O_RDWR;
55
56 if (binary)
57 flags |= _O_BINARY;
58
59 trv = strrchr(path, 'X');
60 trv++;
61 pid = getpid();
62 /* extra X's get set to 0's */
63 while (*--trv == 'X') {
64 *trv = (pid % 10) + '0';
65 pid /= 10;
66 }
67 /*
68 * check the target directory; if you have six X's and it
69 * doesn't exist this runs for a *very* long time.
70 */
71 for (start = trv + 1;; --trv) {
72 if (trv <= path)
73 break;
74 if (*trv == '\\') {
75 *trv = '\0';
76 if (stat(path, &sbuf))
77 return (0);
78 if (!S_ISDIR(sbuf.st_mode)) {
79 errno = ENOTDIR;
80 return (0);
81 }
82 *trv = '\\';
83 break;
84 }
85 }
86
87 for (;;) {
88 if (doopen) {
89 if ((*doopen =
90 open(path, flags, _S_IREAD | _S_IWRITE)) >= 0)
91 return (1);
92 if (errno != EEXIST)
93 return (0);
94 } else if (stat(path, &sbuf))
95 return (errno == ENOENT ? 1 : 0);
96
97 /* tricky little algorithm for backward compatibility */
98 for (trv = start;;) {
99 if (!*trv)
100 return (0);
101 if (*trv == 'z')
102 *trv++ = 'a';
103 else {
104 if (isdigit(*trv))
105 *trv = 'a';
106 else
107 ++*trv;
108 break;
109 }
110 }
111 }
112 /*NOTREACHED*/
113 }
114
115 static int
mkstemp(char * path,isc_boolean_t binary)116 mkstemp(char *path, isc_boolean_t binary) {
117 int fd;
118
119 return (gettemp(path, binary, &fd) ? fd : -1);
120 }
121
122 /*
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().
129 */
130 static isc_result_t
file_stats(const char * file,struct stat * stats)131 file_stats(const char *file, struct stat *stats) {
132 isc_result_t result = ISC_R_SUCCESS;
133
134 REQUIRE(file != NULL);
135 REQUIRE(stats != NULL);
136
137 if (stat(file, stats) != 0)
138 result = isc__errno2result(errno);
139
140 return (result);
141 }
142
143 static isc_result_t
fd_stats(int fd,struct stat * stats)144 fd_stats(int fd, struct stat *stats) {
145 isc_result_t result = ISC_R_SUCCESS;
146
147 REQUIRE(stats != NULL);
148
149 if (fstat(fd, stats) != 0)
150 result = isc__errno2result(errno);
151
152 return (result);
153 }
154
155 isc_result_t
isc_file_getsizefd(int fd,off_t * size)156 isc_file_getsizefd(int fd, off_t *size) {
157 isc_result_t result;
158 struct stat stats;
159
160 REQUIRE(size != NULL);
161
162 result = fd_stats(fd, &stats);
163
164 if (result == ISC_R_SUCCESS)
165 *size = stats.st_size;
166 return (result);
167 }
168
169 isc_result_t
isc_file_mode(const char * file,mode_t * modep)170 isc_file_mode(const char *file, mode_t *modep) {
171 isc_result_t result;
172 struct stat stats;
173
174 REQUIRE(modep != NULL);
175
176 result = file_stats(file, &stats);
177 if (result == ISC_R_SUCCESS)
178 *modep = (stats.st_mode & 07777);
179 return (result);
180 }
181
182 /*
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.
187 */
188
189 int
isc_file_safemovefile(const char * oldname,const char * newname)190 isc_file_safemovefile(const char *oldname, const char *newname) {
191 BOOL filestatus;
192 char buf[512];
193 struct stat sbuf;
194 BOOL exists = FALSE;
195 int tmpfd;
196
197 /*
198 * Make sure we have something to do
199 */
200 if (stat(oldname, &sbuf) != 0) {
201 errno = ENOENT;
202 return (-1);
203 }
204
205 /*
206 * Rename to a backup the new file if it still exists
207 */
208 if (stat(newname, &sbuf) == 0) {
209 exists = TRUE;
210 strcpy(buf, newname);
211 strcat(buf, ".XXXXX");
212 tmpfd = mkstemp(buf, ISC_TRUE);
213 if (tmpfd > 0)
214 _close(tmpfd);
215 (void)DeleteFile(buf);
216 _chmod(newname, _S_IREAD | _S_IWRITE);
217
218 filestatus = MoveFile(newname, buf);
219 }
220 /* Now rename the file to the new name
221 */
222 _chmod(oldname, _S_IREAD | _S_IWRITE);
223
224 filestatus = MoveFile(oldname, newname);
225 if (filestatus == 0) {
226 /*
227 * Try to rename the backup back to the original name
228 * if the backup got created
229 */
230 if (exists == TRUE) {
231 filestatus = MoveFile(buf, newname);
232 if (filestatus == 0)
233 errno = EACCES;
234 }
235 return (-1);
236 }
237
238 /*
239 * Delete the backup file if it got created
240 */
241 if (exists == TRUE)
242 (void)DeleteFile(buf);
243 return (0);
244 }
245
246 isc_result_t
isc_file_getmodtime(const char * file,isc_time_t * time)247 isc_file_getmodtime(const char *file, isc_time_t *time) {
248 int fh;
249
250 REQUIRE(file != NULL);
251 REQUIRE(time != NULL);
252
253 if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0)
254 return (isc__errno2result(errno));
255
256 if (!GetFileTime((HANDLE) _get_osfhandle(fh),
257 NULL,
258 NULL,
259 &time->absolute))
260 {
261 close(fh);
262 errno = EINVAL;
263 return (isc__errno2result(errno));
264 }
265 close(fh);
266 return (ISC_R_SUCCESS);
267 }
268
269 isc_result_t
isc_file_getsize(const char * file,off_t * size)270 isc_file_getsize(const char *file, off_t *size) {
271 isc_result_t result;
272 struct stat stats;
273
274 REQUIRE(file != NULL);
275 REQUIRE(size != NULL);
276
277 result = file_stats(file, &stats);
278
279 if (result == ISC_R_SUCCESS)
280 *size = stats.st_size;
281
282 return (result);
283 }
284
285 isc_result_t
isc_file_settime(const char * file,isc_time_t * time)286 isc_file_settime(const char *file, isc_time_t *time) {
287 int fh;
288
289 REQUIRE(file != NULL && time != NULL);
290
291 if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0)
292 return (isc__errno2result(errno));
293
294 /*
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.
298 */
299 if (!SetFileTime((HANDLE) _get_osfhandle(fh),
300 NULL,
301 &time->absolute,
302 &time->absolute))
303 {
304 close(fh);
305 errno = EINVAL;
306 return (isc__errno2result(errno));
307 }
308
309 close(fh);
310 return (ISC_R_SUCCESS);
311
312 }
313
314 #undef TEMPLATE
315 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
316
317 isc_result_t
isc_file_mktemplate(const char * path,char * buf,size_t buflen)318 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
319 return (isc_file_template(path, TEMPLATE, buf, buflen));
320 }
321
322 isc_result_t
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)323 isc_file_template(const char *path, const char *templet, char *buf,
324 size_t buflen) {
325 char *s;
326
327 REQUIRE(path != NULL);
328 REQUIRE(templet != NULL);
329 REQUIRE(buf != NULL);
330
331 s = strrchr(templet, '\\');
332 if (s != NULL)
333 templet = s + 1;
334
335 s = strrchr(path, '\\');
336
337 if (s != NULL) {
338 if ((s - path + 1 + strlen(templet) + 1) > (ssize_t)buflen)
339 return (ISC_R_NOSPACE);
340
341 strncpy(buf, path, s - path + 1);
342 buf[s - path + 1] = '\0';
343 strcat(buf, templet);
344 } else {
345 if ((strlen(templet) + 1) > buflen)
346 return (ISC_R_NOSPACE);
347
348 strcpy(buf, templet);
349 }
350
351 return (ISC_R_SUCCESS);
352 }
353
354 isc_result_t
isc_file_renameunique(const char * file,char * templet)355 isc_file_renameunique(const char *file, char *templet) {
356 int fd;
357 int res = 0;
358 isc_result_t result = ISC_R_SUCCESS;
359
360 REQUIRE(file != NULL);
361 REQUIRE(templet != NULL);
362
363 fd = mkstemp(templet, ISC_TRUE);
364 if (fd == -1)
365 result = isc__errno2result(errno);
366 else
367 close(fd);
368
369 if (result == ISC_R_SUCCESS) {
370 res = isc_file_safemovefile(file, templet);
371 if (res != 0) {
372 result = isc__errno2result(errno);
373 (void)unlink(templet);
374 }
375 }
376 return (result);
377 }
378
379 static isc_result_t
openuniquemode(char * templet,int mode,isc_boolean_t binary,FILE ** fp)380 openuniquemode(char *templet, int mode, isc_boolean_t binary, FILE **fp) {
381 int fd;
382 FILE *f;
383 isc_result_t result = ISC_R_SUCCESS;
384
385 REQUIRE(templet != NULL);
386 REQUIRE(fp != NULL && *fp == NULL);
387
388 /*
389 * Win32 does not have mkstemp. Using emulation above.
390 */
391 fd = mkstemp(templet, binary);
392
393 if (fd == -1)
394 result = isc__errno2result(errno);
395 if (result == ISC_R_SUCCESS) {
396 #if 1
397 UNUSED(mode);
398 #else
399 (void)fchmod(fd, mode);
400 #endif
401 f = fdopen(fd, binary ? "wb+" : "w+");
402 if (f == NULL) {
403 result = isc__errno2result(errno);
404 (void)remove(templet);
405 (void)close(fd);
406 } else
407 *fp = f;
408 }
409
410 return (result);
411 }
412
413 isc_result_t
isc_file_openuniqueprivate(char * templet,FILE ** fp)414 isc_file_openuniqueprivate(char *templet, FILE **fp) {
415 int mode = _S_IREAD | _S_IWRITE;
416 return (openuniquemode(templet, mode, ISC_FALSE, fp));
417 }
418
419 isc_result_t
isc_file_openunique(char * templet,FILE ** fp)420 isc_file_openunique(char *templet, FILE **fp) {
421 int mode = _S_IREAD | _S_IWRITE;
422 return (openuniquemode(templet, mode, ISC_FALSE, fp));
423 }
424
425 isc_result_t
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)426 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
427 return (openuniquemode(templet, mode, ISC_FALSE, fp));
428 }
429
430 isc_result_t
isc_file_bopenuniqueprivate(char * templet,FILE ** fp)431 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
432 int mode = _S_IREAD | _S_IWRITE;
433 return (openuniquemode(templet, mode, ISC_TRUE, fp));
434 }
435
436 isc_result_t
isc_file_bopenunique(char * templet,FILE ** fp)437 isc_file_bopenunique(char *templet, FILE **fp) {
438 int mode = _S_IREAD | _S_IWRITE;
439 return (openuniquemode(templet, mode, ISC_TRUE, fp));
440 }
441
442 isc_result_t
isc_file_bopenuniquemode(char * templet,int mode,FILE ** fp)443 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
444 return (openuniquemode(templet, mode, ISC_TRUE, fp));
445 }
446
447 isc_result_t
isc_file_remove(const char * filename)448 isc_file_remove(const char *filename) {
449 int r;
450
451 REQUIRE(filename != NULL);
452
453 r = unlink(filename);
454 if (r == 0)
455 return (ISC_R_SUCCESS);
456 else
457 return (isc__errno2result(errno));
458 }
459
460 isc_result_t
isc_file_rename(const char * oldname,const char * newname)461 isc_file_rename(const char *oldname, const char *newname) {
462 int r;
463
464 REQUIRE(oldname != NULL);
465 REQUIRE(newname != NULL);
466
467 r = isc_file_safemovefile(oldname, newname);
468 if (r == 0)
469 return (ISC_R_SUCCESS);
470 else
471 return (isc__errno2result(errno));
472 }
473
474 isc_boolean_t
isc_file_exists(const char * pathname)475 isc_file_exists(const char *pathname) {
476 struct stat stats;
477
478 REQUIRE(pathname != NULL);
479
480 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
481 }
482
483 isc_result_t
isc_file_isplainfile(const char * filename)484 isc_file_isplainfile(const char *filename) {
485 /*
486 * This function returns success if filename is a plain file.
487 */
488 struct stat filestat;
489 memset(&filestat,0,sizeof(struct stat));
490
491 if ((stat(filename, &filestat)) == -1)
492 return(isc__errno2result(errno));
493
494 if(! S_ISREG(filestat.st_mode))
495 return(ISC_R_INVALIDFILE);
496
497 return(ISC_R_SUCCESS);
498 }
499
500 isc_result_t
isc_file_isplainfilefd(int fd)501 isc_file_isplainfilefd(int fd) {
502 /*
503 * This function returns success if filename is a plain file.
504 */
505 struct stat filestat;
506 memset(&filestat,0,sizeof(struct stat));
507
508 if ((fstat(fd, &filestat)) == -1)
509 return(isc__errno2result(errno));
510
511 if(! S_ISREG(filestat.st_mode))
512 return(ISC_R_INVALIDFILE);
513
514 return(ISC_R_SUCCESS);
515 }
516
517 isc_result_t
isc_file_isdirectory(const char * filename)518 isc_file_isdirectory(const char *filename) {
519 /*
520 * This function returns success if filename is a directory.
521 */
522 struct stat filestat;
523 memset(&filestat,0,sizeof(struct stat));
524
525 if ((stat(filename, &filestat)) == -1)
526 return(isc__errno2result(errno));
527
528 if(! S_ISDIR(filestat.st_mode))
529 return(ISC_R_INVALIDFILE);
530
531 return(ISC_R_SUCCESS);
532 }
533
534
535 isc_boolean_t
isc_file_isabsolute(const char * filename)536 isc_file_isabsolute(const char *filename) {
537 REQUIRE(filename != NULL);
538 /*
539 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
540 * the UNC style file specs
541 */
542 if ((filename[0] == '\\') && (filename[1] == '\\'))
543 return (ISC_TRUE);
544 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\')
545 return (ISC_TRUE);
546 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
547 return (ISC_TRUE);
548 return (ISC_FALSE);
549 }
550
551 isc_boolean_t
isc_file_iscurrentdir(const char * filename)552 isc_file_iscurrentdir(const char *filename) {
553 REQUIRE(filename != NULL);
554 return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
555 }
556
557 isc_boolean_t
isc_file_ischdiridempotent(const char * filename)558 isc_file_ischdiridempotent(const char *filename) {
559 REQUIRE(filename != NULL);
560
561 if (isc_file_isabsolute(filename))
562 return (ISC_TRUE);
563 if (filename[0] == '\\')
564 return (ISC_TRUE);
565 if (filename[0] == '/')
566 return (ISC_TRUE);
567 if (isc_file_iscurrentdir(filename))
568 return (ISC_TRUE);
569 return (ISC_FALSE);
570 }
571
572 const char *
isc_file_basename(const char * filename)573 isc_file_basename(const char *filename) {
574 char *s;
575
576 REQUIRE(filename != NULL);
577
578 s = strrchr(filename, '\\');
579 if (s == NULL)
580 return (filename);
581 return (s + 1);
582 }
583
584 isc_result_t
isc_file_progname(const char * filename,char * progname,size_t namelen)585 isc_file_progname(const char *filename, char *progname, size_t namelen) {
586 const char *s;
587 char *p;
588 size_t len;
589
590 REQUIRE(filename != NULL);
591 REQUIRE(progname != NULL);
592
593 /*
594 * Strip the path from the name
595 */
596 s = isc_file_basename(filename);
597 if (s == NULL) {
598 return (ISC_R_NOSPACE);
599 }
600
601 /*
602 * Strip any and all suffixes
603 */
604 p = strchr(s, '.');
605 if (p == NULL) {
606 if (namelen <= strlen(s))
607 return (ISC_R_NOSPACE);
608
609 strcpy(progname, s);
610 return (ISC_R_SUCCESS);
611 }
612
613 /*
614 * Copy the result to the buffer
615 */
616 len = p - s;
617 if (len >= namelen)
618 return (ISC_R_NOSPACE);
619
620 strncpy(progname, s, len);
621 progname[len] = '\0';
622 return (ISC_R_SUCCESS);
623 }
624
625 isc_result_t
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)626 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
627 char *ptrname;
628 DWORD retval;
629
630 REQUIRE(filename != NULL);
631 REQUIRE(path != NULL);
632
633 retval = GetFullPathName(filename, (DWORD) pathlen, path, &ptrname);
634
635 /* Something went wrong in getting the path */
636 if (retval == 0)
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);
642 }
643
644 isc_result_t
isc_file_truncate(const char * filename,isc_offset_t size)645 isc_file_truncate(const char *filename, isc_offset_t size) {
646 int fh;
647
648 REQUIRE(filename != NULL && size >= 0);
649
650 if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0)
651 return (isc__errno2result(errno));
652
653 if(_chsize(fh, size) != 0) {
654 close(fh);
655 return (isc__errno2result(errno));
656 }
657 close(fh);
658
659 return (ISC_R_SUCCESS);
660 }
661
662 isc_result_t
isc_file_safecreate(const char * filename,FILE ** fp)663 isc_file_safecreate(const char *filename, FILE **fp) {
664 isc_result_t result;
665 int flags;
666 struct stat sb;
667 FILE *f;
668 int fd;
669
670 REQUIRE(filename != NULL);
671 REQUIRE(fp != NULL && *fp == NULL);
672
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;
680 } else
681 return (result);
682
683 fd = open(filename, flags, S_IRUSR | S_IWUSR);
684 if (fd == -1)
685 return (isc__errno2result(errno));
686
687 f = fdopen(fd, "w");
688 if (f == NULL) {
689 result = isc__errno2result(errno);
690 close(fd);
691 return (result);
692 }
693
694 *fp = f;
695 return (ISC_R_SUCCESS);
696 }
697
698 isc_result_t
isc_file_splitpath(isc_mem_t * mctx,char * path,char ** dirname,char ** basename)699 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
700 {
701 char *dir, *file, *slash;
702 char *backslash;
703
704 slash = strrchr(path, '/');
705
706 backslash = strrchr(path, '\\');
707 if ((slash != NULL && backslash != NULL && backslash > slash) ||
708 (slash == NULL && backslash != NULL))
709 slash = backslash;
710
711 if (slash == path) {
712 file = ++slash;
713 dir = isc_mem_strdup(mctx, "/");
714 } else if (slash != NULL) {
715 file = ++slash;
716 dir = isc_mem_allocate(mctx, slash - path);
717 if (dir != NULL)
718 strlcpy(dir, path, slash - path);
719 } else {
720 file = path;
721 dir = isc_mem_strdup(mctx, ".");
722 }
723
724 if (dir == NULL)
725 return (ISC_R_NOMEMORY);
726
727 if (*file == '\0') {
728 isc_mem_free(mctx, dir);
729 return (ISC_R_INVALIDFILE);
730 }
731
732 *dirname = dir;
733 *basename = file;
734
735 return (ISC_R_SUCCESS);
736 }
737
738 void *
isc_file_mmap(void * addr,size_t len,int prot,int flags,int fd,off_t offset)739 isc_file_mmap(void *addr, size_t len, int prot,
740 int flags, int fd, off_t offset)
741 {
742 void *buf;
743 ssize_t ret;
744 off_t end;
745
746 UNUSED(addr);
747 UNUSED(prot);
748 UNUSED(flags);
749
750 end = lseek(fd, 0, SEEK_END);
751 lseek(fd, offset, SEEK_SET);
752 if (end - offset < (off_t) len)
753 len = end - offset;
754
755 buf = malloc(len);
756 ret = read(fd, buf, (unsigned int) len);
757 if (ret != (ssize_t) len) {
758 free(buf);
759 buf = NULL;
760 }
761
762 return (buf);
763 }
764
765 int
isc_file_munmap(void * addr,size_t len)766 isc_file_munmap(void *addr, size_t len) {
767 UNUSED(len);
768 free(addr);
769 return (0);
770 }
771