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