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 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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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 */
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);
383 case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
384 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
385 _("Internal error scanning `%s'.\n"),
387 GNUNET_FS_directory_scan_abort(uc->dscan);
389 uc->emsg = GNUNET_strdup(_("Failed to get KSKs from directory scan."));
390 GNUNET_FS_unindex_sync_(uc);
401 * If necessary, connect to the datastore and remove the UBlocks.
403 * @param uc context for the unindex operation.
406 GNUNET_FS_unindex_do_extract_keywords_(struct GNUNET_FS_UnindexContext *uc)
411 GNUNET_CONFIGURATION_get_value_string(uc->h->cfg, "FS", "EXTRACTORS", &ex))
413 uc->dscan = GNUNET_FS_directory_scan_start(uc->filename,
415 &unindex_directory_scan_cb,
417 GNUNET_free_non_null(ex);
422 * Continuation called to notify client about result of the remove
423 * operation for the UBlock.
425 * @param cls the 'struct GNUNET_FS_UnindexContext *'
426 * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
427 * GNUNET_NO if content was already there
428 * GNUNET_YES (or other positive value) on success
429 * @param min_expiration minimum expiration time required for 0-priority content to be stored
430 * by the datacache at this time, zero for unknown, forever if we have no
431 * space for 0-priority content
432 * @param msg NULL on success, otherwise an error message
435 continue_after_remove(void *cls,
437 struct GNUNET_TIME_Absolute min_expiration,
440 struct GNUNET_FS_UnindexContext *uc = cls;
443 if (success != GNUNET_YES)
444 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
445 _("Failed to remove UBlock: %s\n"),
448 GNUNET_FS_unindex_do_remove_kblocks_(uc);
453 * Function called from datastore with result from us looking for
454 * a UBlock. There are four cases:
455 * 1) no result, means we move on to the next keyword
456 * 2) data hash is the same as an already seen data hash, means we move on to
458 * 3) UBlock for a different CHK, means we keep looking for more
459 * 4) UBlock is for our CHK, means we remove the block and then move
460 * on to the next keyword
462 * @param cls the 'struct GNUNET_FS_UnindexContext *'
463 * @param key key for the content
464 * @param size number of bytes in data
465 * @param data content stored
466 * @param type type of the content
467 * @param priority priority of the content
468 * @param anonymity anonymity-level for the content
469 * @param replication replication-level for the content
470 * @param expiration expiration time for the content
471 * @param uid unique identifier for the datum;
472 * maybe 0 if no unique identifier is available
475 process_kblock_for_unindex(void *cls,
476 const struct GNUNET_HashCode *key,
479 enum GNUNET_BLOCK_Type type,
482 uint32_t replication,
483 struct GNUNET_TIME_Absolute expiration,
486 struct GNUNET_FS_UnindexContext *uc = cls;
487 const struct UBlock *ub;
488 struct GNUNET_FS_Uri *chk_uri;
489 struct GNUNET_HashCode query;
496 GNUNET_FS_unindex_do_remove_kblocks_(uc);
499 GNUNET_assert(GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
500 if (size < sizeof(struct UBlock))
506 GNUNET_CRYPTO_hash(&ub->verification_key,
507 sizeof(ub->verification_key),
509 if (0 != memcmp(&query,
511 sizeof(struct GNUNET_HashCode)))
513 /* result does not match our keyword, skip */
517 char pt[size - sizeof(struct UBlock)];
518 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
521 GNUNET_CRYPTO_ecdsa_key_get_public(GNUNET_CRYPTO_ecdsa_key_get_anonymous(),
523 keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
524 GNUNET_FS_ublock_decrypt_(&ub[1], size - sizeof(struct UBlock),
528 if (NULL == memchr(&pt[1], 0, sizeof(pt) - 1))
530 GNUNET_break_op(0); /* malformed UBlock */
533 chk_uri = GNUNET_FS_uri_parse(&pt[1], NULL);
536 GNUNET_break_op(0); /* malformed UBlock */
540 if (0 != memcmp(&uc->chk,
541 &chk_uri->data.chk.chk,
542 sizeof(struct ContentHashKey)))
544 /* different CHK, ignore */
545 GNUNET_FS_uri_destroy(chk_uri);
548 GNUNET_FS_uri_destroy(chk_uri);
550 uc->dqe = GNUNET_DATASTORE_remove(uc->dsh,
556 &continue_after_remove,
560 uc->dqe = GNUNET_DATASTORE_get_key(uc->dsh,
561 uid + 1 /* next_uid */,
564 GNUNET_BLOCK_TYPE_FS_UBLOCK,
567 &process_kblock_for_unindex,
573 * If necessary, connect to the datastore and remove the KBlocks.
575 * @param uc context for the unindex operation.
578 GNUNET_FS_unindex_do_remove_kblocks_(struct GNUNET_FS_UnindexContext *uc)
581 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
582 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
583 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
586 uc->dsh = GNUNET_DATASTORE_connect(uc->h->cfg);
589 uc->state = UNINDEX_STATE_ERROR;
590 uc->emsg = GNUNET_strdup(_("Failed to connect to `datastore' service."));
591 GNUNET_FS_unindex_sync_(uc);
592 signal_unindex_error(uc);
595 if ((NULL == uc->ksk_uri) ||
596 (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount))
601 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous();
602 GNUNET_CRYPTO_ecdsa_key_get_public(anon,
604 keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
605 GNUNET_CRYPTO_ecdsa_public_key_derive(&anon_pub,
609 GNUNET_CRYPTO_hash(&dpub,
612 uc->dqe = GNUNET_DATASTORE_get_key(uc->dsh,
616 GNUNET_BLOCK_TYPE_FS_UBLOCK,
619 &process_kblock_for_unindex,
625 * Function called when the tree encoder has
626 * processed all blocks. Clean up.
628 * @param cls our unindexing context
631 unindex_extract_keywords(void *cls)
633 struct GNUNET_FS_UnindexContext *uc = cls;
635 uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
636 GNUNET_FS_unindex_sync_(uc);
637 GNUNET_FS_unindex_do_extract_keywords_(uc);
642 * Connect to the datastore and remove the blocks.
644 * @param uc context for the unindex operation.
647 GNUNET_FS_unindex_do_remove_(struct GNUNET_FS_UnindexContext *uc)
650 uc->dsh = GNUNET_DATASTORE_connect(uc->h->cfg);
653 uc->state = UNINDEX_STATE_ERROR;
654 uc->emsg = GNUNET_strdup(_("Failed to connect to `datastore' service."));
655 GNUNET_FS_unindex_sync_(uc);
656 signal_unindex_error(uc);
660 GNUNET_DISK_file_open(uc->filename, GNUNET_DISK_OPEN_READ,
661 GNUNET_DISK_PERM_NONE);
664 GNUNET_DATASTORE_disconnect(uc->dsh, GNUNET_NO);
666 uc->state = UNINDEX_STATE_ERROR;
667 uc->emsg = GNUNET_strdup(_("Failed to open file for unindexing."));
668 GNUNET_FS_unindex_sync_(uc);
669 signal_unindex_error(uc);
673 GNUNET_FS_tree_encoder_create(uc->h,
679 &unindex_extract_keywords);
680 GNUNET_FS_tree_encoder_next(uc->tc);
685 * Function called once the hash of the file
686 * that is being unindexed has been computed.
688 * @param cls closure, unindex context
689 * @param file_id computed hash, NULL on error
692 GNUNET_FS_unindex_process_hash_(void *cls,
693 const struct GNUNET_HashCode *file_id)
695 struct GNUNET_FS_UnindexContext *uc = cls;
698 if (uc->state != UNINDEX_STATE_HASHING)
700 GNUNET_FS_unindex_stop(uc);
705 uc->state = UNINDEX_STATE_ERROR;
706 uc->emsg = GNUNET_strdup(_("Failed to compute hash of file."));
707 GNUNET_FS_unindex_sync_(uc);
708 signal_unindex_error(uc);
711 uc->file_id = *file_id;
712 uc->state = UNINDEX_STATE_DS_REMOVE;
713 GNUNET_FS_unindex_sync_(uc);
714 GNUNET_FS_unindex_do_remove_(uc);
719 * Create SUSPEND event for the given unindex operation
720 * and then clean up our state (without stop signal).
722 * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
725 GNUNET_FS_unindex_signal_suspend_(void *cls)
727 struct GNUNET_FS_UnindexContext *uc = cls;
728 struct GNUNET_FS_ProgressInfo pi;
730 /* FIXME: lots of duplication with unindex_stop here! */
731 if (uc->dscan != NULL)
733 GNUNET_FS_directory_scan_abort(uc->dscan);
738 GNUNET_DATASTORE_cancel(uc->dqe);
743 GNUNET_CRYPTO_hash_file_cancel(uc->fhc);
746 if (NULL != uc->ksk_uri)
748 GNUNET_FS_uri_destroy(uc->ksk_uri);
753 GNUNET_MQ_destroy(uc->mq);
758 GNUNET_DATASTORE_disconnect(uc->dsh, GNUNET_NO);
763 GNUNET_FS_tree_encoder_finish(uc->tc, NULL);
768 GNUNET_DISK_file_close(uc->fh);
771 GNUNET_FS_end_top(uc->h, uc->top);
772 pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
773 GNUNET_FS_unindex_make_status_(&pi, uc,
775 UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
776 GNUNET_break(NULL == uc->client_info);
777 GNUNET_free(uc->filename);
778 GNUNET_free_non_null(uc->serialization);
779 GNUNET_free_non_null(uc->emsg);
787 * @param h handle to the file sharing subsystem
788 * @param filename file to unindex
789 * @param cctx initial value for the client context
790 * @return NULL on error, otherwise handle
792 struct GNUNET_FS_UnindexContext *
793 GNUNET_FS_unindex_start(struct GNUNET_FS_Handle *h,
794 const char *filename,
797 struct GNUNET_FS_UnindexContext *uc;
798 struct GNUNET_FS_ProgressInfo pi;
802 GNUNET_DISK_file_size(filename,
807 uc = GNUNET_new(struct GNUNET_FS_UnindexContext);
809 uc->filename = GNUNET_strdup(filename);
810 uc->start_time = GNUNET_TIME_absolute_get();
811 uc->file_size = size;
812 uc->client_info = cctx;
813 GNUNET_FS_unindex_sync_(uc);
814 pi.status = GNUNET_FS_STATUS_UNINDEX_START;
815 pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
816 GNUNET_FS_unindex_make_status_(&pi, uc, 0);
818 GNUNET_CRYPTO_hash_file(GNUNET_SCHEDULER_PRIORITY_IDLE,
821 &GNUNET_FS_unindex_process_hash_, uc);
822 uc->top = GNUNET_FS_make_top(h,
823 &GNUNET_FS_unindex_signal_suspend_,
830 * Clean up after completion of an unindex operation.
835 GNUNET_FS_unindex_stop(struct GNUNET_FS_UnindexContext *uc)
837 struct GNUNET_FS_ProgressInfo pi;
839 if (NULL != uc->dscan)
841 GNUNET_FS_directory_scan_abort(uc->dscan);
846 GNUNET_DATASTORE_cancel(uc->dqe);
851 GNUNET_CRYPTO_hash_file_cancel(uc->fhc);
856 GNUNET_MQ_destroy(uc->mq);
861 GNUNET_DATASTORE_disconnect(uc->dsh, GNUNET_NO);
864 if (NULL != uc->ksk_uri)
866 GNUNET_FS_uri_destroy(uc->ksk_uri);
871 GNUNET_FS_tree_encoder_finish(uc->tc, NULL);
876 GNUNET_DISK_file_close(uc->fh);
879 GNUNET_FS_end_top(uc->h, uc->top);
880 if (uc->serialization != NULL)
882 GNUNET_FS_remove_sync_file_(uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
884 GNUNET_free(uc->serialization);
885 uc->serialization = NULL;
887 pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
888 pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
889 GNUNET_FS_unindex_make_status_(&pi, uc,
891 UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
892 GNUNET_break(NULL == uc->client_info);
893 GNUNET_free_non_null(uc->emsg);
894 GNUNET_free(uc->filename);
898 /* end of fs_unindex.c */