2 This file is part of GNUnet
3 (C) 2005-2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
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'.
26 * @author Christian Grothoff
29 #include "gnunet_fs_service.h"
30 #include "gnunet_scheduler_lib.h"
35 * An opaque structure a pointer to which is returned to the
36 * caller to be used to control the scanner.
38 struct GNUNET_FS_DirScanner
44 struct GNUNET_HELPER_Handle *helper;
47 * Expanded filename (as given by the scan initiator).
48 * The scanner thread stores a copy here, and frees it when it finishes.
50 char *filename_expanded;
53 * Second argument to helper process.
58 * The function that will be called every time there's a progress
61 GNUNET_FS_DirScannerProgressCallback progress_callback;
64 * A closure for progress_callback.
66 void *progress_callback_cls;
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
73 struct GNUNET_FS_ShareTreeItem *toplevel;
76 * Current position during processing.
78 struct GNUNET_FS_ShareTreeItem *pos;
81 * Arguments for helper.
92 * @param ds directory scanner structure
95 GNUNET_FS_directory_scan_abort (struct GNUNET_FS_DirScanner *ds)
97 /* terminate helper */
98 if (NULL != ds->helper)
99 GNUNET_HELPER_stop (ds->helper);
102 if (NULL != ds->toplevel)
103 GNUNET_FS_share_tree_free (ds->toplevel);
104 GNUNET_free_non_null (ds->ex_arg);
105 GNUNET_free (ds->filename_expanded);
111 * Obtain the result of the scan after the scan has signalled
112 * completion. Must not be called prior to completion. The 'ds' is
113 * freed as part of this call.
115 * @param ds directory scanner structure
116 * @return the results of the scan (a directory tree)
118 struct GNUNET_FS_ShareTreeItem *
119 GNUNET_FS_directory_scan_get_result (struct GNUNET_FS_DirScanner *ds)
121 struct GNUNET_FS_ShareTreeItem *result;
123 /* check that we're actually done */
124 GNUNET_assert (NULL == ds->helper);
125 /* preserve result */
126 result = ds->toplevel;
128 GNUNET_FS_directory_scan_abort (ds);
134 * Move in the directory from the given position to the next file
137 * @param pos current position
138 * @return next file, NULL for none
140 static struct GNUNET_FS_ShareTreeItem *
141 advance (struct GNUNET_FS_ShareTreeItem *pos)
145 GNUNET_assert (NULL != pos);
146 moved = 0; /* must not terminate, even on file, otherwise "normal" */
147 while ( (pos->is_directory) ||
150 if ( (moved != -1) &&
151 (NULL != pos->children_head) )
153 pos = pos->children_head;
154 moved = 1; /* can terminate if file */
157 if (NULL != pos->next)
160 moved = 1; /* can terminate if file */
163 if (NULL != pos->parent)
166 moved = -1; /* force move to 'next' or 'parent' */
169 /* no more options, end of traversal */
177 * Add another child node to the tree.
179 * @param parent parent of the child, NULL for top level
180 * @param filename name of the file or directory
181 * @param is_directory GNUNET_YES for directories
182 * @return new entry that was just created
184 static struct GNUNET_FS_ShareTreeItem *
185 expand_tree (struct GNUNET_FS_ShareTreeItem *parent,
186 const char *filename,
189 struct GNUNET_FS_ShareTreeItem *chld;
191 chld = GNUNET_malloc (sizeof (struct GNUNET_FS_ShareTreeItem));
192 chld->parent = parent;
193 chld->filename = GNUNET_strdup (filename);
194 chld->short_filename = GNUNET_strdup (GNUNET_STRINGS_get_short_name (filename));
195 chld->is_directory = is_directory;
197 GNUNET_CONTAINER_DLL_insert (parent->children_head,
198 parent->children_tail,
205 * Called every time there is data to read from the scanner.
206 * Calls the scanner progress handler.
208 * @param cls the closure (directory scanner object)
209 * @param client always NULL
210 * @param msg message from the helper process
213 process_helper_msgs (void *cls,
215 const struct GNUNET_MessageHeader *msg)
217 struct GNUNET_FS_DirScanner *ds = cls;
218 const char *filename;
221 left = ntohs (msg->size) - sizeof (struct GNUNET_MessageHeader);
222 filename = (const char*) &msg[1];
223 switch (ntohs (msg->type))
225 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_FILE:
226 if (filename[left-1] != '\0')
231 ds->progress_callback (ds->progress_callback_cls,
233 GNUNET_FS_DIRSCANNER_FILE_START);
234 if (NULL == ds->toplevel)
235 ds->toplevel = expand_tree (ds->pos,
236 filename, GNUNET_NO);
238 (void) expand_tree (ds->pos,
239 filename, GNUNET_NO);
241 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY:
242 if (filename[left-1] != '\0')
247 if (0 == strcmp ("..", filename))
254 ds->pos = ds->pos->parent;
257 ds->progress_callback (ds->progress_callback_cls,
258 filename, GNUNET_YES,
259 GNUNET_FS_DIRSCANNER_FILE_START);
260 ds->pos = expand_tree (ds->pos,
261 filename, GNUNET_YES);
262 if (NULL == ds->toplevel)
263 ds->toplevel = ds->pos;
265 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR:
267 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE:
268 if (filename[left-1] != '\0')
270 ds->progress_callback (ds->progress_callback_cls,
271 filename, GNUNET_SYSERR,
272 GNUNET_FS_DIRSCANNER_FILE_IGNORED);
274 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE:
280 if (NULL == ds->toplevel)
285 ds->progress_callback (ds->progress_callback_cls,
287 GNUNET_FS_DIRSCANNER_ALL_COUNTED);
288 ds->pos = ds->toplevel;
289 if (ds->pos->is_directory)
290 ds->pos = advance (ds->pos);
292 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA:
302 end = memchr (filename, 0, left);
309 nlen = end - filename;
311 if (0 != strcmp (filename,
317 ds->progress_callback (ds->progress_callback_cls,
318 filename, GNUNET_YES,
319 GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED);
322 ds->pos->meta = GNUNET_CONTAINER_meta_data_deserialize (end, left);
323 if (NULL == ds->pos->meta)
328 /* having full filenames is too dangerous; always make sure we clean them up */
329 GNUNET_CONTAINER_meta_data_delete (ds->pos->meta,
330 EXTRACTOR_METATYPE_FILENAME,
332 GNUNET_CONTAINER_meta_data_insert (ds->pos->meta, "<libgnunetfs>",
333 EXTRACTOR_METATYPE_FILENAME,
334 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
335 ds->pos->short_filename,
336 strlen (ds->pos->short_filename) + 1);
338 ds->pos = advance (ds->pos);
341 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED:
352 if (NULL == ds->toplevel)
357 GNUNET_HELPER_stop (ds->helper);
359 ds->progress_callback (ds->progress_callback_cls,
361 GNUNET_FS_DIRSCANNER_FINISHED);
367 ds->progress_callback (ds->progress_callback_cls,
369 GNUNET_FS_DIRSCANNER_INTERNAL_ERROR);
375 * Start a directory scanner thread.
377 * @param filename name of the directory to scan
378 * @param GNUNET_YES to not to run libextractor on files (only build a tree)
379 * @param ex if not NULL, must be a list of extra plugins for extractor
380 * @param cb the callback to call when there are scanning progress messages
381 * @param cb_cls closure for 'cb'
382 * @return directory scanner object to be used for controlling the scanner
384 struct GNUNET_FS_DirScanner *
385 GNUNET_FS_directory_scan_start (const char *filename,
386 int disable_extractor, const char *ex,
387 GNUNET_FS_DirScannerProgressCallback cb,
391 char *filename_expanded;
392 struct GNUNET_FS_DirScanner *ds;
394 if (0 != STAT (filename, &sbuf))
396 filename_expanded = GNUNET_STRINGS_filename_expand (filename);
397 if (NULL == filename_expanded)
399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400 "Starting to scan directory `%s'\n",
402 ds = GNUNET_malloc (sizeof (struct GNUNET_FS_DirScanner));
403 ds->progress_callback = cb;
404 ds->progress_callback_cls = cb_cls;
405 ds->filename_expanded = filename_expanded;
406 if (disable_extractor)
407 ds->ex_arg = GNUNET_strdup ("-");
409 ds->ex_arg = (NULL != ex) ? GNUNET_strdup (ex) : NULL;
410 ds->args[0] = "gnunet-helper-fs-publish";
411 ds->args[1] = ds->filename_expanded;
412 ds->args[2] = ds->ex_arg;
414 ds->helper = GNUNET_HELPER_start ("gnunet-helper-fs-publish",
416 &process_helper_msgs,
418 if (NULL == ds->helper)
420 GNUNET_free (filename_expanded);
428 /* end of fs_dirmetascan.c */