]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/isc/unix/file.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / lib / isc / unix / file.c
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012  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$ */
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_mode(const char *file, mode_t *modep) {
102         isc_result_t result;
103         struct stat stats;
104
105         REQUIRE(modep != NULL);
106
107         result = file_stats(file, &stats);
108         if (result == ISC_R_SUCCESS)
109                 *modep = (stats.st_mode & 07777);
110
111         return (result);
112 }
113
114 isc_result_t
115 isc_file_getmodtime(const char *file, isc_time_t *time) {
116         isc_result_t result;
117         struct stat stats;
118
119         REQUIRE(file != NULL);
120         REQUIRE(time != NULL);
121
122         result = file_stats(file, &stats);
123
124         if (result == ISC_R_SUCCESS)
125                 /*
126                  * XXXDCL some operating systems provide nanoseconds, too,
127                  * such as BSD/OS via st_mtimespec.
128                  */
129                 isc_time_set(time, stats.st_mtime, 0);
130
131         return (result);
132 }
133
134 isc_result_t
135 isc_file_settime(const char *file, isc_time_t *time) {
136         struct timeval times[2];
137
138         REQUIRE(file != NULL && time != NULL);
139
140         /*
141          * tv_sec is at least a 32 bit quantity on all platforms we're
142          * dealing with, but it is signed on most (all?) of them,
143          * so we need to make sure the high bit isn't set.  This unfortunately
144          * loses when either:
145          *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
146          *      and isc_time_seconds > LONG_MAX, or
147          *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
148          *      and isc_time_seconds has at least 33 significant bits.
149          */
150         times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
151
152         /*
153          * Here is the real check for the high bit being set.
154          */
155         if ((times[0].tv_sec &
156              (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
157                 return (ISC_R_RANGE);
158
159         /*
160          * isc_time_nanoseconds guarantees a value that divided by 1000 will
161          * fit into the minimum possible size tv_usec field.  Unfortunately,
162          * we don't know what that type is so can't cast directly ... but
163          * we can at least cast to signed so the IRIX compiler shuts up.
164          */
165         times[0].tv_usec = times[1].tv_usec =
166                 (isc_int32_t)(isc_time_nanoseconds(time) / 1000);
167
168         if (utimes(file, times) < 0)
169                 return (isc__errno2result(errno));
170
171         return (ISC_R_SUCCESS);
172 }
173
174 #undef TEMPLATE
175 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
176
177 isc_result_t
178 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
179         return (isc_file_template(path, TEMPLATE, buf, buflen));
180 }
181
182 isc_result_t
183 isc_file_template(const char *path, const char *templet, char *buf,
184                         size_t buflen) {
185         char *s;
186
187         REQUIRE(path != NULL);
188         REQUIRE(templet != NULL);
189         REQUIRE(buf != NULL);
190
191         s = strrchr(templet, '/');
192         if (s != NULL)
193                 templet = s + 1;
194
195         s = strrchr(path, '/');
196
197         if (s != NULL) {
198                 if ((s - path + 1 + strlen(templet) + 1) > buflen)
199                         return (ISC_R_NOSPACE);
200
201                 strncpy(buf, path, s - path + 1);
202                 buf[s - path + 1] = '\0';
203                 strcat(buf, templet);
204         } else {
205                 if ((strlen(templet) + 1) > buflen)
206                         return (ISC_R_NOSPACE);
207
208                 strcpy(buf, templet);
209         }
210
211         return (ISC_R_SUCCESS);
212 }
213
214 static char alphnum[] =
215         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
216
217 isc_result_t
218 isc_file_renameunique(const char *file, char *templet) {
219         char *x;
220         char *cp;
221         isc_uint32_t which;
222
223         REQUIRE(file != NULL);
224         REQUIRE(templet != NULL);
225
226         cp = templet;
227         while (*cp != '\0')
228                 cp++;
229         if (cp == templet)
230                 return (ISC_R_FAILURE);
231
232         x = cp--;
233         while (cp >= templet && *cp == 'X') {
234                 isc_random_get(&which);
235                 *cp = alphnum[which % (sizeof(alphnum) - 1)];
236                 x = cp--;
237         }
238         while (link(file, templet) == -1) {
239                 if (errno != EEXIST)
240                         return (isc__errno2result(errno));
241                 for (cp = x;;) {
242                         char *t;
243                         if (*cp == '\0')
244                                 return (ISC_R_FAILURE);
245                         t = strchr(alphnum, *cp);
246                         if (t == NULL || *++t == '\0')
247                                 *cp++ = alphnum[0];
248                         else {
249                                 *cp = *t;
250                                 break;
251                         }
252                 }
253         }
254         if (unlink(file) < 0)
255                 if (errno != ENOENT)
256                         return (isc__errno2result(errno));
257         return (ISC_R_SUCCESS);
258 }
259
260 isc_result_t
261 isc_file_openunique(char *templet, FILE **fp) {
262         int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
263         return (isc_file_openuniquemode(templet, mode, fp));
264 }
265
266 isc_result_t
267 isc_file_openuniqueprivate(char *templet, FILE **fp) {
268         int mode = S_IWUSR|S_IRUSR;
269         return (isc_file_openuniquemode(templet, mode, fp));
270 }
271
272 isc_result_t
273 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
274         int fd;
275         FILE *f;
276         isc_result_t result = ISC_R_SUCCESS;
277         char *x;
278         char *cp;
279         isc_uint32_t which;
280
281         REQUIRE(templet != NULL);
282         REQUIRE(fp != NULL && *fp == NULL);
283
284         cp = templet;
285         while (*cp != '\0')
286                 cp++;
287         if (cp == templet)
288                 return (ISC_R_FAILURE);
289
290         x = cp--;
291         while (cp >= templet && *cp == 'X') {
292                 isc_random_get(&which);
293                 *cp = alphnum[which % (sizeof(alphnum) - 1)];
294                 x = cp--;
295         }
296
297
298         while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
299                 if (errno != EEXIST)
300                         return (isc__errno2result(errno));
301                 for (cp = x;;) {
302                         char *t;
303                         if (*cp == '\0')
304                                 return (ISC_R_FAILURE);
305                         t = strchr(alphnum, *cp);
306                         if (t == NULL || *++t == '\0')
307                                 *cp++ = alphnum[0];
308                         else {
309                                 *cp = *t;
310                                 break;
311                         }
312                 }
313         }
314         f = fdopen(fd, "w+");
315         if (f == NULL) {
316                 result = isc__errno2result(errno);
317                 if (remove(templet) < 0) {
318                         isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
319                                       ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
320                                       "remove '%s': failed", templet);
321                 }
322                 (void)close(fd);
323         } else
324                 *fp = f;
325
326         return (result);
327 }
328
329 isc_result_t
330 isc_file_bopenunique(char *templet, FILE **fp) {
331         int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
332         return (isc_file_openuniquemode(templet, mode, fp));
333 }
334
335 isc_result_t
336 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
337         int mode = S_IWUSR|S_IRUSR;
338         return (isc_file_openuniquemode(templet, mode, fp));
339 }
340
341 isc_result_t
342 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
343         return (isc_file_openuniquemode(templet, mode, fp));
344 }
345
346 isc_result_t
347 isc_file_remove(const char *filename) {
348         int r;
349
350         REQUIRE(filename != NULL);
351
352         r = unlink(filename);
353         if (r == 0)
354                 return (ISC_R_SUCCESS);
355         else
356                 return (isc__errno2result(errno));
357 }
358
359 isc_result_t
360 isc_file_rename(const char *oldname, const char *newname) {
361         int r;
362
363         REQUIRE(oldname != NULL);
364         REQUIRE(newname != NULL);
365
366         r = rename(oldname, newname);
367         if (r == 0)
368                 return (ISC_R_SUCCESS);
369         else
370                 return (isc__errno2result(errno));
371 }
372
373 isc_boolean_t
374 isc_file_exists(const char *pathname) {
375         struct stat stats;
376
377         REQUIRE(pathname != NULL);
378
379         return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
380 }
381
382 isc_result_t
383 isc_file_isplainfile(const char *filename) {
384         /*
385          * This function returns success if filename is a plain file.
386          */
387         struct stat filestat;
388         memset(&filestat,0,sizeof(struct stat));
389
390         if ((stat(filename, &filestat)) == -1)
391                 return(isc__errno2result(errno));
392
393         if(! S_ISREG(filestat.st_mode))
394                 return(ISC_R_INVALIDFILE);
395
396         return(ISC_R_SUCCESS);
397 }
398
399 isc_boolean_t
400 isc_file_isabsolute(const char *filename) {
401         REQUIRE(filename != NULL);
402         return (ISC_TF(filename[0] == '/'));
403 }
404
405 isc_boolean_t
406 isc_file_iscurrentdir(const char *filename) {
407         REQUIRE(filename != NULL);
408         return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
409 }
410
411 isc_boolean_t
412 isc_file_ischdiridempotent(const char *filename) {
413         REQUIRE(filename != NULL);
414         if (isc_file_isabsolute(filename))
415                 return (ISC_TRUE);
416         if (isc_file_iscurrentdir(filename))
417                 return (ISC_TRUE);
418         return (ISC_FALSE);
419 }
420
421 const char *
422 isc_file_basename(const char *filename) {
423         char *s;
424
425         REQUIRE(filename != NULL);
426
427         s = strrchr(filename, '/');
428         if (s == NULL)
429                 return (filename);
430
431         return (s + 1);
432 }
433
434 isc_result_t
435 isc_file_progname(const char *filename, char *buf, size_t buflen) {
436         const char *base;
437         size_t len;
438
439         REQUIRE(filename != NULL);
440         REQUIRE(buf != NULL);
441
442         base = isc_file_basename(filename);
443         len = strlen(base) + 1;
444
445         if (len > buflen)
446                 return (ISC_R_NOSPACE);
447         memcpy(buf, base, len);
448
449         return (ISC_R_SUCCESS);
450 }
451
452 /*
453  * Put the absolute name of the current directory into 'dirname', which is
454  * a buffer of at least 'length' characters.  End the string with the
455  * appropriate path separator, such that the final product could be
456  * concatenated with a relative pathname to make a valid pathname string.
457  */
458 static isc_result_t
459 dir_current(char *dirname, size_t length) {
460         char *cwd;
461         isc_result_t result = ISC_R_SUCCESS;
462
463         REQUIRE(dirname != NULL);
464         REQUIRE(length > 0U);
465
466         cwd = getcwd(dirname, length);
467
468         if (cwd == NULL) {
469                 if (errno == ERANGE)
470                         result = ISC_R_NOSPACE;
471                 else
472                         result = isc__errno2result(errno);
473         } else {
474                 if (strlen(dirname) + 1 == length)
475                         result = ISC_R_NOSPACE;
476                 else if (dirname[1] != '\0')
477                         strcat(dirname, "/");
478         }
479
480         return (result);
481 }
482
483 isc_result_t
484 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
485         isc_result_t result;
486         result = dir_current(path, pathlen);
487         if (result != ISC_R_SUCCESS)
488                 return (result);
489         if (strlen(path) + strlen(filename) + 1 > pathlen)
490                 return (ISC_R_NOSPACE);
491         strcat(path, filename);
492         return (ISC_R_SUCCESS);
493 }
494
495 isc_result_t
496 isc_file_truncate(const char *filename, isc_offset_t size) {
497         isc_result_t result = ISC_R_SUCCESS;
498
499         if (truncate(filename, size) < 0)
500                 result = isc__errno2result(errno);
501         return (result);
502 }
503
504 isc_result_t
505 isc_file_safecreate(const char *filename, FILE **fp) {
506         isc_result_t result;
507         int flags;
508         struct stat sb;
509         FILE *f;
510         int fd;
511
512         REQUIRE(filename != NULL);
513         REQUIRE(fp != NULL && *fp == NULL);
514
515         result = file_stats(filename, &sb);
516         if (result == ISC_R_SUCCESS) {
517                 if ((sb.st_mode & S_IFREG) == 0)
518                         return (ISC_R_INVALIDFILE);
519                 flags = O_WRONLY | O_TRUNC;
520         } else if (result == ISC_R_FILENOTFOUND) {
521                 flags = O_WRONLY | O_CREAT | O_EXCL;
522         } else
523                 return (result);
524
525         fd = open(filename, flags, S_IRUSR | S_IWUSR);
526         if (fd == -1)
527                 return (isc__errno2result(errno));
528
529         f = fdopen(fd, "w");
530         if (f == NULL) {
531                 result = isc__errno2result(errno);
532                 close(fd);
533                 return (result);
534         }
535
536         *fp = f;
537         return (ISC_R_SUCCESS);
538 }
539
540 isc_result_t
541 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
542 {
543         char *dir, *file, *slash;
544
545         slash = strrchr(path, '/');
546
547         if (slash == path) {
548                 file = ++slash;
549                 dir = isc_mem_strdup(mctx, "/");
550         } else if (slash != NULL) {
551                 file = ++slash;
552                 dir = isc_mem_allocate(mctx, slash - path);
553                 if (dir != NULL)
554                         strlcpy(dir, path, slash - path);
555         } else {
556                 file = path;
557                 dir = isc_mem_strdup(mctx, ".");
558         }
559
560         if (dir == NULL)
561                 return (ISC_R_NOMEMORY);
562
563         if (*file == '\0') {
564                 isc_mem_free(mctx, dir);
565                 return (ISC_R_INVALIDFILE);
566         }
567
568         *dirname = dir;
569         *basename = file;
570
571         return (ISC_R_SUCCESS);
572 }