Merge branch 'vendor/BINUTILS220' into bu220
[dragonfly.git] / contrib / bind-9.3 / lib / isc / unix / file.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2002  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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.38.12.8 2004/03/16 05:50:25 marka Exp $ */
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 #include <isc/dir.h>
67 #include <isc/file.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 isc_result_t
97 isc_file_getmodtime(const char *file, isc_time_t *time) {
98         isc_result_t result;
99         struct stat stats;
100
101         REQUIRE(file != NULL);
102         REQUIRE(time != NULL);
103
104         result = file_stats(file, &stats);
105
106         if (result == ISC_R_SUCCESS)
107                 /*
108                  * XXXDCL some operating systems provide nanoseconds, too,
109                  * such as BSD/OS via st_mtimespec.
110                  */
111                 isc_time_set(time, stats.st_mtime, 0);
112
113         return (result);
114 }
115
116 isc_result_t
117 isc_file_settime(const char *file, isc_time_t *time) {
118         struct timeval times[2];
119
120         REQUIRE(file != NULL && time != NULL);
121
122         /*
123          * tv_sec is at least a 32 bit quantity on all platforms we're
124          * dealing with, but it is signed on most (all?) of them,
125          * so we need to make sure the high bit isn't set.  This unfortunately
126          * loses when either:
127          *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
128          *      and isc_time_seconds > LONG_MAX, or
129          *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
130          *      and isc_time_seconds has at least 33 significant bits.
131          */
132         times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
133
134         /*
135          * Here is the real check for the high bit being set.
136          */
137         if ((times[0].tv_sec &
138              (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
139                 return (ISC_R_RANGE);
140
141         /*
142          * isc_time_nanoseconds guarantees a value that divided by 1000 will
143          * fit into the minimum possible size tv_usec field.  Unfortunately,
144          * we don't know what that type is so can't cast directly ... but
145          * we can at least cast to signed so the IRIX compiler shuts up.
146          */
147         times[0].tv_usec = times[1].tv_usec =
148                 (isc_int32_t)(isc_time_nanoseconds(time) / 1000);
149
150         if (utimes(file, times) < 0)
151                 return (isc__errno2result(errno));
152
153         return (ISC_R_SUCCESS);
154 }
155
156 #undef TEMPLATE
157 #define TEMPLATE "tmp-XXXXXXXXXX" /* 14 characters. */
158
159 isc_result_t
160 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
161         return (isc_file_template(path, TEMPLATE, buf, buflen));
162 }
163
164 isc_result_t
165 isc_file_template(const char *path, const char *templet, char *buf,
166                         size_t buflen) {
167         char *s;
168
169         REQUIRE(path != NULL);
170         REQUIRE(templet != NULL);
171         REQUIRE(buf != NULL);
172
173         s = strrchr(templet, '/');
174         if (s != NULL)
175                 templet = s + 1;
176
177         s = strrchr(path, '/');
178
179         if (s != NULL) {
180                 if ((s - path + 1 + strlen(templet) + 1) > buflen)
181                         return (ISC_R_NOSPACE);
182
183                 strncpy(buf, path, s - path + 1);
184                 buf[s - path + 1] = '\0';
185                 strcat(buf, templet);
186         } else {
187                 if ((strlen(templet) + 1) > buflen)
188                         return (ISC_R_NOSPACE);
189
190                 strcpy(buf, templet);
191         }
192
193         return (ISC_R_SUCCESS);
194 }
195
196 static char alphnum[] =
197         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
198
199 isc_result_t
200 isc_file_renameunique(const char *file, char *templet) {
201         char *x;
202         char *cp;
203         isc_uint32_t which;
204
205         REQUIRE(file != NULL);
206         REQUIRE(templet != NULL);
207
208         cp = templet;
209         while (*cp != '\0')
210                 cp++;
211         if (cp == templet)
212                 return (ISC_R_FAILURE);
213
214         x = cp--;
215         while (cp >= templet && *cp == 'X') {
216                 isc_random_get(&which);
217                 *cp = alphnum[which % (sizeof(alphnum) - 1)];
218                 x = cp--;
219         }
220         while (link(file, templet) == -1) {
221                 if (errno != EEXIST)
222                         return (isc__errno2result(errno));
223                 for (cp = x;;) {
224                         char *t;
225                         if (*cp == '\0')
226                                 return (ISC_R_FAILURE);
227                         t = strchr(alphnum, *cp);
228                         if (t == NULL || *++t == '\0')
229                                 *cp++ = alphnum[0];
230                         else {
231                                 *cp = *t;
232                                 break;
233                         }
234                 }
235         }
236         (void)unlink(file);
237         return (ISC_R_SUCCESS);
238 }
239
240
241 isc_result_t
242 isc_file_openunique(char *templet, FILE **fp) {
243         int fd;
244         FILE *f;
245         isc_result_t result = ISC_R_SUCCESS;
246         char *x;
247         char *cp;
248         isc_uint32_t which;
249         int mode;
250
251         REQUIRE(templet != NULL);
252         REQUIRE(fp != NULL && *fp == NULL);
253
254         cp = templet;
255         while (*cp != '\0')
256                 cp++;
257         if (cp == templet)
258                 return (ISC_R_FAILURE);
259
260         x = cp--;
261         while (cp >= templet && *cp == 'X') {
262                 isc_random_get(&which);
263                 *cp = alphnum[which % (sizeof(alphnum) - 1)];
264                 x = cp--;
265         }
266
267         mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
268
269         while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
270                 if (errno != EEXIST)
271                         return (isc__errno2result(errno));
272                 for (cp = x;;) {
273                         char *t;
274                         if (*cp == '\0')
275                                 return (ISC_R_FAILURE);
276                         t = strchr(alphnum, *cp);
277                         if (t == NULL || *++t == '\0')
278                                 *cp++ = alphnum[0];
279                         else {
280                                 *cp = *t;
281                                 break;
282                         }
283                 }
284         }
285         f = fdopen(fd, "w+");
286         if (f == NULL) {
287                 result = isc__errno2result(errno);
288                 (void)remove(templet);
289                 (void)close(fd);
290         } else
291                 *fp = f;
292
293         return (result);
294 }
295
296 isc_result_t
297 isc_file_remove(const char *filename) {
298         int r;
299
300         REQUIRE(filename != NULL);
301
302         r = unlink(filename);
303         if (r == 0)
304                 return (ISC_R_SUCCESS);
305         else
306                 return (isc__errno2result(errno));
307 }
308
309 isc_result_t
310 isc_file_rename(const char *oldname, const char *newname) {
311         int r;
312
313         REQUIRE(oldname != NULL);
314         REQUIRE(newname != NULL);
315
316         r = rename(oldname, newname);
317         if (r == 0)
318                 return (ISC_R_SUCCESS);
319         else
320                 return (isc__errno2result(errno));
321 }
322
323 isc_boolean_t
324 isc_file_exists(const char *pathname) {
325         struct stat stats;
326
327         REQUIRE(pathname != NULL);
328
329         return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
330 }
331
332 isc_boolean_t
333 isc_file_isabsolute(const char *filename) {
334         REQUIRE(filename != NULL);
335         return (ISC_TF(filename[0] == '/'));
336 }
337
338 isc_boolean_t
339 isc_file_iscurrentdir(const char *filename) {
340         REQUIRE(filename != NULL);
341         return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
342 }
343
344 isc_boolean_t
345 isc_file_ischdiridempotent(const char *filename) {
346         REQUIRE(filename != NULL);
347         if (isc_file_isabsolute(filename))
348                 return (ISC_TRUE);
349         if (isc_file_iscurrentdir(filename))
350                 return (ISC_TRUE);
351         return (ISC_FALSE);
352 }
353
354 const char *
355 isc_file_basename(const char *filename) {
356         char *s;
357
358         REQUIRE(filename != NULL);
359
360         s = strrchr(filename, '/');
361         if (s == NULL)
362                 return (filename);
363
364         return (s + 1);
365 }
366
367 isc_result_t
368 isc_file_progname(const char *filename, char *buf, size_t buflen) {
369         const char *base;
370         size_t len;
371
372         REQUIRE(filename != NULL);
373         REQUIRE(buf != NULL);
374
375         base = isc_file_basename(filename);
376         len = strlen(base) + 1;
377
378         if (len > buflen)
379                 return (ISC_R_NOSPACE);
380         memcpy(buf, base, len);
381
382         return (ISC_R_SUCCESS);
383 }
384
385 /*
386  * Put the absolute name of the current directory into 'dirname', which is
387  * a buffer of at least 'length' characters.  End the string with the 
388  * appropriate path separator, such that the final product could be
389  * concatenated with a relative pathname to make a valid pathname string.
390  */
391 static isc_result_t
392 dir_current(char *dirname, size_t length) {
393         char *cwd;
394         isc_result_t result = ISC_R_SUCCESS;
395
396         REQUIRE(dirname != NULL);
397         REQUIRE(length > 0U);
398
399         cwd = getcwd(dirname, length);
400
401         if (cwd == NULL) {
402                 if (errno == ERANGE)
403                         result = ISC_R_NOSPACE;
404                 else
405                         result = isc__errno2result(errno);
406         } else {
407                 if (strlen(dirname) + 1 == length)
408                         result = ISC_R_NOSPACE;
409                 else if (dirname[1] != '\0')
410                         strcat(dirname, "/");
411         }
412
413         return (result);
414 }
415
416 isc_result_t
417 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
418         isc_result_t result;
419         result = dir_current(path, pathlen);
420         if (result != ISC_R_SUCCESS)
421                 return (result);
422         if (strlen(path) + strlen(filename) + 1 > pathlen)
423                 return (ISC_R_NOSPACE);
424         strcat(path, filename);
425         return (ISC_R_SUCCESS);
426 }
427
428 isc_result_t
429 isc_file_truncate(const char *filename, isc_offset_t size) {
430         isc_result_t result = ISC_R_SUCCESS;
431
432         if (truncate(filename, size) < 0) 
433                 result = isc__errno2result(errno);
434         return (result);
435 }