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