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