uncrustify as demanded.
[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", &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   uc->ksk_offset++;
448   GNUNET_FS_unindex_do_remove_kblocks_(uc);
449 }
450
451
452 /**
453  * Function called from datastore with result from us looking for
454  * a UBlock.  There are four cases:
455  * 1) no result, means we move on to the next keyword
456  * 2) data hash is the same as an already seen data hash, means we move on to
457  *    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 replication replication-level for the content
470  * @param expiration expiration time for the content
471  * @param uid unique identifier for the datum;
472  *        maybe 0 if no unique identifier is available
473  */
474 static void
475 process_kblock_for_unindex(void *cls,
476                            const struct GNUNET_HashCode *key,
477                            size_t size,
478                            const void *data,
479                            enum GNUNET_BLOCK_Type type,
480                            uint32_t priority,
481                            uint32_t anonymity,
482                            uint32_t replication,
483                            struct GNUNET_TIME_Absolute expiration,
484                            uint64_t uid)
485 {
486   struct GNUNET_FS_UnindexContext *uc = cls;
487   const struct UBlock *ub;
488   struct GNUNET_FS_Uri *chk_uri;
489   struct GNUNET_HashCode query;
490
491   uc->dqe = NULL;
492   if (NULL == data)
493     {
494       /* no result */
495       uc->ksk_offset++;
496       GNUNET_FS_unindex_do_remove_kblocks_(uc);
497       return;
498     }
499   GNUNET_assert(GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
500   if (size < sizeof(struct UBlock))
501     {
502       GNUNET_break(0);
503       goto get_next;
504     }
505   ub = data;
506   GNUNET_CRYPTO_hash(&ub->verification_key,
507                      sizeof(ub->verification_key),
508                      &query);
509   if (0 != memcmp(&query,
510                   key,
511                   sizeof(struct GNUNET_HashCode)))
512     {
513       /* result does not match our keyword, skip */
514       goto get_next;
515     }
516   {
517     char pt[size - sizeof(struct UBlock)];
518     struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
519     const char *keyword;
520
521     GNUNET_CRYPTO_ecdsa_key_get_public(GNUNET_CRYPTO_ecdsa_key_get_anonymous(),
522                                        &anon_pub);
523     keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
524     GNUNET_FS_ublock_decrypt_(&ub[1], size - sizeof(struct UBlock),
525                               &anon_pub,
526                               keyword,
527                               pt);
528     if (NULL == memchr(&pt[1], 0, sizeof(pt) - 1))
529       {
530         GNUNET_break_op(0); /* malformed UBlock */
531         goto get_next;
532       }
533     chk_uri = GNUNET_FS_uri_parse(&pt[1], NULL);
534     if (NULL == chk_uri)
535       {
536         GNUNET_break_op(0); /* malformed UBlock */
537         goto get_next;
538       }
539   }
540   if (0 != memcmp(&uc->chk,
541                   &chk_uri->data.chk.chk,
542                   sizeof(struct ContentHashKey)))
543     {
544       /* different CHK, ignore */
545       GNUNET_FS_uri_destroy(chk_uri);
546       goto get_next;
547     }
548   GNUNET_FS_uri_destroy(chk_uri);
549   /* matches! */
550   uc->dqe = GNUNET_DATASTORE_remove(uc->dsh,
551                                     key,
552                                     size,
553                                     data,
554                                     0 /* priority */,
555                                     1 /* queue size */,
556                                     &continue_after_remove,
557                                     uc);
558   return;
559 get_next:
560   uc->dqe = GNUNET_DATASTORE_get_key(uc->dsh,
561                                      uid + 1 /* next_uid */,
562                                      false /* random */,
563                                      &uc->uquery,
564                                      GNUNET_BLOCK_TYPE_FS_UBLOCK,
565                                      0 /* priority */,
566                                      1 /* queue size */,
567                                      &process_kblock_for_unindex,
568                                      uc);
569 }
570
571
572 /**
573  * If necessary, connect to the datastore and remove the KBlocks.
574  *
575  * @param uc context for the unindex operation.
576  */
577 void
578 GNUNET_FS_unindex_do_remove_kblocks_(struct GNUNET_FS_UnindexContext *uc)
579 {
580   const char *keyword;
581   const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
582   struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
583   struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
584
585   if (NULL == uc->dsh)
586     uc->dsh = GNUNET_DATASTORE_connect(uc->h->cfg);
587   if (NULL == uc->dsh)
588     {
589       uc->state = UNINDEX_STATE_ERROR;
590       uc->emsg = GNUNET_strdup(_("Failed to connect to `datastore' service."));
591       GNUNET_FS_unindex_sync_(uc);
592       signal_unindex_error(uc);
593       return;
594     }
595   if ((NULL == uc->ksk_uri) ||
596       (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount))
597     {
598       unindex_finish(uc);
599       return;
600     }
601   anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous();
602   GNUNET_CRYPTO_ecdsa_key_get_public(anon,
603                                      &anon_pub);
604   keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
605   GNUNET_CRYPTO_ecdsa_public_key_derive(&anon_pub,
606                                         keyword,
607                                         "fs-ublock",
608                                         &dpub);
609   GNUNET_CRYPTO_hash(&dpub,
610                      sizeof(dpub),
611                      &uc->uquery);
612   uc->dqe = GNUNET_DATASTORE_get_key(uc->dsh,
613                                      0 /* next_uid */,
614                                      false /* random */,
615                                      &uc->uquery,
616                                      GNUNET_BLOCK_TYPE_FS_UBLOCK,
617                                      0 /* priority */,
618                                      1 /* queue size */,
619                                      &process_kblock_for_unindex,
620                                      uc);
621 }
622
623
624 /**
625  * Function called when the tree encoder has
626  * processed all blocks.  Clean up.
627  *
628  * @param cls our unindexing context
629  */
630 static void
631 unindex_extract_keywords(void *cls)
632 {
633   struct GNUNET_FS_UnindexContext *uc = cls;
634
635   uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
636   GNUNET_FS_unindex_sync_(uc);
637   GNUNET_FS_unindex_do_extract_keywords_(uc);
638 }
639
640
641 /**
642  * Connect to the datastore and remove the blocks.
643  *
644  * @param uc context for the unindex operation.
645  */
646 void
647 GNUNET_FS_unindex_do_remove_(struct GNUNET_FS_UnindexContext *uc)
648 {
649   if (NULL == uc->dsh)
650     uc->dsh = GNUNET_DATASTORE_connect(uc->h->cfg);
651   if (NULL == uc->dsh)
652     {
653       uc->state = UNINDEX_STATE_ERROR;
654       uc->emsg = GNUNET_strdup(_("Failed to connect to `datastore' service."));
655       GNUNET_FS_unindex_sync_(uc);
656       signal_unindex_error(uc);
657       return;
658     }
659   uc->fh =
660     GNUNET_DISK_file_open(uc->filename, GNUNET_DISK_OPEN_READ,
661                           GNUNET_DISK_PERM_NONE);
662   if (NULL == uc->fh)
663     {
664       GNUNET_DATASTORE_disconnect(uc->dsh, GNUNET_NO);
665       uc->dsh = NULL;
666       uc->state = UNINDEX_STATE_ERROR;
667       uc->emsg = GNUNET_strdup(_("Failed to open file for unindexing."));
668       GNUNET_FS_unindex_sync_(uc);
669       signal_unindex_error(uc);
670       return;
671     }
672   uc->tc =
673     GNUNET_FS_tree_encoder_create(uc->h,
674                                   uc->file_size,
675                                   uc,
676                                   &unindex_reader,
677                                   &unindex_process,
678                                   &unindex_progress,
679                                   &unindex_extract_keywords);
680   GNUNET_FS_tree_encoder_next(uc->tc);
681 }
682
683
684 /**
685  * Function called once the hash of the file
686  * that is being unindexed has been computed.
687  *
688  * @param cls closure, unindex context
689  * @param file_id computed hash, NULL on error
690  */
691 void
692 GNUNET_FS_unindex_process_hash_(void *cls,
693                                 const struct GNUNET_HashCode *file_id)
694 {
695   struct GNUNET_FS_UnindexContext *uc = cls;
696
697   uc->fhc = NULL;
698   if (uc->state != UNINDEX_STATE_HASHING)
699     {
700       GNUNET_FS_unindex_stop(uc);
701       return;
702     }
703   if (file_id == NULL)
704     {
705       uc->state = UNINDEX_STATE_ERROR;
706       uc->emsg = GNUNET_strdup(_("Failed to compute hash of file."));
707       GNUNET_FS_unindex_sync_(uc);
708       signal_unindex_error(uc);
709       return;
710     }
711   uc->file_id = *file_id;
712   uc->state = UNINDEX_STATE_DS_REMOVE;
713   GNUNET_FS_unindex_sync_(uc);
714   GNUNET_FS_unindex_do_remove_(uc);
715 }
716
717
718 /**
719  * Create SUSPEND event for the given unindex operation
720  * and then clean up our state (without stop signal).
721  *
722  * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
723  */
724 void
725 GNUNET_FS_unindex_signal_suspend_(void *cls)
726 {
727   struct GNUNET_FS_UnindexContext *uc = cls;
728   struct GNUNET_FS_ProgressInfo pi;
729
730   /* FIXME: lots of duplication with unindex_stop here! */
731   if (uc->dscan != NULL)
732     {
733       GNUNET_FS_directory_scan_abort(uc->dscan);
734       uc->dscan = NULL;
735     }
736   if (NULL != uc->dqe)
737     {
738       GNUNET_DATASTORE_cancel(uc->dqe);
739       uc->dqe = NULL;
740     }
741   if (uc->fhc != NULL)
742     {
743       GNUNET_CRYPTO_hash_file_cancel(uc->fhc);
744       uc->fhc = NULL;
745     }
746   if (NULL != uc->ksk_uri)
747     {
748       GNUNET_FS_uri_destroy(uc->ksk_uri);
749       uc->ksk_uri = NULL;
750     }
751   if (NULL != uc->mq)
752     {
753       GNUNET_MQ_destroy(uc->mq);
754       uc->mq = NULL;
755     }
756   if (NULL != uc->dsh)
757     {
758       GNUNET_DATASTORE_disconnect(uc->dsh, GNUNET_NO);
759       uc->dsh = NULL;
760     }
761   if (NULL != uc->tc)
762     {
763       GNUNET_FS_tree_encoder_finish(uc->tc, NULL);
764       uc->tc = NULL;
765     }
766   if (uc->fh != NULL)
767     {
768       GNUNET_DISK_file_close(uc->fh);
769       uc->fh = NULL;
770     }
771   GNUNET_FS_end_top(uc->h, uc->top);
772   pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
773   GNUNET_FS_unindex_make_status_(&pi, uc,
774                                  (uc->state ==
775                                   UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
776   GNUNET_break(NULL == uc->client_info);
777   GNUNET_free(uc->filename);
778   GNUNET_free_non_null(uc->serialization);
779   GNUNET_free_non_null(uc->emsg);
780   GNUNET_free(uc);
781 }
782
783
784 /**
785  * Unindex a file.
786  *
787  * @param h handle to the file sharing subsystem
788  * @param filename file to unindex
789  * @param cctx initial value for the client context
790  * @return NULL on error, otherwise handle
791  */
792 struct GNUNET_FS_UnindexContext *
793 GNUNET_FS_unindex_start(struct GNUNET_FS_Handle *h,
794                         const char *filename,
795                         void *cctx)
796 {
797   struct GNUNET_FS_UnindexContext *uc;
798   struct GNUNET_FS_ProgressInfo pi;
799   uint64_t size;
800
801   if (GNUNET_OK !=
802       GNUNET_DISK_file_size(filename,
803                             &size,
804                             GNUNET_YES,
805                             GNUNET_YES))
806     return NULL;
807   uc = GNUNET_new(struct GNUNET_FS_UnindexContext);
808   uc->h = h;
809   uc->filename = GNUNET_strdup(filename);
810   uc->start_time = GNUNET_TIME_absolute_get();
811   uc->file_size = size;
812   uc->client_info = cctx;
813   GNUNET_FS_unindex_sync_(uc);
814   pi.status = GNUNET_FS_STATUS_UNINDEX_START;
815   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
816   GNUNET_FS_unindex_make_status_(&pi, uc, 0);
817   uc->fhc =
818     GNUNET_CRYPTO_hash_file(GNUNET_SCHEDULER_PRIORITY_IDLE,
819                             filename,
820                             HASHING_BLOCKSIZE,
821                             &GNUNET_FS_unindex_process_hash_, uc);
822   uc->top = GNUNET_FS_make_top(h,
823                                &GNUNET_FS_unindex_signal_suspend_,
824                                uc);
825   return uc;
826 }
827
828
829 /**
830  * Clean up after completion of an unindex operation.
831  *
832  * @param uc handle
833  */
834 void
835 GNUNET_FS_unindex_stop(struct GNUNET_FS_UnindexContext *uc)
836 {
837   struct GNUNET_FS_ProgressInfo pi;
838
839   if (NULL != uc->dscan)
840     {
841       GNUNET_FS_directory_scan_abort(uc->dscan);
842       uc->dscan = NULL;
843     }
844   if (NULL != uc->dqe)
845     {
846       GNUNET_DATASTORE_cancel(uc->dqe);
847       uc->dqe = NULL;
848     }
849   if (NULL != uc->fhc)
850     {
851       GNUNET_CRYPTO_hash_file_cancel(uc->fhc);
852       uc->fhc = NULL;
853     }
854   if (NULL != uc->mq)
855     {
856       GNUNET_MQ_destroy(uc->mq);
857       uc->mq = NULL;
858     }
859   if (NULL != uc->dsh)
860     {
861       GNUNET_DATASTORE_disconnect(uc->dsh, GNUNET_NO);
862       uc->dsh = NULL;
863     }
864   if (NULL != uc->ksk_uri)
865     {
866       GNUNET_FS_uri_destroy(uc->ksk_uri);
867       uc->ksk_uri = NULL;
868     }
869   if (NULL != uc->tc)
870     {
871       GNUNET_FS_tree_encoder_finish(uc->tc, NULL);
872       uc->tc = NULL;
873     }
874   if (uc->fh != NULL)
875     {
876       GNUNET_DISK_file_close(uc->fh);
877       uc->fh = NULL;
878     }
879   GNUNET_FS_end_top(uc->h, uc->top);
880   if (uc->serialization != NULL)
881     {
882       GNUNET_FS_remove_sync_file_(uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
883                                   uc->serialization);
884       GNUNET_free(uc->serialization);
885       uc->serialization = NULL;
886     }
887   pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
888   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
889   GNUNET_FS_unindex_make_status_(&pi, uc,
890                                  (uc->state ==
891                                   UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
892   GNUNET_break(NULL == uc->client_info);
893   GNUNET_free_non_null(uc->emsg);
894   GNUNET_free(uc->filename);
895   GNUNET_free(uc);
896 }
897
898 /* end of fs_unindex.c */