]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/lib/isc/win32/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 / win32 / file.c
1 /*
2  * Copyright (C) 2004, 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 /* $Id$ */
19
20 #include <config.h>
21
22 #undef rename
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <io.h>
27 #include <process.h>
28
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <sys/utime.h>
32
33 #include <isc/file.h>
34 #include <isc/mem.h>
35 #include <isc/result.h>
36 #include <isc/time.h>
37 #include <isc/util.h>
38 #include <isc/stat.h>
39 #include <isc/string.h>
40
41 #include "errno2result.h"
42
43 /*
44  * Emulate UNIX mkstemp, which returns an open FD to the new file
45  *
46  */
47 static int
48 gettemp(char *path, int *doopen) {
49         char *start, *trv;
50         struct stat sbuf;
51         int pid;
52
53         trv = strrchr(path, 'X');
54         trv++;
55         pid = getpid();
56         /* extra X's get set to 0's */
57         while (*--trv == 'X') {
58                 *trv = (pid % 10) + '0';
59                 pid /= 10;
60         }
61         /*
62          * check the target directory; if you have six X's and it
63          * doesn't exist this runs for a *very* long time.
64          */
65         for (start = trv + 1;; --trv) {
66                 if (trv <= path)
67                         break;
68                 if (*trv == '\\') {
69                         *trv = '\0';
70                         if (stat(path, &sbuf))
71                                 return (0);
72                         if (!S_ISDIR(sbuf.st_mode)) {
73                                 errno = ENOTDIR;
74                                 return (0);
75                         }
76                         *trv = '\\';
77                         break;
78                 }
79         }
80
81         for (;;) {
82                 if (doopen) {
83                         if ((*doopen =
84                             open(path, O_CREAT|O_EXCL|O_RDWR,
85                                  _S_IREAD | _S_IWRITE)) >= 0)
86                                 return (1);
87                         if (errno != EEXIST)
88                                 return (0);
89                 } else if (stat(path, &sbuf))
90                         return (errno == ENOENT ? 1 : 0);
91
92                 /* tricky little algorithm for backward compatibility */
93                 for (trv = start;;) {
94                         if (!*trv)
95                                 return (0);
96                         if (*trv == 'z')
97                                 *trv++ = 'a';
98                         else {
99                                 if (isdigit(*trv))
100                                         *trv = 'a';
101                                 else
102                                         ++*trv;
103                                 break;
104                         }
105                 }
106         }
107         /*NOTREACHED*/
108 }
109
110 static int
111 mkstemp(char *path) {
112         int fd;
113
114         return (gettemp(path, &fd) ? fd : -1);
115 }
116
117 /*
118  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
119  * it might be good to provide a mechanism that allows for the results
120  * of a previous stat() to be used again without having to do another stat,
121  * such as perl's mechanism of using "_" in place of a file name to indicate
122  * that the results of the last stat should be used.  But then you get into
123  * annoying MP issues.   BTW, Win32 has stat().
124  */
125 static isc_result_t
126 file_stats(const char *file, struct stat *stats) {
127         isc_result_t result = ISC_R_SUCCESS;
128
129         REQUIRE(file != NULL);
130         REQUIRE(stats != NULL);
131
132         if (stat(file, stats) != 0)
133                 result = isc__errno2result(errno);
134
135         return (result);
136 }
137
138 /*
139  * isc_file_safemovefile is needed to be defined here to ensure that
140  * any file with the new name is renamed to a backup name and then the
141  * rename is done. If all goes well then the backup can be deleted,
142  * otherwise it gets renamed back.
143  */
144
145 int
146 isc_file_safemovefile(const char *oldname, const char *newname) {
147         BOOL filestatus;
148         char buf[512];
149         struct stat sbuf;
150         BOOL exists = FALSE;
151         int tmpfd;
152
153         /*
154          * Make sure we have something to do
155          */
156         if (stat(oldname, &sbuf) != 0) {
157                 errno = ENOENT;
158                 return (-1);
159         }
160
161         /*
162          * Rename to a backup the new file if it still exists
163          */
164         if (stat(newname, &sbuf) == 0) {
165                 exists = TRUE;
166                 strcpy(buf, newname);
167                 strcat(buf, ".XXXXX");
168                 tmpfd = mkstemp(buf);
169                 if (tmpfd > 0)
170                         _close(tmpfd);
171                 DeleteFile(buf);
172                 _chmod(newname, _S_IREAD | _S_IWRITE);
173
174                 filestatus = MoveFile(newname, buf);
175         }
176         /* Now rename the file to the new name
177          */
178         _chmod(oldname, _S_IREAD | _S_IWRITE);
179
180         filestatus = MoveFile(oldname, newname);
181         if (filestatus == 0) {
182                 /*
183                  * Try to rename the backup back to the original name
184                  * if the backup got created
185                  */
186                 if (exists == TRUE) {
187                         filestatus = MoveFile(buf, newname);
188                         if (filestatus == 0)
189                                 errno = EACCES;
190                 }
191                 return (-1);
192         }
193
194         /*
195          * Delete the backup file if it got created
196          */
197         if (exists == TRUE)
198                 filestatus = DeleteFile(buf);
199         return (0);
200 }
201
202 isc_result_t
203 isc_file_getmodtime(const char *file, isc_time_t *time) {
204         int fh;
205
206         REQUIRE(file != NULL);
207         REQUIRE(time != NULL);
208
209         if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0)
210                 return (isc__errno2result(errno));
211
212         if (!GetFileTime((HANDLE) _get_osfhandle(fh),
213                          NULL,
214                          NULL,
215                          &time->absolute))
216         {
217                 close(fh);
218                 errno = EINVAL;
219                 return (isc__errno2result(errno));
220         }
221         close(fh);
222         return (ISC_R_SUCCESS);
223 }
224
225 isc_result_t
226 isc_file_settime(const char *file, isc_time_t *time) {
227         int fh;
228
229         REQUIRE(file != NULL && time != NULL);
230
231         if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0)
232                 return (isc__errno2result(errno));
233
234         /*
235          * Set the date via the filedate system call and return.  Failing
236          * this call implies the new file times are not supported by the
237          * underlying file system.
238          */
239         if (!SetFileTime((HANDLE) _get_osfhandle(fh),
240                          NULL,
241                          &time->absolute,
242                          &time->absolute))
243         {
244                 close(fh);
245                 errno = EINVAL;
246                 return (isc__errno2result(errno));
247         }
248
249         close(fh);
250         return (ISC_R_SUCCESS);
251
252 }
253
254 #undef TEMPLATE
255 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
256
257 isc_result_t
258 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
259         return (isc_file_template(path, TEMPLATE, buf, buflen));
260 }
261
262 isc_result_t
263 isc_file_template(const char *path, const char *templet, char *buf,
264                         size_t buflen) {
265         char *s;
266
267         REQUIRE(path != NULL);
268         REQUIRE(templet != NULL);
269         REQUIRE(buf != NULL);
270
271         s = strrchr(templet, '\\');
272         if (s != NULL)
273                 templet = s + 1;
274
275         s = strrchr(path, '\\');
276
277         if (s != NULL) {
278                 if ((s - path + 1 + strlen(templet) + 1) > buflen)
279                         return (ISC_R_NOSPACE);
280
281                 strncpy(buf, path, s - path + 1);
282                 buf[s - path + 1] = '\0';
283                 strcat(buf, templet);
284         } else {
285                 if ((strlen(templet) + 1) > buflen)
286                         return (ISC_R_NOSPACE);
287
288                 strcpy(buf, templet);
289         }
290
291         return (ISC_R_SUCCESS);
292 }
293
294 isc_result_t
295 isc_file_renameunique(const char *file, char *templet) {
296         int fd = -1;
297         int res = 0;
298         isc_result_t result = ISC_R_SUCCESS;
299
300         REQUIRE(file != NULL);
301         REQUIRE(templet != NULL);
302
303         fd = mkstemp(templet);
304         if (fd == -1)
305                 result = isc__errno2result(errno);
306         else
307                 close(fd);
308
309         if (result == ISC_R_SUCCESS) {
310                 res = isc_file_safemovefile(file, templet);
311                 if (res != 0) {
312                         result = isc__errno2result(errno);
313                         (void)unlink(templet);
314                 }
315         }
316         return (result);
317 }
318
319 isc_result_t
320 isc_file_openuniqueprivate(char *templet, FILE **fp) {
321         int mode = _S_IREAD | _S_IWRITE;
322         return (isc_file_openuniquemode(templet, mode, fp));
323 }
324
325 isc_result_t
326 isc_file_openunique(char *templet, FILE **fp) {
327         int mode = _S_IREAD | _S_IWRITE;
328         return (isc_file_openuniquemode(templet, mode, fp));
329 }
330
331 isc_result_t
332 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
333         int fd;
334         FILE *f;
335         isc_result_t result = ISC_R_SUCCESS;
336
337         REQUIRE(templet != NULL);
338         REQUIRE(fp != NULL && *fp == NULL);
339
340         /*
341          * Win32 does not have mkstemp. Using emulation above.
342          */
343         fd = mkstemp(templet);
344
345         if (fd == -1)
346                 result = isc__errno2result(errno);
347         if (result == ISC_R_SUCCESS) {
348 #if 1
349                 UNUSED(mode);
350 #else
351                 (void)fchmod(fd, mode);
352 #endif
353                 f = fdopen(fd, "w+");
354                 if (f == NULL) {
355                         result = isc__errno2result(errno);
356                         (void)remove(templet);
357                         (void)close(fd);
358                 } else
359                         *fp = f;
360         }
361
362         return (result);
363 }
364
365 isc_result_t
366 isc_file_remove(const char *filename) {
367         int r;
368
369         REQUIRE(filename != NULL);
370
371         r = unlink(filename);
372         if (r == 0)
373                 return (ISC_R_SUCCESS);
374         else
375                 return (isc__errno2result(errno));
376 }
377
378 isc_result_t
379 isc_file_rename(const char *oldname, const char *newname) {
380         int r;
381
382         REQUIRE(oldname != NULL);
383         REQUIRE(newname != NULL);
384
385         r = isc_file_safemovefile(oldname, newname);
386         if (r == 0)
387                 return (ISC_R_SUCCESS);
388         else
389                 return (isc__errno2result(errno));
390 }
391
392 isc_boolean_t
393 isc_file_exists(const char *pathname) {
394         struct stat stats;
395
396         REQUIRE(pathname != NULL);
397
398         return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
399 }
400
401 isc_result_t
402 isc_file_isplainfile(const char *filename) {
403         /*
404          * This function returns success if filename is a plain file.
405          */
406         struct stat filestat;
407         memset(&filestat,0,sizeof(struct stat));
408
409         if ((stat(filename, &filestat)) == -1)
410                 return(isc__errno2result(errno));
411
412         if(! S_ISREG(filestat.st_mode))
413                 return(ISC_R_INVALIDFILE);
414
415         return(ISC_R_SUCCESS);
416 }
417
418 isc_boolean_t
419 isc_file_isabsolute(const char *filename) {
420         REQUIRE(filename != NULL);
421         /*
422          * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
423          * the UNC style file specs
424          */
425         if ((filename[0] == '\\') && (filename[1] == '\\'))
426                 return (ISC_TRUE);
427         if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\')
428                 return (ISC_TRUE);
429         if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
430                 return (ISC_TRUE);
431         return (ISC_FALSE);
432 }
433
434 isc_boolean_t
435 isc_file_iscurrentdir(const char *filename) {
436         REQUIRE(filename != NULL);
437         return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
438 }
439
440 isc_boolean_t
441 isc_file_ischdiridempotent(const char *filename) {
442         REQUIRE(filename != NULL);
443
444         if (isc_file_isabsolute(filename))
445                 return (ISC_TRUE);
446         if (filename[0] == '\\')
447                 return (ISC_TRUE);
448         if (filename[0] == '/')
449                 return (ISC_TRUE);
450         if (isc_file_iscurrentdir(filename))
451                 return (ISC_TRUE);
452         return (ISC_FALSE);
453 }
454
455 const char *
456 isc_file_basename(const char *filename) {
457         char *s;
458
459         REQUIRE(filename != NULL);
460
461         s = strrchr(filename, '\\');
462         if (s == NULL)
463                 return (filename);
464         return (s + 1);
465 }
466
467 isc_result_t
468 isc_file_progname(const char *filename, char *progname, size_t namelen) {
469         const char *s;
470         char *p;
471         size_t len;
472
473         REQUIRE(filename != NULL);
474         REQUIRE(progname != NULL);
475
476         /*
477          * Strip the path from the name
478          */
479         s = isc_file_basename(filename);
480         if (s == NULL) {
481                 return (ISC_R_NOSPACE);
482         }
483
484         /*
485          * Strip any and all suffixes
486          */
487         p = strchr(s, '.');
488         if (p == NULL) {
489                 if (namelen <= strlen(s))
490                         return (ISC_R_NOSPACE);
491
492                 strcpy(progname, s);
493                 return (ISC_R_SUCCESS);
494         }
495
496         /*
497          * Copy the result to the buffer
498          */
499         len = p - s;
500         if (len >= namelen)
501                 return (ISC_R_NOSPACE);
502
503         strncpy(progname, s, len);
504         progname[len] = '\0';
505         return (ISC_R_SUCCESS);
506 }
507
508 isc_result_t
509 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
510         char *ptrname;
511         DWORD retval;
512
513         REQUIRE(filename != NULL);
514         REQUIRE(path != NULL);
515
516         retval = GetFullPathName(filename, pathlen, path, &ptrname);
517
518         /* Something went wrong in getting the path */
519         if (retval == 0)
520                 return (ISC_R_NOTFOUND);
521         /* Caller needs to provide a larger buffer to contain the string */
522         if (retval >= pathlen)
523                 return (ISC_R_NOSPACE);
524         return (ISC_R_SUCCESS);
525 }
526
527 isc_result_t
528 isc_file_truncate(const char *filename, isc_offset_t size) {
529         int fh;
530
531         REQUIRE(filename != NULL && size >= 0);
532
533         if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0)
534                 return (isc__errno2result(errno));
535
536         if(_chsize(fh, size) != 0) {
537                 close(fh);
538                 return (isc__errno2result(errno));
539         }
540         close(fh);
541
542         return (ISC_R_SUCCESS);
543 }
544
545 isc_result_t
546 isc_file_safecreate(const char *filename, FILE **fp) {
547         isc_result_t result;
548         int flags;
549         struct stat sb;
550         FILE *f;
551         int fd;
552
553         REQUIRE(filename != NULL);
554         REQUIRE(fp != NULL && *fp == NULL);
555
556         result = file_stats(filename, &sb);
557         if (result == ISC_R_SUCCESS) {
558                 if ((sb.st_mode & S_IFREG) == 0)
559                         return (ISC_R_INVALIDFILE);
560                 flags = O_WRONLY | O_TRUNC;
561         } else if (result == ISC_R_FILENOTFOUND) {
562                 flags = O_WRONLY | O_CREAT | O_EXCL;
563         } else
564                 return (result);
565
566         fd = open(filename, flags, S_IRUSR | S_IWUSR);
567         if (fd == -1)
568                 return (isc__errno2result(errno));
569
570         f = fdopen(fd, "w");
571         if (f == NULL) {
572                 result = isc__errno2result(errno);
573                 close(fd);
574                 return (result);
575         }
576
577         *fp = f;
578         return (ISC_R_SUCCESS);
579 }
580
581 isc_result_t
582 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
583 {
584         char *dir, *file, *slash;
585         char *backslash;
586
587         slash = strrchr(path, '/');
588
589         backslash = strrchr(path, '\\');
590         if ((slash != NULL && backslash != NULL && backslash > slash) ||
591             (slash == NULL && backslash != NULL))
592                 slash = backslash;
593
594         if (slash == path) {
595                 file = ++slash;
596                 dir = isc_mem_strdup(mctx, "/");
597         } else if (slash != NULL) {
598                 file = ++slash;
599                 dir = isc_mem_allocate(mctx, slash - path);
600                 if (dir != NULL)
601                         strlcpy(dir, path, slash - path);
602         } else {
603                 file = path;
604                 dir = isc_mem_strdup(mctx, ".");
605         }
606
607         if (dir == NULL)
608                 return (ISC_R_NOMEMORY);
609
610         if (*file == '\0') {
611                 isc_mem_free(mctx, dir);
612                 return (ISC_R_INVALIDFILE);
613         }
614
615         *dirname = dir;
616         *basename = file;
617
618         return (ISC_R_SUCCESS);
619 }