Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / isc / win32 / file.c
blob9b56aa23ffe7bf72413a25bc0d1b365d3b6ef235
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2002 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: file.c,v 1.35 2009/09/02 17:58:06 each Exp */
22 #include <config.h>
24 #undef rename
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <io.h>
29 #include <process.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <sys/utime.h>
35 #include <isc/file.h>
36 #include <isc/mem.h>
37 #include <isc/result.h>
38 #include <isc/time.h>
39 #include <isc/util.h>
40 #include <isc/stat.h>
41 #include <isc/string.h>
43 #include "errno2result.h"
46 * Emulate UNIX mkstemp, which returns an open FD to the new file
49 static int
50 gettemp(char *path, int *doopen) {
51 char *start, *trv;
52 struct stat sbuf;
53 int pid;
55 trv = strrchr(path, 'X');
56 trv++;
57 pid = getpid();
58 /* extra X's get set to 0's */
59 while (*--trv == 'X') {
60 *trv = (pid % 10) + '0';
61 pid /= 10;
64 * check the target directory; if you have six X's and it
65 * doesn't exist this runs for a *very* long time.
67 for (start = trv + 1;; --trv) {
68 if (trv <= path)
69 break;
70 if (*trv == '\\') {
71 *trv = '\0';
72 if (stat(path, &sbuf))
73 return (0);
74 if (!S_ISDIR(sbuf.st_mode)) {
75 errno = ENOTDIR;
76 return (0);
78 *trv = '\\';
79 break;
83 for (;;) {
84 if (doopen) {
85 if ((*doopen =
86 open(path, O_CREAT|O_EXCL|O_RDWR,
87 _S_IREAD | _S_IWRITE)) >= 0)
88 return (1);
89 if (errno != EEXIST)
90 return (0);
91 } else if (stat(path, &sbuf))
92 return (errno == ENOENT ? 1 : 0);
94 /* tricky little algorithm for backward compatibility */
95 for (trv = start;;) {
96 if (!*trv)
97 return (0);
98 if (*trv == 'z')
99 *trv++ = 'a';
100 else {
101 if (isdigit(*trv))
102 *trv = 'a';
103 else
104 ++*trv;
105 break;
109 /*NOTREACHED*/
112 static int
113 mkstemp(char *path) {
114 int fd;
116 return (gettemp(path, &fd) ? fd : -1);
120 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
121 * it might be good to provide a mechanism that allows for the results
122 * of a previous stat() to be used again without having to do another stat,
123 * such as perl's mechanism of using "_" in place of a file name to indicate
124 * that the results of the last stat should be used. But then you get into
125 * annoying MP issues. BTW, Win32 has stat().
127 static isc_result_t
128 file_stats(const char *file, struct stat *stats) {
129 isc_result_t result = ISC_R_SUCCESS;
131 REQUIRE(file != NULL);
132 REQUIRE(stats != NULL);
134 if (stat(file, stats) != 0)
135 result = isc__errno2result(errno);
137 return (result);
141 * isc_file_safemovefile is needed to be defined here to ensure that
142 * any file with the new name is renamed to a backup name and then the
143 * rename is done. If all goes well then the backup can be deleted,
144 * otherwise it gets renamed back.
148 isc_file_safemovefile(const char *oldname, const char *newname) {
149 BOOL filestatus;
150 char buf[512];
151 struct stat sbuf;
152 BOOL exists = FALSE;
153 int tmpfd;
156 * Make sure we have something to do
158 if (stat(oldname, &sbuf) != 0) {
159 errno = ENOENT;
160 return (-1);
164 * Rename to a backup the new file if it still exists
166 if (stat(newname, &sbuf) == 0) {
167 exists = TRUE;
168 strcpy(buf, newname);
169 strcat(buf, ".XXXXX");
170 tmpfd = mkstemp(buf);
171 if (tmpfd > 0)
172 _close(tmpfd);
173 DeleteFile(buf);
174 _chmod(newname, _S_IREAD | _S_IWRITE);
176 filestatus = MoveFile(newname, buf);
178 /* Now rename the file to the new name
180 _chmod(oldname, _S_IREAD | _S_IWRITE);
182 filestatus = MoveFile(oldname, newname);
183 if (filestatus == 0) {
185 * Try to rename the backup back to the original name
186 * if the backup got created
188 if (exists == TRUE) {
189 filestatus = MoveFile(buf, newname);
190 if (filestatus == 0)
191 errno = EACCES;
193 return (-1);
197 * Delete the backup file if it got created
199 if (exists == TRUE)
200 filestatus = DeleteFile(buf);
201 return (0);
204 isc_result_t
205 isc_file_getmodtime(const char *file, isc_time_t *time) {
206 int fh;
208 REQUIRE(file != NULL);
209 REQUIRE(time != NULL);
211 if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0)
212 return (isc__errno2result(errno));
214 if (!GetFileTime((HANDLE) _get_osfhandle(fh),
215 NULL,
216 NULL,
217 &time->absolute))
219 close(fh);
220 errno = EINVAL;
221 return (isc__errno2result(errno));
223 close(fh);
224 return (ISC_R_SUCCESS);
227 isc_result_t
228 isc_file_settime(const char *file, isc_time_t *time) {
229 int fh;
231 REQUIRE(file != NULL && time != NULL);
233 if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0)
234 return (isc__errno2result(errno));
237 * Set the date via the filedate system call and return. Failing
238 * this call implies the new file times are not supported by the
239 * underlying file system.
241 if (!SetFileTime((HANDLE) _get_osfhandle(fh),
242 NULL,
243 &time->absolute,
244 &time->absolute))
246 close(fh);
247 errno = EINVAL;
248 return (isc__errno2result(errno));
251 close(fh);
252 return (ISC_R_SUCCESS);
256 #undef TEMPLATE
257 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
259 isc_result_t
260 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
261 return (isc_file_template(path, TEMPLATE, buf, buflen));
264 isc_result_t
265 isc_file_template(const char *path, const char *templet, char *buf,
266 size_t buflen) {
267 char *s;
269 REQUIRE(path != NULL);
270 REQUIRE(templet != NULL);
271 REQUIRE(buf != NULL);
273 s = strrchr(templet, '\\');
274 if (s != NULL)
275 templet = s + 1;
277 s = strrchr(path, '\\');
279 if (s != NULL) {
280 if ((s - path + 1 + strlen(templet) + 1) > buflen)
281 return (ISC_R_NOSPACE);
283 strncpy(buf, path, s - path + 1);
284 buf[s - path + 1] = '\0';
285 strcat(buf, templet);
286 } else {
287 if ((strlen(templet) + 1) > buflen)
288 return (ISC_R_NOSPACE);
290 strcpy(buf, templet);
293 return (ISC_R_SUCCESS);
296 isc_result_t
297 isc_file_renameunique(const char *file, char *templet) {
298 int fd = -1;
299 int res = 0;
300 isc_result_t result = ISC_R_SUCCESS;
302 REQUIRE(file != NULL);
303 REQUIRE(templet != NULL);
305 fd = mkstemp(templet);
306 if (fd == -1)
307 result = isc__errno2result(errno);
308 else
309 close(fd);
311 if (result == ISC_R_SUCCESS) {
312 res = isc_file_safemovefile(file, templet);
313 if (res != 0) {
314 result = isc__errno2result(errno);
315 (void)unlink(templet);
318 return (result);
321 isc_result_t
322 isc_file_openunique(char *templet, FILE **fp) {
323 int fd;
324 FILE *f;
325 isc_result_t result = ISC_R_SUCCESS;
327 REQUIRE(templet != NULL);
328 REQUIRE(fp != NULL && *fp == NULL);
331 * Win32 does not have mkstemp. Using emulation above.
333 fd = mkstemp(templet);
335 if (fd == -1)
336 result = isc__errno2result(errno);
337 if (result == ISC_R_SUCCESS) {
338 f = fdopen(fd, "w+");
339 if (f == NULL) {
340 result = isc__errno2result(errno);
341 (void)remove(templet);
342 (void)close(fd);
343 } else
344 *fp = f;
347 return (result);
350 isc_result_t
351 isc_file_remove(const char *filename) {
352 int r;
354 REQUIRE(filename != NULL);
356 r = unlink(filename);
357 if (r == 0)
358 return (ISC_R_SUCCESS);
359 else
360 return (isc__errno2result(errno));
363 isc_result_t
364 isc_file_rename(const char *oldname, const char *newname) {
365 int r;
367 REQUIRE(oldname != NULL);
368 REQUIRE(newname != NULL);
370 r = isc_file_safemovefile(oldname, newname);
371 if (r == 0)
372 return (ISC_R_SUCCESS);
373 else
374 return (isc__errno2result(errno));
377 isc_boolean_t
378 isc_file_exists(const char *pathname) {
379 struct stat stats;
381 REQUIRE(pathname != NULL);
383 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
386 isc_boolean_t
387 isc_file_isabsolute(const char *filename) {
388 REQUIRE(filename != NULL);
390 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
391 * the UNC style file specs
393 if ((filename[0] == '\\') && (filename[1] == '\\'))
394 return (ISC_TRUE);
395 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\')
396 return (ISC_TRUE);
397 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
398 return (ISC_TRUE);
399 return (ISC_FALSE);
402 isc_boolean_t
403 isc_file_iscurrentdir(const char *filename) {
404 REQUIRE(filename != NULL);
405 return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
408 isc_boolean_t
409 isc_file_ischdiridempotent(const char *filename) {
410 REQUIRE(filename != NULL);
412 if (isc_file_isabsolute(filename))
413 return (ISC_TRUE);
414 if (filename[0] == '\\')
415 return (ISC_TRUE);
416 if (filename[0] == '/')
417 return (ISC_TRUE);
418 if (isc_file_iscurrentdir(filename))
419 return (ISC_TRUE);
420 return (ISC_FALSE);
423 const char *
424 isc_file_basename(const char *filename) {
425 char *s;
427 REQUIRE(filename != NULL);
429 s = strrchr(filename, '\\');
430 if (s == NULL)
431 return (filename);
432 return (s + 1);
435 isc_result_t
436 isc_file_progname(const char *filename, char *progname, size_t namelen) {
437 const char *s;
438 char *p;
439 size_t len;
441 REQUIRE(filename != NULL);
442 REQUIRE(progname != NULL);
445 * Strip the path from the name
447 s = isc_file_basename(filename);
448 if (s == NULL) {
449 return (ISC_R_NOSPACE);
453 * Strip any and all suffixes
455 p = strchr(s, '.');
456 if (p == NULL) {
457 if (namelen <= strlen(s))
458 return (ISC_R_NOSPACE);
460 strcpy(progname, s);
461 return (ISC_R_SUCCESS);
465 * Copy the result to the buffer
467 len = p - s;
468 if (len >= namelen)
469 return (ISC_R_NOSPACE);
471 strncpy(progname, s, len);
472 progname[len] = '\0';
473 return (ISC_R_SUCCESS);
476 isc_result_t
477 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
478 char *ptrname;
479 DWORD retval;
481 REQUIRE(filename != NULL);
482 REQUIRE(path != NULL);
484 retval = GetFullPathName(filename, pathlen, path, &ptrname);
486 /* Something went wrong in getting the path */
487 if (retval == 0)
488 return (ISC_R_NOTFOUND);
489 /* Caller needs to provide a larger buffer to contain the string */
490 if (retval >= pathlen)
491 return (ISC_R_NOSPACE);
492 return (ISC_R_SUCCESS);
495 isc_result_t
496 isc_file_truncate(const char *filename, isc_offset_t size) {
497 int fh;
499 REQUIRE(filename != NULL && size >= 0);
501 if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0)
502 return (isc__errno2result(errno));
504 if(_chsize(fh, size) != 0) {
505 close(fh);
506 return (isc__errno2result(errno));
508 close(fh);
510 return (ISC_R_SUCCESS);
513 isc_result_t
514 isc_file_safecreate(const char *filename, FILE **fp) {
515 isc_result_t result;
516 int flags;
517 struct stat sb;
518 FILE *f;
519 int fd;
521 REQUIRE(filename != NULL);
522 REQUIRE(fp != NULL && *fp == NULL);
524 result = file_stats(filename, &sb);
525 if (result == ISC_R_SUCCESS) {
526 if ((sb.st_mode & S_IFREG) == 0)
527 return (ISC_R_INVALIDFILE);
528 flags = O_WRONLY | O_TRUNC;
529 } else if (result == ISC_R_FILENOTFOUND) {
530 flags = O_WRONLY | O_CREAT | O_EXCL;
531 } else
532 return (result);
534 fd = open(filename, flags, S_IRUSR | S_IWUSR);
535 if (fd == -1)
536 return (isc__errno2result(errno));
538 f = fdopen(fd, "w");
539 if (f == NULL) {
540 result = isc__errno2result(errno);
541 close(fd);
542 return (result);
545 *fp = f;
546 return (ISC_R_SUCCESS);
549 isc_result_t
550 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
552 char *dir, *file, *slash;
553 char *backslash;
555 slash = strrchr(path, '/');
557 backslash = strrchr(path, '\\');
558 if ((slash != NULL && backslash != NULL && backslash > slash) ||
559 (slash == NULL && backslash != NULL))
560 slash = backslash;
562 if (slash == path) {
563 file = ++slash;
564 dir = isc_mem_strdup(mctx, "/");
565 } else if (slash != NULL) {
566 file = ++slash;
567 dir = isc_mem_allocate(mctx, slash - path);
568 if (dir != NULL)
569 strlcpy(dir, path, slash - path);
570 } else {
571 file = path;
572 dir = isc_mem_strdup(mctx, ".");
575 if (dir == NULL)
576 return (ISC_R_NOMEMORY);
578 if (*file == '\0') {
579 isc_mem_free(mctx, dir);
580 return (ISC_R_INVALIDFILE);
583 *dirname = dir;
584 *basename = file;
586 return (ISC_R_SUCCESS);