start fewer FS probes concurrently, run them all in the same task to avoid undue...
[oweals/gnunet.git] / src / fs / fs_unindex.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003--2013 Christian Grothoff (and other contributing authors)
4
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 3, or (at your
8      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      General Public License for more details.
14
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.
19 */
20
21 /**
22  * @file fs/fs_unindex.c
23  * @author Krista Grothoff
24  * @author Christian Grothoff
25  * @brief Unindex file.
26  */
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_fs_service.h"
30 #include "gnunet_protocols.h"
31 #include "fs_api.h"
32 #include "fs_tree.h"
33 #include "block_fs.h"
34 #include "fs_publish_ublock.h"
35
36
37 /**
38  * Function called by the tree encoder to obtain
39  * a block of plaintext data (for the lowest level
40  * of the tree).
41  *
42  * @param cls our publishing context
43  * @param offset identifies which block to get
44  * @param max (maximum) number of bytes to get; returning
45  *        fewer will also cause errors
46  * @param buf where to copy the plaintext buffer
47  * @param emsg location to store an error message (on error)
48  * @return number of bytes copied to buf, 0 on error
49  */
50 static size_t
51 unindex_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
52 {
53   struct GNUNET_FS_UnindexContext *uc = cls;
54   size_t pt_size;
55
56   pt_size = GNUNET_MIN (max, uc->file_size - offset);
57   if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET))
58   {
59     *emsg = GNUNET_strdup (_("Failed to find given position in file"));
60     return 0;
61   }
62   if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size))
63   {
64     *emsg = GNUNET_strdup (_("Failed to read file"));
65     return 0;
66   }
67   return pt_size;
68 }
69
70
71 /**
72  * Fill in all of the generic fields for
73  * an unindex event and call the callback.
74  *
75  * @param pi structure to fill in
76  * @param uc overall unindex context
77  * @param offset where we are in the file (for progress)
78  */
79 void
80 GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
81                                 struct GNUNET_FS_UnindexContext *uc,
82                                 uint64_t offset)
83 {
84   pi->value.unindex.uc = uc;
85   pi->value.unindex.cctx = uc->client_info;
86   pi->value.unindex.filename = uc->filename;
87   pi->value.unindex.size = uc->file_size;
88   pi->value.unindex.eta =
89       GNUNET_TIME_calculate_eta (uc->start_time, offset, uc->file_size);
90   pi->value.unindex.duration =
91       GNUNET_TIME_absolute_get_duration (uc->start_time);
92   pi->value.unindex.completed = offset;
93   pi->fsh = uc->h;
94   uc->client_info = uc->h->upcb (uc->h->upcb_cls, pi);
95 }
96
97
98 /**
99  * Function called with information about our
100  * progress in computing the tree encoding.
101  *
102  * @param cls closure
103  * @param offset where are we in the file
104  * @param pt_block plaintext of the currently processed block
105  * @param pt_size size of pt_block
106  * @param depth depth of the block in the tree, 0 for DBLOCK
107  */
108 static void
109 unindex_progress (void *cls, uint64_t offset, const void *pt_block,
110                   size_t pt_size, unsigned int depth)
111 {
112   struct GNUNET_FS_UnindexContext *uc = cls;
113   struct GNUNET_FS_ProgressInfo pi;
114
115   pi.status = GNUNET_FS_STATUS_UNINDEX_PROGRESS;
116   pi.value.unindex.specifics.progress.data = pt_block;
117   pi.value.unindex.specifics.progress.offset = offset;
118   pi.value.unindex.specifics.progress.data_len = pt_size;
119   pi.value.unindex.specifics.progress.depth = depth;
120   GNUNET_FS_unindex_make_status_ (&pi, uc, offset);
121 }
122
123
124 /**
125  * We've encountered an error during
126  * unindexing.  Signal the client.
127  *
128  * @param uc context for the failed unindexing operation
129  */
130 static void
131 signal_unindex_error (struct GNUNET_FS_UnindexContext *uc)
132 {
133   struct GNUNET_FS_ProgressInfo pi;
134
135   pi.status = GNUNET_FS_STATUS_UNINDEX_ERROR;
136   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
137   pi.value.unindex.specifics.error.message = uc->emsg;
138   GNUNET_FS_unindex_make_status_ (&pi, uc, 0);
139 }
140
141
142 /**
143  * Continuation called to notify client about result of the
144  * datastore removal operation.
145  *
146  * @param cls closure
147  * @param success GNUNET_SYSERR on failure
148  * @param min_expiration minimum expiration time required for content to be stored
149  * @param msg NULL on success, otherwise an error message
150  */
151 static void
152 process_cont (void *cls, int success, struct GNUNET_TIME_Absolute min_expiration, const char *msg)
153 {
154   struct GNUNET_FS_UnindexContext *uc = cls;
155
156   if (success == GNUNET_SYSERR)
157   {
158     uc->emsg = GNUNET_strdup (msg);
159     signal_unindex_error (uc);
160     return;
161   }
162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163               "Datastore REMOVE operation succeeded\n");
164   GNUNET_FS_tree_encoder_next (uc->tc);
165 }
166
167
168 /**
169  * Function called asking for the current (encoded)
170  * block to be processed.  After processing the
171  * client should either call "GNUNET_FS_tree_encode_next"
172  * or (on error) "GNUNET_FS_tree_encode_finish".
173  *
174  * @param cls closure
175  * @param chk content hash key for the block (key for lookup in the datastore)
176  * @param offset offset of the block
177  * @param depth depth of the block, 0 for DBLOCK
178  * @param type type of the block (IBLOCK or DBLOCK)
179  * @param block the (encrypted) block
180  * @param block_size size of block (in bytes)
181  */
182 static void
183 unindex_process (void *cls, const struct ContentHashKey *chk, uint64_t offset,
184                  unsigned int depth, enum GNUNET_BLOCK_Type type,
185                  const void *block, uint16_t block_size)
186 {
187   struct GNUNET_FS_UnindexContext *uc = cls;
188   uint32_t size;
189   const void *data;
190   struct OnDemandBlock odb;
191
192   if (type != GNUNET_BLOCK_TYPE_FS_DBLOCK)
193   {
194     size = block_size;
195     data = block;
196   }
197   else                          /* on-demand encoded DBLOCK */
198   {
199     size = sizeof (struct OnDemandBlock);
200     odb.offset = GNUNET_htonll (offset);
201     odb.file_id = uc->file_id;
202     data = &odb;
203   }
204   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205               "Sending REMOVE request to DATASTORE service\n");
206   GNUNET_DATASTORE_remove (uc->dsh, &chk->query, size, data, -2, 1,
207                            GNUNET_CONSTANTS_SERVICE_TIMEOUT, &process_cont, uc);
208   uc->chk = *chk;
209 }
210
211
212 /**
213  * Function called with the response from the
214  * FS service to our unindexing request.
215  *
216  * @param cls closure, unindex context
217  * @param msg NULL on timeout, otherwise the response
218  */
219 static void
220 process_fs_response (void *cls, const struct GNUNET_MessageHeader *msg)
221 {
222   struct GNUNET_FS_UnindexContext *uc = cls;
223   struct GNUNET_FS_ProgressInfo pi;
224
225   if (uc->client != NULL)
226   {
227     GNUNET_CLIENT_disconnect (uc->client);
228     uc->client = NULL;
229   }
230   if (uc->state != UNINDEX_STATE_FS_NOTIFY)
231   {
232     uc->state = UNINDEX_STATE_ERROR;
233     uc->emsg =
234         GNUNET_strdup (_("Unexpected time for a response from `fs' service."));
235     GNUNET_FS_unindex_sync_ (uc);
236     signal_unindex_error (uc);
237     return;
238   }
239   if (NULL == msg)
240   {
241     uc->state = UNINDEX_STATE_ERROR;
242     uc->emsg = GNUNET_strdup (_("Timeout waiting for `fs' service."));
243     GNUNET_FS_unindex_sync_ (uc);
244     signal_unindex_error (uc);
245     return;
246   }
247   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK)
248   {
249     uc->state = UNINDEX_STATE_ERROR;
250     uc->emsg = GNUNET_strdup (_("Invalid response from `fs' service."));
251     GNUNET_FS_unindex_sync_ (uc);
252     signal_unindex_error (uc);
253     return;
254   }
255   uc->state = UNINDEX_STATE_COMPLETE;
256   pi.status = GNUNET_FS_STATUS_UNINDEX_COMPLETED;
257   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
258   GNUNET_FS_unindex_sync_ (uc);
259   GNUNET_FS_unindex_make_status_ (&pi, uc, uc->file_size);
260 }
261
262
263 /**
264  * Function called when we are done with removing UBlocks.
265  * Disconnect from datastore and notify FS service about
266  * the unindex event.
267  *
268  * @param uc our unindexing context
269  */
270 static void
271 unindex_finish (struct GNUNET_FS_UnindexContext *uc)
272 {
273   char *emsg;
274   struct UnindexMessage req;
275
276   /* generate final progress message */
277   unindex_progress (uc, uc->file_size, NULL, 0, 0);
278   GNUNET_FS_tree_encoder_finish (uc->tc, &emsg);
279   uc->tc = NULL;
280   GNUNET_DISK_file_close (uc->fh);
281   uc->fh = NULL;
282   GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
283   uc->dsh = NULL;
284   uc->state = UNINDEX_STATE_FS_NOTIFY;
285   GNUNET_FS_unindex_sync_ (uc);
286   uc->client = GNUNET_CLIENT_connect ("fs", uc->h->cfg);
287   if (uc->client == NULL)
288   {
289     uc->state = UNINDEX_STATE_ERROR;
290     uc->emsg =
291         GNUNET_strdup (_("Failed to connect to FS service for unindexing."));
292     GNUNET_FS_unindex_sync_ (uc);
293     signal_unindex_error (uc);
294     return;
295   }
296   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
297               "Sending UNINDEX message to FS service\n");
298   req.header.size = htons (sizeof (struct UnindexMessage));
299   req.header.type = htons (GNUNET_MESSAGE_TYPE_FS_UNINDEX);
300   req.reserved = 0;
301   req.file_id = uc->file_id;
302   GNUNET_break (GNUNET_OK ==
303                 GNUNET_CLIENT_transmit_and_get_response (uc->client,
304                                                          &req.header,
305                                                          GNUNET_CONSTANTS_SERVICE_TIMEOUT,
306                                                          GNUNET_YES,
307                                                          &process_fs_response,
308                                                          uc));
309 }
310
311
312
313 /**
314  * Function called by the directory scanner as we extract keywords
315  * that we will need to remove UBlocks.
316  *
317  * @param cls the 'struct GNUNET_FS_UnindexContext *'
318  * @param filename which file we are making progress on
319  * @param is_directory GNUNET_YES if this is a directory,
320  *                     GNUNET_NO if this is a file
321  *                     GNUNET_SYSERR if it is neither (or unknown)
322  * @param reason kind of progress we are making
323  */
324 static void
325 unindex_directory_scan_cb (void *cls,
326                            const char *filename,
327                            int is_directory,
328                            enum GNUNET_FS_DirScannerProgressUpdateReason reason)
329 {
330   struct GNUNET_FS_UnindexContext *uc = cls;
331   static struct GNUNET_FS_ShareTreeItem * directory_scan_result;
332
333   switch (reason)
334   {
335   case GNUNET_FS_DIRSCANNER_FINISHED:
336     directory_scan_result = GNUNET_FS_directory_scan_get_result (uc->dscan);
337     uc->dscan = NULL;
338     if (NULL != directory_scan_result->ksk_uri)
339     {
340       uc->ksk_uri = GNUNET_FS_uri_dup (directory_scan_result->ksk_uri);
341       uc->state = UNINDEX_STATE_DS_REMOVE_KBLOCKS;
342       GNUNET_FS_unindex_sync_ (uc);
343       GNUNET_FS_unindex_do_remove_kblocks_ (uc);
344     }
345     else
346     {
347       uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
348       GNUNET_FS_unindex_sync_ (uc);
349       unindex_finish (uc);
350     }
351     GNUNET_FS_share_tree_free (directory_scan_result);
352     break;
353   case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
354     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
355                 _("Internal error scanning `%s'.\n"),
356                 uc->filename);
357     GNUNET_FS_directory_scan_abort (uc->dscan);
358     uc->dscan = NULL;
359     uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
360     GNUNET_FS_unindex_sync_ (uc);
361     unindex_finish (uc);
362     break;
363   default:
364     break;
365   }
366 }
367
368
369 /**
370  * If necessary, connect to the datastore and remove the UBlocks.
371  *
372  * @param uc context for the unindex operation.
373  */
374 void
375 GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc)
376 {
377   char *ex;
378
379   if (GNUNET_OK !=
380       GNUNET_CONFIGURATION_get_value_string (uc->h->cfg, "FS", "EXTRACTORS", &ex))
381     ex = NULL;
382   uc->dscan = GNUNET_FS_directory_scan_start (uc->filename,
383                                               GNUNET_NO, ex,
384                                               &unindex_directory_scan_cb,
385                                               uc);
386   GNUNET_free_non_null (ex);
387 }
388
389
390 /**
391  * Continuation called to notify client about result of the remove
392  * operation for the UBlock.
393  *
394  * @param cls the 'struct GNUNET_FS_UnindexContext *'
395  * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
396  *                GNUNET_NO if content was already there
397  *                GNUNET_YES (or other positive value) on success
398  * @param min_expiration minimum expiration time required for 0-priority content to be stored
399  *                by the datacache at this time, zero for unknown, forever if we have no
400  *                space for 0-priority content
401  * @param msg NULL on success, otherwise an error message
402  */
403 static void
404 continue_after_remove (void *cls,
405                        int32_t success,
406                        struct GNUNET_TIME_Absolute min_expiration,
407                        const char *msg)
408 {
409   struct GNUNET_FS_UnindexContext *uc = cls;
410
411   uc->dqe = NULL;
412   if (success != GNUNET_YES)
413     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
414                 _("Failed to remove UBlock: %s\n"),
415                 msg);
416   uc->ksk_offset++;
417   GNUNET_FS_unindex_do_remove_kblocks_ (uc);
418 }
419
420
421 /**
422  * Function called from datastore with result from us looking for
423  * a UBlock.  There are four cases:
424  * 1) no result, means we move on to the next keyword
425  * 2) UID is the same as the first UID, means we move on to next keyword
426  * 3) UBlock for a different CHK, means we keep looking for more
427  * 4) UBlock is for our CHK, means we remove the block and then move
428  *           on to the next keyword
429  *
430  * @param cls the 'struct GNUNET_FS_UnindexContext *'
431  * @param key key for the content
432  * @param size number of bytes in data
433  * @param data content stored
434  * @param type type of the content
435  * @param priority priority of the content
436  * @param anonymity anonymity-level for the content
437  * @param expiration expiration time for the content
438  * @param uid unique identifier for the datum;
439  *        maybe 0 if no unique identifier is available
440  */
441 static void
442 process_kblock_for_unindex (void *cls,
443                             const struct GNUNET_HashCode *key,
444                             size_t size, const void *data,
445                             enum GNUNET_BLOCK_Type type,
446                             uint32_t priority,
447                             uint32_t anonymity,
448                             struct GNUNET_TIME_Absolute
449                             expiration, uint64_t uid)
450 {
451   struct GNUNET_FS_UnindexContext *uc = cls;
452   const struct UBlock *ub;
453   struct GNUNET_FS_Uri *chk_uri;
454   struct GNUNET_HashCode query;
455
456   uc->dqe = NULL;
457   if (NULL == data)
458   {
459     /* no result */
460     uc->ksk_offset++;
461     GNUNET_FS_unindex_do_remove_kblocks_ (uc);
462     return;
463   }
464   if (0 == uc->first_uid)
465   {
466     /* remember UID of first result to detect cycles */
467     uc->first_uid = uid;
468   }
469   else if (uid == uc->first_uid)
470   {
471     /* no more additional results */
472     uc->ksk_offset++;
473     GNUNET_FS_unindex_do_remove_kblocks_ (uc);
474     return;
475   }
476   GNUNET_assert (GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
477   if (size < sizeof (struct UBlock))
478   {
479     GNUNET_break (0);
480     goto get_next;
481   }
482   ub = data;
483   GNUNET_CRYPTO_hash (&ub->verification_key,
484                       sizeof (ub->verification_key),
485                       &query);
486   if (0 != memcmp (&query, key, sizeof (struct GNUNET_HashCode)))
487   {
488     /* result does not match our keyword, skip */
489     goto get_next;
490   }
491   {
492     char pt[size - sizeof (struct UBlock)];
493     struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
494     const char *keyword;
495
496     GNUNET_CRYPTO_ecdsa_key_get_public (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
497                                                     &anon_pub);
498     keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
499     GNUNET_FS_ublock_decrypt_ (&ub[1], size - sizeof (struct UBlock),
500                                &anon_pub,
501                                keyword,
502                                pt);
503     if (NULL == memchr (&pt[1], 0, sizeof (pt) - 1))
504     {
505       GNUNET_break_op (0); /* malformed UBlock */
506       goto get_next;
507     }
508     chk_uri = GNUNET_FS_uri_parse (&pt[1], NULL);
509     if (NULL == chk_uri)
510     {
511       GNUNET_break_op (0); /* malformed UBlock */
512       goto get_next;
513     }
514   }
515   if (0 != memcmp (&uc->chk,
516                    &chk_uri->data.chk.chk,
517                    sizeof (struct ContentHashKey)))
518   {
519     /* different CHK, ignore */
520     GNUNET_FS_uri_destroy (chk_uri);
521     goto get_next;
522   }
523   GNUNET_FS_uri_destroy (chk_uri);
524   /* matches! */
525   uc->dqe = GNUNET_DATASTORE_remove (uc->dsh,
526                                      key, size, data,
527                                      0 /* priority */, 1 /* queue size */,
528                                      GNUNET_TIME_UNIT_FOREVER_REL,
529                                      &continue_after_remove,
530                                      uc);
531   return;
532  get_next:
533   uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
534                                       uc->roff++,
535                                       &uc->uquery,
536                                       GNUNET_BLOCK_TYPE_FS_UBLOCK,
537                                       0 /* priority */, 1 /* queue size */,
538                                       GNUNET_TIME_UNIT_FOREVER_REL,
539                                       &process_kblock_for_unindex,
540                                       uc);
541 }
542
543
544 /**
545  * If necessary, connect to the datastore and remove the KBlocks.
546  *
547  * @param uc context for the unindex operation.
548  */
549 void
550 GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc)
551 {
552   const char *keyword;
553   const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
554   struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
555   struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
556
557   if (NULL == uc->dsh)
558     uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
559   if (NULL == uc->dsh)
560   {
561     uc->state = UNINDEX_STATE_ERROR;
562     uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
563     GNUNET_FS_unindex_sync_ (uc);
564     signal_unindex_error (uc);
565     return;
566   }
567   if ( (NULL == uc->ksk_uri) ||
568        (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount) )
569   {
570     unindex_finish (uc);
571     return;
572   }
573   anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
574   GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
575   keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
576   GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
577                                        keyword,
578                                        "fs-ublock",
579                                        &dpub);
580   GNUNET_CRYPTO_hash (&dpub,
581                       sizeof (dpub),
582                       &uc->uquery);
583   uc->first_uid = 0;
584   uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
585                                       uc->roff++,
586                                       &uc->uquery,
587                                       GNUNET_BLOCK_TYPE_FS_UBLOCK,
588                                       0 /* priority */, 1 /* queue size */,
589                                       GNUNET_TIME_UNIT_FOREVER_REL,
590                                       &process_kblock_for_unindex,
591                                       uc);
592 }
593
594
595 /**
596  * Function called when the tree encoder has
597  * processed all blocks.  Clean up.
598  *
599  * @param cls our unindexing context
600  * @param tc not used
601  */
602 static void
603 unindex_extract_keywords (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
604 {
605   struct GNUNET_FS_UnindexContext *uc = cls;
606
607   uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
608   GNUNET_FS_unindex_sync_ (uc);
609   GNUNET_FS_unindex_do_extract_keywords_ (uc);
610 }
611
612
613 /**
614  * Connect to the datastore and remove the blocks.
615  *
616  * @param uc context for the unindex operation.
617  */
618 void
619 GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
620 {
621   if (NULL == uc->dsh)
622     uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
623   if (NULL == uc->dsh)
624   {
625     uc->state = UNINDEX_STATE_ERROR;
626     uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
627     GNUNET_FS_unindex_sync_ (uc);
628     signal_unindex_error (uc);
629     return;
630   }
631   uc->fh =
632       GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
633                              GNUNET_DISK_PERM_NONE);
634   if (NULL == uc->fh)
635   {
636     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
637     uc->dsh = NULL;
638     uc->state = UNINDEX_STATE_ERROR;
639     uc->emsg = GNUNET_strdup (_("Failed to open file for unindexing."));
640     GNUNET_FS_unindex_sync_ (uc);
641     signal_unindex_error (uc);
642     return;
643   }
644   uc->tc =
645       GNUNET_FS_tree_encoder_create (uc->h, uc->file_size, uc, &unindex_reader,
646                                      &unindex_process, &unindex_progress,
647                                      &unindex_extract_keywords);
648   GNUNET_FS_tree_encoder_next (uc->tc);
649 }
650
651
652 /**
653  * Function called once the hash of the file
654  * that is being unindexed has been computed.
655  *
656  * @param cls closure, unindex context
657  * @param file_id computed hash, NULL on error
658  */
659 void
660 GNUNET_FS_unindex_process_hash_ (void *cls, const struct GNUNET_HashCode * file_id)
661 {
662   struct GNUNET_FS_UnindexContext *uc = cls;
663
664   uc->fhc = NULL;
665   if (uc->state != UNINDEX_STATE_HASHING)
666   {
667     GNUNET_FS_unindex_stop (uc);
668     return;
669   }
670   if (file_id == NULL)
671   {
672     uc->state = UNINDEX_STATE_ERROR;
673     uc->emsg = GNUNET_strdup (_("Failed to compute hash of file."));
674     GNUNET_FS_unindex_sync_ (uc);
675     signal_unindex_error (uc);
676     return;
677   }
678   uc->file_id = *file_id;
679   uc->state = UNINDEX_STATE_DS_REMOVE;
680   GNUNET_FS_unindex_sync_ (uc);
681   GNUNET_FS_unindex_do_remove_ (uc);
682 }
683
684
685 /**
686  * Create SUSPEND event for the given unindex operation
687  * and then clean up our state (without stop signal).
688  *
689  * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
690  */
691 void
692 GNUNET_FS_unindex_signal_suspend_ (void *cls)
693 {
694   struct GNUNET_FS_UnindexContext *uc = cls;
695   struct GNUNET_FS_ProgressInfo pi;
696
697   /* FIXME: lots of duplication with unindex_stop here! */
698   if (uc->dscan != NULL)
699   {
700     GNUNET_FS_directory_scan_abort (uc->dscan);
701     uc->dscan = NULL;
702   }
703   if (NULL != uc->dqe)
704   {
705     GNUNET_DATASTORE_cancel (uc->dqe);
706     uc->dqe = NULL;
707   }
708   if (uc->fhc != NULL)
709   {
710     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
711     uc->fhc = NULL;
712   }
713   if (NULL != uc->ksk_uri)
714   {
715     GNUNET_FS_uri_destroy (uc->ksk_uri);
716     uc->ksk_uri = NULL;
717   }
718   if (uc->client != NULL)
719   {
720     GNUNET_CLIENT_disconnect (uc->client);
721     uc->client = NULL;
722   }
723   if (NULL != uc->dsh)
724   {
725     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
726     uc->dsh = NULL;
727   }
728   if (NULL != uc->tc)
729   {
730     GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
731     uc->tc = NULL;
732   }
733   if (uc->fh != NULL)
734   {
735     GNUNET_DISK_file_close (uc->fh);
736     uc->fh = NULL;
737   }
738   GNUNET_FS_end_top (uc->h, uc->top);
739   pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
740   GNUNET_FS_unindex_make_status_ (&pi, uc,
741                                   (uc->state ==
742                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
743   GNUNET_break (NULL == uc->client_info);
744   GNUNET_free (uc->filename);
745   GNUNET_free_non_null (uc->serialization);
746   GNUNET_free_non_null (uc->emsg);
747   GNUNET_free (uc);
748 }
749
750
751 /**
752  * Unindex a file.
753  *
754  * @param h handle to the file sharing subsystem
755  * @param filename file to unindex
756  * @param cctx initial value for the client context
757  * @return NULL on error, otherwise handle
758  */
759 struct GNUNET_FS_UnindexContext *
760 GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h,
761                          const char *filename,
762                          void *cctx)
763 {
764   struct GNUNET_FS_UnindexContext *ret;
765   struct GNUNET_FS_ProgressInfo pi;
766   uint64_t size;
767
768   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES))
769     return NULL;
770   ret = GNUNET_new (struct GNUNET_FS_UnindexContext);
771   ret->h = h;
772   ret->filename = GNUNET_strdup (filename);
773   ret->start_time = GNUNET_TIME_absolute_get ();
774   ret->file_size = size;
775   ret->client_info = cctx;
776   GNUNET_FS_unindex_sync_ (ret);
777   pi.status = GNUNET_FS_STATUS_UNINDEX_START;
778   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
779   GNUNET_FS_unindex_make_status_ (&pi, ret, 0);
780   ret->fhc =
781       GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, filename,
782                                HASHING_BLOCKSIZE,
783                                &GNUNET_FS_unindex_process_hash_, ret);
784   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, ret);
785   return ret;
786 }
787
788
789 /**
790  * Clean up after completion of an unindex operation.
791  *
792  * @param uc handle
793  */
794 void
795 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
796 {
797   struct GNUNET_FS_ProgressInfo pi;
798
799   if (uc->dscan != NULL)
800   {
801     GNUNET_FS_directory_scan_abort (uc->dscan);
802     uc->dscan = NULL;
803   }
804   if (NULL != uc->dqe)
805   {
806     GNUNET_DATASTORE_cancel (uc->dqe);
807     uc->dqe = NULL;
808   }
809   if (uc->fhc != NULL)
810   {
811     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
812     uc->fhc = NULL;
813   }
814   if (uc->client != NULL)
815   {
816     GNUNET_CLIENT_disconnect (uc->client);
817     uc->client = NULL;
818   }
819   if (NULL != uc->dsh)
820   {
821     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
822     uc->dsh = NULL;
823   }
824   if (NULL != uc->ksk_uri)
825   {
826     GNUNET_FS_uri_destroy (uc->ksk_uri);
827     uc->ksk_uri = NULL;
828   }
829   if (NULL != uc->tc)
830   {
831     GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
832     uc->tc = NULL;
833   }
834   if (uc->fh != NULL)
835   {
836     GNUNET_DISK_file_close (uc->fh);
837     uc->fh = NULL;
838   }
839   GNUNET_FS_end_top (uc->h, uc->top);
840   if (uc->serialization != NULL)
841   {
842     GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
843                                  uc->serialization);
844     GNUNET_free (uc->serialization);
845     uc->serialization = NULL;
846   }
847   pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
848   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
849   GNUNET_FS_unindex_make_status_ (&pi, uc,
850                                   (uc->state ==
851                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
852   GNUNET_break (NULL == uc->client_info);
853   GNUNET_free_non_null (uc->emsg);
854   GNUNET_free (uc->filename);
855   GNUNET_free (uc);
856 }
857
858 /* end of fs_unindex.c */