global reindent, now with uncrustify hook enabled
[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 it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file fs/fs_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   uc->state = UNINDEX_STATE_FS_NOTIFY;
316   GNUNET_FS_unindex_sync_ (uc);
317   uc->mq = GNUNET_CLIENT_connect (uc->h->cfg,
318                                   "fs",
319                                   handlers,
320                                   &unindex_mq_error_handler,
321                                   uc);
322   if (NULL == uc->mq)
323   {
324     uc->state = UNINDEX_STATE_ERROR;
325     uc->emsg =
326       GNUNET_strdup (_ ("Failed to connect to FS service for unindexing."));
327     GNUNET_FS_unindex_sync_ (uc);
328     signal_unindex_error (uc);
329     return;
330   }
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332               "Sending UNINDEX message to FS service\n");
333   env = GNUNET_MQ_msg (req,
334                        GNUNET_MESSAGE_TYPE_FS_UNINDEX);
335   req->reserved = 0;
336   req->file_id = uc->file_id;
337   GNUNET_MQ_send (uc->mq,
338                   env);
339 }
340
341
342 /**
343  * Function called by the directory scanner as we extract keywords
344  * that we will need to remove UBlocks.
345  *
346  * @param cls the 'struct GNUNET_FS_UnindexContext *'
347  * @param filename which file we are making progress on
348  * @param is_directory #GNUNET_YES if this is a directory,
349  *                     #GNUNET_NO if this is a file
350  *                     #GNUNET_SYSERR if it is neither (or unknown)
351  * @param reason kind of progress we are making
352  */
353 static void
354 unindex_directory_scan_cb (void *cls,
355                            const char *filename,
356                            int is_directory,
357                            enum GNUNET_FS_DirScannerProgressUpdateReason reason)
358 {
359   struct GNUNET_FS_UnindexContext *uc = cls;
360   static struct GNUNET_FS_ShareTreeItem *directory_scan_result;
361
362   switch (reason)
363   {
364   case GNUNET_FS_DIRSCANNER_FINISHED:
365     directory_scan_result = GNUNET_FS_directory_scan_get_result (uc->dscan);
366     uc->dscan = NULL;
367     if (NULL != directory_scan_result->ksk_uri)
368     {
369       uc->ksk_uri = GNUNET_FS_uri_dup (directory_scan_result->ksk_uri);
370       uc->state = UNINDEX_STATE_DS_REMOVE_KBLOCKS;
371       GNUNET_FS_unindex_sync_ (uc);
372       GNUNET_FS_unindex_do_remove_kblocks_ (uc);
373     }
374     else
375     {
376       uc->emsg = GNUNET_strdup (_ ("Failed to get KSKs from directory scan."));
377       GNUNET_FS_unindex_sync_ (uc);
378       unindex_finish (uc);
379     }
380     GNUNET_FS_share_tree_free (directory_scan_result);
381     break;
382
383   case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
384     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
385                 _ ("Internal error scanning `%s'.\n"),
386                 uc->filename);
387     GNUNET_FS_directory_scan_abort (uc->dscan);
388     uc->dscan = NULL;
389     uc->emsg = GNUNET_strdup (_ ("Failed to get KSKs from directory scan."));
390     GNUNET_FS_unindex_sync_ (uc);
391     unindex_finish (uc);
392     break;
393
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",
412                                              &ex))
413     ex = NULL;
414   uc->dscan = GNUNET_FS_directory_scan_start (uc->filename,
415                                               GNUNET_NO, ex,
416                                               &unindex_directory_scan_cb,
417                                               uc);
418   GNUNET_free_non_null (ex);
419 }
420
421
422 /**
423  * Continuation called to notify client about result of the remove
424  * operation for the UBlock.
425  *
426  * @param cls the 'struct GNUNET_FS_UnindexContext *'
427  * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
428  *                GNUNET_NO if content was already there
429  *                GNUNET_YES (or other positive value) on success
430  * @param min_expiration minimum expiration time required for 0-priority content to be stored
431  *                by the datacache at this time, zero for unknown, forever if we have no
432  *                space for 0-priority content
433  * @param msg NULL on success, otherwise an error message
434  */
435 static void
436 continue_after_remove (void *cls,
437                        int32_t success,
438                        struct GNUNET_TIME_Absolute min_expiration,
439                        const char *msg)
440 {
441   struct GNUNET_FS_UnindexContext *uc = cls;
442
443   uc->dqe = NULL;
444   if (success != GNUNET_YES)
445     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
446                 _ ("Failed to remove UBlock: %s\n"),
447                 msg);
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) data hash is the same as an already seen data hash, means we move on to
458  *    next keyword
459  * 3) UBlock for a different CHK, means we keep looking for more
460  * 4) UBlock is for our CHK, means we remove the block and then move
461  *           on to the next keyword
462  *
463  * @param cls the 'struct GNUNET_FS_UnindexContext *'
464  * @param key key for the content
465  * @param size number of bytes in data
466  * @param data content stored
467  * @param type type of the content
468  * @param priority priority of the content
469  * @param anonymity anonymity-level for the content
470  * @param replication replication-level for the content
471  * @param expiration expiration time for the content
472  * @param uid unique identifier for the datum;
473  *        maybe 0 if no unique identifier is available
474  */
475 static void
476 process_kblock_for_unindex (void *cls,
477                             const struct GNUNET_HashCode *key,
478                             size_t size,
479                             const void *data,
480                             enum GNUNET_BLOCK_Type type,
481                             uint32_t priority,
482                             uint32_t anonymity,
483                             uint32_t replication,
484                             struct GNUNET_TIME_Absolute expiration,
485                             uint64_t uid)
486 {
487   struct GNUNET_FS_UnindexContext *uc = cls;
488   const struct UBlock *ub;
489   struct GNUNET_FS_Uri *chk_uri;
490   struct GNUNET_HashCode query;
491
492   uc->dqe = NULL;
493   if (NULL == data)
494   {
495     /* no result */
496     uc->ksk_offset++;
497     GNUNET_FS_unindex_do_remove_kblocks_ (uc);
498     return;
499   }
500   GNUNET_assert (GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
501   if (size < sizeof(struct UBlock))
502   {
503     GNUNET_break (0);
504     goto get_next;
505   }
506   ub = data;
507   GNUNET_CRYPTO_hash (&ub->verification_key,
508                       sizeof(ub->verification_key),
509                       &query);
510   if (0 != memcmp (&query,
511                    key,
512                    sizeof(struct GNUNET_HashCode)))
513   {
514     /* result does not match our keyword, skip */
515     goto get_next;
516   }
517   {
518     char pt[size - sizeof(struct UBlock)];
519     struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
520     const char *keyword;
521
522     GNUNET_CRYPTO_ecdsa_key_get_public (
523       GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
524       &anon_pub);
525     keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
526     GNUNET_FS_ublock_decrypt_ (&ub[1], size - sizeof(struct UBlock),
527                                &anon_pub,
528                                keyword,
529                                pt);
530     if (NULL == memchr (&pt[1], 0, sizeof(pt) - 1))
531     {
532       GNUNET_break_op (0);  /* malformed UBlock */
533       goto get_next;
534     }
535     chk_uri = GNUNET_FS_uri_parse (&pt[1], NULL);
536     if (NULL == chk_uri)
537     {
538       GNUNET_break_op (0);  /* malformed UBlock */
539       goto get_next;
540     }
541   }
542   if (0 != memcmp (&uc->chk,
543                    &chk_uri->data.chk.chk,
544                    sizeof(struct ContentHashKey)))
545   {
546     /* different CHK, ignore */
547     GNUNET_FS_uri_destroy (chk_uri);
548     goto get_next;
549   }
550   GNUNET_FS_uri_destroy (chk_uri);
551   /* matches! */
552   uc->dqe = GNUNET_DATASTORE_remove (uc->dsh,
553                                      key,
554                                      size,
555                                      data,
556                                      0 /* priority */,
557                                      1 /* queue size */,
558                                      &continue_after_remove,
559                                      uc);
560   return;
561 get_next:
562   uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
563                                       uid + 1 /* next_uid */,
564                                       false /* random */,
565                                       &uc->uquery,
566                                       GNUNET_BLOCK_TYPE_FS_UBLOCK,
567                                       0 /* priority */,
568                                       1 /* queue size */,
569                                       &process_kblock_for_unindex,
570                                       uc);
571 }
572
573
574 /**
575  * If necessary, connect to the datastore and remove the KBlocks.
576  *
577  * @param uc context for the unindex operation.
578  */
579 void
580 GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc)
581 {
582   const char *keyword;
583   const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
584   struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
585   struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
586
587   if (NULL == uc->dsh)
588     uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
589   if (NULL == uc->dsh)
590   {
591     uc->state = UNINDEX_STATE_ERROR;
592     uc->emsg = GNUNET_strdup (_ ("Failed to connect to `datastore' service."));
593     GNUNET_FS_unindex_sync_ (uc);
594     signal_unindex_error (uc);
595     return;
596   }
597   if ((NULL == uc->ksk_uri) ||
598       (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount))
599   {
600     unindex_finish (uc);
601     return;
602   }
603   anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
604   GNUNET_CRYPTO_ecdsa_key_get_public (anon,
605                                       &anon_pub);
606   keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
607   GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
608                                          keyword,
609                                          "fs-ublock",
610                                          &dpub);
611   GNUNET_CRYPTO_hash (&dpub,
612                       sizeof(dpub),
613                       &uc->uquery);
614   uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
615                                       0 /* next_uid */,
616                                       false /* random */,
617                                       &uc->uquery,
618                                       GNUNET_BLOCK_TYPE_FS_UBLOCK,
619                                       0 /* priority */,
620                                       1 /* queue size */,
621                                       &process_kblock_for_unindex,
622                                       uc);
623 }
624
625
626 /**
627  * Function called when the tree encoder has
628  * processed all blocks.  Clean up.
629  *
630  * @param cls our unindexing context
631  */
632 static void
633 unindex_extract_keywords (void *cls)
634 {
635   struct GNUNET_FS_UnindexContext *uc = cls;
636
637   uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
638   GNUNET_FS_unindex_sync_ (uc);
639   GNUNET_FS_unindex_do_extract_keywords_ (uc);
640 }
641
642
643 /**
644  * Connect to the datastore and remove the blocks.
645  *
646  * @param uc context for the unindex operation.
647  */
648 void
649 GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
650 {
651   if (NULL == uc->dsh)
652     uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
653   if (NULL == uc->dsh)
654   {
655     uc->state = UNINDEX_STATE_ERROR;
656     uc->emsg = GNUNET_strdup (_ ("Failed to connect to `datastore' service."));
657     GNUNET_FS_unindex_sync_ (uc);
658     signal_unindex_error (uc);
659     return;
660   }
661   uc->fh =
662     GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
663                            GNUNET_DISK_PERM_NONE);
664   if (NULL == uc->fh)
665   {
666     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
667     uc->dsh = NULL;
668     uc->state = UNINDEX_STATE_ERROR;
669     uc->emsg = GNUNET_strdup (_ ("Failed to open file for unindexing."));
670     GNUNET_FS_unindex_sync_ (uc);
671     signal_unindex_error (uc);
672     return;
673   }
674   uc->tc =
675     GNUNET_FS_tree_encoder_create (uc->h,
676                                    uc->file_size,
677                                    uc,
678                                    &unindex_reader,
679                                    &unindex_process,
680                                    &unindex_progress,
681                                    &unindex_extract_keywords);
682   GNUNET_FS_tree_encoder_next (uc->tc);
683 }
684
685
686 /**
687  * Function called once the hash of the file
688  * that is being unindexed has been computed.
689  *
690  * @param cls closure, unindex context
691  * @param file_id computed hash, NULL on error
692  */
693 void
694 GNUNET_FS_unindex_process_hash_ (void *cls,
695                                  const struct GNUNET_HashCode *file_id)
696 {
697   struct GNUNET_FS_UnindexContext *uc = cls;
698
699   uc->fhc = NULL;
700   if (uc->state != UNINDEX_STATE_HASHING)
701   {
702     GNUNET_FS_unindex_stop (uc);
703     return;
704   }
705   if (file_id == NULL)
706   {
707     uc->state = UNINDEX_STATE_ERROR;
708     uc->emsg = GNUNET_strdup (_ ("Failed to compute hash of file."));
709     GNUNET_FS_unindex_sync_ (uc);
710     signal_unindex_error (uc);
711     return;
712   }
713   uc->file_id = *file_id;
714   uc->state = UNINDEX_STATE_DS_REMOVE;
715   GNUNET_FS_unindex_sync_ (uc);
716   GNUNET_FS_unindex_do_remove_ (uc);
717 }
718
719
720 /**
721  * Create SUSPEND event for the given unindex operation
722  * and then clean up our state (without stop signal).
723  *
724  * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
725  */
726 void
727 GNUNET_FS_unindex_signal_suspend_ (void *cls)
728 {
729   struct GNUNET_FS_UnindexContext *uc = cls;
730   struct GNUNET_FS_ProgressInfo pi;
731
732   /* FIXME: lots of duplication with unindex_stop here! */
733   if (uc->dscan != NULL)
734   {
735     GNUNET_FS_directory_scan_abort (uc->dscan);
736     uc->dscan = NULL;
737   }
738   if (NULL != uc->dqe)
739   {
740     GNUNET_DATASTORE_cancel (uc->dqe);
741     uc->dqe = NULL;
742   }
743   if (uc->fhc != NULL)
744   {
745     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
746     uc->fhc = NULL;
747   }
748   if (NULL != uc->ksk_uri)
749   {
750     GNUNET_FS_uri_destroy (uc->ksk_uri);
751     uc->ksk_uri = NULL;
752   }
753   if (NULL != uc->mq)
754   {
755     GNUNET_MQ_destroy (uc->mq);
756     uc->mq = NULL;
757   }
758   if (NULL != uc->dsh)
759   {
760     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
761     uc->dsh = NULL;
762   }
763   if (NULL != uc->tc)
764   {
765     GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
766     uc->tc = NULL;
767   }
768   if (uc->fh != NULL)
769   {
770     GNUNET_DISK_file_close (uc->fh);
771     uc->fh = NULL;
772   }
773   GNUNET_FS_end_top (uc->h, uc->top);
774   pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
775   GNUNET_FS_unindex_make_status_ (&pi, uc,
776                                   (uc->state ==
777                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
778   GNUNET_break (NULL == uc->client_info);
779   GNUNET_free (uc->filename);
780   GNUNET_free_non_null (uc->serialization);
781   GNUNET_free_non_null (uc->emsg);
782   GNUNET_free (uc);
783 }
784
785
786 /**
787  * Unindex a file.
788  *
789  * @param h handle to the file sharing subsystem
790  * @param filename file to unindex
791  * @param cctx initial value for the client context
792  * @return NULL on error, otherwise handle
793  */
794 struct GNUNET_FS_UnindexContext *
795 GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h,
796                          const char *filename,
797                          void *cctx)
798 {
799   struct GNUNET_FS_UnindexContext *uc;
800   struct GNUNET_FS_ProgressInfo pi;
801   uint64_t size;
802
803   if (GNUNET_OK !=
804       GNUNET_DISK_file_size (filename,
805                              &size,
806                              GNUNET_YES,
807                              GNUNET_YES))
808     return NULL;
809   uc = GNUNET_new (struct GNUNET_FS_UnindexContext);
810   uc->h = h;
811   uc->filename = GNUNET_strdup (filename);
812   uc->start_time = GNUNET_TIME_absolute_get ();
813   uc->file_size = size;
814   uc->client_info = cctx;
815   GNUNET_FS_unindex_sync_ (uc);
816   pi.status = GNUNET_FS_STATUS_UNINDEX_START;
817   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
818   GNUNET_FS_unindex_make_status_ (&pi, uc, 0);
819   uc->fhc =
820     GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
821                              filename,
822                              HASHING_BLOCKSIZE,
823                              &GNUNET_FS_unindex_process_hash_, uc);
824   uc->top = GNUNET_FS_make_top (h,
825                                 &GNUNET_FS_unindex_signal_suspend_,
826                                 uc);
827   return uc;
828 }
829
830
831 /**
832  * Clean up after completion of an unindex operation.
833  *
834  * @param uc handle
835  */
836 void
837 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
838 {
839   struct GNUNET_FS_ProgressInfo pi;
840
841   if (NULL != uc->dscan)
842   {
843     GNUNET_FS_directory_scan_abort (uc->dscan);
844     uc->dscan = NULL;
845   }
846   if (NULL != uc->dqe)
847   {
848     GNUNET_DATASTORE_cancel (uc->dqe);
849     uc->dqe = NULL;
850   }
851   if (NULL != uc->fhc)
852   {
853     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
854     uc->fhc = NULL;
855   }
856   if (NULL != uc->mq)
857   {
858     GNUNET_MQ_destroy (uc->mq);
859     uc->mq = NULL;
860   }
861   if (NULL != uc->dsh)
862   {
863     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
864     uc->dsh = NULL;
865   }
866   if (NULL != uc->ksk_uri)
867   {
868     GNUNET_FS_uri_destroy (uc->ksk_uri);
869     uc->ksk_uri = NULL;
870   }
871   if (NULL != uc->tc)
872   {
873     GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
874     uc->tc = NULL;
875   }
876   if (uc->fh != NULL)
877   {
878     GNUNET_DISK_file_close (uc->fh);
879     uc->fh = NULL;
880   }
881   GNUNET_FS_end_top (uc->h, uc->top);
882   if (uc->serialization != NULL)
883   {
884     GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
885                                  uc->serialization);
886     GNUNET_free (uc->serialization);
887     uc->serialization = NULL;
888   }
889   pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
890   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
891   GNUNET_FS_unindex_make_status_ (&pi, uc,
892                                   (uc->state ==
893                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
894   GNUNET_break (NULL == uc->client_info);
895   GNUNET_free_non_null (uc->emsg);
896   GNUNET_free (uc->filename);
897   GNUNET_free (uc);
898 }
899
900 /* end of fs_unindex.c */