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: 84d4f14add0c4ee422532a0e2f5ed468f30a1407 $")
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 struct stat stat_buf;
437
438 /*
439 * Filter the filenames.
440 */
441 if (try[0] == '.') return false;
442
443 /*
444 * Check for valid characters
445 */
446 p = try;
447 while (*p) {
448 /*
449 * Control characters are invalid UTF-8, too.
450 */
451 len = fr_utf8_char((uint8_t const *) p, -1);
452 if (!len) return false;
453
454 /*
455 * The string is valid UTF-8, but is NOT an ASCII
456 * character. We allow it.
457 */
458 if (len > 1) {
459 p += len;
460 continue;
461 }
462
463 /*
464 * Limit the ASCII characters we allow.
465 */
466 if (isalpha((uint8_t)*p) ||
467 isdigit((uint8_t)*p) ||
468 (*p == '-') ||
469 (*p == '_') ||
470 (*p == '.')) {
471 p++;
472 continue;
473 }
474
475 /*
476 * Invalid character in filename, it's not a match.
477 */
478 return false;
479 }
480
481 len = p - try;
482 if (!len) return false;
483
484 /*
485 * Ignore files generated by deb / rpm packaging updates.
486 */
487 if ((len > 10) && (strncmp(&try[len - 10], ".dpkg-dist", 10) == 0)) return false;
488 if ((len > 9) && (strncmp(&try[len - 9], ".dpkg-old", 9) == 0)) return false;
489 if ((len > 7) && (strncmp(&try[len - 7], ".rpmnew", 9) == 0)) return false;
490 if ((len > 8) && (strncmp(&try[len - 8], ".rpmsave", 10) == 0)) return false;
491
492 /*
493 * strlcpy() returns the length of the input, which can be larger than the available space.
494 */
495 room = (iter->path + PATH_MAX) - iter->filename;
496
497 if (strlcpy(iter->filename, try, room) >= room) return false;
498
499 /*
500 * We only read normal files which are NOT executable, and symlinks.
501 */
502 if (stat(iter->path, &stat_buf) != 0) {
503 return false;
504 }
505
506 if (S_ISREG(stat_buf.st_mode)) {
507 if ((stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0) {
508 return false;
509 }
510 } else if (!S_ISLNK(stat_buf.st_mode)) { /* soft links are executable */
511 return false;
512 }
513
514 return true;
515}
516
517#ifdef HAVE_DIRENT_H
518/*
519 * Filter filenames when reading a directory.
520 */
521static int fr_globdir_dir_next(char const **filename, fr_globdir_iter_t *iter)
522{
523 struct dirent *dp;
524
525 fr_assert(iter->dir != NULL);
526
527 /*
528 * Reading all of the directory entries will result in
529 * reading ones we don't want, so we filter them.
530 */
531 while (true) {
532 errno = 0;
533 dp = readdir(iter->dir);
534 if (!dp) {
535 if (errno != 0) return -1;
536
537 *filename = NULL;
538 return 0;
539 }
540
541 if (!fr_globdir_file_ok(dp->d_name, iter)) continue;
542
543 /*
544 * It's mostly ASCII, and not a leftover file name, stop.
545 */
546 break;
547 }
548
549 *filename = iter->filename;
550 return 0;
551}
552#endif
553
554/** Create a full path from dir + pattern.
555 *
556 */
557static int fr_globdir_get_path(char const *dir, char const *pattern, fr_globdir_iter_t *iter)
558{
559 char const *p;
560 char *q;
561 bool slash;
562
563 /*
564 * Bootstrap the full path.
565 */
566 iter->path = malloc(PATH_MAX);
567 if (!iter->path) {
568 errno = ENOMEM;
569 return -1;
570 }
571
572 /*
573 * Sanity check the directory name.
574 *
575 * We assume that the directory name is otherwise safe.
576 */
577 p = dir;
578 q = iter->path;
579 while (*p) {
580 /*
581 * Suppress duplicate '/', and trailing '/'.
582 */
583 if (*p == '/') {
584 *(q++) = *(p++);
585
586 while (*p == '/') p++;
587
588 if (!p[1]) break;
589 continue;
590 }
591
592 if ((size_t) (q - iter->path) >= PATH_MAX) {
593 free(iter->path);
594 errno = ENOMEM;
595 return -1;
596 }
597
598 *(q++) = *(p++);
599 }
600
601 /*
602 * Add in a trailing '/' to the directory.
603 */
604 *(q++) = '/';
605 slash = true;
606 iter->filename = q;
607
608 /*
609 * Copy over the pattern name, but sanity check it.
610 */
611 p = pattern;
612 while (*p) {
613 if ((size_t) (q - iter->path) >= PATH_MAX) {
614 free(iter->path);
615 errno = ENOMEM;
616 return -1;
617 }
618
619 /*
620 * @todo - if it's a pattern, then handle [./] as a special case.
621 */
622
623 /*
624 * Handle the case of bad things after the directory name, too.
625 *
626 * But otherwise copy over any '/' which we see in the pattern.
627 */
628 if (!slash) {
629 slash = (*p == '/');
630 *(q++) = *(p++);
631
632 if (!slash) continue;
633 }
634
635 more_slash:
636 /*
637 * ///// --> /
638 */
639 while (*p == '/') p++;
640
641 /*
642 * foo/. may be special
643 */
644 if (*p != '.') {
645 slash = false;
646 continue;
647 }
648
649 /*
650 * foo/./ --> foo/
651 */
652 if (p[1] == '/') {
653 p += 2;
654 goto more_slash;
655 }
656
657 /*
658 * foo/../ --> error
659 */
660 if ((p[1] == '.') && (p[2] == '/')) {
661 free(iter->path);
662 errno = ENOENT;
663 return -1;
664 }
665
666 /*
667 * foo/.bar is OK
668 */
669 slash = false;
670 }
671 *q = '\0';
672
673 return 0;
674}
675
676/** Initialize an iterator over filenames.
677 *
678 * @param[out] filename the _relative_ filename which should be opened
679 * @param[in] dir the directory to read
680 * @param[in] pattern the filename or pattern to read. Cannot be an absolute path.
681 * @param[in,out] iter the iteration structure.
682 * @return
683 * - <0 on error
684 * - 0 on success, but no filename
685 * - 1 for "have file".
686 */
687int fr_globdir_iter_init(char const **filename, char const *dir, char const *pattern, fr_globdir_iter_t *iter)
688{
689 char const *p;
690 char const *to_open;
691
692 /*
693 * Default to files, which is the most common case.
694 */
695 *iter = (fr_globdir_iter_t) {
697 };
698
699 /*
700 * Figure out what kind of thing we're opening.
701 */
702 for (p = pattern; *p != '\0'; p++) {
703 /*
704 * foo/ - read the entire directory.
705 */
706 if (*p == '/') {
707 if (p[1]) continue;
708
709#ifdef HAVE_DIRENT_H
710 iter->type = FR_GLOBDIR_DIR;
711 break;
712#else
713 errno = ENOENT;
714 return -1;
715#endif
716 }
717
718 /*
719 * foo*.txt
720 * foo?.txt
721 * foo.[ch]
722 *
723 * - file globbing.
724 *
725 * File globbing is either full path, or a path relative to CWD. It is most notably NOT
726 * relative to the input "dir". So if there are globs, we need a full path.
727 */
728 if ((*p == '*') || (*p == '?') || (*p == '[')) {
729#ifdef HAVE_GLOB_H
730 /*
731 * @todo - call realpath() to get the canonical filename?
732 */
733 if ((pattern[0] != '/') && (dir[0] != '/')) {
734 errno = ENOENT;
735 return -1;
736 }
737
738 iter->type = FR_GLOBDIR_GLOB;
739 break;
740#else
741 errno = ENOENT;
742 return -1;
743#endif
744 }
745 }
746
747 /*
748 * The pattern is an absolute path, we just use that as-is.
749 */
750 if (pattern[0] == '/') {
751 to_open = pattern;
752
753 } else if (iter->type == FR_GLOBDIR_FILE) {
754 /*
755 * Short-circuit the common case for files. We're just opening a file, and the file is
756 * relative to the directory which was passed in.
757 */
758 *filename = pattern;
759 return 1;
760
761 } else {
762 /*
763 * Either dir is absolute and pattern is relative, or they're both relative. Merge dir +
764 * pattern into a path.
765 *
766 * Note that globs are relative to CWD, so relative globs must be passed in correctly
767 * (that's an @todo), otherwise they won't work.
768 */
769 if (fr_globdir_get_path(dir, pattern, iter) < 0) {
770 return -1;
771 }
772
773 to_open = iter->path;
774 }
775
776 /*
777 * Now that we know what type of thing it is, go do the
778 * right thing.
779 */
780 switch (iter->type) {
782 (void) fr_globdir_iter_free(iter);
783 errno = ENOENT;
784 return -1;
785
786 case FR_GLOBDIR_FILE:
787 *filename = iter->path;
788 break;
789
790#ifdef HAVE_DIRENT_H
791 case FR_GLOBDIR_DIR:
792 /*
793 * No directory means no file. The caller then decides if the file is required.
794 */
795 iter->dir = opendir(to_open);
796 if (!iter->dir) {
797 (void) fr_globdir_iter_free(iter);
798 return 0;
799 }
800
801 if (fr_globdir_dir_next(filename, iter) < 0) {
802 (void) fr_globdir_iter_free(iter);
803 return -1;
804 }
805 break;
806#endif
807
808#ifdef HAVE_GLOB_H
809 case FR_GLOBDIR_GLOB:
810 if (glob(to_open, GLOB_NOESCAPE | GLOB_ERR, NULL, &iter->glob) < 0) {
811 (void) fr_globdir_iter_free(iter);
812 return -1;
813 }
814
815 if (iter->glob.gl_pathc == 0) {
816 *filename = NULL;
817 } else {
818 iter->gl_current = 0;
819
820 /*
821 * @todo - check the filenames using fr_globdir_file_ok()
822 */
823 *filename = iter->glob.gl_pathv[iter->gl_current];
824 }
825 break;
826#endif
827 }
828
829 return 0 + (*filename != NULL);
830}
831
832/** Get the next filename.
833 *
834 * fr_globdir_iter_init()
835 * do {
836 * ... use filename
837 * } while (fr_globdir_iter_next() == 1);
838 * fr_globdir_iter_free()
839 *
840 * @return
841 * - <0 for error
842 * - 0 for done
843 * - 1 for "have filename"
844 */
845int fr_globdir_iter_next(char const **filename, fr_globdir_iter_t *iter)
846{
847 switch (iter->type) {
849 break;
850
851 case FR_GLOBDIR_FILE:
852 *filename = NULL;
853 return 0;
854
855#ifdef HAVE_DIRENT_H
856 case FR_GLOBDIR_DIR:
857 if (fr_globdir_dir_next(filename, iter) < 0) return -1;
858
859 return 0 + (*filename != NULL);
860#endif
861
862#ifdef HAVE_GLOB_H
863 case FR_GLOBDIR_GLOB:
864 iter->gl_current++;
865 if (iter->gl_current >= iter->glob.gl_pathc) {
866 return 0;
867 }
868
869 *filename = iter->glob.gl_pathv[iter->gl_current];
870 fr_assert(*filename != NULL);
871
872 return 1;
873#endif
874 }
875
876 return -1;
877}
878
880{
881 if (iter->path) {
882 free(iter->path);
883 iter->path = NULL;
884 }
885
886 switch (iter->type) {
888 return -1;
889
890 case FR_GLOBDIR_FILE:
891 break;
892
893#ifdef HAVE_DIRENT_H
894 case FR_GLOBDIR_DIR:
895 if (!iter->dir) return 0;
896
897 return closedir(iter->dir);
898#endif
899
900#ifdef HAVE_GLOB_H
901 case FR_GLOBDIR_GLOB:
902 globfree(&iter->glob);
903 break;
904#endif
905 }
906
907 return 0;
908}
#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:687
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:557
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:845
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:879
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