Merge branch 'master' of ssh://gnunet.org/gnunet
[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   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   case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
383     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
384                 _("Internal error scanning `%s'.\n"),
385                 uc->filename);
386     GNUNET_FS_directory_scan_abort (uc->dscan);
387     uc->dscan = NULL;
388     uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
389     GNUNET_FS_unindex_sync_ (uc);
390     unindex_finish (uc);
391     break;
392   default:
393     break;
394   }
395 }
396
397
398 /**
399  * If necessary, connect to the datastore and remove the UBlocks.
400  *
401  * @param uc context for the unindex operation.
402  */
403 void
404 GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc)
405 {
406   char *ex;
407
408   if (GNUNET_OK !=
409       GNUNET_CONFIGURATION_get_value_string (uc->h->cfg, "FS", "EXTRACTORS", &ex))
410     ex = NULL;
411   uc->dscan = GNUNET_FS_directory_scan_start (uc->filename,
412                                               GNUNET_NO, ex,
413                                               &unindex_directory_scan_cb,
414                                               uc);
415   GNUNET_free_non_null (ex);
416 }
417
418
419 /**
420  * Continuation called to notify client about result of the remove
421  * operation for the UBlock.
422  *
423  * @param cls the 'struct GNUNET_FS_UnindexContext *'
424  * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
425  *                GNUNET_NO if content was already there
426  *                GNUNET_YES (or other positive value) on success
427  * @param min_expiration minimum expiration time required for 0-priority content to be stored
428  *                by the datacache at this time, zero for unknown, forever if we have no
429  *                space for 0-priority content
430  * @param msg NULL on success, otherwise an error message
431  */
432 static void
433 continue_after_remove (void *cls,
434                        int32_t success,
435                        struct GNUNET_TIME_Absolute min_expiration,
436                        const char *msg)
437 {
438   struct GNUNET_FS_UnindexContext *uc = cls;
439
440   uc->dqe = NULL;
441   if (success != GNUNET_YES)
442     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
443                 _("Failed to remove UBlock: %s\n"),
444                 msg);
445   uc->ksk_offset++;
446   GNUNET_FS_unindex_do_remove_kblocks_ (uc);
447 }
448
449
450 /**
451  * Function called from datastore with result from us looking for
452  * a UBlock.  There are four cases:
453  * 1) no result, means we move on to the next keyword
454  * 2) data hash is the same as an already seen data hash, means we move on to
455  *    next keyword
456  * 3) UBlock for a different CHK, means we keep looking for more
457  * 4) UBlock is for our CHK, means we remove the block and then move
458  *           on to the next keyword
459  *
460  * @param cls the 'struct GNUNET_FS_UnindexContext *'
461  * @param key key for the content
462  * @param size number of bytes in data
463  * @param data content stored
464  * @param type type of the content
465  * @param priority priority of the content
466  * @param anonymity anonymity-level for the content
467  * @param expiration expiration time for the content
468  * @param uid unique identifier for the datum;
469  *        maybe 0 if no unique identifier is available
470  */
471 static void
472 process_kblock_for_unindex (void *cls,
473                             const struct GNUNET_HashCode *key,
474                             size_t size,
475                             const void *data,
476                             enum GNUNET_BLOCK_Type type,
477                             uint32_t priority,
478                             uint32_t anonymity,
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 */