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