Merge from vendor branch SENDMAIL:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / unix / file.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  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 /* $Id: file.c,v 1.38.2.1 2004/03/09 06:12:09 marka Exp $ */
19
20 #include <config.h>
21
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <time.h>               /* Required for utimes on some platforms. */
26 #include <unistd.h>             /* Required for mkstemp on NetBSD. */
27
28 #include <sys/stat.h>
29 #include <sys/time.h>
30
31 #include <isc/dir.h>
32 #include <isc/file.h>
33 #include <isc/string.h>
34 #include <isc/time.h>
35 #include <isc/util.h>
36
37 #include "errno2result.h"
38
39 /*
40  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
41  * it might be good to provide a mechanism that allows for the results
42  * of a previous stat() to be used again without having to do another stat,
43  * such as perl's mechanism of using "_" in place of a file name to indicate
44  * that the results of the last stat should be used.  But then you get into
45  * annoying MP issues.   BTW, Win32 has stat().
46  */
47 static isc_result_t
48 file_stats(const char *file, struct stat *stats) {
49         isc_result_t result = ISC_R_SUCCESS;
50
51         REQUIRE(file != NULL);
52         REQUIRE(stats != NULL);
53
54         if (stat(file, stats) != 0)
55                 result = isc__errno2result(errno);
56
57         return (result);
58 }
59
60 isc_result_t
61 isc_file_getmodtime(const char *file, isc_time_t *time) {
62         isc_result_t result;
63         struct stat stats;
64
65         REQUIRE(file != NULL);
66         REQUIRE(time != NULL);
67
68         result = file_stats(file, &stats);
69
70         if (result == ISC_R_SUCCESS)
71                 /*
72                  * XXXDCL some operating systems provide nanoseconds, too,
73                  * such as BSD/OS via st_mtimespec.
74                  */
75                 isc_time_set(time, stats.st_mtime, 0);
76
77         return (result);
78 }
79
80 isc_result_t
81 isc_file_settime(const char *file, isc_time_t *time) {
82         struct timeval times[2];
83
84         REQUIRE(file != NULL && time != NULL);
85
86         /*
87          * tv_sec is at least a 32 bit quantity on all platforms we're
88          * dealing with, but it is signed on most (all?) of them,
89          * so we need to make sure the high bit isn't set.  This unfortunately
90          * loses when either:
91          *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
92          *      and isc_time_seconds > LONG_MAX, or
93          *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
94          *      and isc_time_seconds has at least 33 significant bits.
95          */
96         times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
97
98         /*
99          * Here is the real check for the high bit being set.
100          */
101         if ((times[0].tv_sec &
102              (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
103                 return (ISC_R_RANGE);
104
105         /*
106          * isc_time_nanoseconds guarantees a value that divided by 1000 will
107          * fit into the minimum possible size tv_usec field.  Unfortunately,
108          * we don't know what that type is so can't cast directly ... but
109          * we can at least cast to signed so the IRIX compiler shuts up.
110          */
111         times[0].tv_usec = times[1].tv_usec =
112                 (isc_int32_t)(isc_time_nanoseconds(time) / 1000);
113
114         if (utimes(file, times) < 0)
115                 return (isc__errno2result(errno));
116
117         return (ISC_R_SUCCESS);
118
119 }
120
121 #undef TEMPLATE
122 #define TEMPLATE "tmp-XXXXXXXXXX" /* 14 characters. */
123
124 isc_result_t
125 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
126         return (isc_file_template(path, TEMPLATE, buf, buflen));
127 }
128
129 isc_result_t
130 isc_file_template(const char *path, const char *templet, char *buf,
131                         size_t buflen) {
132         char *s;
133
134         REQUIRE(path != NULL);
135         REQUIRE(templet != NULL);
136         REQUIRE(buf != NULL);
137
138         s = strrchr(templet, '/');
139         if (s != NULL)
140                 templet = s + 1;
141
142         s = strrchr(path, '/');
143
144         if (s != NULL) {
145                 if ((s - path + 1 + strlen(templet) + 1) > buflen)
146                         return (ISC_R_NOSPACE);
147
148                 strncpy(buf, path, s - path + 1);
149                 buf[s - path + 1] = '\0';
150                 strcat(buf, templet);
151         } else {
152                 if ((strlen(templet) + 1) > buflen)
153                         return (ISC_R_NOSPACE);
154
155                 strcpy(buf, templet);
156         }
157
158         return (ISC_R_SUCCESS);
159 }
160
161 isc_result_t
162 isc_file_renameunique(const char *file, char *templet) {
163         int fd = -1;
164         int res = 0;
165         isc_result_t result = ISC_R_SUCCESS;
166
167         REQUIRE(file != NULL);
168         REQUIRE(templet != NULL);
169
170         fd = mkstemp(templet);
171         if (fd == -1) {
172                 result = isc__errno2result(errno);
173         }
174         if (result == ISC_R_SUCCESS) {
175                 res = rename(file, templet);
176                 if (res != 0) {
177                         result = isc__errno2result(errno);
178                         (void)unlink(templet);
179                 }
180         }
181         if (fd != -1)
182                 close(fd);
183         return (result);
184 }
185
186 isc_result_t
187 isc_file_openunique(char *templet, FILE **fp) {
188         int fd;
189         FILE *f;
190         isc_result_t result = ISC_R_SUCCESS;
191
192         REQUIRE(templet != NULL);
193         REQUIRE(fp != NULL && *fp == NULL);
194
195         /*
196          * Win32 does not have mkstemp.
197          */
198         fd = mkstemp(templet);
199
200         if (fd == -1)
201                 result = isc__errno2result(errno);
202         if (result == ISC_R_SUCCESS) {
203                 f = fdopen(fd, "w+");
204                 if (f == NULL) {
205                         result = isc__errno2result(errno);
206                         (void)remove(templet);
207                         (void)close(fd);
208
209                 } else
210                         *fp = f;
211         }
212
213         return (result);
214 }
215
216 isc_result_t
217 isc_file_remove(const char *filename) {
218         int r;
219
220         REQUIRE(filename != NULL);
221
222         r = unlink(filename);
223         if (r == 0)
224                 return (ISC_R_SUCCESS);
225         else
226                 return (isc__errno2result(errno));
227 }
228
229 isc_result_t
230 isc_file_rename(const char *oldname, const char *newname) {
231         int r;
232
233         REQUIRE(oldname != NULL);
234         REQUIRE(newname != NULL);
235
236         r = rename(oldname, newname);
237         if (r == 0)
238                 return (ISC_R_SUCCESS);
239         else
240                 return (isc__errno2result(errno));
241 }
242
243 isc_boolean_t
244 isc_file_exists(const char *pathname) {
245         struct stat stats;
246
247         REQUIRE(pathname != NULL);
248
249         return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
250 }
251
252 isc_boolean_t
253 isc_file_isabsolute(const char *filename) {
254         REQUIRE(filename != NULL);
255         return (ISC_TF(filename[0] == '/'));
256 }
257
258 isc_boolean_t
259 isc_file_iscurrentdir(const char *filename) {
260         REQUIRE(filename != NULL);
261         return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
262 }
263
264 isc_boolean_t
265 isc_file_ischdiridempotent(const char *filename) {
266         REQUIRE(filename != NULL);
267         if (isc_file_isabsolute(filename))
268                 return (ISC_TRUE);
269         if (isc_file_iscurrentdir(filename))
270                 return (ISC_TRUE);
271         return (ISC_FALSE);
272 }
273
274 const char *
275 isc_file_basename(const char *filename) {
276         char *s;
277
278         REQUIRE(filename != NULL);
279
280         s = strrchr(filename, '/');
281         if (s == NULL)
282                 return (filename);
283
284         return (s + 1);
285 }
286
287 isc_result_t
288 isc_file_progname(const char *filename, char *buf, size_t buflen) {
289         const char *base;
290         size_t len;
291
292         REQUIRE(filename != NULL);
293         REQUIRE(buf != NULL);
294
295         base = isc_file_basename(filename);
296         len = strlen(base) + 1;
297
298         if (len > buflen)
299                 return (ISC_R_NOSPACE);
300         memcpy(buf, base, len);
301
302         return (ISC_R_SUCCESS);
303 }
304
305 isc_result_t
306 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
307         isc_result_t result;
308         result = isc_dir_current(path, pathlen, ISC_TRUE);
309         if (result != ISC_R_SUCCESS)
310                 return (result);
311         if (strlen(path) + strlen(filename) + 1 > pathlen)
312                 return (ISC_R_NOSPACE);
313         strcat(path, filename);
314         return (ISC_R_SUCCESS);
315 }