]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/isc/unix/file.c
MFC r363988:
[FreeBSD/stable/9.git] / contrib / bind9 / lib / isc / unix / file.c
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2009, 2011-2015  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 *modtime) {
139         isc_result_t result;
140         struct stat stats;
141
142         REQUIRE(file != NULL);
143         REQUIRE(modtime != 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(modtime, stats.st_mtime, 0);
153
154         return (result);
155 }
156
157 isc_result_t
158 isc_file_settime(const char *file, isc_time_t *when) {
159         struct timeval times[2];
160
161         REQUIRE(file != NULL && when != 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(when);
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(when) / 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 {
209         const char *s;
210
211         REQUIRE(path != NULL);
212         REQUIRE(templet != NULL);
213         REQUIRE(buf != NULL);
214
215         s = strrchr(templet, '/');
216         if (s != NULL)
217                 templet = s + 1;
218
219         s = strrchr(path, '/');
220
221         if (s != NULL) {
222                 if ((s - path + 1 + strlen(templet) + 1) > buflen)
223                         return (ISC_R_NOSPACE);
224
225                 strncpy(buf, path, s - path + 1);
226                 buf[s - path + 1] = '\0';
227                 strcat(buf, templet);
228         } else {
229                 if ((strlen(templet) + 1) > buflen)
230                         return (ISC_R_NOSPACE);
231
232                 strcpy(buf, templet);
233         }
234
235         return (ISC_R_SUCCESS);
236 }
237
238 static char alphnum[] =
239         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
240
241 isc_result_t
242 isc_file_renameunique(const char *file, char *templet) {
243         char *x;
244         char *cp;
245         isc_uint32_t which;
246
247         REQUIRE(file != NULL);
248         REQUIRE(templet != NULL);
249
250         cp = templet;
251         while (*cp != '\0')
252                 cp++;
253         if (cp == templet)
254                 return (ISC_R_FAILURE);
255
256         x = cp--;
257         while (cp >= templet && *cp == 'X') {
258                 isc_random_get(&which);
259                 *cp = alphnum[which % (sizeof(alphnum) - 1)];
260                 x = cp--;
261         }
262         while (link(file, templet) == -1) {
263                 if (errno != EEXIST)
264                         return (isc__errno2result(errno));
265                 for (cp = x;;) {
266                         const char *t;
267                         if (*cp == '\0')
268                                 return (ISC_R_FAILURE);
269                         t = strchr(alphnum, *cp);
270                         if (t == NULL || *++t == '\0')
271                                 *cp++ = alphnum[0];
272                         else {
273                                 *cp = *t;
274                                 break;
275                         }
276                 }
277         }
278         if (unlink(file) < 0)
279                 if (errno != ENOENT)
280                         return (isc__errno2result(errno));
281         return (ISC_R_SUCCESS);
282 }
283
284 isc_result_t
285 isc_file_openunique(char *templet, FILE **fp) {
286         int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
287         return (isc_file_openuniquemode(templet, mode, fp));
288 }
289
290 isc_result_t
291 isc_file_openuniqueprivate(char *templet, FILE **fp) {
292         int mode = S_IWUSR|S_IRUSR;
293         return (isc_file_openuniquemode(templet, mode, fp));
294 }
295
296 isc_result_t
297 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
298         int fd;
299         FILE *f;
300         isc_result_t result = ISC_R_SUCCESS;
301         char *x;
302         char *cp;
303         isc_uint32_t which;
304
305         REQUIRE(templet != NULL);
306         REQUIRE(fp != NULL && *fp == NULL);
307
308         cp = templet;
309         while (*cp != '\0')
310                 cp++;
311         if (cp == templet)
312                 return (ISC_R_FAILURE);
313
314         x = cp--;
315         while (cp >= templet && *cp == 'X') {
316                 isc_random_get(&which);
317                 *cp = alphnum[which % (sizeof(alphnum) - 1)];
318                 x = cp--;
319         }
320
321
322         while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
323                 if (errno != EEXIST)
324                         return (isc__errno2result(errno));
325                 for (cp = x;;) {
326                         char *t;
327                         if (*cp == '\0')
328                                 return (ISC_R_FAILURE);
329                         t = strchr(alphnum, *cp);
330                         if (t == NULL || *++t == '\0')
331                                 *cp++ = alphnum[0];
332                         else {
333                                 *cp = *t;
334                                 break;
335                         }
336                 }
337         }
338         f = fdopen(fd, "w+");
339         if (f == NULL) {
340                 result = isc__errno2result(errno);
341                 if (remove(templet) < 0) {
342                         isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
343                                       ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
344                                       "remove '%s': failed", templet);
345                 }
346                 (void)close(fd);
347         } else
348                 *fp = f;
349
350         return (result);
351 }
352
353 isc_result_t
354 isc_file_bopenunique(char *templet, FILE **fp) {
355         int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
356         return (isc_file_openuniquemode(templet, mode, fp));
357 }
358
359 isc_result_t
360 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
361         int mode = S_IWUSR|S_IRUSR;
362         return (isc_file_openuniquemode(templet, mode, fp));
363 }
364
365 isc_result_t
366 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
367         return (isc_file_openuniquemode(templet, mode, fp));
368 }
369
370 isc_result_t
371 isc_file_remove(const char *filename) {
372         int r;
373
374         REQUIRE(filename != NULL);
375
376         r = unlink(filename);
377         if (r == 0)
378                 return (ISC_R_SUCCESS);
379         else
380                 return (isc__errno2result(errno));
381 }
382
383 isc_result_t
384 isc_file_rename(const char *oldname, const char *newname) {
385         int r;
386
387         REQUIRE(oldname != NULL);
388         REQUIRE(newname != NULL);
389
390         r = rename(oldname, newname);
391         if (r == 0)
392                 return (ISC_R_SUCCESS);
393         else
394                 return (isc__errno2result(errno));
395 }
396
397 isc_boolean_t
398 isc_file_exists(const char *pathname) {
399         struct stat stats;
400
401         REQUIRE(pathname != NULL);
402
403         return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
404 }
405
406 isc_result_t
407 isc_file_isplainfile(const char *filename) {
408         /*
409          * This function returns success if filename is a plain file.
410          */
411         struct stat filestat;
412         memset(&filestat,0,sizeof(struct stat));
413
414         if ((stat(filename, &filestat)) == -1)
415                 return(isc__errno2result(errno));
416
417         if(! S_ISREG(filestat.st_mode))
418                 return(ISC_R_INVALIDFILE);
419
420         return(ISC_R_SUCCESS);
421 }
422
423 isc_result_t
424 isc_file_isdirectory(const char *filename) {
425         /*
426          * This function returns success if filename exists and is a
427          * directory.
428          */
429         struct stat filestat;
430         memset(&filestat,0,sizeof(struct stat));
431
432         if ((stat(filename, &filestat)) == -1)
433                 return(isc__errno2result(errno));
434
435         if(! S_ISDIR(filestat.st_mode))
436                 return(ISC_R_INVALIDFILE);
437
438         return(ISC_R_SUCCESS);
439 }
440
441 isc_boolean_t
442 isc_file_isabsolute(const char *filename) {
443         REQUIRE(filename != NULL);
444         return (ISC_TF(filename[0] == '/'));
445 }
446
447 isc_boolean_t
448 isc_file_iscurrentdir(const char *filename) {
449         REQUIRE(filename != NULL);
450         return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
451 }
452
453 isc_boolean_t
454 isc_file_ischdiridempotent(const char *filename) {
455         REQUIRE(filename != NULL);
456         if (isc_file_isabsolute(filename))
457                 return (ISC_TRUE);
458         if (isc_file_iscurrentdir(filename))
459                 return (ISC_TRUE);
460         return (ISC_FALSE);
461 }
462
463 const char *
464 isc_file_basename(const char *filename) {
465         const char *s;
466
467         REQUIRE(filename != NULL);
468
469         s = strrchr(filename, '/');
470         if (s == NULL)
471                 return (filename);
472
473         return (s + 1);
474 }
475
476 isc_result_t
477 isc_file_progname(const char *filename, char *buf, size_t buflen) {
478         const char *base;
479         size_t len;
480
481         REQUIRE(filename != NULL);
482         REQUIRE(buf != NULL);
483
484         base = isc_file_basename(filename);
485         len = strlen(base) + 1;
486
487         if (len > buflen)
488                 return (ISC_R_NOSPACE);
489         memmove(buf, base, len);
490
491         return (ISC_R_SUCCESS);
492 }
493
494 /*
495  * Put the absolute name of the current directory into 'dirname', which is
496  * a buffer of at least 'length' characters.  End the string with the
497  * appropriate path separator, such that the final product could be
498  * concatenated with a relative pathname to make a valid pathname string.
499  */
500 static isc_result_t
501 dir_current(char *dirname, size_t length) {
502         char *cwd;
503         isc_result_t result = ISC_R_SUCCESS;
504
505         REQUIRE(dirname != NULL);
506         REQUIRE(length > 0U);
507
508         cwd = getcwd(dirname, length);
509
510         if (cwd == NULL) {
511                 if (errno == ERANGE)
512                         result = ISC_R_NOSPACE;
513                 else
514                         result = isc__errno2result(errno);
515         } else {
516                 if (strlen(dirname) + 1 == length)
517                         result = ISC_R_NOSPACE;
518                 else if (dirname[1] != '\0')
519                         strcat(dirname, "/");
520         }
521
522         return (result);
523 }
524
525 isc_result_t
526 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
527         isc_result_t result;
528         result = dir_current(path, pathlen);
529         if (result != ISC_R_SUCCESS)
530                 return (result);
531         if (strlen(path) + strlen(filename) + 1 > pathlen)
532                 return (ISC_R_NOSPACE);
533         strcat(path, filename);
534         return (ISC_R_SUCCESS);
535 }
536
537 isc_result_t
538 isc_file_truncate(const char *filename, isc_offset_t size) {
539         isc_result_t result = ISC_R_SUCCESS;
540
541         if (truncate(filename, size) < 0)
542                 result = isc__errno2result(errno);
543         return (result);
544 }
545
546 isc_result_t
547 isc_file_safecreate(const char *filename, FILE **fp) {
548         isc_result_t result;
549         int flags;
550         struct stat sb;
551         FILE *f;
552         int fd;
553
554         REQUIRE(filename != NULL);
555         REQUIRE(fp != NULL && *fp == NULL);
556
557         result = file_stats(filename, &sb);
558         if (result == ISC_R_SUCCESS) {
559                 if ((sb.st_mode & S_IFREG) == 0)
560                         return (ISC_R_INVALIDFILE);
561                 flags = O_WRONLY | O_TRUNC;
562         } else if (result == ISC_R_FILENOTFOUND) {
563                 flags = O_WRONLY | O_CREAT | O_EXCL;
564         } else
565                 return (result);
566
567         fd = open(filename, flags, S_IRUSR | S_IWUSR);
568         if (fd == -1)
569                 return (isc__errno2result(errno));
570
571         f = fdopen(fd, "w");
572         if (f == NULL) {
573                 result = isc__errno2result(errno);
574                 close(fd);
575                 return (result);
576         }
577
578         *fp = f;
579         return (ISC_R_SUCCESS);
580 }
581
582 isc_result_t
583 isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
584                    char const **basename)
585 {
586         char *dir;
587         const char *file, *slash;
588
589         if (path == NULL)
590                 return (ISC_R_INVALIDFILE);
591
592         slash = strrchr(path, '/');
593
594         if (slash == path) {
595                 file = ++slash;
596                 dir = isc_mem_strdup(mctx, "/");
597         } else if (slash != NULL) {
598                 file = ++slash;
599                 dir = isc_mem_allocate(mctx, slash - path);
600                 if (dir != NULL)
601                         strlcpy(dir, path, slash - path);
602         } else {
603                 file = path;
604                 dir = isc_mem_strdup(mctx, ".");
605         }
606
607         if (dir == NULL)
608                 return (ISC_R_NOMEMORY);
609
610         if (*file == '\0') {
611                 isc_mem_free(mctx, dir);
612                 return (ISC_R_INVALIDFILE);
613         }
614
615         *dirname = dir;
616         *basename = file;
617
618         return (ISC_R_SUCCESS);
619 }