util: Add file tree walk interface
[slow/samba.git] / lib / util / tftw.c
1 /*
2  * Copyright (c) 2008-2018 by Andreas Schneider <asn@samba.org>
3  *
4  * Adopted from the csync source code
5  */
6
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <dirent.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <sys/stat.h>
14 #include "memory.h"
15 #include "debug.h"
16 #include "replace.h"
17 #include "system/locale.h"
18 #include "lib/util/asn1.h"
19 #include "lib/util/debug.h"
20 #include "lib/util/samba_util.h"
21
22 #include "tftw.h"
23
24
25 int tftw(TALLOC_CTX *mem_ctx, const char *fpath, tftw_walker_fn fn, size_t depth, void *userdata)
26 {
27         char *filename = NULL;
28         char *d_name = NULL;
29         DIR *dh = NULL;
30         struct dirent *dirent = NULL;
31         struct stat sb = {0};
32         int rc = 0;
33
34         if (fpath[0] == '\0') {
35                 errno = ENOENT;
36                 goto error;
37         }
38
39         if ((dh = opendir(fpath)) == NULL) {
40                 /* permission denied */
41                 if (errno == EACCES) {
42                         return 0;
43                 } else {
44                         DBG_ERR("opendir failed for: [%s]\n", strerror(errno));
45                         goto error;
46                 }
47         }
48
49         while ((dirent = readdir(dh))) {
50                 int flag;
51
52                 d_name = dirent->d_name;
53                 if (d_name == NULL) {
54                         goto error;
55                 }
56
57                 /* skip "." and ".." */
58                 if (d_name[0] == '.' && (d_name[1] == '\0'
59                                         || (d_name[1] == '.' && d_name[2] == '\0'))) {
60                         dirent = NULL;
61                         continue;
62                 }
63
64                 filename = talloc_asprintf(mem_ctx, "%s/%s", fpath, d_name);
65                 if (filename == NULL) {
66                         goto error;
67                 }
68
69                 rc = lstat(filename, &sb);
70                 if (rc < 0) {
71                         dirent = NULL;
72                         goto error;
73                 }
74
75                 switch (sb.st_mode & S_IFMT) {
76                         case S_IFLNK:
77                                 flag = TFTW_FLAG_SLINK;
78                                 break;
79                         case S_IFDIR:
80                                 flag = TFTW_FLAG_DIR;
81                                 break;
82                         case S_IFBLK:
83                         case S_IFCHR:
84                         case S_IFSOCK:
85                         case S_IFIFO:
86                                 flag = TFTW_FLAG_SPEC;
87                                 break;
88                         default:
89                                 flag = TFTW_FLAG_FILE;
90                                 break;
91                 }
92
93                 DBG_INFO("walk: [%s]\n", filename);
94
95                 /* Call walker function for each file */
96                 rc = fn(mem_ctx, filename, &sb, flag, userdata);
97
98                 if (rc < 0) {
99                         DBG_ERR("provided callback fn() failed: [%s]\n",
100                                 strerror(errno));
101                         closedir(dh);
102                         goto done;
103                 }
104
105                 if (flag == TFTW_FLAG_DIR && depth) {
106                         rc = tftw(mem_ctx, filename, fn, depth - 1, userdata);
107                         if (rc < 0) {
108                                 closedir(dh);
109                                 goto done;
110                         }
111                 }
112                 TALLOC_FREE(filename);
113                 dirent = NULL;
114         }
115         closedir(dh);
116
117 done:
118         TALLOC_FREE(filename);
119         return rc;
120 error:
121         if (dh != NULL) {
122                 closedir(dh);
123         }
124         TALLOC_FREE(filename);
125         return -1;
126 }