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