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