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