The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
file.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Various miscellaneous functions to manipulate files and paths
18 *
19 * @file src/lib/util/file.c
20 *
21 * @copyright 2019 The FreeRADIUS project
22 * @copyright 2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 3f8b7bc786dee6703308a2e0b3e3c7624ccc7844 $")
25
26#include <sys/param.h>
27#include <fcntl.h>
28#include <stdio.h>
29#include <sys/stat.h>
30
31#include <freeradius-devel/util/file.h>
32#include <freeradius-devel/util/strerror.h>
33#include <freeradius-devel/util/syserror.h>
34#include <freeradius-devel/util/value.h>
35
36/** Callback for the common case of chown() of the directory.
37 *
38 */
39int fr_mkdir_chown(int fd, char const *path, void *uctx)
40{
41 fr_mkdir_chown_t const *ctx = uctx;
42
43 if ((ctx->uid == (uid_t) -1) && (ctx->gid == (gid_t) -1)) return 0;
44
45 if (fchown(fd, ctx->uid, ctx->gid) < 0) {
46 fr_strerror_printf("Failed changing ownership on directory \"%s\": %s",
47 path, fr_syserror(errno));
48 return -1;
49 }
50
51 return 0;
52}
53
54
55static ssize_t _fr_mkdir(int *fd_out, char *start, char *path, mode_t mode, fr_mkdir_func_t func, void *uctx)
56{
57 int ret, fd;
58 char *p;
59
60 /*
61 * Try to make the path. If it exists, chmod it.
62 * If a path doesn't exist, that's OK. Otherwise
63 * return with an error.
64 *
65 * Directories permissions are initially set so
66 * that only we should have access. This prevents
67 * an attacker removing them and swapping them
68 * out for a link to somewhere else.
69 * We change them to the correct permissions later.
70 */
71 ret = mkdir(path, 0700);
72 if (ret >= 0) {
73 fd = open(path, O_DIRECTORY);
74 if (fd < 0) {
75 fr_strerror_printf("Failed opening directory we created: %s",
76 fr_syserror(errno));
77 mkdir_error:
78 p = strrchr(path, FR_DIR_SEP);
79 if (!p) return start - path;
80
81 return start - p;
82 }
83
84 if (fchmod(fd, mode) < 0) {
85 fr_strerror_printf("Failed setting permissions on directory "
86 "we created: %s", fr_syserror(errno));
87 close(fd);
88 goto mkdir_error;
89 }
90 *fd_out = fd;
91 return strlen(start);
92 }
93
94 /*
95 * EEXIST is only OK when we're calling mkdir on the
96 * whole path, and it exists which should have been
97 * caught by fr_mkdir before calling this function.
98 *
99 * Unless we're running in an environment with multiple
100 * processes, in which case EEXIST means that another
101 * process created this directory in between our check
102 * and our creation.
103 */
104 if (errno == EEXIST) {
105 fd = open(path, O_DIRECTORY);
106 if (fd < 0) {
107 fr_strerror_printf("Failed opening existing directory: %s", fr_syserror(errno));
108 goto mkdir_error;
109 }
110 *fd_out = fd;
111 return strlen(start);
112 }
113
114 /*
115 * ENOENT means we're trying to create too much path
116 * at once. Recurse to discover the deepest path
117 * component that already exists.
118 */
119 if (errno != ENOENT) {
120 fr_strerror_printf("Failed creating directory path: %s", fr_syserror(errno));
121 goto mkdir_error;
122 }
123
124 /*
125 * A component in the path doesn't
126 * exist. Look for the LAST path name. Try
127 * to create that. If there's an error, we leave
128 * the path path as the one at which the
129 * error occurred.
130 */
131 p = strrchr(path, FR_DIR_SEP);
132 if (!p || (p == path)) return start - path; /* last path component and we've previously failed */
133
134 *p = '\0';
135 if (_fr_mkdir(fd_out, start, path, mode, func, uctx) <= 0) return start - p;
136
137 fr_assert_msg((*fd_out) >= 0, "Logic error - Bad FD %i", *fd_out);
138
139 /*
140 * At this point *fd_out, should be an FD
141 * for the containing directory.
142 *
143 * Dir may already exist if we're racing
144 * other processes as we do in CI.
145 */
146 if (mkdirat(*fd_out, p + 1, 0700) < 0) {
147 /*
148 * This is usually because of a race with
149 * other processes trying to create the
150 * same directory.
151 */
152 if (errno == EEXIST) {
153 fd = openat(*fd_out, p + 1, O_DIRECTORY);
154 if (fd < 0) {
155 fr_strerror_printf_push("Failed opening existing directory path component: %s",
156 fr_syserror(errno));
157 goto mkdirat_error;
158 }
159 *p = FR_DIR_SEP;
160 goto done;
161 }
162
163 fr_strerror_printf_push("Failed creating directory path component: %s", fr_syserror(errno));
164
165 mkdirat_error:
166 close(*fd_out);
167 *fd_out = -1;
168 return start - p;
169 }
170
171 fd = openat(*fd_out, p + 1, O_DIRECTORY);
172 if (fd < 0) {
173 fr_strerror_printf_push("Failed opening directory we "
174 "created: %s", fr_syserror(errno));
175 goto mkdirat_error;
176 }
177
178 if (fchmod(fd, mode) < 0) {
179 fr_strerror_printf_push("Failed setting permissions on "
180 "directory we created: %s", fr_syserror(errno));
181 goto mkdirat_error;
182 }
183
184 *p = FR_DIR_SEP;
185
186 /*
187 * Call the user function
188 */
189 if (func && (func(fd, path, uctx) < 0)) {
190 fr_strerror_printf_push("Callback failed processing directory \"%s\"", path);
191 goto mkdirat_error;
192 }
193
194 /*
195 * Swap active *fd_out to point to the dir
196 * we just created.
197 */
198done:
199 close(*fd_out);
200 *fd_out = fd;
201
202 return strlen(start);
203}
204
205/** Create directories that are missing in the specified path
206 *
207 * @param[out] fd_out If not NULL, will contain a file descriptor
208 * for the deepest path component created.
209 * @param[in] path to populate with directories.
210 * @param[in] len Length of the path string.
211 * @param[in] mode for new directories.
212 * @param[in] func to call each time a new directory is created.
213 * @param[in] uctx to pass to func.
214 * @return
215 * - >0 on success.
216 * - <= 0 on failure. Negative offset pointing to the
217 * path separator of the path component that caused the error.
218 */
219ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
220{
221 char *our_path;
222 int fd = -1;
223 ssize_t slen;
224
225 if (len < 0) len = strlen(path);
226 if (len == 0) return 0;
227
228 /*
229 * Fast path (har har)
230 *
231 * Avoids duping the input for the
232 * common case.
233 */
234 fd = open(path, O_DIRECTORY);
235 if (fd >= 0) goto done;
236
237 /*
238 * Dup the amount of input path
239 * we need.
240 */
241 our_path = talloc_bstrndup(NULL, path, (size_t)len);
242 if (!our_path) {
243 fr_strerror_const("Out of memory");
244 return -1;
245 }
246
247 fr_strerror_clear(); /* We make liberal use of push */
248
249 /*
250 * Call the recursive function to
251 * create any missing dirs in the
252 * specified path.
253 */
254 slen = _fr_mkdir(&fd, our_path, our_path, mode, func, uctx);
255 talloc_free(our_path);
256 if (slen <= 0) return slen;
257
258done:
259 if (fd_out) {
260 *fd_out = fd;
261 } else {
262 close(fd);
263 }
264
265 return len;
266}
267
268/** Convenience wrapper around realpath
269 *
270 * Wraps realpath, but takes a path with an explicit length, and returns
271 * the result in a talloced buffer.
272 *
273 * On error, errno is set, and the string version of the error is
274 * available with fr_strerror().
275 *
276 * @param[in] ctx in which to allocate the result.
277 * @param[in] path To convert to an absolute path.
278 * @param[in] len How much of 'path' to read. If < 0, then
279 * the entire path will be used.
280 * @return
281 * - NULL on error.
282 * - The absolute version of the input path on success.
283 */
284char *fr_realpath(TALLOC_CTX *ctx, char const *path, ssize_t len)
285{
286 char *tmp_path = NULL, *abs_path, *talloc_abs_path;
287
288 if (len > 0) path = tmp_path = talloc_bstrndup(NULL, path, (size_t)len);
289
290 abs_path = realpath(path, NULL);
291 if (!abs_path) {
292 fr_strerror_printf("Failed resolving path \"%pV\": %s",
293 fr_box_strvalue_buffer(path), fr_syserror(errno));
294 talloc_free(tmp_path);
295 return NULL;
296 }
297
298 talloc_free(tmp_path);
299
300 talloc_abs_path = talloc_strdup(ctx, abs_path);
301 free(abs_path);
302 if (!talloc_abs_path) {
303 fr_strerror_const("Out of Memory");
304 return NULL;
305 }
306
307 return talloc_abs_path;
308}
309
310/** Create an empty file
311 *
312 * @param[out] fd_out If not NULL, will contain a file descriptor
313 * for the file we just opened.
314 * @param[in] filename path to file.
315 * @param[in] mode Specifies the file mode bits be applied.
316 * @param[in] mkdir Whether we should create directories
317 * for any missing path components.
318 * @param[in] dir_mode Mode of any directories created.
319 * @return
320 * - >0 on success.
321 * - <= 0 on failure. Error available in error stack (use fr_strerror())
322 */
323ssize_t fr_touch(int *fd_out, char const *filename, mode_t mode, bool mkdir, mode_t dir_mode) {
324 int fd;
325
326 fd = open(filename, O_WRONLY | O_CREAT, mode);
327 if (fd < 0) {
328 ssize_t slen = 0;
329 char *q;
330
331 if (mkdir && (errno == ENOENT) && (q = strrchr(filename, FR_DIR_SEP))) {
332 int dir_fd = -1;
333
334 slen = fr_mkdir(&dir_fd, filename, q - filename, dir_mode, NULL, NULL);
335 if((slen <= 0) || (dir_fd < 0)) return slen;
336
337 fd = openat(dir_fd, q + 1, O_WRONLY | O_CREAT, mode);
338 if (fd >= 0) {
339 close(dir_fd);
340 close(fd);
341 return strlen(filename);
342 }
343 close(dir_fd);
344 slen = -(q - filename);
345 }
346 fr_strerror_printf("Failed creating file: %s", fr_syserror(errno));
347 return slen;
348 }
349
350 if (fd_out) {
351 *fd_out = fd;
352 } else {
353 close(fd);
354 }
355
356 return strlen(filename);
357}
358
359/** Remove a regular file from the filesystem
360 *
361 * @param[in] filename path to file.
362 * @return
363 * - -1 On error.
364 * - 0 if the file was removed.
365 * - 1 if the file didn't exist.
366 */
367int fr_unlink(char const *filename) {
368 if (unlink(filename) == 0) return 0;
369
370 if (errno == ENOENT) return 1;
371
372 fr_strerror_printf("Failed removing regular file \"%s\": %s", filename, fr_syserror(errno));
373
374 return -1;
375}
376
377/** Intended to be used in logging functions to make output more readable
378 *
379 * This function is not performant and should probably not be used at runtime.
380 *
381 * @param[in] filename to strip working directory from.
382 * @return Position in filename after our working directory.
383 */
384char const *fr_cwd_strip(char const *filename)
385{
386 static char our_wd[MAXPATHLEN];
387 char *found;
388
389 if (!getcwd(our_wd, sizeof(our_wd))) return filename;
390
391 found = strstr(filename, our_wd);
392 if (found && (found == our_wd)) {
393 filename += strlen(our_wd);
394 while (*filename == '/') filename++;
395 return filename;
396 }
397
398 return filename;
399}
400
401/** From a pathname, return fd and filename needed for *at() functions
402 *
403 * @param[in] dirfd points to place to store the dirfd
404 * @param[in] filename points to placd to store a pointer into pathname
405 * that points to the filename
406 * @param[in] pathname the full pathname of the file
407 *
408 * @return
409 * - -1 on error
410 * - 0 on success
411 */
412int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
413{
414 char const *last_slash = strrchr(pathname, '/');
415
416 if (last_slash == NULL) {
417 *filename = pathname;
418 *dirfd = AT_FDCWD;
419 return 0;
420 }
421 {
422 char dirpath[(last_slash - pathname) + 1];
423
424 memcpy(dirpath, pathname, last_slash - pathname);
425 dirpath[last_slash - pathname] = '\0';
426 *filename = last_slash + 1;
427 *dirfd = open(dirpath, O_DIRECTORY);
428 return (*dirfd < 0) ? -1 : 0;
429 }
430}
431
432static bool fr_globdir_file_ok(char const *try, fr_globdir_iter_t *iter)
433{
434 size_t len, room;
435 char const *p;
436 char *filename;
437 struct stat stat_buf;
438
439 /*
440 * Filter the filenames.
441 */
442 if (try[0] == '.') return false;
443
444 /*
445 * Check for valid characters
446 */
447 p = try;
448 while (*p) {
449 /*
450 * Control characters are invalid UTF-8, too.
451 */
452 len = fr_utf8_char((uint8_t const *) p, -1);
453 if (!len) return false;
454
455 /*
456 * The string is valid UTF-8, but is NOT an ASCII
457 * character. We allow it.
458 */
459 if (len > 1) {
460 p += len;
461 continue;
462 }
463
464 /*
465 * Limit the ASCII characters we allow.
466 */
467 if (isalpha((uint8_t)*p) ||
468 isdigit((uint8_t)*p) ||
469 (*p == '-') ||
470 (*p == '_') ||
471 (*p == '.')) {
472 p++;
473 continue;
474 }
475
476 /*
477 * Invalid character in filename, it's not a match.
478 */
479 return false;
480 }
481
482 len = p - try;
483 if (!len) return false;
484
485 /*
486 * Ignore files generated by deb / rpm packaging updates.
487 */
488 if ((len > 10) && (strncmp(&try[len - 10], ".dpkg-dist", 10) == 0)) return false;
489 if ((len > 9) && (strncmp(&try[len - 9], ".dpkg-old", 9) == 0)) return false;
490 if ((len > 7) && (strncmp(&try[len - 7], ".rpmnew", 9) == 0)) return false;
491 if ((len > 8) && (strncmp(&try[len - 8], ".rpmsave", 10) == 0)) return false;
492
493 /*
494 * When reading all files in a directory, iter->filename points to the directory
495 * name which is being read. The file being tested needs to be added after that.
496 */
497 filename = iter->filename + (iter->type == FR_GLOBDIR_DIR ? strlen(iter->filename) : 0);
498
499 /*
500 * strlcpy() returns the length of the input, which can be larger than the available space.
501 */
502 room = (iter->path + PATH_MAX) - filename;
503
504 if (strlcpy(filename, try, room) >= room) return false;
505
506 /*
507 * We only read normal files which are NOT executable, and symlinks.
508 */
509 if (stat(iter->path, &stat_buf) != 0) {
510 return false;
511 }
512
513 if (S_ISREG(stat_buf.st_mode)) {
514 if ((stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0) {
515 return false;
516 }
517 } else if (!S_ISLNK(stat_buf.st_mode)) { /* soft links are executable */
518 return false;
519 }
520
521 return true;
522}
523
524#ifdef HAVE_DIRENT_H
525/*
526 * Filter filenames when reading a directory.
527 */
528static int fr_globdir_dir_next(char const **filename, fr_globdir_iter_t *iter)
529{
530 struct dirent *dp;
531
532 fr_assert(iter->dir != NULL);
533
534 /*
535 * Reading all of the directory entries will result in
536 * reading ones we don't want, so we filter them.
537 */
538 while (true) {
539 errno = 0;
540 dp = readdir(iter->dir);
541 if (!dp) {
542 if (errno != 0) return -1;
543
544 *filename = NULL;
545 return 0;
546 }
547
548 if (!fr_globdir_file_ok(dp->d_name, iter)) continue;
549
550 /*
551 * It's mostly ASCII, and not a leftover file name, stop.
552 */
553 break;
554 }
555
556 *filename = iter->filename;
557 return 0;
558}
559#endif
560
561/** Create a full path from dir + pattern.
562 *
563 */
564static int fr_globdir_get_path(char const *dir, char const *pattern, fr_globdir_iter_t *iter)
565{
566 char const *p;
567 char *q;
568 bool slash;
569
570 /*
571 * Bootstrap the full path.
572 */
573 iter->path = malloc(PATH_MAX);
574 if (!iter->path) {
575 errno = ENOMEM;
576 return -1;
577 }
578
579 /*
580 * Sanity check the directory name.
581 *
582 * We assume that the directory name is otherwise safe.
583 */
584 p = dir;
585 q = iter->path;
586 while (*p) {
587 /*
588 * Suppress duplicate '/', and trailing '/'.
589 */
590 if (*p == '/') {
591 *(q++) = *(p++);
592
593 while (*p == '/') p++;
594
595 if (!p[1]) break;
596 continue;
597 }
598
599 if ((size_t) (q - iter->path) >= PATH_MAX) {
600 free(iter->path);
601 errno = ENOMEM;
602 return -1;
603 }
604
605 *(q++) = *(p++);
606 }
607
608 /*
609 * Add in a trailing '/' to the directory.
610 */
611 *(q++) = '/';
612 slash = true;
613 iter->filename = q;
614
615 /*
616 * Copy over the pattern name, but sanity check it.
617 */
618 p = pattern;
619 while (*p) {
620 if ((size_t) (q - iter->path) >= PATH_MAX) {
621 free(iter->path);
622 errno = ENOMEM;
623 return -1;
624 }
625
626 /*
627 * @todo - if it's a pattern, then handle [./] as a special case.
628 */
629
630 /*
631 * Handle the case of bad things after the directory name, too.
632 *
633 * But otherwise copy over any '/' which we see in the pattern.
634 */
635 if (!slash) {
636 slash = (*p == '/');
637 *(q++) = *(p++);
638
639 if (!slash) continue;
640 }
641
642 more_slash:
643 /*
644 * ///// --> /
645 */
646 while (*p == '/') p++;
647
648 /*
649 * foo/. may be special
650 */
651 if (*p != '.') {
652 slash = false;
653 continue;
654 }
655
656 /*
657 * foo/./ --> foo/
658 */
659 if (p[1] == '/') {
660 p += 2;
661 goto more_slash;
662 }
663
664 /*
665 * foo/../ --> error
666 */
667 if ((p[1] == '.') && (p[2] == '/')) {
668 free(iter->path);
669 errno = ENOENT;
670 return -1;
671 }
672
673 /*
674 * foo/.bar is OK
675 */
676 slash = false;
677 }
678 *q = '\0';
679
680 return 0;
681}
682
683/** Initialize an iterator over filenames.
684 *
685 * @param[out] filename the _relative_ filename which should be opened
686 * @param[in] dir the directory to read
687 * @param[in] pattern the filename or pattern to read. Cannot be an absolute path.
688 * @param[in,out] iter the iteration structure.
689 * @return
690 * - <0 on error
691 * - 0 on success, but no filename
692 * - 1 for "have file".
693 */
694int fr_globdir_iter_init(char const **filename, char const *dir, char const *pattern, fr_globdir_iter_t *iter)
695{
696 char const *p;
697 char const *to_open;
698
699 /*
700 * Default to files, which is the most common case.
701 */
702 *iter = (fr_globdir_iter_t) {
704 };
705
706 /*
707 * Figure out what kind of thing we're opening.
708 */
709 for (p = pattern; *p != '\0'; p++) {
710 /*
711 * foo/ - read the entire directory.
712 */
713 if (*p == '/') {
714 if (p[1]) continue;
715
716#ifdef HAVE_DIRENT_H
717 iter->type = FR_GLOBDIR_DIR;
718 break;
719#else
720 errno = ENOENT;
721 return -1;
722#endif
723 }
724
725 /*
726 * foo*.txt
727 * foo?.txt
728 * foo.[ch]
729 *
730 * - file globbing.
731 *
732 * File globbing is either full path, or a path relative to CWD. It is most notably NOT
733 * relative to the input "dir". So if there are globs, we need a full path.
734 */
735 if ((*p == '*') || (*p == '?') || (*p == '[')) {
736#ifdef HAVE_GLOB_H
737 /*
738 * @todo - call realpath() to get the canonical filename?
739 */
740 if ((pattern[0] != '/') && (dir[0] != '/')) {
741 errno = ENOENT;
742 return -1;
743 }
744
745 iter->type = FR_GLOBDIR_GLOB;
746 break;
747#else
748 errno = ENOENT;
749 return -1;
750#endif
751 }
752 }
753
754 /*
755 * The pattern is an absolute path, we just use that as-is.
756 */
757 if (pattern[0] == '/') {
758 to_open = pattern;
759
760 } else if (iter->type == FR_GLOBDIR_FILE) {
761 /*
762 * Short-circuit the common case for files. We're just opening a file, and the file is
763 * relative to the directory which was passed in.
764 */
765 *filename = pattern;
766 return 1;
767
768 } else {
769 /*
770 * Either dir is absolute and pattern is relative, or they're both relative. Merge dir +
771 * pattern into a path.
772 *
773 * Note that globs are relative to CWD, so relative globs must be passed in correctly
774 * (that's an @todo), otherwise they won't work.
775 */
776 if (fr_globdir_get_path(dir, pattern, iter) < 0) {
777 return -1;
778 }
779
780 to_open = iter->path;
781 }
782
783 /*
784 * Now that we know what type of thing it is, go do the
785 * right thing.
786 */
787 switch (iter->type) {
789 (void) fr_globdir_iter_free(iter);
790 errno = ENOENT;
791 return -1;
792
793 case FR_GLOBDIR_FILE:
794 *filename = iter->path;
795 break;
796
797#ifdef HAVE_DIRENT_H
798 case FR_GLOBDIR_DIR:
799 /*
800 * No directory means no file. The caller then decides if the file is required.
801 */
802 iter->dir = opendir(to_open);
803 if (!iter->dir) {
804 (void) fr_globdir_iter_free(iter);
805 return 0;
806 }
807
808 if (fr_globdir_dir_next(filename, iter) < 0) {
809 (void) fr_globdir_iter_free(iter);
810 return -1;
811 }
812 break;
813#endif
814
815#ifdef HAVE_GLOB_H
816 case FR_GLOBDIR_GLOB:
817 if (glob(to_open, GLOB_NOESCAPE | GLOB_ERR, NULL, &iter->glob) < 0) {
818 (void) fr_globdir_iter_free(iter);
819 return -1;
820 }
821
822 if (iter->glob.gl_pathc == 0) {
823 *filename = NULL;
824 } else {
825 iter->gl_current = 0;
826
827 /*
828 * @todo - check the filenames using fr_globdir_file_ok()
829 */
830 *filename = iter->glob.gl_pathv[iter->gl_current];
831 }
832 break;
833#endif
834 }
835
836 return 0 + (*filename != NULL);
837}
838
839/** Get the next filename.
840 *
841 * fr_globdir_iter_init()
842 * do {
843 * ... use filename
844 * } while (fr_globdir_iter_next() == 1);
845 * fr_globdir_iter_free()
846 *
847 * @return
848 * - <0 for error
849 * - 0 for done
850 * - 1 for "have filename"
851 */
852int fr_globdir_iter_next(char const **filename, fr_globdir_iter_t *iter)
853{
854 switch (iter->type) {
856 break;
857
858 case FR_GLOBDIR_FILE:
859 *filename = NULL;
860 return 0;
861
862#ifdef HAVE_DIRENT_H
863 case FR_GLOBDIR_DIR:
864 if (fr_globdir_dir_next(filename, iter) < 0) return -1;
865
866 return 0 + (*filename != NULL);
867#endif
868
869#ifdef HAVE_GLOB_H
870 case FR_GLOBDIR_GLOB:
871 iter->gl_current++;
872 if (iter->gl_current >= iter->glob.gl_pathc) {
873 return 0;
874 }
875
876 *filename = iter->glob.gl_pathv[iter->gl_current];
877 fr_assert(*filename != NULL);
878
879 return 1;
880#endif
881 }
882
883 return -1;
884}
885
887{
888 if (iter->path) {
889 free(iter->path);
890 iter->path = NULL;
891 }
892
893 switch (iter->type) {
895 return -1;
896
897 case FR_GLOBDIR_FILE:
898 break;
899
900#ifdef HAVE_DIRENT_H
901 case FR_GLOBDIR_DIR:
902 if (!iter->dir) return 0;
903
904 return closedir(iter->dir);
905#endif
906
907#ifdef HAVE_GLOB_H
908 case FR_GLOBDIR_GLOB:
909 globfree(&iter->glob);
910 break;
911#endif
912 }
913
914 return 0;
915}
#define RCSID(id)
Definition build.h:485
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:202
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
Definition file.c:219
int fr_unlink(char const *filename)
Remove a regular file from the filesystem.
Definition file.c:367
int fr_globdir_iter_init(char const **filename, char const *dir, char const *pattern, fr_globdir_iter_t *iter)
Initialize an iterator over filenames.
Definition file.c:694
static int fr_globdir_get_path(char const *dir, char const *pattern, fr_globdir_iter_t *iter)
Create a full path from dir + pattern.
Definition file.c:564
int fr_mkdir_chown(int fd, char const *path, void *uctx)
Callback for the common case of chown() of the directory.
Definition file.c:39
static bool fr_globdir_file_ok(char const *try, fr_globdir_iter_t *iter)
Definition file.c:432
ssize_t fr_touch(int *fd_out, char const *filename, mode_t mode, bool mkdir, mode_t dir_mode)
Create an empty file.
Definition file.c:323
int fr_globdir_iter_next(char const **filename, fr_globdir_iter_t *iter)
Get the next filename.
Definition file.c:852
char const * fr_cwd_strip(char const *filename)
Intended to be used in logging functions to make output more readable.
Definition file.c:384
int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
From a pathname, return fd and filename needed for *at() functions.
Definition file.c:412
char * fr_realpath(TALLOC_CTX *ctx, char const *path, ssize_t len)
Convenience wrapper around realpath.
Definition file.c:284
int fr_globdir_iter_free(fr_globdir_iter_t *iter)
Definition file.c:886
static ssize_t _fr_mkdir(int *fd_out, char *start, char *path, mode_t mode, fr_mkdir_func_t func, void *uctx)
Definition file.c:55
fr_globdir_type_t type
Definition file.h:87
char * path
Definition file.h:89
uid_t uid
Definition file.h:56
gid_t gid
Definition file.h:57
@ FR_GLOBDIR_FILE
Definition file.h:77
@ FR_GLOBDIR_INVALID
Definition file.h:76
char * filename
Definition file.h:90
int(* fr_mkdir_func_t)(int fd, char const *path, void *uctx)
Callback for allowing additional operations on newly created directories.
Definition file.h:53
free(array)
talloc_free(reap)
long int ssize_t
unsigned char uint8_t
unsigned int mode_t
size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
Definition print.c:39
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:81
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
close(uq->fd)
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_box_strvalue_buffer(_val)
Definition value.h:311