Line data Source code
1 : #include <apr_file_io.h>
2 : #include <apr_strings.h>
3 : #include <pcre.h>
4 :
5 : #include "debug.h"
6 : #include "ft_traverse.h"
7 : #include "ft_types.h"
8 : #include "ft_config.h"
9 :
10 : #define MATCH_VECTOR_SIZE 210
11 :
12 128 : static apr_status_t traverse_recursive(ft_conf_t *conf, const char *filename, apr_pool_t *gc_pool, struct stats const *stats,
13 : ft_ignore_context_t * parent_ctx)
14 : {
15 : char errbuf[128];
16 : apr_finfo_t finfo;
17 : apr_dir_t *dir;
18 128 : apr_int32_t statmask =
19 : APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_TYPE | APR_FINFO_USER | APR_FINFO_GROUP | APR_FINFO_UPROT |
20 : APR_FINFO_GPROT;
21 : apr_size_t fname_len;
22 : apr_uint32_t hash_value;
23 : apr_status_t status;
24 :
25 128 : if (!is_option_set(conf->mask, OPTION_FSYML)) {
26 128 : statmask |= APR_FINFO_LINK;
27 : }
28 :
29 128 : if (APR_SUCCESS != (status = apr_stat(&finfo, filename, statmask, gc_pool))) {
30 0 : if (is_option_set(conf->mask, OPTION_FSYML)) {
31 0 : statmask ^= APR_FINFO_LINK;
32 0 : if ((APR_SUCCESS == apr_stat(&finfo, filename, statmask, gc_pool)) && (finfo.filetype & APR_LNK)) {
33 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
34 0 : (void) fprintf(stderr, "Skipping : [%s] (broken link)\n", filename);
35 : }
36 0 : return APR_SUCCESS;
37 : }
38 : }
39 :
40 0 : DEBUG_ERR("error calling apr_stat on filename %s : %s", filename, apr_strerror(status, errbuf, 128));
41 0 : return status;
42 : }
43 :
44 128 : if (conf->respect_gitignore && parent_ctx) {
45 128 : ft_ignore_match_result_t match = ft_ignore_match(parent_ctx, filename, finfo.filetype == APR_DIR);
46 128 : if (match == FT_IGNORE_MATCH_IGNORED) {
47 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
48 0 : (void) fprintf(stderr, "Ignoring (gitignore): [%s]\n", filename);
49 : }
50 0 : return APR_SUCCESS;
51 : }
52 : }
53 :
54 128 : if (0 != conf->userid) {
55 128 : if (finfo.user == conf->userid) {
56 128 : if (!(APR_UREAD & finfo.protection)) {
57 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
58 0 : (void) fprintf(stderr, "Skipping : [%s] (bad permission)\n", filename);
59 : }
60 0 : return APR_SUCCESS;
61 : }
62 : }
63 0 : else if (NULL != napr_hash_search(conf->gids, &finfo.group, sizeof(gid_t), &hash_value)) {
64 0 : if (!(APR_GREAD & finfo.protection)) {
65 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
66 0 : (void) fprintf(stderr, "Skipping : [%s] (bad permission)\n", filename);
67 : }
68 0 : return APR_SUCCESS;
69 : }
70 : }
71 0 : else if (!(APR_WREAD & finfo.protection)) {
72 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
73 0 : (void) fprintf(stderr, "Skipping : [%s] (bad permission)\n", filename);
74 : }
75 0 : return APR_SUCCESS;
76 : }
77 : }
78 :
79 128 : if (APR_DIR == finfo.filetype) {
80 16 : if (0 != conf->userid) {
81 16 : if (finfo.user == conf->userid) {
82 16 : if (!(APR_UEXECUTE & finfo.protection)) {
83 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
84 0 : (void) fprintf(stderr, "Skipping : [%s] (bad permission)\n", filename);
85 : }
86 0 : return APR_SUCCESS;
87 : }
88 : }
89 0 : else if (NULL != napr_hash_search(conf->gids, &finfo.group, sizeof(gid_t), &hash_value)) {
90 0 : if (!(APR_GEXECUTE & finfo.protection)) {
91 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
92 0 : (void) fprintf(stderr, "Skipping : [%s] (bad permission)\n", filename);
93 : }
94 0 : return APR_SUCCESS;
95 : }
96 : }
97 0 : else if (!(APR_WEXECUTE & finfo.protection)) {
98 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
99 0 : (void) fprintf(stderr, "Skipping : [%s] (bad permission)\n", filename);
100 : }
101 0 : return APR_SUCCESS;
102 : }
103 : }
104 :
105 16 : if (APR_SUCCESS != (status = apr_dir_open(&dir, filename, gc_pool))) {
106 0 : DEBUG_ERR("error calling apr_dir_open(%s): %s", filename, apr_strerror(status, errbuf, 128));
107 0 : return status;
108 : }
109 16 : fname_len = strlen(filename);
110 :
111 16 : ft_ignore_context_t *current_ctx = parent_ctx;
112 16 : if (conf->respect_gitignore) {
113 16 : const char *gitignore_path = apr_pstrcat(gc_pool, filename, "/.gitignore", NULL);
114 : apr_finfo_t gitignore_finfo;
115 :
116 16 : if (APR_SUCCESS == apr_stat(&gitignore_finfo, gitignore_path, APR_FINFO_TYPE, gc_pool)
117 0 : && gitignore_finfo.filetype == APR_REG) {
118 0 : ft_ignore_context_t *local_ctx = ft_ignore_context_create(gc_pool, parent_ctx, filename);
119 0 : if (APR_SUCCESS == ft_ignore_load_file(local_ctx, gitignore_path)) {
120 0 : current_ctx = local_ctx;
121 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
122 0 : (void) fprintf(stderr, "Loaded .gitignore from: [%s]\n", filename);
123 : }
124 : }
125 : }
126 : }
127 156 : while ((APR_SUCCESS == (status = apr_dir_read(&finfo, APR_FINFO_NAME | APR_FINFO_TYPE, dir)))
128 156 : && (NULL != finfo.name)) {
129 : char *fullname;
130 : apr_size_t fullname_len;
131 : struct stats child;
132 : struct stats const *ancestor;
133 :
134 140 : if (NULL != napr_hash_search(conf->ig_files, finfo.name, strlen(finfo.name), NULL)) {
135 35 : continue;
136 : }
137 :
138 108 : if ('.' == finfo.name[0] && !is_option_set(conf->mask, OPTION_SHOW_HIDDEN)) {
139 2 : continue;
140 : }
141 :
142 106 : if (APR_DIR == finfo.filetype && !is_option_set(conf->mask, OPTION_RECSD)) {
143 1 : continue;
144 : }
145 :
146 105 : fullname = apr_pstrcat(gc_pool, filename, ('/' == filename[fname_len - 1]) ? "" : "/", finfo.name, NULL);
147 105 : fullname_len = strlen(fullname);
148 :
149 105 : if ((NULL != conf->ig_regex) && (APR_DIR != finfo.filetype)) {
150 : int match_code;
151 : int ovector[MATCH_VECTOR_SIZE];
152 0 : match_code = pcre_exec(conf->ig_regex, NULL, fullname, fullname_len, 0, 0, ovector, MATCH_VECTOR_SIZE);
153 0 : if (match_code >= 0) {
154 0 : continue;
155 : }
156 : }
157 :
158 105 : if ((NULL != conf->wl_regex) && (APR_DIR != finfo.filetype)) {
159 : int match_code;
160 : int ovector[MATCH_VECTOR_SIZE];
161 0 : match_code = pcre_exec(conf->wl_regex, NULL, fullname, fullname_len, 0, 0, ovector, MATCH_VECTOR_SIZE);
162 0 : if (match_code < 0) {
163 0 : continue;
164 : }
165 : }
166 :
167 105 : if (stats) {
168 2 : if (stats->stat.inode) {
169 2 : for (ancestor = stats; (ancestor = ancestor->parent) != 0;) {
170 0 : if (ancestor->stat.inode == stats->stat.inode && ancestor->stat.device == stats->stat.device) {
171 0 : if (is_option_set(conf->mask, OPTION_VERBO)) {
172 0 : (void) fprintf(stderr, "Warning: %s: recursive directory loop\n", filename);
173 : }
174 0 : return APR_SUCCESS;
175 : }
176 : }
177 : }
178 : }
179 105 : child.parent = stats;
180 105 : child.stat = finfo;
181 :
182 105 : status = traverse_recursive(conf, fullname, gc_pool, &child, current_ctx);
183 :
184 105 : if (APR_SUCCESS != status) {
185 0 : DEBUG_ERR("error recursively calling traverse_recursive: %s", apr_strerror(status, errbuf, 128));
186 0 : return status;
187 : }
188 : }
189 16 : if ((APR_SUCCESS != status) && (APR_ENOENT != status)) {
190 0 : DEBUG_ERR("error calling apr_dir_read: %s", apr_strerror(status, errbuf, 128));
191 0 : return status;
192 : }
193 :
194 16 : if (APR_SUCCESS != (status = apr_dir_close(dir))) {
195 0 : DEBUG_ERR("error calling apr_dir_close: %s", apr_strerror(status, errbuf, 128));
196 0 : return status;
197 : }
198 : }
199 112 : else if (APR_REG == finfo.filetype || ((APR_LNK == finfo.filetype) && (is_option_set(conf->mask, OPTION_FSYML)))) {
200 112 : if (finfo.size >= conf->minsize && (conf->maxsize == 0 || finfo.size <= conf->maxsize)) {
201 : ft_file_t *file;
202 : ft_fsize_t *fsize;
203 :
204 110 : file = apr_palloc(conf->pool, sizeof(struct ft_file_t));
205 110 : file->path = apr_pstrdup(conf->pool, filename);
206 110 : file->size = finfo.size;
207 110 : file->mtime = finfo.mtime;
208 :
209 110 : if ((conf->p_path) && (strlen(filename) >= conf->p_path_len)
210 0 : && ((is_option_set(conf->mask, OPTION_ICASE) && !strncasecmp(filename, conf->p_path, conf->p_path_len))
211 0 : || (!is_option_set(conf->mask, OPTION_ICASE) && !memcmp(filename, conf->p_path, conf->p_path_len)))) {
212 0 : file->prioritized |= 0x1;
213 : }
214 : else {
215 110 : file->prioritized &= 0x0;
216 : }
217 110 : file->cvec_ok &= 0x0;
218 110 : napr_heap_insert(conf->heap, file);
219 :
220 110 : if (NULL == (fsize = napr_hash_search(conf->sizes, &finfo.size, sizeof(apr_off_t), &hash_value))) {
221 40 : fsize = apr_palloc(conf->pool, sizeof(struct ft_fsize_t));
222 40 : fsize->val = finfo.size;
223 40 : fsize->chksum_array = NULL;
224 40 : fsize->nb_checksumed = 0;
225 40 : fsize->nb_files = 0;
226 40 : napr_hash_set(conf->sizes, fsize, hash_value);
227 : }
228 110 : fsize->nb_files++;
229 : }
230 : }
231 :
232 128 : return APR_SUCCESS;
233 : }
234 :
235 23 : apr_status_t ft_traverse_path(ft_conf_t *conf, const char *path)
236 : {
237 : apr_pool_t *gc_pool;
238 : apr_status_t status;
239 :
240 23 : if (APR_SUCCESS != (status = apr_pool_create(&gc_pool, conf->pool))) {
241 : char errbuf[128];
242 0 : DEBUG_ERR("error calling apr_pool_create: %s", apr_strerror(status, errbuf, 128));
243 0 : return status;
244 : }
245 :
246 23 : status = traverse_recursive(conf, path, gc_pool, NULL, conf->global_ignores);
247 :
248 23 : apr_pool_destroy(gc_pool);
249 :
250 23 : return status;
251 : }
|