aboutsummaryrefslogtreecommitdiff
path: root/src/fs/fs_dirmetascan.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-01-15 11:59:08 +0000
committerChristian Grothoff <christian@grothoff.org>2012-01-15 11:59:08 +0000
commit943110ad52014e30c1ea29b0c68166149902ab85 (patch)
treedfaa498c6f31af54ae86d31ad56ddadbfb73835c /src/fs/fs_dirmetascan.c
parent72724deec7f8d42fb33d02034bd24cd9e95eac7a (diff)
downloadgnunet-943110ad52014e30c1ea29b0c68166149902ab85.tar.gz
gnunet-943110ad52014e30c1ea29b0c68166149902ab85.zip
-LRN: harmonize dirmetascanner, plus fixing compile errors on GNU
Diffstat (limited to 'src/fs/fs_dirmetascan.c')
-rw-r--r--src/fs/fs_dirmetascan.c2574
1 files changed, 1288 insertions, 1286 deletions
diff --git a/src/fs/fs_dirmetascan.c b/src/fs/fs_dirmetascan.c
index fd8ab15c4..b9e400c00 100644
--- a/src/fs/fs_dirmetascan.c
+++ b/src/fs/fs_dirmetascan.c
@@ -1,1286 +1,1288 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 (C) 2005-2012 Christian Grothoff (and other contributing authors) 3 (C) 2005-2012 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 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 7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version. 8 option) any later version.
9 9
10 GNUnet is distributed in the hope that it will be useful, but 10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of 11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details. 13 General Public License for more details.
14 14
15 You should have received a copy of the GNU General Public License 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 16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. 18 Boston, MA 02111-1307, USA.
19*/ 19*/
20 20
21#include "platform.h" 21#include "platform.h"
22#include "gnunet_fs_service.h" 22#include "gnunet_fs_service.h"
23#include "gnunet_scheduler_lib.h" 23#include "gnunet_scheduler_lib.h"
24 24#include <pthread.h>
25/** 25
26 * Entry for each unique keyword to track how often 26/**
27 * it occured. Contains the keyword and the counter. 27 * Entry for each unique keyword to track how often
28 */ 28 * it occured. Contains the keyword and the counter.
29struct KeywordCounter 29 */
30{ 30struct KeywordCounter
31 31{
32 /** 32
33 * Keyword that was found. 33 /**
34 */ 34 * Keyword that was found.
35 const char *value; 35 */
36 36 const char *value;
37 /** 37
38 * How many files have this keyword? 38 /**
39 */ 39 * How many files have this keyword?
40 unsigned int count; 40 */
41 41 unsigned int count;
42 /** 42
43 * This is a doubly-linked list 43 /**
44 */ 44 * This is a doubly-linked list
45 struct KeywordCounter *prev; 45 */
46 46 struct KeywordCounter *prev;
47 /** 47
48 * This is a doubly-linked list 48 /**
49 */ 49 * This is a doubly-linked list
50 struct KeywordCounter *next; 50 */
51}; 51 struct KeywordCounter *next;
52 52};
53/** 53
54 * Aggregate information we keep for meta data in each directory. 54/**
55 */ 55 * Aggregate information we keep for meta data in each directory.
56struct MetaCounter 56 */
57{ 57struct MetaCounter
58 /** 58{
59 * The actual meta data. 59 /**
60 */ 60 * The actual meta data.
61 const char *data; 61 */
62 62 const char *data;
63 /** 63
64 * Number of bytes in 'data'. 64 /**
65 */ 65 * Number of bytes in 'data'.
66 size_t data_size; 66 */
67 67 size_t data_size;
68 /** 68
69 * Name of the plugin that provided that piece of metadata 69 /**
70 */ 70 * Name of the plugin that provided that piece of metadata
71 const char *plugin_name; 71 */
72 72 const char *plugin_name;
73 /** 73
74 * Type of the data 74 /**
75 */ 75 * Type of the data
76 enum EXTRACTOR_MetaType type; 76 */
77 77 enum EXTRACTOR_MetaType type;
78 /** 78
79 * Format of the data 79 /**
80 */ 80 * Format of the data
81 enum EXTRACTOR_MetaFormat format; 81 */
82 82 enum EXTRACTOR_MetaFormat format;
83 /** 83
84 * MIME-type of the metadata itself 84 /**
85 */ 85 * MIME-type of the metadata itself
86 const char *data_mime_type; 86 */
87 87 const char *data_mime_type;
88 /** 88
89 * How many files have meta entries matching this value? 89 /**
90 * (type and format do not have to match). 90 * How many files have meta entries matching this value?
91 */ 91 * (type and format do not have to match).
92 unsigned int count; 92 */
93 93 unsigned int count;
94 /** 94
95 * This is a doubly-linked list 95 /**
96 */ 96 * This is a doubly-linked list
97 struct MetaCounter *prev; 97 */
98 98 struct MetaCounter *prev;
99 /** 99
100 * This is a doubly-linked list 100 /**
101 */ 101 * This is a doubly-linked list
102 struct MetaCounter *next; 102 */
103}; 103 struct MetaCounter *next;
104 104};
105struct AddDirContext; 105
106 106struct AddDirContext;
107/** 107
108 * A structure used to hold a pointer to the tree item that is being 108/**
109 * processed. 109 * A structure used to hold a pointer to the tree item that is being
110 * Needed to avoid changing the context for every recursive call. 110 * processed.
111 */ 111 * Needed to avoid changing the context for every recursive call.
112struct AddDirStack 112 */
113{ 113struct AddDirStack
114 /** 114{
115 * Context pointer 115 /**
116 */ 116 * Context pointer
117 struct AddDirContext *adc; 117 */
118 118 struct AddDirContext *adc;
119 /** 119
120 * Parent directory 120 /**
121 */ 121 * Parent directory
122 struct ShareTreeItem *parent; 122 */
123}; 123 struct GNUNET_FS_ShareTreeItem *parent;
124 124};
125/** 125
126 * Execution context for 'add_dir' 126/**
127 * Owned by the initiator thread. 127 * Execution context for 'add_dir'
128 */ 128 * Owned by the initiator thread.
129struct AddDirContext 129 */
130{ 130struct AddDirContext
131 /** 131{
132 * After the scan is finished, it will contain a pointer to the 132 /**
133 * top-level directory entry in the directory tree built by the 133 * After the scan is finished, it will contain a pointer to the
134 * scanner. 134 * top-level directory entry in the directory tree built by the
135 */ 135 * scanner.
136 struct ShareTreeItem *toplevel; 136 */
137 137 struct GNUNET_FS_ShareTreeItem *toplevel;
138 /** 138
139 * Expanded filename (as given by the scan initiator). 139 /**
140 * The scanner thread stores a copy here, and frees it when it finishes. 140 * Expanded filename (as given by the scan initiator).
141 */ 141 * The scanner thread stores a copy here, and frees it when it finishes.
142 char *filename_expanded; 142 */
143 143 char *filename_expanded;
144 /** 144
145 * A pipe end to read signals from. 145 /**
146 * Owned by the initiator thread. 146 * A pipe end to read signals from.
147 */ 147 * Owned by the initiator thread.
148 const struct GNUNET_DISK_FileHandle *stop_read; 148 */
149 149 const struct GNUNET_DISK_FileHandle *stop_read;
150 /** 150
151 * 1 if the scanner should stop, 0 otherwise. Set in response 151 /**
152 * to communication errors or when the initiator wants the scanning 152 * 1 if the scanner should stop, 0 otherwise. Set in response
153 * process to stop. 153 * to communication errors or when the initiator wants the scanning
154 */ 154 * process to stop.
155 char do_stop; 155 */
156 156 char do_stop;
157 /** 157
158 * Handle of the pipe end into which the progress messages are written 158 /**
159 * The pipe is owned by the initiator thread, and there's no way to 159 * Handle of the pipe end into which the progress messages are written
160 * close this end without having access to the pipe, so it won't 160 * The pipe is owned by the initiator thread, and there's no way to
161 * be closed by the scanner thread. 161 * close this end without having access to the pipe, so it won't
162 * The initiator MUST keep it alive until the scanner thread is finished. 162 * be closed by the scanner thread.
163 */ 163 * The initiator MUST keep it alive until the scanner thread is finished.
164 const struct GNUNET_DISK_FileHandle *progress_write; 164 */
165 165 const struct GNUNET_DISK_FileHandle *progress_write;
166 166
167 /** 167
168 * List of libextractor plugins to use for extracting. 168 /**
169 * Initialized when the scan starts, removed when it finishes. 169 * List of libextractor plugins to use for extracting.
170 */ 170 * Initialized when the scan starts, removed when it finishes.
171 struct EXTRACTOR_PluginList *plugins; 171 */
172}; 172 struct EXTRACTOR_PluginList *plugins;
173 173};
174/** 174
175 * An opaque structure a pointer to which is returned to the 175/**
176 * caller to be used to control the scanner. 176 * An opaque structure a pointer to which is returned to the
177 */ 177 * caller to be used to control the scanner.
178struct GNUNET_FS_DirScanner 178 */
179{ 179struct GNUNET_FS_DirScanner
180 /** 180{
181 * A pipe end to read signals from. 181 /**
182 * Owned by the initiator thread. 182 * A pipe end to read signals from.
183 */ 183 * Owned by the initiator thread.
184 const struct GNUNET_DISK_FileHandle *stop_write; 184 */
185 185 const struct GNUNET_DISK_FileHandle *stop_write;
186 /** 186
187 * A pipe transfer signals to the scanner. 187 /**
188 * Owned by the initiator thread. 188 * A pipe transfer signals to the scanner.
189 */ 189 * Owned by the initiator thread.
190 struct GNUNET_DISK_PipeHandle *stop_pipe; 190 */
191 191 struct GNUNET_DISK_PipeHandle *stop_pipe;
192 /** 192
193 * A thread object for the scanner thread. 193 /**
194 * Owned by the initiator thread. 194 * A thread object for the scanner thread.
195 */ 195 * Owned by the initiator thread.
196#if WINDOWS 196 */
197 HANDLE thread; 197#if WINDOWS
198#else 198 HANDLE thread;
199 pthread_t thread; 199#else
200#endif 200 pthread_t thread;
201 201#endif
202 /** 202
203 * A task for reading progress messages from the scanner. 203 /**
204 */ 204 * A task for reading progress messages from the scanner.
205 GNUNET_SCHEDULER_TaskIdentifier progress_read_task; 205 */
206 206 GNUNET_SCHEDULER_TaskIdentifier progress_read_task;
207 /** 207
208 * The end of the pipe that is used to read progress messages. 208 /**
209 */ 209 * The end of the pipe that is used to read progress messages.
210 const struct GNUNET_DISK_FileHandle *progress_read; 210 */
211 211 const struct GNUNET_DISK_FileHandle *progress_read;
212 /** 212
213 * The pipe that is used to read progress messages. 213 /**
214 * Owned (along with both of its ends) by the initiator thread. 214 * The pipe that is used to read progress messages.
215 * Only closed after the scanner thread is finished. 215 * Owned (along with both of its ends) by the initiator thread.
216 */ 216 * Only closed after the scanner thread is finished.
217 struct GNUNET_DISK_PipeHandle *progress_pipe; 217 */
218 218 struct GNUNET_DISK_PipeHandle *progress_pipe;
219 /** 219
220 * The function that will be called every time there's a progress 220 /**
221 * message. 221 * The function that will be called every time there's a progress
222 */ 222 * message.
223 GNUNET_FS_DirScannerProgressCallback progress_callback; 223 */
224 224 GNUNET_FS_DirScannerProgressCallback progress_callback;
225 /** 225
226 * A closure for progress_callback. 226 /**
227 */ 227 * A closure for progress_callback.
228 void *cls; 228 */
229 229 void *cls;
230 /** 230
231 * A pointer to the context of the scanner. 231 /**
232 * Owned by the initiator thread. 232 * A pointer to the context of the scanner.
233 * Initiator thread shouldn't touch it until the scanner thread 233 * Owned by the initiator thread.
234 * is finished. 234 * Initiator thread shouldn't touch it until the scanner thread
235 */ 235 * is finished.
236 struct AddDirContext *adc; 236 */
237}; 237 struct AddDirContext *adc;
238 238};
239/** 239
240 * A structure that forms a singly-linked list that serves as a stack 240/**
241 * for metadata-processing function. 241 * A structure that forms a singly-linked list that serves as a stack
242 */ 242 * for metadata-processing function.
243struct ProcessMetadataStackItem 243 */
244{ 244struct ProcessMetadataStackItem
245 /** 245{
246 * A pointer to metadata-processing context. 246 /**
247 * The same in every stack item. 247 * A pointer to metadata-processing context.
248 */ 248 * The same in every stack item.
249 struct ProcessMetadataContext *ctx; 249 */
250 250 struct GNUNET_FS_ProcessMetadataContext *ctx;
251 /** 251
252 * This is a singly-linked list. A pointer to its end is kept, and 252 /**
253 * this pointer is used to walk it backwards. 253 * This is a singly-linked list. A pointer to its end is kept, and
254 */ 254 * this pointer is used to walk it backwards.
255 struct ProcessMetadataStackItem *parent; 255 */
256 256 struct ProcessMetadataStackItem *parent;
257 /** 257
258 * Map from the hash over the keyword to an 'struct KeywordCounter *' 258 /**
259 * counter that says how often this keyword was 259 * Map from the hash over the keyword to an 'struct KeywordCounter *'
260 * encountered in the current directory. 260 * counter that says how often this keyword was
261 */ 261 * encountered in the current directory.
262 struct GNUNET_CONTAINER_MultiHashMap *keywordcounter; 262 */
263 263 struct GNUNET_CONTAINER_MultiHashMap *keywordcounter;
264 /** 264
265 * Map from the hash over the metadata to an 'struct MetaCounter *' 265 /**
266 * counter that says how often this metadata was 266 * Map from the hash over the metadata to an 'struct MetaCounter *'
267 * encountered in the current directory. 267 * counter that says how often this metadata was
268 */ 268 * encountered in the current directory.
269 struct GNUNET_CONTAINER_MultiHashMap *metacounter; 269 */
270 270 struct GNUNET_CONTAINER_MultiHashMap *metacounter;
271 /** 271
272 * Number of files in the current directory. 272 /**
273 */ 273 * Number of files in the current directory.
274 unsigned int dir_entry_count; 274 */
275 275 unsigned int dir_entry_count;
276 /** 276
277 * Keywords to exclude from using for KSK since they'll be associated 277 /**
278 * with the parent as well. NULL for nothing blocked. 278 * Keywords to exclude from using for KSK since they'll be associated
279 */ 279 * with the parent as well. NULL for nothing blocked.
280 struct GNUNET_FS_Uri *exclude_ksk; 280 */
281 281 struct GNUNET_FS_Uri *exclude_ksk;
282 /** 282
283 * A share tree item that is being processed. 283 /**
284 */ 284 * A share tree item that is being processed.
285 struct ShareTreeItem *item; 285 */
286 286 struct GNUNET_FS_ShareTreeItem *item;
287 /** 287
288 * Set to GNUNET_YES to indicate that the directory pointer by 'item' 288 /**
289 * was processed, and we should move on to the next. 289 * Set to GNUNET_YES to indicate that the directory pointer by 'item'
290 * Otherwise the directory will be recursed into. 290 * was processed, and we should move on to the next.
291 */ 291 * Otherwise the directory will be recursed into.
292 int end_directory; 292 */
293 293 int end_directory;
294}; 294
295 295};
296/** 296
297 * The structure to keep the state of metadata processing 297/**
298 */ 298 * The structure to keep the state of metadata processing
299struct ProcessMetadataContext 299 */
300{ 300struct GNUNET_FS_ProcessMetadataContext
301 /** 301{
302 * The top of the stack. 302 /**
303 */ 303 * The top of the stack.
304 struct ProcessMetadataStackItem *stack; 304 */
305 305 struct ProcessMetadataStackItem *stack;
306 /** 306
307 * Callback to invoke when processing is finished 307 /**
308 */ 308 * Callback to invoke when processing is finished
309 GNUNET_SCHEDULER_Task cb; 309 */
310 310 GNUNET_SCHEDULER_Task cb;
311 /** 311
312 * Closure for 'cb' 312 /**
313 */ 313 * Closure for 'cb'
314 void *cls; 314 */
315 315 void *cls;
316 /** 316
317 * Toplevel directory item of the tree to process. 317 /**
318 */ 318 * Toplevel directory item of the tree to process.
319 struct ShareTreeItem *toplevel; 319 */
320}; 320 struct GNUNET_FS_ShareTreeItem *toplevel;
321 321};
322/** 322
323 * Called every now and then by the scanner. 323/**
324 * Checks the synchronization privitive. 324 * Called every now and then by the scanner.
325 * Returns 1 if the scanner should stop, 0 otherwise. 325 * Checks the synchronization privitive.
326 */ 326 * Returns 1 if the scanner should stop, 0 otherwise.
327static int 327 */
328should_stop (struct AddDirContext *adc) 328static int
329{ 329should_stop (struct AddDirContext *adc)
330 errno = 0; 330{
331 char c; 331 errno = 0;
332 if (GNUNET_DISK_file_read_non_blocking (adc->stop_read, &c, 1) == 1 332 char c;
333 || errno != EAGAIN) 333 if (GNUNET_DISK_file_read_non_blocking (adc->stop_read, &c, 1) == 1
334 { 334 || errno != EAGAIN)
335 adc->do_stop = 1; 335 {
336 } 336 adc->do_stop = 1;
337 return adc->do_stop; 337 }
338} 338 return adc->do_stop;
339 339}
340/** 340
341 * Write progress message. 341/**
342 * Format is: 342 * Write progress message.
343 * <reason><filename length><filename><directory flag> 343 * Format is:
344 * If filename is NULL, filename is not written, and its length 344 * <reason><filename length><filename><directory flag>
345 * is written as 0, and nothing else is written. It signals the initiator 345 * If filename is NULL, filename is not written, and its length
346 * thread that the scanner is finished, and that it can now join its thread. 346 * is written as 0, and nothing else is written. It signals the initiator
347 * 347 * thread that the scanner is finished, and that it can now join its thread.
348 * Also checks if the initiator thread wants the scanner to stop, 348 *
349 * Returns 1 to stop scanning (if the signal was received, or 349 * Also checks if the initiator thread wants the scanner to stop,
350 * if the pipe was broken somehow), 0 otherwise. 350 * Returns 1 to stop scanning (if the signal was received, or
351 */ 351 * if the pipe was broken somehow), 0 otherwise.
352static int 352 */
353write_progress (struct AddDirContext *adc, const char *filename, 353static int
354 char is_directory, enum GNUNET_DirScannerProgressUpdateReason reason) 354write_progress (struct AddDirContext *adc, const char *filename,
355{ 355 char is_directory, enum GNUNET_FS_DirScannerProgressUpdateReason reason)
356 size_t filename_len; 356{
357 ssize_t wr; 357 size_t filename_len;
358 size_t total_write; 358 ssize_t wr;
359 if ((adc->do_stop || should_stop (adc)) && reason != GNUNET_DIR_SCANNER_ASKED_TO_STOP 359 size_t total_write;
360 && reason != GNUNET_DIR_SCANNER_FINISHED) 360 if ((adc->do_stop || should_stop (adc)) && reason != GNUNET_DIR_SCANNER_ASKED_TO_STOP
361 return 1; 361 && reason != GNUNET_DIR_SCANNER_FINISHED)
362 total_write = 0; 362 return 1;
363 wr = 1; 363 total_write = 0;
364 while ((wr > 0 || errno == EAGAIN) && total_write < sizeof (reason)) 364 wr = 1;
365 { 365 while ((wr > 0 || errno == EAGAIN) && total_write < sizeof (reason))
366 wr = GNUNET_DISK_file_write_blocking (adc->progress_write, 366 {
367 &((char *)&reason)[total_write], sizeof (reason) - total_write); 367 wr = GNUNET_DISK_file_write_blocking (adc->progress_write,
368 if (wr > 0) 368 &((char *)&reason)[total_write], sizeof (reason) - total_write);
369 total_write += wr; 369 if (wr > 0)
370 } 370 total_write += wr;
371 if (sizeof (reason) != total_write) 371 }
372 return adc->do_stop = 1; 372 if (sizeof (reason) != total_write)
373 if (filename) 373 return adc->do_stop = 1;
374 filename_len = strlen (filename) + 1; 374 if (filename)
375 else 375 filename_len = strlen (filename) + 1;
376 filename_len = 0; 376 else
377 total_write = 0; 377 filename_len = 0;
378 wr = 1; 378 total_write = 0;
379 while ((wr > 0 || errno == EAGAIN) && total_write < sizeof (size_t)) 379 wr = 1;
380 { 380 while ((wr > 0 || errno == EAGAIN) && total_write < sizeof (size_t))
381 wr = GNUNET_DISK_file_write_blocking (adc->progress_write, 381 {
382 &((char *)&filename_len)[total_write], sizeof (size_t) - total_write); 382 wr = GNUNET_DISK_file_write_blocking (adc->progress_write,
383 if (wr > 0) 383 &((char *)&filename_len)[total_write], sizeof (size_t) - total_write);
384 total_write += wr; 384 if (wr > 0)
385 } 385 total_write += wr;
386 if (sizeof (size_t) != total_write) 386 }
387 return adc->do_stop = 1; 387 if (sizeof (size_t) != total_write)
388 if (filename) 388 return adc->do_stop = 1;
389 { 389 if (filename)
390 total_write = 0; 390 {
391 wr = 1; 391 total_write = 0;
392 while ((wr > 0 || errno == EAGAIN) && total_write < filename_len) 392 wr = 1;
393 { 393 while ((wr > 0 || errno == EAGAIN) && total_write < filename_len)
394 wr = GNUNET_DISK_file_write_blocking (adc->progress_write, 394 {
395 &((char *)filename)[total_write], filename_len - total_write); 395 wr = GNUNET_DISK_file_write_blocking (adc->progress_write,
396 if (wr > 0) 396 &((char *)filename)[total_write], filename_len - total_write);
397 total_write += wr; 397 if (wr > 0)
398 } 398 total_write += wr;
399 if (filename_len != total_write) 399 }
400 return adc->do_stop = 1; 400 if (filename_len != total_write)
401 total_write = 0; 401 return adc->do_stop = 1;
402 wr = 1; 402 total_write = 0;
403 while ((wr > 0 || errno == EAGAIN) && total_write < sizeof (char)) 403 wr = 1;
404 { 404 while ((wr > 0 || errno == EAGAIN) && total_write < sizeof (char))
405 wr = GNUNET_DISK_file_write_blocking (adc->progress_write, 405 {
406 &((char *)&is_directory)[total_write], sizeof (char) - total_write); 406 wr = GNUNET_DISK_file_write_blocking (adc->progress_write,
407 if (wr > 0) 407 &((char *)&is_directory)[total_write], sizeof (char) - total_write);
408 total_write += wr; 408 if (wr > 0)
409 } 409 total_write += wr;
410 if (sizeof (char) != total_write) 410 }
411 return adc->do_stop = 1; 411 if (sizeof (char) != total_write)
412 } 412 return adc->do_stop = 1;
413 return 0; 413 }
414} 414 return 0;
415 415}
416/** 416
417 * Add the given keyword to the 417/**
418 * keyword statistics tracker. 418 * Add the given keyword to the
419 * 419 * keyword statistics tracker.
420 * @param cls closure (user-defined) 420 *
421 * @param keyword the keyword to count 421 * @param cls closure (user-defined)
422 * @param is_mandatory ignored 422 * @param keyword the keyword to count
423 * @return always GNUNET_OK 423 * @param is_mandatory ignored
424 */ 424 * @return always GNUNET_OK
425static int 425 */
426add_to_keyword_counter (void *cls, const char *keyword, int is_mandatory) 426static int
427{ 427add_to_keyword_counter (void *cls, const char *keyword, int is_mandatory)
428 struct GNUNET_CONTAINER_MultiHashMap *mcm = cls; 428{
429 struct KeywordCounter *cnt, *first_cnt; 429 struct GNUNET_CONTAINER_MultiHashMap *mcm = cls;
430 GNUNET_HashCode hc; 430 struct KeywordCounter *cnt, *first_cnt;
431 size_t klen; 431 GNUNET_HashCode hc;
432 432 size_t klen;
433 klen = strlen (keyword) + 1; 433
434 GNUNET_CRYPTO_hash (keyword, klen - 1, &hc); 434 klen = strlen (keyword) + 1;
435 /* Since the map might contain multiple values per keyword, we only 435 GNUNET_CRYPTO_hash (keyword, klen - 1, &hc);
436 * store one value, and attach all other to it, forming a linked list. 436 /* Since the map might contain multiple values per keyword, we only
437 * Somewhat easier than retrieving multiple items via callback. 437 * store one value, and attach all other to it, forming a linked list.
438 */ 438 * Somewhat easier than retrieving multiple items via callback.
439 first_cnt = GNUNET_CONTAINER_multihashmap_get (mcm, &hc); 439 */
440 for (cnt = first_cnt; cnt && strcmp (cnt->value, keyword) != 0; cnt = cnt->next); 440 first_cnt = GNUNET_CONTAINER_multihashmap_get (mcm, &hc);
441 if (cnt == NULL) 441 for (cnt = first_cnt; cnt && strcmp (cnt->value, keyword) != 0; cnt = cnt->next);
442 { 442 if (cnt == NULL)
443 cnt = GNUNET_malloc (sizeof (struct KeywordCounter) + klen); 443 {
444 cnt->value = (const char *) &cnt[1]; 444 cnt = GNUNET_malloc (sizeof (struct KeywordCounter) + klen);
445 memcpy (&cnt[1], keyword, klen); 445 cnt->value = (const char *) &cnt[1];
446 if (first_cnt != NULL) 446 memcpy (&cnt[1], keyword, klen);
447 { 447 if (first_cnt != NULL)
448 if (first_cnt->prev != NULL) 448 {
449 { 449 if (first_cnt->prev != NULL)
450 first_cnt->prev->next = cnt; 450 {
451 cnt->prev = first_cnt->prev; 451 first_cnt->prev->next = cnt;
452 } 452 cnt->prev = first_cnt->prev;
453 first_cnt->prev = cnt; 453 }
454 cnt->next = first_cnt; 454 first_cnt->prev = cnt;
455 } 455 cnt->next = first_cnt;
456 else 456 }
457 GNUNET_CONTAINER_multihashmap_put (mcm, &hc, cnt, 457 else
458 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); 458 GNUNET_CONTAINER_multihashmap_put (mcm, &hc, cnt,
459 } 459 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
460 cnt->count++; 460 }
461 return GNUNET_OK; 461 cnt->count++;
462} 462 return GNUNET_OK;
463 463}
464/** 464
465 * Type of a function that libextractor calls for each 465/**
466 * meta data item found. 466 * Type of a function that libextractor calls for each
467 * 467 * meta data item found.
468 * @param cls the container multihashmap to update 468 *
469 * @param plugin_name name of the plugin that produced this value; 469 * @param cls the container multihashmap to update
470 * special values can be used (i.e. '&lt;zlib&gt;' for zlib being 470 * @param plugin_name name of the plugin that produced this value;
471 * used in the main libextractor library and yielding 471 * special values can be used (i.e. '&lt;zlib&gt;' for zlib being
472 * meta data). 472 * used in the main libextractor library and yielding
473 * @param type libextractor-type describing the meta data 473 * meta data).
474 * @param format basic format information about data 474 * @param type libextractor-type describing the meta data
475 * @param data_mime_type mime-type of data (not of the original file); 475 * @param format basic format information about data
476 * can be NULL (if mime-type is not known) 476 * @param data_mime_type mime-type of data (not of the original file);
477 * @param data actual meta-data found 477 * can be NULL (if mime-type is not known)
478 * @param data_len number of bytes in data 478 * @param data actual meta-data found
479 * @return GNUNET_OK to continue extracting / iterating 479 * @param data_len number of bytes in data
480 */ 480 * @return GNUNET_OK to continue extracting / iterating
481static int 481 */
482add_to_meta_counter (void *cls, const char *plugin_name, 482static int
483 enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format, 483add_to_meta_counter (void *cls, const char *plugin_name,
484 const char *data_mime_type, const char *data, size_t data_len) 484 enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format,
485{ 485 const char *data_mime_type, const char *data, size_t data_len)
486 struct GNUNET_CONTAINER_MultiHashMap *map = cls; 486{
487 GNUNET_HashCode key; 487 struct GNUNET_CONTAINER_MultiHashMap *map = cls;
488 struct MetaCounter *cnt, *first_cnt; 488 GNUNET_HashCode key;
489 489 struct MetaCounter *cnt, *first_cnt;
490 GNUNET_CRYPTO_hash (data, data_len, &key); 490
491 first_cnt = GNUNET_CONTAINER_multihashmap_get (map, &key); 491 GNUNET_CRYPTO_hash (data, data_len, &key);
492 for (cnt = first_cnt; cnt 492 first_cnt = GNUNET_CONTAINER_multihashmap_get (map, &key);
493 && cnt->data_size != data_len 493 for (cnt = first_cnt; cnt
494 && memcmp (cnt->data, data, cnt->data_size) != 0; cnt = cnt->next); 494 && cnt->data_size != data_len
495 if (cnt == NULL) 495 && memcmp (cnt->data, data, cnt->data_size) != 0; cnt = cnt->next);
496 { 496 if (cnt == NULL)
497 cnt = GNUNET_malloc (sizeof (struct MetaCounter)); 497 {
498 cnt->data = data; 498 cnt = GNUNET_malloc (sizeof (struct MetaCounter));
499 cnt->data_size = data_len; 499 cnt->data = data;
500 cnt->plugin_name = plugin_name; 500 cnt->data_size = data_len;
501 cnt->type = type; 501 cnt->plugin_name = plugin_name;
502 cnt->format = format; 502 cnt->type = type;
503 cnt->data_mime_type = data_mime_type; 503 cnt->format = format;
504 504 cnt->data_mime_type = data_mime_type;
505 if (first_cnt != NULL) 505
506 { 506 if (first_cnt != NULL)
507 if (first_cnt->prev != NULL) 507 {
508 { 508 if (first_cnt->prev != NULL)
509 first_cnt->prev->next = cnt; 509 {
510 cnt->prev = first_cnt->prev; 510 first_cnt->prev->next = cnt;
511 } 511 cnt->prev = first_cnt->prev;
512 first_cnt->prev = cnt; 512 }
513 cnt->next = first_cnt; 513 first_cnt->prev = cnt;
514 } 514 cnt->next = first_cnt;
515 else 515 }
516 GNUNET_CONTAINER_multihashmap_put (map, &key, cnt, 516 else
517 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); 517 GNUNET_CONTAINER_multihashmap_put (map, &key, cnt,
518 } 518 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
519 cnt->count++; 519 }
520 return 0; 520 cnt->count++;
521} 521 return 0;
522 522}
523/** 523
524 * Allocates a struct ShareTreeItem and adds it to its parent. 524/**
525 */ 525 * Allocates a struct GNUNET_FS_ShareTreeItem and adds it to its parent.
526static struct ShareTreeItem * 526 */
527make_item (struct ShareTreeItem *parent) 527static struct GNUNET_FS_ShareTreeItem *
528{ 528make_item (struct GNUNET_FS_ShareTreeItem *parent)
529 struct ShareTreeItem *item; 529{
530 item = GNUNET_malloc (sizeof (struct ShareTreeItem)); 530 struct GNUNET_FS_ShareTreeItem *item;
531 531 item = GNUNET_malloc (sizeof (struct GNUNET_FS_ShareTreeItem));
532 item->parent = parent; 532
533 if (parent) 533 item->parent = parent;
534 GNUNET_CONTAINER_DLL_insert (parent->children_head, parent->children_tail, 534 if (parent)
535 item); 535 GNUNET_CONTAINER_DLL_insert (parent->children_head, parent->children_tail,
536 return item; 536 item);
537} 537 return item;
538 538}
539/** 539
540 * Extract metadata from a file and add it to the share tree 540/**
541 * 541 * Extract metadata from a file and add it to the share tree
542 * @param adc context to modify 542 *
543 * @param filename name of the file to process 543 * @param adc context to modify
544 */ 544 * @param filename name of the file to process
545static void 545 */
546extract_file (struct AddDirStack *ads, const char *filename) 546static void
547{ 547extract_file (struct AddDirStack *ads, const char *filename)
548 struct ShareTreeItem *item; 548{
549 const char *short_fn; 549 struct GNUNET_FS_ShareTreeItem *item;
550 550 const char *short_fn;
551 item = make_item (ads->parent); 551
552 552 item = make_item (ads->parent);
553 GNUNET_DISK_file_size (filename, &item->file_size, GNUNET_YES); 553
554 item->is_directory = GNUNET_NO; 554 GNUNET_DISK_file_size (filename, &item->file_size, GNUNET_YES);
555 555 item->is_directory = GNUNET_NO;
556 item->meta = GNUNET_CONTAINER_meta_data_create (); 556
557 GNUNET_FS_meta_data_extract_from_file (item->meta, filename, 557 item->meta = GNUNET_CONTAINER_meta_data_create ();
558 ads->adc->plugins); 558 GNUNET_FS_meta_data_extract_from_file (item->meta, filename,
559 GNUNET_CONTAINER_meta_data_delete (item->meta, EXTRACTOR_METATYPE_FILENAME, 559 ads->adc->plugins);
560 NULL, 0); 560 GNUNET_CONTAINER_meta_data_delete (item->meta, EXTRACTOR_METATYPE_FILENAME,
561 short_fn = GNUNET_STRINGS_get_short_name (filename); 561 NULL, 0);
562 562 short_fn = GNUNET_STRINGS_get_short_name (filename);
563 item->filename = GNUNET_strdup (filename); 563
564 item->short_filename = GNUNET_strdup (short_fn); 564 item->filename = GNUNET_strdup (filename);
565 565 item->short_filename = GNUNET_strdup (short_fn);
566 GNUNET_CONTAINER_meta_data_insert (item->meta, "<libgnunetfs>", 566
567 EXTRACTOR_METATYPE_FILENAME, 567 GNUNET_CONTAINER_meta_data_insert (item->meta, "<libgnunetfs>",
568 EXTRACTOR_METAFORMAT_UTF8, "text/plain", 568 EXTRACTOR_METATYPE_FILENAME,
569 short_fn, strlen (short_fn) + 1); 569 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
570} 570 short_fn, strlen (short_fn) + 1);
571 571}
572/** 572
573 * Remove the keyword from the ksk URI. 573/**
574 * 574 * Remove the keyword from the ksk URI.
575 * @param cls the ksk uri 575 *
576 * @param keyword the word to remove 576 * @param cls the ksk uri
577 * @param is_mandatory ignored 577 * @param keyword the word to remove
578 * @return always GNUNET_OK 578 * @param is_mandatory ignored
579 */ 579 * @return always GNUNET_OK
580static int 580 */
581remove_keyword (void *cls, const char *keyword, int is_mandatory) 581static int
582{ 582remove_keyword (void *cls, const char *keyword, int is_mandatory)
583 struct GNUNET_FS_Uri *ksk = cls; 583{
584 584 struct GNUNET_FS_Uri *ksk = cls;
585 GNUNET_FS_uri_ksk_remove_keyword (ksk, keyword); 585
586 return GNUNET_OK; 586 GNUNET_FS_uri_ksk_remove_keyword (ksk, keyword);
587} 587 return GNUNET_OK;
588 588}
589/** 589
590 * Remove keywords from current directory's children, if they are 590/**
591 * in the exluded keywords list of that directory. 591 * Remove keywords from current directory's children, if they are
592 * 592 * in the exluded keywords list of that directory.
593 * @param cls the ksk uri 593 *
594 * @param keyword the word to remove 594 * @param cls the ksk uri
595 * @param is_mandatory ignored 595 * @param keyword the word to remove
596 * @return always GNUNET_OK 596 * @param is_mandatory ignored
597 */ 597 * @return always GNUNET_OK
598static int 598 */
599remove_keywords (struct ProcessMetadataStackItem *stack, struct ShareTreeItem *dir) 599static int
600{ 600remove_keywords (struct ProcessMetadataStackItem *stack, struct GNUNET_FS_ShareTreeItem *dir)
601 struct ShareTreeItem *item; 601{
602 602 struct GNUNET_FS_ShareTreeItem *item;
603 for (item = dir->children_head; item; item = item->next) 603
604 { 604 for (item = dir->children_head; item; item = item->next)
605 if (stack->exclude_ksk != NULL) 605 {
606 GNUNET_FS_uri_ksk_get_keywords (stack->exclude_ksk, &remove_keyword, item->ksk_uri); 606 if (stack->exclude_ksk != NULL)
607 } 607 GNUNET_FS_uri_ksk_get_keywords (stack->exclude_ksk, &remove_keyword, item->ksk_uri);
608 return GNUNET_OK; 608 }
609} 609 return GNUNET_OK;
610 610}
611/** 611
612 * Context passed to 'migrate_and_drop'. 612/**
613 */ 613 * Context passed to 'migrate_and_drop'.
614struct KeywordProcessContext 614 */
615{ 615struct KeywordProcessContext
616 /** 616{
617 * All the keywords we migrated to the parent. 617 /**
618 */ 618 * All the keywords we migrated to the parent.
619 struct GNUNET_FS_Uri *ksk; 619 */
620 620 struct GNUNET_FS_Uri *ksk;
621 /** 621
622 * How often does a keyword have to occur to be 622 /**
623 * migrated to the parent? 623 * How often does a keyword have to occur to be
624 */ 624 * migrated to the parent?
625 unsigned int threshold; 625 */
626}; 626 unsigned int threshold;
627 627};
628/** 628
629 * Context passed to 'migrate_and_drop'. 629/**
630 */ 630 * Context passed to 'migrate_and_drop'.
631struct MetaProcessContext 631 */
632{ 632struct MetaProcessContext
633 /** 633{
634 * All the metadata we copy to the parent. 634 /**
635 */ 635 * All the metadata we copy to the parent.
636 struct GNUNET_CONTAINER_MetaData *meta; 636 */
637 637 struct GNUNET_CONTAINER_MetaData *meta;
638 /** 638
639 * How often does a metadata have to occur to be 639 /**
640 * migrated to the parent? 640 * How often does a metadata have to occur to be
641 */ 641 * migrated to the parent?
642 unsigned int threshold; 642 */
643}; 643 unsigned int threshold;
644 644};
645 645
646/** 646
647 * Move "frequent" keywords over to the 647/**
648 * target ksk uri, free the counters. 648 * Move "frequent" keywords over to the
649 * 649 * target ksk uri, free the counters.
650 */ 650 *
651static int 651 */
652migrate_and_drop (void *cls, const GNUNET_HashCode * key, void *value) 652static int
653{ 653migrate_and_drop (void *cls, const GNUNET_HashCode * key, void *value)
654 struct KeywordProcessContext *kpc = cls; 654{
655 struct KeywordCounter *counter = value; 655 struct KeywordProcessContext *kpc = cls;
656 656 struct KeywordCounter *counter = value;
657 if (counter->count >= kpc->threshold && counter->count > 1) 657
658 { 658 if (counter->count >= kpc->threshold && counter->count > 1)
659 GNUNET_FS_uri_ksk_add_keyword (kpc->ksk, counter->value, GNUNET_NO); 659 {
660 } 660 GNUNET_FS_uri_ksk_add_keyword (kpc->ksk, counter->value, GNUNET_NO);
661 GNUNET_free (counter); 661 }
662 return GNUNET_YES; 662 GNUNET_free (counter);
663} 663 return GNUNET_YES;
664/** 664}
665 * Copy "frequent" metadata items over to the 665/**
666 * target metadata container, free the counters. 666 * Copy "frequent" metadata items over to the
667 * 667 * target metadata container, free the counters.
668 */ 668 *
669static int 669 */
670migrate_and_drop_metadata (void *cls, const GNUNET_HashCode * key, void *value) 670static int
671{ 671migrate_and_drop_metadata (void *cls, const GNUNET_HashCode * key, void *value)
672 struct MetaProcessContext *mpc = cls; 672{
673 struct MetaCounter *counter = value; 673 struct MetaProcessContext *mpc = cls;
674 674 struct MetaCounter *counter = value;
675 if (counter->count >= mpc->threshold && counter->count > 1) 675
676 { 676 if (counter->count >= mpc->threshold && counter->count > 1)
677 GNUNET_CONTAINER_meta_data_insert (mpc->meta, 677 {
678 counter->plugin_name, 678 GNUNET_CONTAINER_meta_data_insert (mpc->meta,
679 counter->type, 679 counter->plugin_name,
680 counter->format, 680 counter->type,
681 counter->data_mime_type, counter->data, 681 counter->format,
682 counter->data_size); 682 counter->data_mime_type, counter->data,
683 } 683 counter->data_size);
684 GNUNET_free (counter); 684 }
685 return GNUNET_YES; 685 GNUNET_free (counter);
686} 686 return GNUNET_YES;
687 687}
688/** 688
689 * Go over the collected keywords from all entries in the 689/**
690 * directory and push common keywords up one level (by 690 * Go over the collected keywords from all entries in the
691 * adding it to the returned struct). Do the same for metadata. 691 * directory and push common keywords up one level (by
692 * Destroys keywordcounter and metacoutner for current directory. 692 * adding it to the returned struct). Do the same for metadata.
693 * 693 * Destroys keywordcounter and metacoutner for current directory.
694 * @param adc collection of child meta data 694 *
695 * @param exclude_ksk pointer to where moveable keywords will be stored 695 * @param adc collection of child meta data
696 * @param copy_meta pointer to where copyable metadata will be stored 696 * @param exclude_ksk pointer to where moveable keywords will be stored
697 */ 697 * @param copy_meta pointer to where copyable metadata will be stored
698static void 698 */
699process_keywords_and_metadata (struct ProcessMetadataStackItem *stack, 699static void
700 struct GNUNET_FS_Uri **exclude_ksk, 700process_keywords_and_metadata (struct ProcessMetadataStackItem *stack,
701 struct GNUNET_CONTAINER_MetaData **copy_meta) 701 struct GNUNET_FS_Uri **exclude_ksk,
702{ 702 struct GNUNET_CONTAINER_MetaData **copy_meta)
703 struct KeywordProcessContext kpc; 703{
704 struct MetaProcessContext mpc; 704 struct KeywordProcessContext kpc;
705 struct GNUNET_CONTAINER_MetaData *tmp; 705 struct MetaProcessContext mpc;
706 706 struct GNUNET_CONTAINER_MetaData *tmp;
707 /* Surprisingly, it's impossible to create a ksk with 0 keywords directly. 707
708 * But we can create one from an empty metadata set 708 /* Surprisingly, it's impossible to create a ksk with 0 keywords directly.
709 */ 709 * But we can create one from an empty metadata set
710 tmp = GNUNET_CONTAINER_meta_data_create (); 710 */
711 kpc.ksk = GNUNET_FS_uri_ksk_create_from_meta_data (tmp); 711 tmp = GNUNET_CONTAINER_meta_data_create ();
712 GNUNET_CONTAINER_meta_data_destroy (tmp); 712 kpc.ksk = GNUNET_FS_uri_ksk_create_from_meta_data (tmp);
713 mpc.meta = GNUNET_CONTAINER_meta_data_create (); 713 GNUNET_CONTAINER_meta_data_destroy (tmp);
714 714 mpc.meta = GNUNET_CONTAINER_meta_data_create ();
715 kpc.threshold = mpc.threshold = (stack->dir_entry_count + 1) / 2; /* 50% */ 715
716 716 kpc.threshold = mpc.threshold = (stack->dir_entry_count + 1) / 2; /* 50% */
717 GNUNET_CONTAINER_multihashmap_iterate (stack->keywordcounter, 717
718 &migrate_and_drop, &kpc); 718 GNUNET_CONTAINER_multihashmap_iterate (stack->keywordcounter,
719 GNUNET_CONTAINER_multihashmap_iterate (stack->metacounter, 719 &migrate_and_drop, &kpc);
720 &migrate_and_drop_metadata, &mpc); 720 GNUNET_CONTAINER_multihashmap_iterate (stack->metacounter,
721 721 &migrate_and_drop_metadata, &mpc);
722 GNUNET_CONTAINER_multihashmap_destroy (stack->keywordcounter); 722
723 GNUNET_CONTAINER_multihashmap_destroy (stack->metacounter); 723 GNUNET_CONTAINER_multihashmap_destroy (stack->keywordcounter);
724 *exclude_ksk = kpc.ksk; 724 GNUNET_CONTAINER_multihashmap_destroy (stack->metacounter);
725 *copy_meta = mpc.meta; 725 *exclude_ksk = kpc.ksk;
726} 726 *copy_meta = mpc.meta;
727 727}
728/** 728
729 * Function called by the directory iterator to 729/**
730 * (recursively) add all of the files in the 730 * Function called by the directory iterator to
731 * directory to the tree. 731 * (recursively) add all of the files in the
732 * Called by the directory scanner to initiate the 732 * directory to the tree.
733 * scan. 733 * Called by the directory scanner to initiate the
734 * TODO: find a way to make it non-recursive. 734 * scan.
735 * 735 * TODO: find a way to make it non-recursive.
736 * @param cls the 'struct AddDirStack *' we're in 736 *
737 * @param filename file or directory to scan 737 * @param cls the 'struct AddDirStack *' we're in
738 */ 738 * @param filename file or directory to scan
739static int 739 */
740scan_directory (void *cls, const char *filename) 740static int
741{ 741scan_directory (void *cls, const char *filename)
742 struct AddDirStack *ads = cls, recurse_ads; 742{
743 struct AddDirContext *adc = ads->adc; 743 struct AddDirStack *ads = cls, recurse_ads;
744 struct stat sbuf; 744 struct AddDirContext *adc = ads->adc;
745 struct ShareTreeItem *item; 745 struct stat sbuf;
746 const char *short_fn; 746 struct GNUNET_FS_ShareTreeItem *item;
747 int do_stop = 0; 747 const char *short_fn;
748 748 int do_stop = 0;
749 /* Wrap up fast */ 749
750 if (adc->do_stop) 750 /* Wrap up fast */
751 return GNUNET_SYSERR; 751 if (adc->do_stop)
752 752 return GNUNET_SYSERR;
753 /* If the file doesn't exist (or is not statable for any other reason, 753
754 * skip it, and report it. 754 /* If the file doesn't exist (or is not statable for any other reason,
755 */ 755 * skip it, and report it.
756 if (0 != STAT (filename, &sbuf)) 756 */
757 { 757 if (0 != STAT (filename, &sbuf))
758 do_stop = write_progress (adc, filename, S_ISDIR (sbuf.st_mode), 758 {
759 GNUNET_DIR_SCANNER_DOES_NOT_EXIST); 759 do_stop = write_progress (adc, filename, S_ISDIR (sbuf.st_mode),
760 return GNUNET_OK; 760 GNUNET_DIR_SCANNER_DOES_NOT_EXIST);
761 } 761 return GNUNET_OK;
762 762 }
763 /* Report the progress */ 763
764 do_stop = write_progress (adc, filename, S_ISDIR (sbuf.st_mode), 764 /* Report the progress */
765 GNUNET_DIR_SCANNER_NEW_FILE); 765 do_stop = write_progress (adc, filename, S_ISDIR (sbuf.st_mode),
766 if (do_stop) 766 GNUNET_DIR_SCANNER_NEW_FILE);
767 { 767 if (do_stop)
768 /* We were asked to stop, acknowledge that and return */ 768 {
769 do_stop = write_progress (adc, filename, S_ISDIR (sbuf.st_mode), 769 /* We were asked to stop, acknowledge that and return */
770 GNUNET_DIR_SCANNER_ASKED_TO_STOP); 770 do_stop = write_progress (adc, filename, S_ISDIR (sbuf.st_mode),
771 return GNUNET_SYSERR; 771 GNUNET_DIR_SCANNER_ASKED_TO_STOP);
772 } 772 return GNUNET_SYSERR;
773 773 }
774 if (!S_ISDIR (sbuf.st_mode)) 774
775 extract_file (ads, filename); 775 if (!S_ISDIR (sbuf.st_mode))
776 else 776 extract_file (ads, filename);
777 { 777 else
778 item = make_item (ads->parent); 778 {
779 item->meta = GNUNET_CONTAINER_meta_data_create (); 779 item = make_item (ads->parent);
780 780 item->meta = GNUNET_CONTAINER_meta_data_create ();
781 item->is_directory = GNUNET_YES; 781
782 782 item->is_directory = GNUNET_YES;
783 recurse_ads.adc = adc; 783
784 recurse_ads.parent = item; 784 recurse_ads.adc = adc;
785 785 recurse_ads.parent = item;
786 /* recurse into directory */ 786
787 GNUNET_DISK_directory_scan (filename, &scan_directory, &recurse_ads); 787 /* recurse into directory */
788 788 GNUNET_DISK_directory_scan (filename, &scan_directory, &recurse_ads);
789 short_fn = GNUNET_STRINGS_get_short_name (filename); 789
790 790 short_fn = GNUNET_STRINGS_get_short_name (filename);
791 item->filename = GNUNET_strdup (filename); 791
792 item->short_filename = GNUNET_strdup (short_fn); 792 item->filename = GNUNET_strdup (filename);
793 793 item->short_filename = GNUNET_strdup (short_fn);
794 if (ads->parent == NULL) 794
795 { 795 if (ads->parent == NULL)
796 /* we're finished with the scan, make sure caller gets the top-level 796 {
797 * directory pointer 797 /* we're finished with the scan, make sure caller gets the top-level
798 */ 798 * directory pointer
799 adc->toplevel = item; 799 */
800 } 800 adc->toplevel = item;
801 } 801 }
802 return GNUNET_OK; 802 }
803} 803 return GNUNET_OK;
804 804}
805/** 805
806 * Signals the scanner to finish the scan as fast as possible. 806/**
807 * Does not block. 807 * Signals the scanner to finish the scan as fast as possible.
808 * Can close the pipe if asked to, but that is only used by the 808 * Does not block.
809 * internal call to this function during cleanup. The client 809 * Can close the pipe if asked to, but that is only used by the
810 * must understand the consequences of closing the pipe too early. 810 * internal call to this function during cleanup. The client
811 * 811 * must understand the consequences of closing the pipe too early.
812 * @param ds directory scanner structure 812 *
813 * @param close_pipe GNUNET_YES to close 813 * @param ds directory scanner structure
814 */ 814 * @param close_pipe GNUNET_YES to close
815void 815 */
816GNUNET_FS_directory_scan_finish (struct GNUNET_FS_DirScanner *ds, 816void
817 int close_pipe) 817GNUNET_FS_directory_scan_finish (struct GNUNET_FS_DirScanner *ds,
818{ 818 int close_pipe)
819 char c = 1; 819{
820 GNUNET_DISK_file_write (ds->stop_write, &c, 1); 820 char c = 1;
821 821 GNUNET_DISK_file_write (ds->stop_write, &c, 1);
822 if (close_pipe) 822
823 { 823 if (close_pipe)
824 if (ds->progress_read_task != GNUNET_SCHEDULER_NO_TASK) 824 {
825 { 825 if (ds->progress_read_task != GNUNET_SCHEDULER_NO_TASK)
826 GNUNET_SCHEDULER_cancel (ds->progress_read_task); 826 {
827 ds->progress_read_task = GNUNET_SCHEDULER_NO_TASK; 827 GNUNET_SCHEDULER_cancel (ds->progress_read_task);
828 } 828 ds->progress_read_task = GNUNET_SCHEDULER_NO_TASK;
829 GNUNET_DISK_pipe_close_end (ds->progress_pipe, GNUNET_DISK_PIPE_END_READ); 829 }
830 ds->progress_read = NULL; 830 GNUNET_DISK_pipe_close_end (ds->progress_pipe, GNUNET_DISK_PIPE_END_READ);
831 } 831 ds->progress_read = NULL;
832} 832 }
833 833}
834/** 834
835 * Signals the scanner thread to finish (in case it isn't finishing 835/**
836 * already) and joins the scanner thread. Closes the pipes, frees the 836 * Signals the scanner thread to finish (in case it isn't finishing
837 * scanner contexts (both of them), returns the results of the scan. 837 * already) and joins the scanner thread. Closes the pipes, frees the
838 * Results are valid (and have to be freed) even if the scanner had 838 * scanner contexts (both of them), returns the results of the scan.
839 * an error or was rushed to finish prematurely. 839 * Results are valid (and have to be freed) even if the scanner had
840 * Blocks until the scanner is finished. 840 * an error or was rushed to finish prematurely.
841 * 841 * Blocks until the scanner is finished.
842 * @param ds directory scanner structure 842 *
843 * @return the results of the scan (a directory tree) 843 * @param ds directory scanner structure
844 */ 844 * @return the results of the scan (a directory tree)
845struct ShareTreeItem * 845 */
846GNUNET_FS_directory_scan_cleanup (struct GNUNET_FS_DirScanner *ds) 846struct GNUNET_FS_ShareTreeItem *
847{ 847GNUNET_FS_directory_scan_cleanup (struct GNUNET_FS_DirScanner *ds)
848 struct ShareTreeItem *result; 848{
849 849 struct GNUNET_FS_ShareTreeItem *result;
850 GNUNET_FS_directory_scan_finish (ds, GNUNET_YES); 850
851#if WINDOWS 851 GNUNET_FS_directory_scan_finish (ds, GNUNET_YES);
852 WaitForSingleObject (ds->thread, INFINITE); 852#if WINDOWS
853 CloseHandle (ds->thread); 853 WaitForSingleObject (ds->thread, INFINITE);
854#else 854 CloseHandle (ds->thread);
855 pthread_join (ds->thread, NULL); 855#else
856 pthread_detach (ds->thread); 856 pthread_join (ds->thread, NULL);
857#endif 857 pthread_detach (ds->thread);
858 858#endif
859 GNUNET_DISK_pipe_close (ds->stop_pipe); 859
860 GNUNET_DISK_pipe_close (ds->progress_pipe); 860 GNUNET_DISK_pipe_close (ds->stop_pipe);
861 result = ds->adc->toplevel; 861 GNUNET_DISK_pipe_close (ds->progress_pipe);
862 GNUNET_free (ds->adc); 862 result = ds->adc->toplevel;
863 GNUNET_free (ds); 863 GNUNET_free (ds->adc);
864 return result; 864 GNUNET_free (ds);
865} 865 return result;
866 866}
867/** 867
868 * The function from which the scanner thread starts 868/**
869 */ 869 * The function from which the scanner thread starts
870#if WINDOWS 870 */
871static DWORD 871#if WINDOWS
872#else 872static DWORD
873static int 873#else
874#endif 874static void *
875run_directory_scan_thread (struct AddDirContext *adc) 875#endif
876{ 876run_directory_scan_thread (void *cls)
877 struct AddDirStack ads; 877{
878 ads.adc = adc; 878 struct AddDirContext *adc = cls;
879 ads.parent = NULL; 879 struct AddDirStack ads;
880 scan_directory (&ads, adc->filename_expanded); 880 ads.adc = adc;
881 GNUNET_free (adc->filename_expanded); 881 ads.parent = NULL;
882 if (adc->plugins != NULL) 882 scan_directory (&ads, adc->filename_expanded);
883 EXTRACTOR_plugin_remove_all (adc->plugins); 883 GNUNET_free (adc->filename_expanded);
884 /* Tell the initiator that we're finished, it can now join the thread */ 884 if (adc->plugins != NULL)
885 write_progress (adc, NULL, 0, GNUNET_DIR_SCANNER_FINISHED); 885 EXTRACTOR_plugin_remove_all (adc->plugins);
886 return 0; 886 /* Tell the initiator that we're finished, it can now join the thread */
887} 887 write_progress (adc, NULL, 0, GNUNET_DIR_SCANNER_FINISHED);
888 888 return 0;
889/** 889}
890 * Called every time there is data to read from the scanner. 890
891 * Calls the scanner progress handler. 891/**
892 * 892 * Called every time there is data to read from the scanner.
893 * @param cls the closure (directory scanner object) 893 * Calls the scanner progress handler.
894 * @param tc task context in which the task is running 894 *
895 */ 895 * @param cls the closure (directory scanner object)
896static void 896 * @param tc task context in which the task is running
897read_progress_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 897 */
898{ 898static void
899 struct GNUNET_FS_DirScanner *ds; 899read_progress_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
900 int end_it = 0; 900{
901 enum GNUNET_DirScannerProgressUpdateReason reason; 901 struct GNUNET_FS_DirScanner *ds;
902 ssize_t rd; 902 int end_it = 0;
903 ssize_t total_read; 903 enum GNUNET_FS_DirScannerProgressUpdateReason reason;
904 904 ssize_t rd;
905 size_t filename_len; 905 ssize_t total_read;
906 char is_directory; 906
907 char *filename; 907 size_t filename_len;
908 908 char is_directory;
909 ds = cls; 909 char *filename;
910 910
911 ds->progress_read_task = GNUNET_SCHEDULER_NO_TASK; 911 ds = cls;
912 912
913 if (!(tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) 913 ds->progress_read_task = GNUNET_SCHEDULER_NO_TASK;
914 { 914
915 ds->progress_callback (ds->cls, ds, NULL, 0, GNUNET_DIR_SCANNER_SHUTDOWN); 915 if (!(tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
916 return; 916 {
917 } 917 ds->progress_callback (ds->cls, ds, NULL, 0, GNUNET_DIR_SCANNER_SHUTDOWN);
918 918 return;
919 /* Read one message. If message is malformed or can't be read, end the scanner */ 919 }
920 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, &reason, sizeof (reason)); 920
921 while (rd > 0 && total_read < sizeof (reason)) 921 /* Read one message. If message is malformed or can't be read, end the scanner */
922 { 922 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, &reason, sizeof (reason));
923 rd = GNUNET_DISK_file_read (ds->progress_read, 923 while (rd > 0 && total_read < sizeof (reason))
924 &((char *) &reason)[total_read], 924 {
925 sizeof (reason) - total_read); 925 rd = GNUNET_DISK_file_read (ds->progress_read,
926 if (rd > 0) 926 &((char *) &reason)[total_read],
927 total_read += rd; 927 sizeof (reason) - total_read);
928 } 928 if (rd > 0)
929 if (total_read != sizeof (reason) 929 total_read += rd;
930 || reason <= GNUNET_DIR_SCANNER_FIRST 930 }
931 || reason >= GNUNET_DIR_SCANNER_LAST) 931 if (total_read != sizeof (reason)
932 { 932 || reason <= GNUNET_DIR_SCANNER_FIRST
933 end_it = 1; 933 || reason >= GNUNET_DIR_SCANNER_LAST)
934 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR; 934 {
935 } 935 end_it = 1;
936 936 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR;
937 if (!end_it) 937 }
938 { 938
939 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, &filename_len, 939 if (!end_it)
940 sizeof (size_t)); 940 {
941 while (rd > 0 && total_read < sizeof (size_t)) 941 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, &filename_len,
942 { 942 sizeof (size_t));
943 rd = GNUNET_DISK_file_read (ds->progress_read, 943 while (rd > 0 && total_read < sizeof (size_t))
944 &((char *) &filename_len)[total_read], 944 {
945 sizeof (size_t) - total_read); 945 rd = GNUNET_DISK_file_read (ds->progress_read,
946 if (rd > 0) 946 &((char *) &filename_len)[total_read],
947 total_read += rd; 947 sizeof (size_t) - total_read);
948 } 948 if (rd > 0)
949 if (rd != sizeof (size_t)) 949 total_read += rd;
950 { 950 }
951 end_it = 1; 951 if (rd != sizeof (size_t))
952 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR; 952 {
953 } 953 end_it = 1;
954 } 954 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR;
955 if (!end_it) 955 }
956 { 956 }
957 if (filename_len == 0) 957 if (!end_it)
958 end_it = 1; 958 {
959 else if (filename_len > MAX_PATH) 959 if (filename_len == 0)
960 { 960 end_it = 1;
961 end_it = 1; 961 else if (filename_len > PATH_MAX)
962 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR; 962 {
963 } 963 end_it = 1;
964 } 964 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR;
965 if (!end_it) 965 }
966 { 966 }
967 filename = GNUNET_malloc (filename_len); 967 if (!end_it)
968 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, filename, 968 {
969 filename_len); 969 filename = GNUNET_malloc (filename_len);
970 while (rd > 0 && total_read < filename_len) 970 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, filename,
971 { 971 filename_len);
972 rd = GNUNET_DISK_file_read (ds->progress_read, &filename[total_read], 972 while (rd > 0 && total_read < filename_len)
973 filename_len - total_read); 973 {
974 if (rd > 0) 974 rd = GNUNET_DISK_file_read (ds->progress_read, &filename[total_read],
975 total_read += rd; 975 filename_len - total_read);
976 } 976 if (rd > 0)
977 if (rd != filename_len) 977 total_read += rd;
978 { 978 }
979 GNUNET_free (filename); 979 if (rd != filename_len)
980 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR; 980 {
981 end_it = 1; 981 GNUNET_free (filename);
982 } 982 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR;
983 } 983 end_it = 1;
984 if (!end_it && filename_len > 0) 984 }
985 { 985 }
986 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, &is_directory, 986 if (!end_it && filename_len > 0)
987 sizeof (char)); 987 {
988 while (rd > 0 && total_read < sizeof (char)) 988 total_read = rd = GNUNET_DISK_file_read (ds->progress_read, &is_directory,
989 { 989 sizeof (char));
990 rd = GNUNET_DISK_file_read (ds->progress_read, &(&is_directory)[total_read], 990 while (rd > 0 && total_read < sizeof (char))
991 sizeof (char) - total_read); 991 {
992 if (rd > 0) 992 rd = GNUNET_DISK_file_read (ds->progress_read, &(&is_directory)[total_read],
993 total_read += rd; 993 sizeof (char) - total_read);
994 } 994 if (rd > 0)
995 if (rd != sizeof (char)) 995 total_read += rd;
996 { 996 }
997 GNUNET_free (filename); 997 if (rd != sizeof (char))
998 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR; 998 {
999 end_it = 1; 999 GNUNET_free (filename);
1000 } 1000 reason = GNUNET_DIR_SCANNER_PROTOCOL_ERROR;
1001 } 1001 end_it = 1;
1002 if (!end_it) 1002 }
1003 { 1003 }
1004 end_it = ds->progress_callback (ds->cls, ds, (const char *) filename, is_directory, reason); 1004 if (!end_it)
1005 GNUNET_free (filename); 1005 {
1006 if (!end_it) 1006 end_it = ds->progress_callback (ds->cls, ds, (const char *) filename, is_directory, reason);
1007 { 1007 GNUNET_free (filename);
1008 ds->progress_read_task = GNUNET_SCHEDULER_add_read_file ( 1008 if (!end_it)
1009 GNUNET_TIME_UNIT_FOREVER_REL, ds->progress_read, &read_progress_task, 1009 {
1010 cls); 1010 ds->progress_read_task = GNUNET_SCHEDULER_add_read_file (
1011 } 1011 GNUNET_TIME_UNIT_FOREVER_REL, ds->progress_read, &read_progress_task,
1012 } 1012 cls);
1013 else 1013 }
1014 { 1014 }
1015 ds->progress_callback (ds->cls, ds, NULL, 0, reason); 1015 else
1016 } 1016 {
1017} 1017 ds->progress_callback (ds->cls, ds, NULL, 0, reason);
1018 1018 }
1019 1019}
1020/** 1020
1021 * Start a directory scanner thread. 1021
1022 * 1022/**
1023 * @param filename name of the directory to scan 1023 * Start a directory scanner thread.
1024 * @param GNUNET_YES to not to run libextractor on files (only build a tree) 1024 *
1025 * @param ex if not NULL, must be a list of extra plugins for extractor 1025 * @param filename name of the directory to scan
1026 * @param cb the callback to call when there are scanning progress messages 1026 * @param GNUNET_YES to not to run libextractor on files (only build a tree)
1027 * @param cls closure for 'cb' 1027 * @param ex if not NULL, must be a list of extra plugins for extractor
1028 * @return directory scanner object to be used for controlling the scanner 1028 * @param cb the callback to call when there are scanning progress messages
1029 */ 1029 * @param cls closure for 'cb'
1030struct GNUNET_FS_DirScanner * 1030 * @return directory scanner object to be used for controlling the scanner
1031GNUNET_FS_directory_scan_start (const char *filename, 1031 */
1032 int disable_extractor, const char *ex, 1032struct GNUNET_FS_DirScanner *
1033 GNUNET_FS_DirScannerProgressCallback cb, void *cls) 1033GNUNET_FS_directory_scan_start (const char *filename,
1034{ 1034 int disable_extractor, const char *ex,
1035 struct stat sbuf; 1035 GNUNET_FS_DirScannerProgressCallback cb, void *cls)
1036 struct AddDirContext *adc; 1036{
1037 char *filename_expanded; 1037 struct stat sbuf;
1038 struct GNUNET_FS_DirScanner *ds; 1038 struct AddDirContext *adc;
1039 struct GNUNET_DISK_PipeHandle *progress_pipe; 1039 char *filename_expanded;
1040 int ok; 1040 struct GNUNET_FS_DirScanner *ds;
1041 1041 struct GNUNET_DISK_PipeHandle *progress_pipe;
1042 if (0 != STAT (filename, &sbuf)) 1042 int ok;
1043 return NULL; 1043
1044 /* TODO: consider generalizing this for files too! */ 1044 if (0 != STAT (filename, &sbuf))
1045 if (!S_ISDIR (sbuf.st_mode)) 1045 return NULL;
1046 { 1046 /* TODO: consider generalizing this for files too! */
1047 GNUNET_break (0); 1047 if (!S_ISDIR (sbuf.st_mode))
1048 return NULL; 1048 {
1049 } 1049 GNUNET_break (0);
1050 /* scan_directory() is guaranteed to be given expanded filenames, 1050 return NULL;
1051 * so expand we will! 1051 }
1052 */ 1052 /* scan_directory() is guaranteed to be given expanded filenames,
1053 filename_expanded = GNUNET_STRINGS_filename_expand (filename); 1053 * so expand we will!
1054 if (filename_expanded == NULL) 1054 */
1055 return NULL; 1055 filename_expanded = GNUNET_STRINGS_filename_expand (filename);
1056 1056 if (filename_expanded == NULL)
1057 progress_pipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO); 1057 return NULL;
1058 if (progress_pipe == NULL) 1058
1059 { 1059 progress_pipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
1060 GNUNET_free (filename_expanded); 1060 if (progress_pipe == NULL)
1061 return NULL; 1061 {
1062 } 1062 GNUNET_free (filename_expanded);
1063 1063 return NULL;
1064 adc = GNUNET_malloc (sizeof (struct AddDirContext)); 1064 }
1065 1065
1066 ds = GNUNET_malloc (sizeof (struct GNUNET_FS_DirScanner)); 1066 adc = GNUNET_malloc (sizeof (struct AddDirContext));
1067 1067
1068 ds->adc = adc; 1068 ds = GNUNET_malloc (sizeof (struct GNUNET_FS_DirScanner));
1069 1069
1070 ds->stop_pipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO); 1070 ds->adc = adc;
1071 if (ds->stop_pipe == NULL) 1071
1072 { 1072 ds->stop_pipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
1073 GNUNET_free (adc); 1073 if (ds->stop_pipe == NULL)
1074 GNUNET_free (ds); 1074 {
1075 GNUNET_free (filename_expanded); 1075 GNUNET_free (adc);
1076 GNUNET_DISK_pipe_close (progress_pipe); 1076 GNUNET_free (ds);
1077 return NULL; 1077 GNUNET_free (filename_expanded);
1078 } 1078 GNUNET_DISK_pipe_close (progress_pipe);
1079 ds->stop_write = GNUNET_DISK_pipe_handle (ds->stop_pipe, 1079 return NULL;
1080 GNUNET_DISK_PIPE_END_WRITE); 1080 }
1081 adc->stop_read = GNUNET_DISK_pipe_handle (ds->stop_pipe, 1081 ds->stop_write = GNUNET_DISK_pipe_handle (ds->stop_pipe,
1082 GNUNET_DISK_PIPE_END_READ); 1082 GNUNET_DISK_PIPE_END_WRITE);
1083 1083 adc->stop_read = GNUNET_DISK_pipe_handle (ds->stop_pipe,
1084 adc->plugins = NULL; 1084 GNUNET_DISK_PIPE_END_READ);
1085 if (!disable_extractor) 1085
1086 { 1086 adc->plugins = NULL;
1087 adc->plugins = EXTRACTOR_plugin_add_defaults ( 1087 if (!disable_extractor)
1088 EXTRACTOR_OPTION_DEFAULT_POLICY); 1088 {
1089 if (ex && strlen (ex) > 0) 1089 adc->plugins = EXTRACTOR_plugin_add_defaults (
1090 adc->plugins = EXTRACTOR_plugin_add_config (adc->plugins, ex, 1090 EXTRACTOR_OPTION_DEFAULT_POLICY);
1091 EXTRACTOR_OPTION_DEFAULT_POLICY); 1091 if (ex && strlen (ex) > 0)
1092 } 1092 adc->plugins = EXTRACTOR_plugin_add_config (adc->plugins, ex,
1093 1093 EXTRACTOR_OPTION_DEFAULT_POLICY);
1094 adc->filename_expanded = filename_expanded; 1094 }
1095 adc->progress_write = GNUNET_DISK_pipe_handle (progress_pipe, 1095
1096 GNUNET_DISK_PIPE_END_WRITE); 1096 adc->filename_expanded = filename_expanded;
1097 1097 adc->progress_write = GNUNET_DISK_pipe_handle (progress_pipe,
1098 1098 GNUNET_DISK_PIPE_END_WRITE);
1099 ds->progress_read = GNUNET_DISK_pipe_handle (progress_pipe, 1099
1100 GNUNET_DISK_PIPE_END_READ); 1100
1101 1101 ds->progress_read = GNUNET_DISK_pipe_handle (progress_pipe,
1102#if WINDOWS 1102 GNUNET_DISK_PIPE_END_READ);
1103 ds->thread = CreateThread (NULL, 0, 1103
1104 (LPTHREAD_START_ROUTINE) &run_directory_scan_thread, (LPVOID) adc, 1104#if WINDOWS
1105 0, NULL); 1105 ds->thread = CreateThread (NULL, 0,
1106 ok = ds->thread != NULL; 1106 (LPTHREAD_START_ROUTINE) &run_directory_scan_thread, (LPVOID) adc,
1107#else 1107 0, NULL);
1108 ok = !pthread_create (&ds->thread, NULL, &run_directory_scan_thread, 1108 ok = ds->thread != NULL;
1109 (void *) adc); 1109#else
1110#endif 1110 ok = !pthread_create (&ds->thread, NULL, &run_directory_scan_thread,
1111 if (!ok) 1111 (void *) adc);
1112 { 1112#endif
1113 GNUNET_free (adc); 1113 if (!ok)
1114 GNUNET_free (filename_expanded); 1114 {
1115 GNUNET_DISK_pipe_close (progress_pipe); 1115 GNUNET_free (adc);
1116 GNUNET_free (ds); 1116 GNUNET_free (filename_expanded);
1117 return NULL; 1117 GNUNET_DISK_pipe_close (progress_pipe);
1118 } 1118 GNUNET_free (ds);
1119 1119 return NULL;
1120 ds->progress_callback = cb; 1120 }
1121 ds->cls = cls; 1121
1122 ds->adc = adc; 1122 ds->progress_callback = cb;
1123 ds->progress_pipe = progress_pipe; 1123 ds->cls = cls;
1124 1124 ds->adc = adc;
1125 ds->progress_read_task = GNUNET_SCHEDULER_add_read_file ( 1125 ds->progress_pipe = progress_pipe;
1126 GNUNET_TIME_UNIT_FOREVER_REL, ds->progress_read, &read_progress_task, 1126
1127 ds); 1127 ds->progress_read_task = GNUNET_SCHEDULER_add_read_file (
1128 1128 GNUNET_TIME_UNIT_FOREVER_REL, ds->progress_read, &read_progress_task,
1129 return ds; 1129 ds);
1130} 1130
1131 1131 return ds;
1132/** 1132}
1133 * Task that post-processes the share item tree. 1133
1134 * This processing has to be done in the main thread, because 1134/**
1135 * it requires access to libgcrypt's hashing functions, and 1135 * Task that post-processes the share item tree.
1136 * libgcrypt is not thread-safe without some special magic. 1136 * This processing has to be done in the main thread, because
1137 * 1137 * it requires access to libgcrypt's hashing functions, and
1138 * @param cls top of the stack 1138 * libgcrypt is not thread-safe without some special magic.
1139 * @param tc task context 1139 *
1140 */ 1140 * @param cls top of the stack
1141static void 1141 * @param tc task context
1142trim_share_tree_task (void *cls, 1142 */
1143 const struct GNUNET_SCHEDULER_TaskContext *tc) 1143static void
1144{ 1144trim_share_tree_task (void *cls,
1145 struct ProcessMetadataStackItem *stack = cls; 1145 const struct GNUNET_SCHEDULER_TaskContext *tc)
1146 struct ProcessMetadataStackItem *next = stack; 1146{
1147 /* FIXME: figure out what to do when tc says we're shutting down */ 1147 struct ProcessMetadataStackItem *stack = cls;
1148 1148 struct ProcessMetadataStackItem *next = stack;
1149 /* item == NULL means that we've just finished going over the children of 1149 /* FIXME: figure out what to do when tc says we're shutting down */
1150 * current directory. 1150
1151 */ 1151 /* item == NULL means that we've just finished going over the children of
1152 if (stack->item == NULL) 1152 * current directory.
1153 { 1153 */
1154 if (stack->parent->item != NULL) 1154 if (stack->item == NULL)
1155 { 1155 {
1156 /* end of a directory */ 1156 if (stack->parent->item != NULL)
1157 struct GNUNET_FS_Uri *ksk; 1157 {
1158 1158 /* end of a directory */
1159 /* use keyword and metadata counters to create lists of keywords to move 1159 struct GNUNET_FS_Uri *ksk;
1160 * and metadata to copy. 1160
1161 */ 1161 /* use keyword and metadata counters to create lists of keywords to move
1162 process_keywords_and_metadata (stack, &stack->parent->exclude_ksk, &stack->parent->item->meta); 1162 * and metadata to copy.
1163 1163 */
1164 /* create keywords from metadata (copies all text-metadata as keywords, 1164 process_keywords_and_metadata (stack, &stack->parent->exclude_ksk, &stack->parent->item->meta);
1165 * AND parses the directory name we've just added, producing even more 1165
1166 * keywords. 1166 /* create keywords from metadata (copies all text-metadata as keywords,
1167 * then merge these keywords with the ones moved from children. 1167 * AND parses the directory name we've just added, producing even more
1168 */ 1168 * keywords.
1169 ksk = GNUNET_FS_uri_ksk_create_from_meta_data (stack->parent->item->meta); 1169 * then merge these keywords with the ones moved from children.
1170 stack->parent->item->ksk_uri = GNUNET_FS_uri_ksk_merge (ksk, stack->parent->exclude_ksk); 1170 */
1171 GNUNET_FS_uri_destroy (ksk); 1171 ksk = GNUNET_FS_uri_ksk_create_from_meta_data (stack->parent->item->meta);
1172 1172 stack->parent->item->ksk_uri = GNUNET_FS_uri_ksk_merge (ksk, stack->parent->exclude_ksk);
1173 /* remove moved keywords from children (complete the move) */ 1173 GNUNET_FS_uri_destroy (ksk);
1174 remove_keywords (stack->parent, stack->parent->item); 1174
1175 GNUNET_FS_uri_destroy (stack->parent->exclude_ksk); 1175 /* remove moved keywords from children (complete the move) */
1176 1176 remove_keywords (stack->parent, stack->parent->item);
1177 /* go up the stack */ 1177 GNUNET_FS_uri_destroy (stack->parent->exclude_ksk);
1178 next = stack->parent; 1178
1179 GNUNET_free (stack); 1179 /* go up the stack */
1180 next->end_directory = GNUNET_YES; 1180 next = stack->parent;
1181 } 1181 GNUNET_free (stack);
1182 else 1182 next->end_directory = GNUNET_YES;
1183 { 1183 }
1184 /* we've just finished processing the toplevel directory */ 1184 else
1185 struct ProcessMetadataContext *ctx = stack->ctx; 1185 {
1186 next = NULL; 1186 /* we've just finished processing the toplevel directory */
1187 GNUNET_SCHEDULER_add_continuation (ctx->cb, ctx->cls, 1187 struct GNUNET_FS_ProcessMetadataContext *ctx = stack->ctx;
1188 GNUNET_SCHEDULER_REASON_PREREQ_DONE); 1188 next = NULL;
1189 GNUNET_free (stack->parent); 1189 GNUNET_SCHEDULER_add_continuation (ctx->cb, ctx->cls,
1190 GNUNET_free (stack); 1190 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1191 GNUNET_free (ctx); 1191 GNUNET_free (stack->parent);
1192 } 1192 GNUNET_free (stack);
1193 } 1193 GNUNET_free (ctx);
1194 else if (stack->item->is_directory 1194 }
1195 && !stack->end_directory 1195 }
1196 && stack->item->children_head != NULL) 1196 else if (stack->item->is_directory
1197 { 1197 && !stack->end_directory
1198 /* recurse into subdirectory */ 1198 && stack->item->children_head != NULL)
1199 next = GNUNET_malloc (sizeof (struct ProcessMetadataStackItem)); 1199 {
1200 next->ctx = stack->ctx; 1200 /* recurse into subdirectory */
1201 next->item = stack->item->children_head; 1201 next = GNUNET_malloc (sizeof (struct ProcessMetadataStackItem));
1202 next->keywordcounter = GNUNET_CONTAINER_multihashmap_create (1024); 1202 next->ctx = stack->ctx;
1203 next->metacounter = GNUNET_CONTAINER_multihashmap_create (1024); 1203 next->item = stack->item->children_head;
1204 next->dir_entry_count = 0; 1204 next->keywordcounter = GNUNET_CONTAINER_multihashmap_create (1024);
1205 next->parent = stack; 1205 next->metacounter = GNUNET_CONTAINER_multihashmap_create (1024);
1206 } 1206 next->dir_entry_count = 0;
1207 else 1207 next->parent = stack;
1208 { 1208 }
1209 /* process a child entry (a file or a directory) and move to the next one*/ 1209 else
1210 if (stack->item->is_directory) 1210 {
1211 stack->end_directory = GNUNET_NO; 1211 /* process a child entry (a file or a directory) and move to the next one*/
1212 stack->dir_entry_count++; 1212 if (stack->item->is_directory)
1213 GNUNET_CONTAINER_meta_data_iterate (stack->item->meta, &add_to_meta_counter, stack->metacounter); 1213 stack->end_directory = GNUNET_NO;
1214 1214 stack->dir_entry_count++;
1215 if (stack->item->is_directory) 1215 GNUNET_CONTAINER_meta_data_iterate (stack->item->meta, &add_to_meta_counter, stack->metacounter);
1216 { 1216
1217 char *user = getenv ("USER"); 1217 if (stack->item->is_directory)
1218 if ((user == NULL) || (0 != strncasecmp (user, stack->item->short_filename, strlen(user)))) 1218 {
1219 { 1219 char *user = getenv ("USER");
1220 /* only use filename if it doesn't match $USER */ 1220 if ((user == NULL) || (0 != strncasecmp (user, stack->item->short_filename, strlen(user))))
1221 GNUNET_CONTAINER_meta_data_insert (stack->item->meta, "<libgnunetfs>", 1221 {
1222 EXTRACTOR_METATYPE_FILENAME, 1222 /* only use filename if it doesn't match $USER */
1223 EXTRACTOR_METAFORMAT_UTF8, 1223 GNUNET_CONTAINER_meta_data_insert (stack->item->meta, "<libgnunetfs>",
1224 "text/plain", stack->item->short_filename, 1224 EXTRACTOR_METATYPE_FILENAME,
1225 strlen (stack->item->short_filename) + 1); 1225 EXTRACTOR_METAFORMAT_UTF8,
1226 GNUNET_CONTAINER_meta_data_insert (stack->item->meta, "<libgnunetfs>", 1226 "text/plain", stack->item->short_filename,
1227 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME, 1227 strlen (stack->item->short_filename) + 1);
1228 EXTRACTOR_METAFORMAT_UTF8, 1228 GNUNET_CONTAINER_meta_data_insert (stack->item->meta, "<libgnunetfs>",
1229 "text/plain", stack->item->short_filename, 1229 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
1230 strlen (stack->item->short_filename) + 1); 1230 EXTRACTOR_METAFORMAT_UTF8,
1231 } 1231 "text/plain", stack->item->short_filename,
1232 } 1232 strlen (stack->item->short_filename) + 1);
1233 1233 }
1234 stack->item->ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data (stack->item->meta); 1234 }
1235 GNUNET_FS_uri_ksk_get_keywords (stack->item->ksk_uri, &add_to_keyword_counter, stack->keywordcounter); 1235
1236 stack->item = stack->item->next; 1236 stack->item->ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data (stack->item->meta);
1237 } 1237 GNUNET_FS_uri_ksk_get_keywords (stack->item->ksk_uri, &add_to_keyword_counter, stack->keywordcounter);
1238 /* Call this task again later, if there are more entries to process */ 1238 stack->item = stack->item->next;
1239 if (next) 1239 }
1240 GNUNET_SCHEDULER_add_continuation (&trim_share_tree_task, next, 1240 /* Call this task again later, if there are more entries to process */
1241 GNUNET_SCHEDULER_REASON_PREREQ_DONE); 1241 if (next)
1242} 1242 GNUNET_SCHEDULER_add_continuation (&trim_share_tree_task, next,
1243 1243 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1244/** 1244}
1245 * Process a share item tree, moving frequent keywords up and 1245
1246 * copying frequent metadata up. 1246/**
1247 * 1247 * Process a share item tree, moving frequent keywords up and
1248 * @param toplevel toplevel directory in the tree, returned by the scanner 1248 * copying frequent metadata up.
1249 * @param cb called after processing is done 1249 *
1250 * @param cls closure for 'cb' 1250 * @param toplevel toplevel directory in the tree, returned by the scanner
1251 */ 1251 * @param cb called after processing is done
1252struct ProcessMetadataContext * 1252 * @param cls closure for 'cb'
1253GNUNET_FS_trim_share_tree (struct ShareTreeItem *toplevel, 1253 */
1254 GNUNET_SCHEDULER_Task cb, void *cls) 1254struct GNUNET_FS_ProcessMetadataContext *
1255{ 1255GNUNET_FS_trim_share_tree (struct GNUNET_FS_ShareTreeItem *toplevel,
1256 struct ProcessMetadataContext *ret; 1256 GNUNET_SCHEDULER_Task cb, void *cls)
1257 1257{
1258 if (toplevel == NULL) 1258 struct GNUNET_FS_ProcessMetadataContext *ret;
1259 { 1259
1260 struct GNUNET_SCHEDULER_TaskContext tc; 1260 if (toplevel == NULL)
1261 tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; 1261 {
1262 cb (cls, &tc); 1262 struct GNUNET_SCHEDULER_TaskContext tc;
1263 return NULL; 1263 tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1264 } 1264 cb (cls, &tc);
1265 1265 return NULL;
1266 ret = GNUNET_malloc (sizeof (struct ProcessMetadataContext)); 1266 }
1267 ret->toplevel = toplevel; 1267
1268 ret->stack = GNUNET_malloc (sizeof (struct ProcessMetadataStackItem)); 1268 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_ProcessMetadataContext));
1269 ret->stack->ctx = ret; 1269 ret->toplevel = toplevel;
1270 ret->stack->item = toplevel; 1270 ret->stack = GNUNET_malloc (sizeof (struct ProcessMetadataStackItem));
1271 ret->stack->keywordcounter = GNUNET_CONTAINER_multihashmap_create (1024); 1271 ret->stack->ctx = ret;
1272 ret->stack->metacounter = GNUNET_CONTAINER_multihashmap_create (1024); 1272 ret->stack->item = toplevel;
1273 ret->stack->dir_entry_count = 0; 1273 ret->stack->keywordcounter = GNUNET_CONTAINER_multihashmap_create (1024);
1274 ret->stack->end_directory = GNUNET_NO; 1274 ret->stack->metacounter = GNUNET_CONTAINER_multihashmap_create (1024);
1275 1275 ret->stack->dir_entry_count = 0;
1276 /* dummy stack entry that tells us we're at the top of the stack */ 1276 ret->stack->end_directory = GNUNET_NO;
1277 ret->stack->parent = GNUNET_malloc (sizeof (struct ProcessMetadataStackItem)); 1277
1278 ret->stack->parent->ctx = ret; 1278 /* dummy stack entry that tells us we're at the top of the stack */
1279 1279 ret->stack->parent = GNUNET_malloc (sizeof (struct ProcessMetadataStackItem));
1280 ret->cb = cb; 1280 ret->stack->parent->ctx = ret;
1281 ret->cls = cls; 1281
1282 1282 ret->cb = cb;
1283 GNUNET_SCHEDULER_add_continuation (&trim_share_tree_task, ret->stack, 1283 ret->cls = cls;
1284 GNUNET_SCHEDULER_REASON_PREREQ_DONE); 1284
1285 return ret; 1285 GNUNET_SCHEDULER_add_continuation (&trim_share_tree_task, ret->stack,
1286} \ No newline at end of file 1286 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1287 return ret;
1288}