2 This file is part of GNUnet.
3 Copyright (C) 2003--2013, 2016 GNUnet e.V.
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.
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.
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.
22 * @file fs/fs_unindex.c
23 * @author Krista Grothoff
24 * @author Christian Grothoff
25 * @brief Unindex file.
28 #include "gnunet_constants.h"
29 #include "gnunet_fs_service.h"
30 #include "gnunet_protocols.h"
34 #include "fs_publish_ublock.h"
38 * Function called by the tree encoder to obtain
39 * a block of plaintext data (for the lowest level
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
51 unindex_reader (void *cls,
57 struct GNUNET_FS_UnindexContext *uc = cls;
60 pt_size = GNUNET_MIN (max, uc->file_size - offset);
61 if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET))
63 *emsg = GNUNET_strdup (_("Failed to find given position in file"));
66 if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size))
68 *emsg = GNUNET_strdup (_("Failed to read file"));
76 * Fill in all of the generic fields for
77 * an unindex event and call the callback.
79 * @param pi structure to fill in
80 * @param uc overall unindex context
81 * @param offset where we are in the file (for progress)
84 GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
85 struct GNUNET_FS_UnindexContext *uc,
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;
98 uc->client_info = uc->h->upcb (uc->h->upcb_cls, pi);
103 * Function called with information about our
104 * progress in computing the tree encoding.
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
113 unindex_progress (void *cls,
115 const void *pt_block,
119 struct GNUNET_FS_UnindexContext *uc = cls;
120 struct GNUNET_FS_ProgressInfo pi;
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);
132 * We've encountered an error during
133 * unindexing. Signal the client.
135 * @param uc context for the failed unindexing operation
138 signal_unindex_error (struct GNUNET_FS_UnindexContext *uc)
140 struct GNUNET_FS_ProgressInfo pi;
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);
150 * Continuation called to notify client about result of the
151 * datastore removal operation.
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
159 process_cont (void *cls,
161 struct GNUNET_TIME_Absolute min_expiration,
164 struct GNUNET_FS_UnindexContext *uc = cls;
166 if (success == GNUNET_SYSERR)
168 uc->emsg = GNUNET_strdup (msg);
169 signal_unindex_error (uc);
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173 "Datastore REMOVE operation succeeded\n");
174 GNUNET_FS_tree_encoder_next (uc->tc);
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".
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)
193 unindex_process (void *cls,
194 const struct ContentHashKey *chk,
197 enum GNUNET_BLOCK_Type type,
201 struct GNUNET_FS_UnindexContext *uc = cls;
204 struct OnDemandBlock odb;
206 if (type != GNUNET_BLOCK_TYPE_FS_DBLOCK)
211 else /* on-demand encoded DBLOCK */
213 size = sizeof (struct OnDemandBlock);
214 odb.offset = GNUNET_htonll (offset);
215 odb.file_id = uc->file_id;
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,
227 * Function called with the response from the FS service to our
228 * unindexing request.
230 * @param cls closure, unindex context
231 * @param msg the response
234 handle_unindex_response (void *cls,
235 const struct GNUNET_MessageHeader *msg)
237 struct GNUNET_FS_UnindexContext *uc = cls;
238 struct GNUNET_FS_ProgressInfo pi;
242 GNUNET_MQ_destroy (uc->mq);
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,
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.
260 * @param cls closure with the `struct GNUNET_FS_UnindexContext *`
261 * @param error error code
264 unindex_mq_error_handler (void *cls,
265 enum GNUNET_MQ_Error error)
267 struct GNUNET_FS_UnindexContext *uc = cls;
271 GNUNET_MQ_destroy (uc->mq);
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);
282 * Function called when we are done with removing UBlocks.
283 * Disconnect from datastore and notify FS service about
286 * @param uc our unindexing context
289 unindex_finish (struct GNUNET_FS_UnindexContext *uc)
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,
296 GNUNET_MQ_handler_end ()
299 struct GNUNET_MQ_Envelope *env;
300 struct UnindexMessage *req;
302 /* generate final progress message */
303 unindex_progress (uc,
308 GNUNET_FS_tree_encoder_finish (uc->tc,
311 GNUNET_DISK_file_close (uc->fh);
313 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
315 uc->state = UNINDEX_STATE_FS_NOTIFY;
316 GNUNET_FS_unindex_sync_ (uc);
317 uc->mq = GNUNET_CLIENT_connect (uc->h->cfg,
320 &unindex_mq_error_handler,
324 uc->state = UNINDEX_STATE_ERROR;
326 GNUNET_strdup (_("Failed to connect to FS service for unindexing."));
327 GNUNET_FS_unindex_sync_ (uc);
328 signal_unindex_error (uc);
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);
336 req->file_id = uc->file_id;
337 GNUNET_MQ_send (uc->mq,
343 * Function called by the directory scanner as we extract keywords
344 * that we will need to remove UBlocks.
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
354 unindex_directory_scan_cb (void *cls,
355 const char *filename,
357 enum GNUNET_FS_DirScannerProgressUpdateReason reason)
359 struct GNUNET_FS_UnindexContext *uc = cls;
360 static struct GNUNET_FS_ShareTreeItem * directory_scan_result;
364 case GNUNET_FS_DIRSCANNER_FINISHED:
365 directory_scan_result = GNUNET_FS_directory_scan_get_result (uc->dscan);
367 if (NULL != directory_scan_result->ksk_uri)
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);
376 uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
377 GNUNET_FS_unindex_sync_ (uc);
380 GNUNET_FS_share_tree_free (directory_scan_result);
382 case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
383 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
384 _("Internal error scanning `%s'.\n"),
386 GNUNET_FS_directory_scan_abort (uc->dscan);
388 uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
389 GNUNET_FS_unindex_sync_ (uc);
399 * If necessary, connect to the datastore and remove the UBlocks.
401 * @param uc context for the unindex operation.
404 GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc)
409 GNUNET_CONFIGURATION_get_value_string (uc->h->cfg, "FS", "EXTRACTORS", &ex))
411 uc->dscan = GNUNET_FS_directory_scan_start (uc->filename,
413 &unindex_directory_scan_cb,
415 GNUNET_free_non_null (ex);
420 * Continuation called to notify client about result of the remove
421 * operation for the UBlock.
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
433 continue_after_remove (void *cls,
435 struct GNUNET_TIME_Absolute min_expiration,
438 struct GNUNET_FS_UnindexContext *uc = cls;
441 if (success != GNUNET_YES)
442 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
443 _("Failed to remove UBlock: %s\n"),
446 GNUNET_FS_unindex_do_remove_kblocks_ (uc);
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
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
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
472 process_kblock_for_unindex (void *cls,
473 const struct GNUNET_HashCode *key,
476 enum GNUNET_BLOCK_Type type,
479 struct GNUNET_TIME_Absolute expiration,
482 struct GNUNET_FS_UnindexContext *uc = cls;
483 const struct UBlock *ub;
484 struct GNUNET_FS_Uri *chk_uri;
485 struct GNUNET_HashCode query;
492 GNUNET_FS_unindex_do_remove_kblocks_ (uc);
495 GNUNET_assert (GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
496 if (size < sizeof (struct UBlock))
502 GNUNET_CRYPTO_hash (&ub->verification_key,
503 sizeof (ub->verification_key),
505 if (0 != memcmp (&query,
507 sizeof (struct GNUNET_HashCode)))
509 /* result does not match our keyword, skip */
513 char pt[size - sizeof (struct UBlock)];
514 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
517 GNUNET_CRYPTO_ecdsa_key_get_public (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
519 keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
520 GNUNET_FS_ublock_decrypt_ (&ub[1], size - sizeof (struct UBlock),
524 if (NULL == memchr (&pt[1], 0, sizeof (pt) - 1))
526 GNUNET_break_op (0); /* malformed UBlock */
529 chk_uri = GNUNET_FS_uri_parse (&pt[1], NULL);
532 GNUNET_break_op (0); /* malformed UBlock */
536 if (0 != memcmp (&uc->chk,
537 &chk_uri->data.chk.chk,
538 sizeof (struct ContentHashKey)))
540 /* different CHK, ignore */
541 GNUNET_FS_uri_destroy (chk_uri);
544 GNUNET_FS_uri_destroy (chk_uri);
546 uc->dqe = GNUNET_DATASTORE_remove (uc->dsh,
552 &continue_after_remove,
556 uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
557 uid + 1 /* next_uid */,
560 GNUNET_BLOCK_TYPE_FS_UBLOCK,
563 &process_kblock_for_unindex,
569 * If necessary, connect to the datastore and remove the KBlocks.
571 * @param uc context for the unindex operation.
574 GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc)
577 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
578 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
579 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
582 uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
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);
591 if ( (NULL == uc->ksk_uri) ||
592 (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount) )
597 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
598 GNUNET_CRYPTO_ecdsa_key_get_public (anon,
600 keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
601 GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
605 GNUNET_CRYPTO_hash (&dpub,
608 uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
612 GNUNET_BLOCK_TYPE_FS_UBLOCK,
615 &process_kblock_for_unindex,
621 * Function called when the tree encoder has
622 * processed all blocks. Clean up.
624 * @param cls our unindexing context
627 unindex_extract_keywords (void *cls)
629 struct GNUNET_FS_UnindexContext *uc = cls;
631 uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
632 GNUNET_FS_unindex_sync_ (uc);
633 GNUNET_FS_unindex_do_extract_keywords_ (uc);
638 * Connect to the datastore and remove the blocks.
640 * @param uc context for the unindex operation.
643 GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
646 uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
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);
656 GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
657 GNUNET_DISK_PERM_NONE);
660 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
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);
669 GNUNET_FS_tree_encoder_create (uc->h,
675 &unindex_extract_keywords);
676 GNUNET_FS_tree_encoder_next (uc->tc);
681 * Function called once the hash of the file
682 * that is being unindexed has been computed.
684 * @param cls closure, unindex context
685 * @param file_id computed hash, NULL on error
688 GNUNET_FS_unindex_process_hash_ (void *cls,
689 const struct GNUNET_HashCode *file_id)
691 struct GNUNET_FS_UnindexContext *uc = cls;
694 if (uc->state != UNINDEX_STATE_HASHING)
696 GNUNET_FS_unindex_stop (uc);
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);
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);
715 * Create SUSPEND event for the given unindex operation
716 * and then clean up our state (without stop signal).
718 * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
721 GNUNET_FS_unindex_signal_suspend_ (void *cls)
723 struct GNUNET_FS_UnindexContext *uc = cls;
724 struct GNUNET_FS_ProgressInfo pi;
726 /* FIXME: lots of duplication with unindex_stop here! */
727 if (uc->dscan != NULL)
729 GNUNET_FS_directory_scan_abort (uc->dscan);
734 GNUNET_DATASTORE_cancel (uc->dqe);
739 GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
742 if (NULL != uc->ksk_uri)
744 GNUNET_FS_uri_destroy (uc->ksk_uri);
749 GNUNET_MQ_destroy (uc->mq);
754 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
759 GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
764 GNUNET_DISK_file_close (uc->fh);
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,
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);
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
788 struct GNUNET_FS_UnindexContext *
789 GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h,
790 const char *filename,
793 struct GNUNET_FS_UnindexContext *uc;
794 struct GNUNET_FS_ProgressInfo pi;
798 GNUNET_DISK_file_size (filename,
803 uc = GNUNET_new (struct GNUNET_FS_UnindexContext);
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);
814 GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
817 &GNUNET_FS_unindex_process_hash_, uc);
818 uc->top = GNUNET_FS_make_top (h,
819 &GNUNET_FS_unindex_signal_suspend_,
826 * Clean up after completion of an unindex operation.
831 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
833 struct GNUNET_FS_ProgressInfo pi;
835 if (NULL != uc->dscan)
837 GNUNET_FS_directory_scan_abort (uc->dscan);
842 GNUNET_DATASTORE_cancel (uc->dqe);
847 GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
852 GNUNET_MQ_destroy (uc->mq);
857 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
860 if (NULL != uc->ksk_uri)
862 GNUNET_FS_uri_destroy (uc->ksk_uri);
867 GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
872 GNUNET_DISK_file_close (uc->fh);
875 GNUNET_FS_end_top (uc->h, uc->top);
876 if (uc->serialization != NULL)
878 GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
880 GNUNET_free (uc->serialization);
881 uc->serialization = NULL;
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,
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);
894 /* end of fs_unindex.c */