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