diff options
Diffstat (limited to 'src/fs/fs_dirmetascan.c')
-rw-r--r-- | src/fs/fs_dirmetascan.c | 496 |
1 files changed, 0 insertions, 496 deletions
diff --git a/src/fs/fs_dirmetascan.c b/src/fs/fs_dirmetascan.c deleted file mode 100644 index 2379e29ce..000000000 --- a/src/fs/fs_dirmetascan.c +++ /dev/null | |||
@@ -1,496 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2005-2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file fs/fs_dirmetascan.c | ||
23 | * @brief code to asynchronously build a 'struct GNUNET_FS_ShareTreeItem' | ||
24 | * from an on-disk directory for publishing; use the 'gnunet-helper-fs-publish'. | ||
25 | * @author LRN | ||
26 | * @author Christian Grothoff | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | |||
30 | #include "gnunet_fs_service.h" | ||
31 | #include "gnunet_scheduler_lib.h" | ||
32 | #include <pthread.h> | ||
33 | |||
34 | |||
35 | /** | ||
36 | * An opaque structure a pointer to which is returned to the | ||
37 | * caller to be used to control the scanner. | ||
38 | */ | ||
39 | struct GNUNET_FS_DirScanner | ||
40 | { | ||
41 | /** | ||
42 | * Helper process. | ||
43 | */ | ||
44 | struct GNUNET_HELPER_Handle *helper; | ||
45 | |||
46 | /** | ||
47 | * Expanded filename (as given by the scan initiator). | ||
48 | * The scanner thread stores a copy here, and frees it when it finishes. | ||
49 | */ | ||
50 | char *filename_expanded; | ||
51 | |||
52 | /** | ||
53 | * Second argument to helper process. | ||
54 | */ | ||
55 | char *ex_arg; | ||
56 | |||
57 | /** | ||
58 | * The function that will be called every time there's a progress | ||
59 | * message. | ||
60 | */ | ||
61 | GNUNET_FS_DirScannerProgressCallback progress_callback; | ||
62 | |||
63 | /** | ||
64 | * A closure for progress_callback. | ||
65 | */ | ||
66 | void *progress_callback_cls; | ||
67 | |||
68 | /** | ||
69 | * After the scan is finished, it will contain a pointer to the | ||
70 | * top-level directory entry in the directory tree built by the | ||
71 | * scanner. | ||
72 | */ | ||
73 | struct GNUNET_FS_ShareTreeItem *toplevel; | ||
74 | |||
75 | /** | ||
76 | * Current position during processing. | ||
77 | */ | ||
78 | struct GNUNET_FS_ShareTreeItem *pos; | ||
79 | |||
80 | /** | ||
81 | * Task scheduled when we are done. | ||
82 | */ | ||
83 | struct GNUNET_SCHEDULER_Task *stop_task; | ||
84 | |||
85 | /** | ||
86 | * Arguments for helper. | ||
87 | */ | ||
88 | char *args[4]; | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Abort the scan. Must not be called from within the progress_callback | ||
94 | * function. | ||
95 | * | ||
96 | * @param ds directory scanner structure | ||
97 | */ | ||
98 | void | ||
99 | GNUNET_FS_directory_scan_abort (struct GNUNET_FS_DirScanner *ds) | ||
100 | { | ||
101 | /* terminate helper */ | ||
102 | if (NULL != ds->helper) | ||
103 | GNUNET_HELPER_stop (ds->helper, GNUNET_NO); | ||
104 | |||
105 | /* free resources */ | ||
106 | if (NULL != ds->toplevel) | ||
107 | GNUNET_FS_share_tree_free (ds->toplevel); | ||
108 | if (NULL != ds->stop_task) | ||
109 | GNUNET_SCHEDULER_cancel (ds->stop_task); | ||
110 | GNUNET_free (ds->ex_arg); | ||
111 | GNUNET_free (ds->filename_expanded); | ||
112 | GNUNET_free (ds); | ||
113 | } | ||
114 | |||
115 | |||
116 | struct GNUNET_FS_ShareTreeItem * | ||
117 | GNUNET_FS_directory_scan_get_result (struct GNUNET_FS_DirScanner *ds) | ||
118 | { | ||
119 | struct GNUNET_FS_ShareTreeItem *result; | ||
120 | |||
121 | /* check that we're actually done */ | ||
122 | GNUNET_assert (NULL == ds->helper); | ||
123 | /* preserve result */ | ||
124 | result = ds->toplevel; | ||
125 | ds->toplevel = NULL; | ||
126 | GNUNET_FS_directory_scan_abort (ds); | ||
127 | return result; | ||
128 | } | ||
129 | |||
130 | |||
131 | /** | ||
132 | * Move in the directory from the given position to the next file | ||
133 | * in DFS traversal. | ||
134 | * | ||
135 | * @param pos current position | ||
136 | * @return next file, NULL for none | ||
137 | */ | ||
138 | static struct GNUNET_FS_ShareTreeItem * | ||
139 | advance (struct GNUNET_FS_ShareTreeItem *pos) | ||
140 | { | ||
141 | int moved; | ||
142 | |||
143 | GNUNET_assert (NULL != pos); | ||
144 | moved = 0; /* must not terminate, even on file, otherwise "normal" */ | ||
145 | while ((pos->is_directory == GNUNET_YES) || (0 == moved)) | ||
146 | { | ||
147 | if ((moved != -1) && (NULL != pos->children_head)) | ||
148 | { | ||
149 | pos = pos->children_head; | ||
150 | moved = 1; /* can terminate if file */ | ||
151 | continue; | ||
152 | } | ||
153 | if (NULL != pos->next) | ||
154 | { | ||
155 | pos = pos->next; | ||
156 | moved = 1; /* can terminate if file */ | ||
157 | continue; | ||
158 | } | ||
159 | if (NULL != pos->parent) | ||
160 | { | ||
161 | pos = pos->parent; | ||
162 | moved = -1; /* force move to 'next' or 'parent' */ | ||
163 | continue; | ||
164 | } | ||
165 | /* no more options, end of traversal */ | ||
166 | return NULL; | ||
167 | } | ||
168 | return pos; | ||
169 | } | ||
170 | |||
171 | |||
172 | /** | ||
173 | * Add another child node to the tree. | ||
174 | * | ||
175 | * @param parent parent of the child, NULL for top level | ||
176 | * @param filename name of the file or directory | ||
177 | * @param is_directory GNUNET_YES for directories | ||
178 | * @return new entry that was just created | ||
179 | */ | ||
180 | static struct GNUNET_FS_ShareTreeItem * | ||
181 | expand_tree (struct GNUNET_FS_ShareTreeItem *parent, | ||
182 | const char *filename, | ||
183 | int is_directory) | ||
184 | { | ||
185 | struct GNUNET_FS_ShareTreeItem *chld; | ||
186 | size_t slen; | ||
187 | |||
188 | chld = GNUNET_new (struct GNUNET_FS_ShareTreeItem); | ||
189 | chld->parent = parent; | ||
190 | chld->filename = GNUNET_strdup (filename); | ||
191 | GNUNET_asprintf (&chld->short_filename, | ||
192 | "%s%s", | ||
193 | GNUNET_STRINGS_get_short_name (filename), | ||
194 | is_directory == GNUNET_YES ? "/" : ""); | ||
195 | /* make sure we do not end with '//' */ | ||
196 | slen = strlen (chld->short_filename); | ||
197 | if ((slen >= 2) && (chld->short_filename[slen - 1] == '/') && | ||
198 | (chld->short_filename[slen - 2] == '/')) | ||
199 | chld->short_filename[slen - 1] = '\0'; | ||
200 | chld->is_directory = is_directory; | ||
201 | if (NULL != parent) | ||
202 | GNUNET_CONTAINER_DLL_insert (parent->children_head, | ||
203 | parent->children_tail, | ||
204 | chld); | ||
205 | return chld; | ||
206 | } | ||
207 | |||
208 | |||
209 | /** | ||
210 | * Task run last to shut everything down. | ||
211 | * | ||
212 | * @param cls the 'struct GNUNET_FS_DirScanner' | ||
213 | */ | ||
214 | static void | ||
215 | finish_scan (void *cls) | ||
216 | { | ||
217 | struct GNUNET_FS_DirScanner *ds = cls; | ||
218 | |||
219 | ds->stop_task = NULL; | ||
220 | if (NULL != ds->helper) | ||
221 | { | ||
222 | GNUNET_HELPER_stop (ds->helper, GNUNET_NO); | ||
223 | ds->helper = NULL; | ||
224 | } | ||
225 | ds->progress_callback (ds->progress_callback_cls, | ||
226 | NULL, | ||
227 | GNUNET_SYSERR, | ||
228 | GNUNET_FS_DIRSCANNER_FINISHED); | ||
229 | } | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Called every time there is data to read from the scanner. | ||
234 | * Calls the scanner progress handler. | ||
235 | * | ||
236 | * @param cls the closure (directory scanner object) | ||
237 | * @param msg message from the helper process | ||
238 | * @return #GNUNET_OK on success, | ||
239 | * #GNUNET_NO to stop further processing (no error) | ||
240 | * #GNUNET_SYSERR to stop further processing with error | ||
241 | */ | ||
242 | static int | ||
243 | process_helper_msgs (void *cls, const struct GNUNET_MessageHeader *msg) | ||
244 | { | ||
245 | struct GNUNET_FS_DirScanner *ds = cls; | ||
246 | const char *filename; | ||
247 | size_t left; | ||
248 | |||
249 | #if 0 | ||
250 | fprintf (stderr, | ||
251 | "DMS parses %u-byte message of type %u\n", | ||
252 | (unsigned int) ntohs (msg->size), | ||
253 | (unsigned int) ntohs (msg->type)); | ||
254 | #endif | ||
255 | left = ntohs (msg->size) - sizeof(struct GNUNET_MessageHeader); | ||
256 | filename = (const char *) &msg[1]; | ||
257 | switch (ntohs (msg->type)) | ||
258 | { | ||
259 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_FILE: | ||
260 | if (filename[left - 1] != '\0') | ||
261 | { | ||
262 | GNUNET_break (0); | ||
263 | break; | ||
264 | } | ||
265 | ds->progress_callback (ds->progress_callback_cls, | ||
266 | filename, | ||
267 | GNUNET_NO, | ||
268 | GNUNET_FS_DIRSCANNER_FILE_START); | ||
269 | if (NULL == ds->toplevel) | ||
270 | { | ||
271 | ds->toplevel = expand_tree (ds->pos, filename, GNUNET_NO); | ||
272 | } | ||
273 | else | ||
274 | { | ||
275 | GNUNET_assert (NULL != ds->pos); | ||
276 | (void) expand_tree (ds->pos, filename, GNUNET_NO); | ||
277 | } | ||
278 | return GNUNET_OK; | ||
279 | |||
280 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY: | ||
281 | if (filename[left - 1] != '\0') | ||
282 | { | ||
283 | GNUNET_break (0); | ||
284 | break; | ||
285 | } | ||
286 | if (0 == strcmp ("..", filename)) | ||
287 | { | ||
288 | if (NULL == ds->pos) | ||
289 | { | ||
290 | GNUNET_break (0); | ||
291 | break; | ||
292 | } | ||
293 | ds->pos = ds->pos->parent; | ||
294 | return GNUNET_OK; | ||
295 | } | ||
296 | ds->progress_callback (ds->progress_callback_cls, | ||
297 | filename, | ||
298 | GNUNET_YES, | ||
299 | GNUNET_FS_DIRSCANNER_FILE_START); | ||
300 | ds->pos = expand_tree (ds->pos, filename, GNUNET_YES); | ||
301 | if (NULL == ds->toplevel) | ||
302 | ds->toplevel = ds->pos; | ||
303 | return GNUNET_OK; | ||
304 | |||
305 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR: | ||
306 | break; | ||
307 | |||
308 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE: | ||
309 | if ('\0' != filename[left - 1]) | ||
310 | break; | ||
311 | ds->progress_callback (ds->progress_callback_cls, | ||
312 | filename, | ||
313 | GNUNET_SYSERR, | ||
314 | GNUNET_FS_DIRSCANNER_FILE_IGNORED); | ||
315 | return GNUNET_OK; | ||
316 | |||
317 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE: | ||
318 | if (0 != left) | ||
319 | { | ||
320 | GNUNET_break (0); | ||
321 | break; | ||
322 | } | ||
323 | if (NULL == ds->toplevel) | ||
324 | break; | ||
325 | ds->progress_callback (ds->progress_callback_cls, | ||
326 | NULL, | ||
327 | GNUNET_SYSERR, | ||
328 | GNUNET_FS_DIRSCANNER_ALL_COUNTED); | ||
329 | ds->pos = ds->toplevel; | ||
330 | if (GNUNET_YES == ds->pos->is_directory) | ||
331 | ds->pos = advance (ds->pos); | ||
332 | return GNUNET_OK; | ||
333 | |||
334 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA: { | ||
335 | size_t nlen; | ||
336 | const char *end; | ||
337 | |||
338 | if (NULL == ds->pos) | ||
339 | { | ||
340 | GNUNET_break (0); | ||
341 | break; | ||
342 | } | ||
343 | end = memchr (filename, 0, left); | ||
344 | if (NULL == end) | ||
345 | { | ||
346 | GNUNET_break (0); | ||
347 | break; | ||
348 | } | ||
349 | end++; | ||
350 | nlen = end - filename; | ||
351 | left -= nlen; | ||
352 | if (0 != strcmp (filename, ds->pos->filename)) | ||
353 | { | ||
354 | GNUNET_break (0); | ||
355 | break; | ||
356 | } | ||
357 | ds->progress_callback (ds->progress_callback_cls, | ||
358 | filename, | ||
359 | GNUNET_YES, | ||
360 | GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED); | ||
361 | if (0 < left) | ||
362 | { | ||
363 | ds->pos->meta = GNUNET_FS_meta_data_deserialize (end, left); | ||
364 | if (NULL == ds->pos->meta) | ||
365 | { | ||
366 | GNUNET_break (0); | ||
367 | break; | ||
368 | } | ||
369 | /* having full filenames is too dangerous; always make sure we clean them up */ | ||
370 | GNUNET_FS_meta_data_delete (ds->pos->meta, | ||
371 | EXTRACTOR_METATYPE_FILENAME, | ||
372 | NULL, | ||
373 | 0); | ||
374 | /* instead, put in our 'safer' original filename */ | ||
375 | GNUNET_FS_meta_data_insert (ds->pos->meta, | ||
376 | "<libgnunetfs>", | ||
377 | EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, | ||
378 | EXTRACTOR_METAFORMAT_UTF8, | ||
379 | "text/plain", | ||
380 | ds->pos->short_filename, | ||
381 | strlen (ds->pos->short_filename) | ||
382 | + 1); | ||
383 | } | ||
384 | ds->pos->ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data ( | ||
385 | ds->pos->meta); | ||
386 | ds->pos = advance (ds->pos); | ||
387 | return GNUNET_OK; | ||
388 | } | ||
389 | |||
390 | case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED: | ||
391 | if (NULL != ds->pos) | ||
392 | { | ||
393 | GNUNET_break (0); | ||
394 | break; | ||
395 | } | ||
396 | if (0 != left) | ||
397 | { | ||
398 | GNUNET_break (0); | ||
399 | break; | ||
400 | } | ||
401 | if (NULL == ds->toplevel) | ||
402 | break; | ||
403 | ds->stop_task = GNUNET_SCHEDULER_add_now (&finish_scan, ds); | ||
404 | return GNUNET_OK; | ||
405 | |||
406 | default: | ||
407 | GNUNET_break (0); | ||
408 | break; | ||
409 | } | ||
410 | ds->progress_callback (ds->progress_callback_cls, | ||
411 | NULL, | ||
412 | GNUNET_SYSERR, | ||
413 | GNUNET_FS_DIRSCANNER_INTERNAL_ERROR); | ||
414 | return GNUNET_OK; | ||
415 | } | ||
416 | |||
417 | |||
418 | /** | ||
419 | * Function called if our helper process died. | ||
420 | * | ||
421 | * @param cls the 'struct GNUNET_FS_DirScanner' callback. | ||
422 | */ | ||
423 | static void | ||
424 | helper_died_cb (void *cls) | ||
425 | { | ||
426 | struct GNUNET_FS_DirScanner *ds = cls; | ||
427 | |||
428 | ds->helper = NULL; | ||
429 | if (NULL != ds->stop_task) | ||
430 | return; /* normal death, was finished */ | ||
431 | ds->progress_callback (ds->progress_callback_cls, | ||
432 | NULL, | ||
433 | GNUNET_SYSERR, | ||
434 | GNUNET_FS_DIRSCANNER_INTERNAL_ERROR); | ||
435 | } | ||
436 | |||
437 | |||
438 | /** | ||
439 | * Start a directory scanner thread. | ||
440 | * | ||
441 | * @param filename name of the directory to scan | ||
442 | * @param disable_extractor #GNUNET_YES to not run libextractor on files (only | ||
443 | * build a tree) | ||
444 | * @param ex if not NULL, must be a list of extra plugins for extractor | ||
445 | * @param cb the callback to call when there are scanning progress messages | ||
446 | * @param cb_cls closure for 'cb' | ||
447 | * @return directory scanner object to be used for controlling the scanner | ||
448 | */ | ||
449 | struct GNUNET_FS_DirScanner * | ||
450 | GNUNET_FS_directory_scan_start (const char *filename, | ||
451 | int disable_extractor, | ||
452 | const char *ex, | ||
453 | GNUNET_FS_DirScannerProgressCallback cb, | ||
454 | void *cb_cls) | ||
455 | { | ||
456 | struct stat sbuf; | ||
457 | char *filename_expanded; | ||
458 | struct GNUNET_FS_DirScanner *ds; | ||
459 | |||
460 | if (0 != stat (filename, &sbuf)) | ||
461 | return NULL; | ||
462 | filename_expanded = GNUNET_STRINGS_filename_expand (filename); | ||
463 | if (NULL == filename_expanded) | ||
464 | return NULL; | ||
465 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
466 | "Starting to scan directory `%s'\n", | ||
467 | filename_expanded); | ||
468 | ds = GNUNET_new (struct GNUNET_FS_DirScanner); | ||
469 | ds->progress_callback = cb; | ||
470 | ds->progress_callback_cls = cb_cls; | ||
471 | ds->filename_expanded = filename_expanded; | ||
472 | if (disable_extractor) | ||
473 | ds->ex_arg = GNUNET_strdup ("-"); | ||
474 | else | ||
475 | ds->ex_arg = (NULL != ex) ? GNUNET_strdup (ex) : NULL; | ||
476 | ds->args[0] = "gnunet-helper-fs-publish"; | ||
477 | ds->args[1] = ds->filename_expanded; | ||
478 | ds->args[2] = ds->ex_arg; | ||
479 | ds->args[3] = NULL; | ||
480 | ds->helper = GNUNET_HELPER_start (GNUNET_NO, | ||
481 | "gnunet-helper-fs-publish", | ||
482 | ds->args, | ||
483 | &process_helper_msgs, | ||
484 | &helper_died_cb, | ||
485 | ds); | ||
486 | if (NULL == ds->helper) | ||
487 | { | ||
488 | GNUNET_free (filename_expanded); | ||
489 | GNUNET_free (ds); | ||
490 | return NULL; | ||
491 | } | ||
492 | return ds; | ||
493 | } | ||
494 | |||
495 | |||
496 | /* end of fs_dirmetascan.c */ | ||