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