aboutsummaryrefslogtreecommitdiff
path: root/src/fs/fs_dirmetascan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/fs_dirmetascan.c')
-rw-r--r--src/fs/fs_dirmetascan.c496
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 */
39struct 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 */
98void
99GNUNET_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
116struct GNUNET_FS_ShareTreeItem *
117GNUNET_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 */
138static struct GNUNET_FS_ShareTreeItem *
139advance (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 */
180static struct GNUNET_FS_ShareTreeItem *
181expand_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 */
214static void
215finish_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 */
242static int
243process_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 */
423static void
424helper_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 */
449struct GNUNET_FS_DirScanner *
450GNUNET_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 */