1 /* $NetBSD: file.c,v 1.9 2014/12/10 04:38:01 christos Exp $ */
2
3 /*
4 * Copyright (C) 2004, 2005, 2007, 2009, 2011-2014 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 /*
21 * Portions Copyright (c) 1987, 1993
22 * The Regents of the University of California. All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
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. Neither the name of the University nor the names of its contributors
33 * may be used to endorse or promote products derived from this software
34 * without specific prior written permission.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 */
48
49 /* Id */
50
51 /*! \file */
52
53 #include <config.h>
54
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <limits.h>
58 #include <stdlib.h>
59 #include <time.h> /* Required for utimes on some platforms. */
60 #include <unistd.h> /* Required for mkstemp on NetBSD. */
61
62
63 #include <sys/stat.h>
64 #include <sys/time.h>
65
66 #ifdef HAVE_SYS_MMAN_H
67 #include <sys/mman.h>
68 #endif
69
70 #include <isc/dir.h>
71 #include <isc/file.h>
72 #include <isc/log.h>
73 #include <isc/mem.h>
74 #include <isc/random.h>
75 #include <isc/string.h>
76 #include <isc/time.h>
77 #include <isc/util.h>
78
79 #include "errno2result.h"
80
81 /*
82 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
83 * it might be good to provide a mechanism that allows for the results
84 * of a previous stat() to be used again without having to do another stat,
85 * such as perl's mechanism of using "_" in place of a file name to indicate
86 * that the results of the last stat should be used. But then you get into
87 * annoying MP issues. BTW, Win32 has stat().
88 */
89 static isc_result_t
file_stats(const char * file,struct stat * stats)90 file_stats(const char *file, struct stat *stats) {
91 isc_result_t result = ISC_R_SUCCESS;
92
93 REQUIRE(file != NULL);
94 REQUIRE(stats != NULL);
95
96 if (stat(file, stats) != 0)
97 result = isc__errno2result(errno);
98
99 return (result);
100 }
101
102 static isc_result_t
fd_stats(int fd,struct stat * stats)103 fd_stats(int fd, struct stat *stats) {
104 isc_result_t result = ISC_R_SUCCESS;
105
106 REQUIRE(stats != NULL);
107
108 if (fstat(fd, stats) != 0)
109 result = isc__errno2result(errno);
110
111 return (result);
112 }
113
114 isc_result_t
isc_file_getsizefd(int fd,off_t * size)115 isc_file_getsizefd(int fd, off_t *size) {
116 isc_result_t result;
117 struct stat stats;
118
119 REQUIRE(size != NULL);
120
121 result = fd_stats(fd, &stats);
122
123 if (result == ISC_R_SUCCESS)
124 *size = stats.st_size;
125
126 return (result);
127 }
128
129 isc_result_t
isc_file_mode(const char * file,mode_t * modep)130 isc_file_mode(const char *file, mode_t *modep) {
131 isc_result_t result;
132 struct stat stats;
133
134 REQUIRE(modep != NULL);
135
136 result = file_stats(file, &stats);
137 if (result == ISC_R_SUCCESS)
138 *modep = (stats.st_mode & 07777);
139
140 return (result);
141 }
142
143 isc_result_t
isc_file_getmodtime(const char * file,isc_time_t * time)144 isc_file_getmodtime(const char *file, isc_time_t *time) {
145 isc_result_t result;
146 struct stat stats;
147
148 REQUIRE(file != NULL);
149 REQUIRE(time != NULL);
150
151 result = file_stats(file, &stats);
152
153 if (result == ISC_R_SUCCESS)
154 /*
155 * XXXDCL some operating systems provide nanoseconds, too,
156 * such as BSD/OS via st_mtimespec.
157 */
158 isc_time_set(time, stats.st_mtime, 0);
159
160 return (result);
161 }
162
163 isc_result_t
isc_file_getsize(const char * file,off_t * size)164 isc_file_getsize(const char *file, off_t *size) {
165 isc_result_t result;
166 struct stat stats;
167
168 REQUIRE(file != NULL);
169 REQUIRE(size != NULL);
170
171 result = file_stats(file, &stats);
172
173 if (result == ISC_R_SUCCESS)
174 *size = stats.st_size;
175
176 return (result);
177 }
178
179 isc_result_t
isc_file_settime(const char * file,isc_time_t * time)180 isc_file_settime(const char *file, isc_time_t *time) {
181 struct timeval times[2];
182
183 REQUIRE(file != NULL && time != NULL);
184
185 /*
186 * tv_sec is at least a 32 bit quantity on all platforms we're
187 * dealing with, but it is signed on most (all?) of them,
188 * so we need to make sure the high bit isn't set. This unfortunately
189 * loses when either:
190 * * tv_sec becomes a signed 64 bit integer but long is 32 bits
191 * and isc_time_seconds > LONG_MAX, or
192 * * isc_time_seconds is changed to be > 32 bits but long is 32 bits
193 * and isc_time_seconds has at least 33 significant bits.
194 */
195 times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
196
197 /*
198 * Here is the real check for the high bit being set.
199 */
200 if ((times[0].tv_sec &
201 (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
202 return (ISC_R_RANGE);
203
204 /*
205 * isc_time_nanoseconds guarantees a value that divided by 1000 will
206 * fit into the minimum possible size tv_usec field. Unfortunately,
207 * we don't know what that type is so can't cast directly ... but
208 * we can at least cast to signed so the IRIX compiler shuts up.
209 */
210 times[0].tv_usec = times[1].tv_usec =
211 (isc_int32_t)(isc_time_nanoseconds(time) / 1000);
212
213 if (utimes(file, times) < 0)
214 return (isc__errno2result(errno));
215
216 return (ISC_R_SUCCESS);
217 }
218
219 #undef TEMPLATE
220 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
221
222 isc_result_t
isc_file_mktemplate(const char * path,char * buf,size_t buflen)223 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
224 return (isc_file_template(path, TEMPLATE, buf, buflen));
225 }
226
227 isc_result_t
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)228 isc_file_template(const char *path, const char *templet, char *buf,
229 size_t buflen) {
230 char *s;
231
232 REQUIRE(path != NULL);
233 REQUIRE(templet != NULL);
234 REQUIRE(buf != NULL);
235
236 s = strrchr(templet, '/');
237 if (s != NULL)
238 templet = s + 1;
239
240 s = strrchr(path, '/');
241
242 if (s != NULL) {
243 if ((s - path + 1 + strlen(templet) + 1) > buflen)
244 return (ISC_R_NOSPACE);
245
246 strncpy(buf, path, s - path + 1);
247 buf[s - path + 1] = '\0';
248 strcat(buf, templet);
249 } else {
250 if ((strlen(templet) + 1) > buflen)
251 return (ISC_R_NOSPACE);
252
253 strcpy(buf, templet);
254 }
255
256 return (ISC_R_SUCCESS);
257 }
258
259 static char alphnum[] =
260 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
261
262 isc_result_t
isc_file_renameunique(const char * file,char * templet)263 isc_file_renameunique(const char *file, char *templet) {
264 char *x;
265 char *cp;
266 isc_uint32_t which;
267
268 REQUIRE(file != NULL);
269 REQUIRE(templet != NULL);
270
271 cp = templet;
272 while (*cp != '\0')
273 cp++;
274 if (cp == templet)
275 return (ISC_R_FAILURE);
276
277 x = cp--;
278 while (cp >= templet && *cp == 'X') {
279 isc_random_get(&which);
280 *cp = alphnum[which % (sizeof(alphnum) - 1)];
281 x = cp--;
282 }
283 while (link(file, templet) == -1) {
284 if (errno != EEXIST)
285 return (isc__errno2result(errno));
286 for (cp = x;;) {
287 char *t;
288 if (*cp == '\0')
289 return (ISC_R_FAILURE);
290 t = strchr(alphnum, *cp);
291 if (t == NULL || *++t == '\0')
292 *cp++ = alphnum[0];
293 else {
294 *cp = *t;
295 break;
296 }
297 }
298 }
299 if (unlink(file) < 0)
300 if (errno != ENOENT)
301 return (isc__errno2result(errno));
302 return (ISC_R_SUCCESS);
303 }
304
305 isc_result_t
isc_file_openunique(char * templet,FILE ** fp)306 isc_file_openunique(char *templet, FILE **fp) {
307 int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
308 return (isc_file_openuniquemode(templet, mode, fp));
309 }
310
311 isc_result_t
isc_file_openuniqueprivate(char * templet,FILE ** fp)312 isc_file_openuniqueprivate(char *templet, FILE **fp) {
313 int mode = S_IWUSR|S_IRUSR;
314 return (isc_file_openuniquemode(templet, mode, fp));
315 }
316
317 isc_result_t
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)318 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
319 int fd;
320 FILE *f;
321 isc_result_t result = ISC_R_SUCCESS;
322 char *x;
323 char *cp;
324 isc_uint32_t which;
325
326 REQUIRE(templet != NULL);
327 REQUIRE(fp != NULL && *fp == NULL);
328
329 cp = templet;
330 while (*cp != '\0')
331 cp++;
332 if (cp == templet)
333 return (ISC_R_FAILURE);
334
335 x = cp--;
336 while (cp >= templet && *cp == 'X') {
337 isc_random_get(&which);
338 *cp = alphnum[which % (sizeof(alphnum) - 1)];
339 x = cp--;
340 }
341
342
343 while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
344 if (errno != EEXIST)
345 return (isc__errno2result(errno));
346 for (cp = x;;) {
347 char *t;
348 if (*cp == '\0')
349 return (ISC_R_FAILURE);
350 t = strchr(alphnum, *cp);
351 if (t == NULL || *++t == '\0')
352 *cp++ = alphnum[0];
353 else {
354 *cp = *t;
355 break;
356 }
357 }
358 }
359 f = fdopen(fd, "w+");
360 if (f == NULL) {
361 result = isc__errno2result(errno);
362 if (remove(templet) < 0) {
363 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
364 ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
365 "remove '%s': failed", templet);
366 }
367 (void)close(fd);
368 } else
369 *fp = f;
370
371 return (result);
372 }
373
374 isc_result_t
isc_file_bopenunique(char * templet,FILE ** fp)375 isc_file_bopenunique(char *templet, FILE **fp) {
376 int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
377 return (isc_file_openuniquemode(templet, mode, fp));
378 }
379
380 isc_result_t
isc_file_bopenuniqueprivate(char * templet,FILE ** fp)381 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
382 int mode = S_IWUSR|S_IRUSR;
383 return (isc_file_openuniquemode(templet, mode, fp));
384 }
385
386 isc_result_t
isc_file_bopenuniquemode(char * templet,int mode,FILE ** fp)387 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
388 return (isc_file_openuniquemode(templet, mode, fp));
389 }
390
391 isc_result_t
isc_file_remove(const char * filename)392 isc_file_remove(const char *filename) {
393 int r;
394
395 REQUIRE(filename != NULL);
396
397 r = unlink(filename);
398 if (r == 0)
399 return (ISC_R_SUCCESS);
400 else
401 return (isc__errno2result(errno));
402 }
403
404 isc_result_t
isc_file_rename(const char * oldname,const char * newname)405 isc_file_rename(const char *oldname, const char *newname) {
406 int r;
407
408 REQUIRE(oldname != NULL);
409 REQUIRE(newname != NULL);
410
411 r = rename(oldname, newname);
412 if (r == 0)
413 return (ISC_R_SUCCESS);
414 else
415 return (isc__errno2result(errno));
416 }
417
418 isc_boolean_t
isc_file_exists(const char * pathname)419 isc_file_exists(const char *pathname) {
420 struct stat stats;
421
422 REQUIRE(pathname != NULL);
423
424 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
425 }
426
427 isc_result_t
isc_file_isplainfile(const char * filename)428 isc_file_isplainfile(const char *filename) {
429 /*
430 * This function returns success if filename is a plain file.
431 */
432 struct stat filestat;
433 memset(&filestat,0,sizeof(struct stat));
434
435 if ((stat(filename, &filestat)) == -1)
436 return(isc__errno2result(errno));
437
438 if(! S_ISREG(filestat.st_mode))
439 return(ISC_R_INVALIDFILE);
440
441 return(ISC_R_SUCCESS);
442 }
443
444 isc_result_t
isc_file_isplainfilefd(int fd)445 isc_file_isplainfilefd(int fd) {
446 /*
447 * This function returns success if filename is a plain file.
448 */
449 struct stat filestat;
450 memset(&filestat,0,sizeof(struct stat));
451
452 if ((fstat(fd, &filestat)) == -1)
453 return(isc__errno2result(errno));
454
455 if(! S_ISREG(filestat.st_mode))
456 return(ISC_R_INVALIDFILE);
457
458 return(ISC_R_SUCCESS);
459 }
460
461 isc_result_t
isc_file_isdirectory(const char * filename)462 isc_file_isdirectory(const char *filename) {
463 /*
464 * This function returns success if filename exists and is a
465 * directory.
466 */
467 struct stat filestat;
468 memset(&filestat,0,sizeof(struct stat));
469
470 if ((stat(filename, &filestat)) == -1)
471 return(isc__errno2result(errno));
472
473 if(! S_ISDIR(filestat.st_mode))
474 return(ISC_R_INVALIDFILE);
475
476 return(ISC_R_SUCCESS);
477 }
478
479
480 isc_boolean_t
isc_file_isabsolute(const char * filename)481 isc_file_isabsolute(const char *filename) {
482 REQUIRE(filename != NULL);
483 return (ISC_TF(filename[0] == '/'));
484 }
485
486 isc_boolean_t
isc_file_iscurrentdir(const char * filename)487 isc_file_iscurrentdir(const char *filename) {
488 REQUIRE(filename != NULL);
489 return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
490 }
491
492 isc_boolean_t
isc_file_ischdiridempotent(const char * filename)493 isc_file_ischdiridempotent(const char *filename) {
494 REQUIRE(filename != NULL);
495 if (isc_file_isabsolute(filename))
496 return (ISC_TRUE);
497 if (isc_file_iscurrentdir(filename))
498 return (ISC_TRUE);
499 return (ISC_FALSE);
500 }
501
502 const char *
isc_file_basename(const char * filename)503 isc_file_basename(const char *filename) {
504 char *s;
505
506 REQUIRE(filename != NULL);
507
508 s = strrchr(filename, '/');
509 if (s == NULL)
510 return (filename);
511
512 return (s + 1);
513 }
514
515 isc_result_t
isc_file_progname(const char * filename,char * buf,size_t buflen)516 isc_file_progname(const char *filename, char *buf, size_t buflen) {
517 const char *base;
518 size_t len;
519
520 REQUIRE(filename != NULL);
521 REQUIRE(buf != NULL);
522
523 base = isc_file_basename(filename);
524 len = strlen(base) + 1;
525
526 if (len > buflen)
527 return (ISC_R_NOSPACE);
528 memmove(buf, base, len);
529
530 return (ISC_R_SUCCESS);
531 }
532
533 /*
534 * Put the absolute name of the current directory into 'dirname', which is
535 * a buffer of at least 'length' characters. End the string with the
536 * appropriate path separator, such that the final product could be
537 * concatenated with a relative pathname to make a valid pathname string.
538 */
539 static isc_result_t
dir_current(char * dirname,size_t length)540 dir_current(char *dirname, size_t length) {
541 char *cwd;
542 isc_result_t result = ISC_R_SUCCESS;
543
544 REQUIRE(dirname != NULL);
545 REQUIRE(length > 0U);
546
547 cwd = getcwd(dirname, length);
548
549 if (cwd == NULL) {
550 if (errno == ERANGE)
551 result = ISC_R_NOSPACE;
552 else
553 result = isc__errno2result(errno);
554 } else {
555 if (strlen(dirname) + 1 == length)
556 result = ISC_R_NOSPACE;
557 else if (dirname[1] != '\0')
558 strcat(dirname, "/");
559 }
560
561 return (result);
562 }
563
564 isc_result_t
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)565 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
566 isc_result_t result;
567 result = dir_current(path, pathlen);
568 if (result != ISC_R_SUCCESS)
569 return (result);
570 if (strlen(path) + strlen(filename) + 1 > pathlen)
571 return (ISC_R_NOSPACE);
572 strcat(path, filename);
573 return (ISC_R_SUCCESS);
574 }
575
576 isc_result_t
isc_file_truncate(const char * filename,isc_offset_t size)577 isc_file_truncate(const char *filename, isc_offset_t size) {
578 isc_result_t result = ISC_R_SUCCESS;
579
580 if (truncate(filename, size) < 0)
581 result = isc__errno2result(errno);
582 return (result);
583 }
584
585 isc_result_t
isc_file_safecreate(const char * filename,FILE ** fp)586 isc_file_safecreate(const char *filename, FILE **fp) {
587 isc_result_t result;
588 int flags;
589 struct stat sb;
590 FILE *f;
591 int fd;
592
593 REQUIRE(filename != NULL);
594 REQUIRE(fp != NULL && *fp == NULL);
595
596 result = file_stats(filename, &sb);
597 if (result == ISC_R_SUCCESS) {
598 if ((sb.st_mode & S_IFREG) == 0)
599 return (ISC_R_INVALIDFILE);
600 flags = O_WRONLY | O_TRUNC;
601 } else if (result == ISC_R_FILENOTFOUND) {
602 flags = O_WRONLY | O_CREAT | O_EXCL;
603 } else
604 return (result);
605
606 fd = open(filename, flags, S_IRUSR | S_IWUSR);
607 if (fd == -1)
608 return (isc__errno2result(errno));
609
610 f = fdopen(fd, "w");
611 if (f == NULL) {
612 result = isc__errno2result(errno);
613 close(fd);
614 return (result);
615 }
616
617 *fp = f;
618 return (ISC_R_SUCCESS);
619 }
620
621 isc_result_t
isc_file_splitpath(isc_mem_t * mctx,char * path,char ** dirname,char ** basename)622 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
623 {
624 char *dir, *file, *slash;
625
626 if (path == NULL)
627 return (ISC_R_INVALIDFILE);
628
629 slash = strrchr(path, '/');
630
631 if (slash == path) {
632 file = ++slash;
633 dir = isc_mem_strdup(mctx, "/");
634 } else if (slash != NULL) {
635 file = ++slash;
636 dir = isc_mem_allocate(mctx, slash - path);
637 if (dir != NULL)
638 strlcpy(dir, path, slash - path);
639 } else {
640 file = path;
641 dir = isc_mem_strdup(mctx, ".");
642 }
643
644 if (dir == NULL)
645 return (ISC_R_NOMEMORY);
646
647 if (*file == '\0') {
648 isc_mem_free(mctx, dir);
649 return (ISC_R_INVALIDFILE);
650 }
651
652 *dirname = dir;
653 *basename = file;
654
655 return (ISC_R_SUCCESS);
656 }
657
658 void *
isc_file_mmap(void * addr,size_t len,int prot,int flags,int fd,off_t offset)659 isc_file_mmap(void *addr, size_t len, int prot,
660 int flags, int fd, off_t offset)
661 {
662 #ifdef HAVE_MMAP
663 return (mmap(addr, len, prot, flags, fd, offset));
664 #else
665 void *buf;
666 ssize_t ret;
667 off_t end;
668
669 UNUSED(addr);
670 UNUSED(prot);
671 UNUSED(flags);
672
673 end = lseek(fd, 0, SEEK_END);
674 lseek(fd, offset, SEEK_SET);
675 if (end - offset < (off_t) len)
676 len = end - offset;
677
678 buf = malloc(len);
679 ret = read(fd, buf, len);
680 if (ret != (ssize_t) len) {
681 free(buf);
682 buf = NULL;
683 }
684
685 return (buf);
686 #endif
687 }
688
689 int
isc_file_munmap(void * addr,size_t len)690 isc_file_munmap(void *addr, size_t len) {
691 #ifdef HAVE_MMAP
692 return (munmap(addr, len));
693 #else
694 UNUSED(len);
695
696 free(addr);
697 return (0);
698 #endif
699 }
700