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