2 This file is part of GNUnet.
3 (C) 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file fs/fs_unindex.c
23 * @author Krista Bennett
24 * @author Christian Grothoff
25 * @brief Unindex file.
28 * - code cleanup (share more with upload.c)
32 #include "gnunet_constants.h"
33 #include "gnunet_fs_service.h"
34 #include "gnunet_protocols.h"
39 * Context for "GNUNET_FS_get_indexed_files".
41 struct GetIndexedContext
44 * Handle to global FS context.
46 struct GNUNET_FS_Handle *h;
49 * Connection to the FS service.
51 struct GNUNET_CLIENT_Connection *client;
54 * Function to call for each indexed file.
56 GNUNET_FS_IndexedFileProcessor iterator;
59 * Closure for iterator.
64 * Continuation to trigger at the end.
66 GNUNET_SCHEDULER_Task cont;
76 * Function called on each response from the FS
77 * service with information about indexed files.
79 * @param cls closure (of type "struct GetIndexedContext*")
80 * @param msg message with indexing information
83 handle_index_info (void *cls,
84 const struct GNUNET_MessageHeader *msg)
86 struct GetIndexedContext *gic = cls;
87 const struct IndexInfoMessage *iim;
93 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
94 _("Failed to receive response for `%s' request from `%s' service.\n"),
97 GNUNET_SCHEDULER_add_continuation (gic->h->sched,
101 GNUNET_SCHEDULER_REASON_TIMEOUT);
102 GNUNET_CLIENT_disconnect (gic->client);
106 if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END)
108 /* normal end-of-list */
109 GNUNET_SCHEDULER_add_continuation (gic->h->sched,
113 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
114 GNUNET_CLIENT_disconnect (gic->client);
118 msize = ntohs (msg->size);
119 iim = (const struct IndexInfoMessage*) msg;
120 filename = (const char*) &iim[1];
121 if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY) ||
122 (msize <= sizeof (struct IndexInfoMessage)) ||
123 (filename[msize-sizeof (struct IndexInfoMessage) -1] != '\0') )
126 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
127 _("Failed to receive valid response for `%s' request from `%s' service.\n"),
130 GNUNET_SCHEDULER_add_continuation (gic->h->sched,
134 GNUNET_SCHEDULER_REASON_TIMEOUT);
135 GNUNET_CLIENT_disconnect (gic->client);
140 gic->iterator (gic->iterator_cls,
144 GNUNET_SCHEDULER_add_continuation (gic->h->sched,
148 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
149 GNUNET_CLIENT_disconnect (gic->client);
154 GNUNET_CLIENT_receive (gic->client,
157 GNUNET_CONSTANTS_SERVICE_TIMEOUT);
162 * Transmit the request to get a list of all
163 * indexed files to the "FS" service.
165 * @param cls closure (of type "struct GetIndexedContext*")
166 * @param size number of bytes availabe in buf
167 * @param buf where to write the message, NULL on error
168 * @return number of bytes written to buf
171 transmit_get_indexed (void *cls,
175 struct GetIndexedContext *gic = cls;
176 struct GNUNET_MessageHeader *hdr;
180 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
181 _("Failed to transmit `%s' request to `%s' service.\n"),
184 GNUNET_SCHEDULER_add_continuation (gic->h->sched,
188 GNUNET_SCHEDULER_REASON_TIMEOUT);
189 GNUNET_CLIENT_disconnect (gic->client);
193 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
195 hdr->size = htons (sizeof (struct GNUNET_MessageHeader));
196 hdr->type = htons (GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET);
197 GNUNET_CLIENT_receive (gic->client,
200 GNUNET_CONSTANTS_SERVICE_TIMEOUT);
201 return sizeof (struct GNUNET_MessageHeader);
206 * Iterate over all indexed files.
208 * @param h handle to the file sharing subsystem
209 * @param iterator function to call on each indexed file
210 * @param iterator_cls closure for iterator
211 * @param cont continuation to call when done;
212 * reason should be "TIMEOUT" (on
213 * error) or "PREREQ_DONE" (on success)
214 * @param cont_cls closure for cont
217 GNUNET_FS_get_indexed_files (struct GNUNET_FS_Handle *h,
218 GNUNET_FS_IndexedFileProcessor iterator,
220 GNUNET_SCHEDULER_Task cont,
223 struct GNUNET_CLIENT_Connection *client;
224 struct GetIndexedContext *gic;
226 client = GNUNET_CLIENT_connect (h->sched,
231 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
232 _("Failed to not connect to `%s' service.\n"),
234 GNUNET_SCHEDULER_add_continuation (h->sched,
238 GNUNET_SCHEDULER_REASON_TIMEOUT);
242 gic = GNUNET_malloc (sizeof (struct GetIndexedContext));
244 gic->client = client;
245 gic->iterator = iterator;
246 gic->iterator_cls = iterator_cls;
248 gic->cont_cls = cont_cls;
249 GNUNET_CLIENT_notify_transmit_ready (client,
250 sizeof (struct GNUNET_MessageHeader),
251 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
252 &transmit_get_indexed,
260 * @param h handle to the file sharing subsystem
261 * @param filename file to unindex
262 * @return NULL on error, otherwise handle
264 struct GNUNET_FS_UnindexContext *
265 GNUNET_FS_unindex (struct GNUNET_FS_Handle *h,
266 const char *filename)
268 // 1: compute file-id (hash over entire file)
269 // 2: notify FS service about file no longer being indexed
270 // 3: remove corresponding blocks from datastore!
276 * Clean up after completion of an unindex operation.
281 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
289 #define STRICT_CHECKS GNUNET_NO
292 * Append the given key and query to the iblock[level].
293 * If iblock[level] is already full, compute its chk
294 * and push it to level+1. iblocks is guaranteed to
297 * This function matches exactly upload.c::pushBlock,
298 * except in the call to 'GNUNET_FS_delete'. TODO: refactor
299 * to avoid code duplication (move to block.c, pass
300 * GNUNET_FS_delete as argument!).
303 pushBlock (struct GNUNET_ClientServerConnection *sock,
304 const GNUNET_EC_ContentHashKey * chk, unsigned int level,
305 GNUNET_DatastoreValue ** iblocks)
308 unsigned int present;
309 GNUNET_DatastoreValue *value;
310 GNUNET_EC_DBlock *db;
311 GNUNET_EC_ContentHashKey ichk;
313 size = ntohl (iblocks[level]->size) - sizeof (GNUNET_DatastoreValue);
315 (size - sizeof (GNUNET_EC_DBlock)) / sizeof (GNUNET_EC_ContentHashKey);
316 db = (GNUNET_EC_DBlock *) & iblocks[level][1];
317 if (present == GNUNET_ECRS_CHK_PER_INODE)
319 GNUNET_EC_file_block_get_key (db, size, &ichk.key);
320 GNUNET_EC_file_block_get_query (db, size, &ichk.query);
321 if (GNUNET_OK != pushBlock (sock, &ichk, level + 1, iblocks))
323 GNUNET_GE_BREAK (NULL, 0);
324 return GNUNET_SYSERR;
326 GNUNET_EC_file_block_encode (db, size, &ichk.query, &value);
328 if (GNUNET_SYSERR == GNUNET_FS_delete (sock, value))
331 GNUNET_GE_BREAK (NULL, 0);
332 return GNUNET_SYSERR;
335 GNUNET_FS_delete (sock, value);
338 size = sizeof (GNUNET_EC_DBlock);
340 /* append GNUNET_EC_ContentHashKey */
341 memcpy (&((char *) db)[size], chk, sizeof (GNUNET_EC_ContentHashKey));
342 iblocks[level]->size = htonl (size +
343 sizeof (GNUNET_EC_ContentHashKey) +
344 sizeof (GNUNET_DatastoreValue));
351 * Undo sym-linking operation:
352 * a) check if we have a symlink
353 * b) delete symbolic link
356 undoSymlinking (struct GNUNET_GE_Context *ectx,
358 const GNUNET_HashCode * fileId,
359 struct GNUNET_ClientServerConnection *sock)
368 return GNUNET_OK; /* symlinks do not exist? */
370 if (0 != LSTAT (fn, &buf))
372 GNUNET_GE_LOG_STRERROR_FILE (ectx,
373 GNUNET_GE_ERROR | GNUNET_GE_BULK |
374 GNUNET_GE_USER | GNUNET_GE_ADMIN, "stat",
376 return GNUNET_SYSERR;
379 if (!S_ISLNK (buf.st_mode))
383 GNUNET_get_daemon_configuration_value (sock, "FS", "INDEX-DIRECTORY");
384 if (serverDir == NULL)
386 serverFN = GNUNET_malloc (strlen (serverDir) + 2 + sizeof (GNUNET_EncName));
387 strcpy (serverFN, serverDir);
388 GNUNET_free (serverDir);
389 if (serverFN[strlen (serverFN) - 1] != DIR_SEPARATOR)
390 strcat (serverFN, DIR_SEPARATOR_STR);
391 GNUNET_hash_to_enc (fileId, &enc);
392 strcat (serverFN, (char *) &enc);
394 if (0 != UNLINK (serverFN))
396 GNUNET_GE_LOG_STRERROR_FILE (ectx,
397 GNUNET_GE_ERROR | GNUNET_GE_BULK |
398 GNUNET_GE_USER | GNUNET_GE_ADMIN, "unlink",
400 GNUNET_free (serverFN);
401 return GNUNET_SYSERR;
403 GNUNET_free (serverFN);
412 * @return GNUNET_SYSERR if the unindexing failed (i.e. not indexed)
415 GNUNET_ECRS_file_unindex (struct GNUNET_GE_Context *ectx,
416 struct GNUNET_GC_Configuration *cfg,
417 const char *filename,
418 GNUNET_ECRS_UploadProgressCallback upcb,
419 void *upcbClosure, GNUNET_ECRS_TestTerminate tt,
422 unsigned long long filesize;
423 unsigned long long pos;
424 unsigned int treedepth;
428 GNUNET_DatastoreValue **iblocks;
429 GNUNET_DatastoreValue *dblock;
430 GNUNET_EC_DBlock *db;
431 GNUNET_DatastoreValue *value;
432 struct GNUNET_ClientServerConnection *sock;
433 GNUNET_HashCode fileId;
434 GNUNET_EC_ContentHashKey chk;
436 GNUNET_CronTime start;
440 start = GNUNET_get_time ();
441 if (GNUNET_YES != GNUNET_disk_file_test (ectx, filename))
443 GNUNET_GE_BREAK (ectx, 0);
444 return GNUNET_SYSERR;
447 GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES))
448 return GNUNET_SYSERR;
449 sock = GNUNET_client_connection_create (ectx, cfg);
451 return GNUNET_SYSERR;
454 upcb (filesize, 0, eta, upcbClosure);
455 if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId))
457 GNUNET_client_connection_destroy (sock);
458 GNUNET_GE_BREAK (ectx, 0);
459 return GNUNET_SYSERR;
461 now = GNUNET_get_time ();
462 eta = now + 2 * (now - start);
463 /* very rough estimate: GNUNET_hash reads once through the file,
464 we'll do that once more and write it. But of course
465 the second read may be cached, and we have the encryption,
466 so a factor of two is really, really just a rough estimate */
468 /* reset the counter since the formula later does not
469 take the time for GNUNET_hash_file into account */
470 treedepth = GNUNET_ECRS_compute_depth (filesize);
472 /* Test if file is indexed! */
473 wasIndexed = GNUNET_FS_test_indexed (sock, &fileId);
475 fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE);
478 GNUNET_client_connection_destroy (sock);
479 return GNUNET_SYSERR;
482 GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
483 sizeof (GNUNET_EC_DBlock));
485 htonl (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
486 sizeof (GNUNET_EC_DBlock));
487 dblock->anonymity_level = htonl (0);
488 dblock->priority = htonl (0);
489 dblock->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
490 dblock->expiration_time = GNUNET_htonll (0);
491 db = (GNUNET_EC_DBlock *) & dblock[1];
492 db->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
494 GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1));
495 for (i = 0; i <= treedepth; i++)
498 GNUNET_malloc (sizeof (GNUNET_DatastoreValue) +
499 GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock));
501 htonl (sizeof (GNUNET_DatastoreValue) + sizeof (GNUNET_EC_DBlock));
502 iblocks[i]->anonymity_level = htonl (0);
503 iblocks[i]->priority = htonl (0);
504 iblocks[i]->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
505 iblocks[i]->expiration_time = GNUNET_htonll (0);
506 ((GNUNET_EC_DBlock *) & iblocks[i][1])->type =
507 htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
511 while (pos < filesize)
514 upcb (filesize, pos, eta, upcbClosure);
516 if (GNUNET_OK != tt (ttClosure))
518 size = GNUNET_ECRS_DBLOCK_SIZE;
519 if (size > filesize - pos)
521 size = filesize - pos;
522 memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE);
525 htonl (sizeof (GNUNET_DatastoreValue) + size +
526 sizeof (GNUNET_EC_DBlock));
527 if (size != READ (fd, &db[1], size))
529 GNUNET_GE_LOG_STRERROR_FILE (ectx,
530 GNUNET_GE_ERROR | GNUNET_GE_USER |
531 GNUNET_GE_ADMIN | GNUNET_GE_BULK,
536 if (GNUNET_OK != tt (ttClosure))
538 GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock),
540 GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock),
542 if (GNUNET_OK != pushBlock (sock, &chk, 0, /* dblocks are on level 0 */
545 GNUNET_GE_BREAK (ectx, 0);
551 GNUNET_EC_file_block_encode (db, size, &chk.query, &value))
553 *value = *dblock; /* copy options! */
555 if (GNUNET_OK != GNUNET_FS_delete (sock, value))
558 GNUNET_GE_BREAK (ectx, 0);
562 GNUNET_FS_delete (sock, value);
572 now = GNUNET_get_time ();
573 eta = (GNUNET_CronTime) (start +
574 (((double) (now - start) / (double) pos))
575 * (double) filesize);
578 if (GNUNET_OK != tt (ttClosure))
580 for (i = 0; i < treedepth; i++)
582 size = ntohl (iblocks[i]->size) - sizeof (GNUNET_DatastoreValue);
583 db = (GNUNET_EC_DBlock *) & iblocks[i][1];
584 GNUNET_EC_file_block_get_key (db, size, &chk.key);
585 GNUNET_EC_file_block_get_query (db, size, &chk.query);
586 if (GNUNET_OK != pushBlock (sock, &chk, i + 1, iblocks))
588 GNUNET_GE_BREAK (ectx, 0);
591 GNUNET_EC_file_block_encode (db, size, &chk.query, &value);
593 if (GNUNET_OK != GNUNET_FS_delete (sock, value))
596 GNUNET_GE_BREAK (ectx, 0);
600 GNUNET_FS_delete (sock, value);
603 GNUNET_free (iblocks[i]);
609 if (GNUNET_OK == undoSymlinking (ectx, filename, &fileId, sock))
612 GNUNET_FS_unindex (sock, GNUNET_ECRS_DBLOCK_SIZE, &fileId))
614 GNUNET_GE_BREAK (ectx, 0);
620 GNUNET_GE_BREAK (ectx, 0);
624 GNUNET_free (iblocks[treedepth]);
626 GNUNET_free (iblocks);
627 GNUNET_free (dblock);
629 GNUNET_client_connection_destroy (sock);
632 for (i = 0; i <= treedepth; i++)
633 GNUNET_free_non_null (iblocks[i]);
634 GNUNET_free (iblocks);
635 GNUNET_free (dblock);
637 GNUNET_client_connection_destroy (sock);
638 return GNUNET_SYSERR;
643 /* end of fs_unindex.c */