1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12
13 #include <config.h>
14
15 #undef rename
16 #include <errno.h>
17 #include <limits.h>
18 #include <stdbool.h>
19 #include <stdlib.h>
20 #include <io.h>
21 #include <process.h>
22
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <sys/utime.h>
26
27 #include <isc/file.h>
28 #include <isc/mem.h>
29 #include <isc/platform.h>
30 #include <isc/print.h>
31 #include <isc/random.h>
32 #include <isc/result.h>
33 #include <isc/sha2.h>
34 #include <isc/stat.h>
35 #include <isc/string.h>
36 #include <isc/time.h>
37 #include <isc/util.h>
38
39 #include "errno2result.h"
40
41 static const char alphnum[] =
42 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
43
44 /*
45 * Emulate UNIX mkstemp, which returns an open FD to the new file
46 *
47 */
48 static int
gettemp(char * path,bool binary,int * doopen)49 gettemp(char *path, bool binary, int *doopen) {
50 char *start, *trv;
51 struct stat sbuf;
52 int flags = O_CREAT|O_EXCL|O_RDWR;
53
54 if (binary)
55 flags |= _O_BINARY;
56
57 trv = strrchr(path, 'X');
58 trv++;
59 /* extra X's get set to 0's */
60 while (*--trv == 'X') {
61 uint32_t which;
62
63 isc_random_get(&which);
64 *trv = alphnum[which % (sizeof(alphnum) - 1)];
65 }
66 /*
67 * check the target directory; if you have six X's and it
68 * doesn't exist this runs for a *very* long time.
69 */
70 for (start = trv + 1;; --trv) {
71 if (trv <= path)
72 break;
73 if (*trv == '\\') {
74 *trv = '\0';
75 if (stat(path, &sbuf))
76 return (0);
77 if (!S_ISDIR(sbuf.st_mode)) {
78 errno = ENOTDIR;
79 return (0);
80 }
81 *trv = '\\';
82 break;
83 }
84 }
85
86 for (;;) {
87 if (doopen) {
88 if ((*doopen =
89 open(path, flags, _S_IREAD | _S_IWRITE)) >= 0)
90 return (1);
91 if (errno != EEXIST)
92 return (0);
93 } else if (stat(path, &sbuf))
94 return (errno == ENOENT ? 1 : 0);
95
96 /* tricky little algorithm for backward compatibility */
97 for (trv = start;;) {
98 if (!*trv)
99 return (0);
100 if (*trv == 'z')
101 *trv++ = 'a';
102 else {
103 if (isdigit(*trv))
104 *trv = 'a';
105 else
106 ++*trv;
107 break;
108 }
109 }
110 }
111 /*NOTREACHED*/
112 }
113
114 static int
mkstemp(char * path,bool binary)115 mkstemp(char *path, bool binary) {
116 int fd;
117
118 return (gettemp(path, binary, &fd) ? fd : -1);
119 }
120
121 /*
122 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
123 * it might be good to provide a mechanism that allows for the results
124 * of a previous stat() to be used again without having to do another stat,
125 * such as perl's mechanism of using "_" in place of a file name to indicate
126 * that the results of the last stat should be used. But then you get into
127 * annoying MP issues. BTW, Win32 has stat().
128 */
129 static isc_result_t
file_stats(const char * file,struct stat * stats)130 file_stats(const char *file, struct stat *stats) {
131 isc_result_t result = ISC_R_SUCCESS;
132
133 REQUIRE(file != NULL);
134 REQUIRE(stats != NULL);
135
136 if (stat(file, stats) != 0)
137 result = isc__errno2result(errno);
138
139 return (result);
140 }
141
142 static isc_result_t
fd_stats(int fd,struct stat * stats)143 fd_stats(int fd, struct stat *stats) {
144 isc_result_t result = ISC_R_SUCCESS;
145
146 REQUIRE(stats != NULL);
147
148 if (fstat(fd, stats) != 0)
149 result = isc__errno2result(errno);
150
151 return (result);
152 }
153
154 isc_result_t
isc_file_getsizefd(int fd,off_t * size)155 isc_file_getsizefd(int fd, off_t *size) {
156 isc_result_t result;
157 struct stat stats;
158
159 REQUIRE(size != NULL);
160
161 result = fd_stats(fd, &stats);
162
163 if (result == ISC_R_SUCCESS)
164 *size = stats.st_size;
165 return (result);
166 }
167
168 isc_result_t
isc_file_mode(const char * file,mode_t * modep)169 isc_file_mode(const char *file, mode_t *modep) {
170 isc_result_t result;
171 struct stat stats;
172
173 REQUIRE(modep != NULL);
174
175 result = file_stats(file, &stats);
176 if (result == ISC_R_SUCCESS)
177 *modep = (stats.st_mode & 07777);
178 return (result);
179 }
180
181 /*
182 * isc_file_safemovefile is needed to be defined here to ensure that
183 * any file with the new name is renamed to a backup name and then the
184 * rename is done. If all goes well then the backup can be deleted,
185 * otherwise it gets renamed back.
186 */
187
188 int
isc_file_safemovefile(const char * oldname,const char * newname)189 isc_file_safemovefile(const char *oldname, const char *newname) {
190 BOOL filestatus;
191 char buf[512];
192 struct stat sbuf;
193 BOOL exists = FALSE;
194 int tmpfd;
195
196 /*
197 * Make sure we have something to do
198 */
199 if (stat(oldname, &sbuf) != 0) {
200 errno = ENOENT;
201 return (-1);
202 }
203
204 /*
205 * Rename to a backup the new file if it still exists
206 */
207 if (stat(newname, &sbuf) == 0) {
208 exists = TRUE;
209 strlcpy(buf, newname, sizeof(buf));
210 strlcat(buf, ".XXXXX", sizeof(buf));
211 tmpfd = mkstemp(buf, true);
212 if (tmpfd > 0)
213 _close(tmpfd);
214 (void)DeleteFile(buf);
215 _chmod(newname, _S_IREAD | _S_IWRITE);
216
217 filestatus = MoveFile(newname, buf);
218 }
219 /* Now rename the file to the new name
220 */
221 _chmod(oldname, _S_IREAD | _S_IWRITE);
222
223 filestatus = MoveFile(oldname, newname);
224 if (filestatus == 0) {
225 /*
226 * Try to rename the backup back to the original name
227 * if the backup got created
228 */
229 if (exists == TRUE) {
230 filestatus = MoveFile(buf, newname);
231 if (filestatus == 0)
232 errno = EACCES;
233 }
234 return (-1);
235 }
236
237 /*
238 * Delete the backup file if it got created
239 */
240 if (exists == TRUE)
241 (void)DeleteFile(buf);
242 return (0);
243 }
244
245 isc_result_t
isc_file_getmodtime(const char * file,isc_time_t * time)246 isc_file_getmodtime(const char *file, isc_time_t *time) {
247 int fh;
248
249 REQUIRE(file != NULL);
250 REQUIRE(time != NULL);
251
252 if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0)
253 return (isc__errno2result(errno));
254
255 if (!GetFileTime((HANDLE) _get_osfhandle(fh),
256 NULL,
257 NULL,
258 &time->absolute))
259 {
260 close(fh);
261 errno = EINVAL;
262 return (isc__errno2result(errno));
263 }
264 close(fh);
265 return (ISC_R_SUCCESS);
266 }
267
268 isc_result_t
isc_file_getsize(const char * file,off_t * size)269 isc_file_getsize(const char *file, off_t *size) {
270 isc_result_t result;
271 struct stat stats;
272
273 REQUIRE(file != NULL);
274 REQUIRE(size != NULL);
275
276 result = file_stats(file, &stats);
277
278 if (result == ISC_R_SUCCESS)
279 *size = stats.st_size;
280
281 return (result);
282 }
283
284 isc_result_t
isc_file_settime(const char * file,isc_time_t * time)285 isc_file_settime(const char *file, isc_time_t *time) {
286 int fh;
287
288 REQUIRE(file != NULL && time != NULL);
289
290 if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0)
291 return (isc__errno2result(errno));
292
293 /*
294 * Set the date via the filedate system call and return. Failing
295 * this call implies the new file times are not supported by the
296 * underlying file system.
297 */
298 if (!SetFileTime((HANDLE) _get_osfhandle(fh),
299 NULL,
300 &time->absolute,
301 &time->absolute))
302 {
303 close(fh);
304 errno = EINVAL;
305 return (isc__errno2result(errno));
306 }
307
308 close(fh);
309 return (ISC_R_SUCCESS);
310
311 }
312
313 #undef TEMPLATE
314 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
315
316 isc_result_t
isc_file_mktemplate(const char * path,char * buf,size_t buflen)317 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
318 return (isc_file_template(path, TEMPLATE, buf, buflen));
319 }
320
321 isc_result_t
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)322 isc_file_template(const char *path, const char *templet, char *buf,
323 size_t buflen)
324 {
325 char *s;
326
327 REQUIRE(templet != NULL);
328 REQUIRE(buf != NULL);
329
330 if (path == NULL)
331 path = "";
332
333 s = strrchr(templet, '\\');
334 if (s != NULL)
335 templet = s + 1;
336
337 s = strrchr(path, '\\');
338
339 if (s != NULL) {
340 size_t prefixlen = s - path + 1;
341 if ((prefixlen + strlen(templet) + 1) > buflen)
342 return (ISC_R_NOSPACE);
343
344 /* Copy 'prefixlen' bytes and NUL terminate. */
345 strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen));
346 strlcat(buf, templet, buflen);
347 } else {
348 if ((strlen(templet) + 1) > buflen)
349 return (ISC_R_NOSPACE);
350
351 strlcpy(buf, templet, buflen);
352 }
353
354 return (ISC_R_SUCCESS);
355 }
356
357 isc_result_t
isc_file_renameunique(const char * file,char * templet)358 isc_file_renameunique(const char *file, char *templet) {
359 int fd;
360 isc_result_t result = ISC_R_SUCCESS;
361
362 REQUIRE(file != NULL);
363 REQUIRE(templet != NULL);
364
365 fd = mkstemp(templet, true);
366 if (fd == -1)
367 result = isc__errno2result(errno);
368 else
369 close(fd);
370
371 if (result == ISC_R_SUCCESS) {
372 int res;
373 res = isc_file_safemovefile(file, templet);
374 if (res != 0) {
375 result = isc__errno2result(errno);
376 (void)unlink(templet);
377 }
378 }
379 return (result);
380 }
381
382 static isc_result_t
openuniquemode(char * templet,int mode,bool binary,FILE ** fp)383 openuniquemode(char *templet, int mode, bool binary, FILE **fp) {
384 int fd;
385 FILE *f;
386 isc_result_t result = ISC_R_SUCCESS;
387
388 REQUIRE(templet != NULL);
389 REQUIRE(fp != NULL && *fp == NULL);
390
391 /*
392 * Win32 does not have mkstemp. Using emulation above.
393 */
394 fd = mkstemp(templet, binary);
395
396 if (fd == -1)
397 result = isc__errno2result(errno);
398 if (result == ISC_R_SUCCESS) {
399 #if 1
400 UNUSED(mode);
401 #else
402 (void)fchmod(fd, mode);
403 #endif
404 f = fdopen(fd, binary ? "wb+" : "w+");
405 if (f == NULL) {
406 result = isc__errno2result(errno);
407 (void)remove(templet);
408 (void)close(fd);
409 } else
410 *fp = f;
411 }
412
413 return (result);
414 }
415
416 isc_result_t
isc_file_openuniqueprivate(char * templet,FILE ** fp)417 isc_file_openuniqueprivate(char *templet, FILE **fp) {
418 int mode = _S_IREAD | _S_IWRITE;
419 return (openuniquemode(templet, mode, false, fp));
420 }
421
422 isc_result_t
isc_file_openunique(char * templet,FILE ** fp)423 isc_file_openunique(char *templet, FILE **fp) {
424 int mode = _S_IREAD | _S_IWRITE;
425 return (openuniquemode(templet, mode, false, fp));
426 }
427
428 isc_result_t
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)429 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
430 return (openuniquemode(templet, mode, false, fp));
431 }
432
433 isc_result_t
isc_file_bopenuniqueprivate(char * templet,FILE ** fp)434 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
435 int mode = _S_IREAD | _S_IWRITE;
436 return (openuniquemode(templet, mode, true, fp));
437 }
438
439 isc_result_t
isc_file_bopenunique(char * templet,FILE ** fp)440 isc_file_bopenunique(char *templet, FILE **fp) {
441 int mode = _S_IREAD | _S_IWRITE;
442 return (openuniquemode(templet, mode, true, fp));
443 }
444
445 isc_result_t
isc_file_bopenuniquemode(char * templet,int mode,FILE ** fp)446 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
447 return (openuniquemode(templet, mode, true, fp));
448 }
449
450 isc_result_t
isc_file_remove(const char * filename)451 isc_file_remove(const char *filename) {
452 int r;
453
454 REQUIRE(filename != NULL);
455
456 r = unlink(filename);
457 if (r == 0)
458 return (ISC_R_SUCCESS);
459 else
460 return (isc__errno2result(errno));
461 }
462
463 isc_result_t
isc_file_rename(const char * oldname,const char * newname)464 isc_file_rename(const char *oldname, const char *newname) {
465 int r;
466
467 REQUIRE(oldname != NULL);
468 REQUIRE(newname != NULL);
469
470 r = isc_file_safemovefile(oldname, newname);
471 if (r == 0)
472 return (ISC_R_SUCCESS);
473 else
474 return (isc__errno2result(errno));
475 }
476
477 bool
isc_file_exists(const char * pathname)478 isc_file_exists(const char *pathname) {
479 struct stat stats;
480
481 REQUIRE(pathname != NULL);
482
483 return (file_stats(pathname, &stats) == ISC_R_SUCCESS);
484 }
485
486 isc_result_t
isc_file_isplainfile(const char * filename)487 isc_file_isplainfile(const char *filename) {
488 /*
489 * This function returns success if filename is a plain file.
490 */
491 struct stat filestat;
492 memset(&filestat,0,sizeof(struct stat));
493
494 if ((stat(filename, &filestat)) == -1)
495 return(isc__errno2result(errno));
496
497 if(! S_ISREG(filestat.st_mode))
498 return(ISC_R_INVALIDFILE);
499
500 return(ISC_R_SUCCESS);
501 }
502
503 isc_result_t
isc_file_isplainfilefd(int fd)504 isc_file_isplainfilefd(int fd) {
505 /*
506 * This function returns success if filename is a plain file.
507 */
508 struct stat filestat;
509 memset(&filestat,0,sizeof(struct stat));
510
511 if ((fstat(fd, &filestat)) == -1)
512 return(isc__errno2result(errno));
513
514 if(! S_ISREG(filestat.st_mode))
515 return(ISC_R_INVALIDFILE);
516
517 return(ISC_R_SUCCESS);
518 }
519
520 isc_result_t
isc_file_isdirectory(const char * filename)521 isc_file_isdirectory(const char *filename) {
522 /*
523 * This function returns success if filename is a directory.
524 */
525 struct stat filestat;
526 memset(&filestat,0,sizeof(struct stat));
527
528 if ((stat(filename, &filestat)) == -1)
529 return(isc__errno2result(errno));
530
531 if(! S_ISDIR(filestat.st_mode))
532 return(ISC_R_INVALIDFILE);
533
534 return(ISC_R_SUCCESS);
535 }
536
537
538 bool
isc_file_isabsolute(const char * filename)539 isc_file_isabsolute(const char *filename) {
540 REQUIRE(filename != NULL);
541 /*
542 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
543 * the UNC style file specs
544 */
545 if ((filename[0] == '\\') && (filename[1] == '\\'))
546 return (true);
547 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\')
548 return (true);
549 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
550 return (true);
551 return (false);
552 }
553
554 bool
isc_file_iscurrentdir(const char * filename)555 isc_file_iscurrentdir(const char *filename) {
556 REQUIRE(filename != NULL);
557 return (filename[0] == '.' && filename[1] == '\0');
558 }
559
560 bool
isc_file_ischdiridempotent(const char * filename)561 isc_file_ischdiridempotent(const char *filename) {
562 REQUIRE(filename != NULL);
563
564 if (isc_file_isabsolute(filename))
565 return (true);
566 if (filename[0] == '\\')
567 return (true);
568 if (filename[0] == '/')
569 return (true);
570 if (isc_file_iscurrentdir(filename))
571 return (true);
572 return (false);
573 }
574
575 const char *
isc_file_basename(const char * filename)576 isc_file_basename(const char *filename) {
577 char *s;
578
579 REQUIRE(filename != NULL);
580
581 s = strrchr(filename, '\\');
582 if (s == NULL)
583 return (filename);
584 return (s + 1);
585 }
586
587 isc_result_t
isc_file_progname(const char * filename,char * progname,size_t namelen)588 isc_file_progname(const char *filename, char *progname, size_t namelen) {
589 const char *s;
590 const char *p;
591 size_t len;
592
593 REQUIRE(filename != NULL);
594 REQUIRE(progname != NULL);
595
596 /*
597 * Strip the path from the name
598 */
599 s = isc_file_basename(filename);
600 if (s == NULL) {
601 return (ISC_R_NOSPACE);
602 }
603
604 /*
605 * Strip any and all suffixes
606 */
607 p = strchr(s, '.');
608 if (p == NULL) {
609 if (namelen <= strlen(s))
610 return (ISC_R_NOSPACE);
611
612 strlcpy(progname, s, namelen);
613 return (ISC_R_SUCCESS);
614 }
615
616 /*
617 * Copy the result to the buffer
618 */
619 len = p - s;
620 if (len >= namelen)
621 return (ISC_R_NOSPACE);
622
623 /* Copy up to 'len' bytes and NUL terminate. */
624 strlcpy(progname, s, ISC_MIN(len + 1, namelen));
625 return (ISC_R_SUCCESS);
626 }
627
628 isc_result_t
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)629 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
630 char *ptrname;
631 DWORD retval;
632
633 REQUIRE(filename != NULL);
634 REQUIRE(path != NULL);
635
636 retval = GetFullPathName(filename, (DWORD) pathlen, path, &ptrname);
637
638 /* Something went wrong in getting the path */
639 if (retval == 0)
640 return (ISC_R_NOTFOUND);
641 /* Caller needs to provide a larger buffer to contain the string */
642 if (retval >= pathlen)
643 return (ISC_R_NOSPACE);
644 return (ISC_R_SUCCESS);
645 }
646
647 isc_result_t
isc_file_truncate(const char * filename,isc_offset_t size)648 isc_file_truncate(const char *filename, isc_offset_t size) {
649 int fh;
650
651 REQUIRE(filename != NULL && size >= 0);
652
653 if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0)
654 return (isc__errno2result(errno));
655
656 if(_chsize(fh, size) != 0) {
657 close(fh);
658 return (isc__errno2result(errno));
659 }
660 close(fh);
661
662 return (ISC_R_SUCCESS);
663 }
664
665 isc_result_t
isc_file_safecreate(const char * filename,FILE ** fp)666 isc_file_safecreate(const char *filename, FILE **fp) {
667 isc_result_t result;
668 int flags;
669 struct stat sb;
670 FILE *f;
671 int fd;
672
673 REQUIRE(filename != NULL);
674 REQUIRE(fp != NULL && *fp == NULL);
675
676 result = file_stats(filename, &sb);
677 if (result == ISC_R_SUCCESS) {
678 if ((sb.st_mode & S_IFREG) == 0)
679 return (ISC_R_INVALIDFILE);
680 flags = O_WRONLY | O_TRUNC;
681 } else if (result == ISC_R_FILENOTFOUND) {
682 flags = O_WRONLY | O_CREAT | O_EXCL;
683 } else
684 return (result);
685
686 fd = open(filename, flags, S_IRUSR | S_IWUSR);
687 if (fd == -1)
688 return (isc__errno2result(errno));
689
690 f = fdopen(fd, "w");
691 if (f == NULL) {
692 result = isc__errno2result(errno);
693 close(fd);
694 return (result);
695 }
696
697 *fp = f;
698 return (ISC_R_SUCCESS);
699 }
700
701 isc_result_t
isc_file_splitpath(isc_mem_t * mctx,const char * path,char ** dirname,char const ** basename)702 isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
703 char const ** basename)
704 {
705 char *dir;
706 const char *file, *slash;
707 char *backslash;
708
709 slash = strrchr(path, '/');
710
711 backslash = strrchr(path, '\\');
712 if ((slash != NULL && backslash != NULL && backslash > slash) ||
713 (slash == NULL && backslash != NULL))
714 slash = backslash;
715
716 if (slash == path) {
717 file = ++slash;
718 dir = isc_mem_strdup(mctx, "/");
719 } else if (slash != NULL) {
720 file = ++slash;
721 dir = isc_mem_allocate(mctx, slash - path);
722 if (dir != NULL)
723 strlcpy(dir, path, slash - path);
724 } else {
725 file = path;
726 dir = isc_mem_strdup(mctx, ".");
727 }
728
729 if (dir == NULL)
730 return (ISC_R_NOMEMORY);
731
732 if (*file == '\0') {
733 isc_mem_free(mctx, dir);
734 return (ISC_R_INVALIDFILE);
735 }
736
737 *dirname = dir;
738 *basename = file;
739
740 return (ISC_R_SUCCESS);
741 }
742
743 void *
isc_file_mmap(void * addr,size_t len,int prot,int flags,int fd,off_t offset)744 isc_file_mmap(void *addr, size_t len, int prot,
745 int flags, int fd, off_t offset)
746 {
747 void *buf;
748 ssize_t ret;
749 off_t end;
750
751 UNUSED(addr);
752 UNUSED(prot);
753 UNUSED(flags);
754
755 end = lseek(fd, 0, SEEK_END);
756 lseek(fd, offset, SEEK_SET);
757 if (end - offset < (off_t) len)
758 len = end - offset;
759
760 buf = malloc(len);
761 if (buf == NULL)
762 return (NULL);
763
764 ret = read(fd, buf, (unsigned int) len);
765 if (ret != (ssize_t) len) {
766 free(buf);
767 buf = NULL;
768 }
769
770 return (buf);
771 }
772
773 int
isc_file_munmap(void * addr,size_t len)774 isc_file_munmap(void *addr, size_t len) {
775 UNUSED(len);
776 free(addr);
777 return (0);
778 }
779
780 #define DISALLOW "\\/:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
781
782 isc_result_t
isc_file_sanitize(const char * dir,const char * base,const char * ext,char * path,size_t length)783 isc_file_sanitize(const char *dir, const char *base, const char *ext,
784 char *path, size_t length)
785 {
786 char buf[PATH_MAX], hash[ISC_SHA256_DIGESTSTRINGLENGTH];
787 size_t l = 0;
788
789 REQUIRE(base != NULL);
790 REQUIRE(path != NULL);
791
792 l = strlen(base) + 1;
793
794 /*
795 * allow room for a full sha256 hash (64 chars
796 * plus null terminator)
797 */
798 if (l < 65)
799 l = 65;
800
801 if (dir != NULL)
802 l += strlen(dir) + 1;
803 if (ext != NULL)
804 l += strlen(ext) + 1;
805
806 if (l > length || l > PATH_MAX)
807 return (ISC_R_NOSPACE);
808
809 /* Check whether the full-length SHA256 hash filename exists */
810 isc_sha256_data((const void *) base, strlen(base), hash);
811 snprintf(buf, sizeof(buf), "%s%s%s%s%s",
812 dir != NULL ? dir : "", dir != NULL ? "/" : "",
813 hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
814 if (isc_file_exists(buf)) {
815 strlcpy(path, buf, length);
816 return (ISC_R_SUCCESS);
817 }
818
819 /* Check for a truncated SHA256 hash filename */
820 hash[16] = '\0';
821 snprintf(buf, sizeof(buf), "%s%s%s%s%s",
822 dir != NULL ? dir : "", dir != NULL ? "/" : "",
823 hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
824 if (isc_file_exists(buf)) {
825 strlcpy(path, buf, length);
826 return (ISC_R_SUCCESS);
827 }
828
829 /*
830 * If neither hash filename already exists, then we'll use
831 * the original base name if it has no disallowed characters,
832 * or the truncated hash name if it does.
833 */
834 if (strpbrk(base, DISALLOW) != NULL) {
835 strlcpy(path, buf, length);
836 return (ISC_R_SUCCESS);
837 }
838
839 snprintf(buf, sizeof(buf), "%s%s%s%s%s",
840 dir != NULL ? dir : "", dir != NULL ? "/" : "",
841 base, ext != NULL ? "." : "", ext != NULL ? ext : "");
842 strlcpy(path, buf, length);
843 return (ISC_R_SUCCESS);
844 }
845
846 /*
847 * Based on http://blog.aaronballman.com/2011/08/how-to-check-access-rights/
848 */
849 bool
isc_file_isdirwritable(const char * path)850 isc_file_isdirwritable(const char *path) {
851 DWORD length = 0;
852 HANDLE hToken = NULL;
853 PSECURITY_DESCRIPTOR security = NULL;
854 bool answer = false;
855
856 if (isc_file_isdirectory(path) != ISC_R_SUCCESS) {
857 return (answer);
858 }
859
860 /*
861 * Figure out buffer size. GetFileSecurity() should not succeed.
862 */
863 if (GetFileSecurity(path, OWNER_SECURITY_INFORMATION |
864 GROUP_SECURITY_INFORMATION |
865 DACL_SECURITY_INFORMATION,
866 NULL, 0, &length))
867 {
868 return (answer);
869 }
870
871 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
872 return (answer);
873 }
874
875 security = malloc(length);
876 if (security == NULL) {
877 return (answer);
878 }
879
880 /*
881 * GetFileSecurity() should succeed.
882 */
883 if (!GetFileSecurity(path, OWNER_SECURITY_INFORMATION |
884 GROUP_SECURITY_INFORMATION |
885 DACL_SECURITY_INFORMATION,
886 security, length, &length))
887 {
888 return (answer);
889 }
890
891 if (OpenProcessToken(GetCurrentProcess(), TOKEN_IMPERSONATE |
892 TOKEN_QUERY | TOKEN_DUPLICATE |
893 STANDARD_RIGHTS_READ, &hToken))
894 {
895 HANDLE hImpersonatedToken = NULL;
896
897 if (DuplicateToken(hToken, SecurityImpersonation,
898 &hImpersonatedToken))
899 {
900 GENERIC_MAPPING mapping;
901 PRIVILEGE_SET privileges = { 0 };
902 DWORD grantedAccess = 0;
903 DWORD privilegesLength = sizeof(privileges);
904 BOOL result = FALSE;
905 DWORD genericAccessRights = GENERIC_WRITE;
906
907 mapping.GenericRead = FILE_GENERIC_READ;
908 mapping.GenericWrite = FILE_GENERIC_WRITE;
909 mapping.GenericExecute = FILE_GENERIC_EXECUTE;
910 mapping.GenericAll = FILE_ALL_ACCESS;
911
912 MapGenericMask(&genericAccessRights, &mapping);
913 if (AccessCheck(security, hImpersonatedToken,
914 genericAccessRights, &mapping,
915 &privileges, &privilegesLength,
916 &grantedAccess, &result))
917 {
918 answer = result;
919 }
920 CloseHandle(hImpersonatedToken);
921 }
922 CloseHandle(hToken);
923 }
924 free(security);
925 return (answer);
926 }
927