2 This file is part of GNUnet.
3 (C) 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/gnunet-service-fs.c
23 * @brief program that provides the file-sharing service
24 * @author Christian Grothoff
29 * - bloomfilter support (GET, CS-request with BF, etc.)
30 * - handling of on-demand blocks from datastore (need to use indexed-list!)
34 #include "gnunet_protocols.h"
35 #include "gnunet_core_service.h"
36 #include "gnunet_datastore_service.h"
37 #include "gnunet_util_lib.h"
41 * Our connection to the datastore.
43 static struct GNUNET_DATASTORE_Handle *dsh;
48 static struct GNUNET_SCHEDULER_Handle *sched;
53 const struct GNUNET_CONFIGURATION_Handle *cfg;
57 * In-memory information about indexed files (also available
64 * This is a linked list.
66 struct IndexInfo *next;
69 * Name of the indexed file. Memory allocated
70 * at the end of this struct (do not free).
75 * Context for tranmitting confirmation to client,
76 * NULL if we've done this already.
78 struct GNUNET_SERVER_TransmitContext *tc;
81 * Hash of the contents of the file.
83 GNUNET_HashCode file_id;
89 * Linked list of indexed files.
91 static struct IndexInfo *indexed_files;
95 * Write the current index information list to disk.
105 * Read index information from disk.
115 * We've validated the hash of the file we're about to
116 * index. Signal success to the client and update
117 * our internal data structures.
119 * @param ii the index info entry for the request
122 signal_index_ok (struct IndexInfo *ii)
124 ii->next = indexed_files;
127 GNUNET_SERVER_transmit_context_append (ii->tc,
129 GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK);
130 GNUNET_SERVER_transmit_context_run (ii->tc,
131 GNUNET_TIME_UNIT_MINUTES);
138 * Function called once the hash computation over an
139 * indexed file has completed.
141 * @param cls closure, our publishing context
142 * @param res resulting hash, NULL on error
145 hash_for_index_val (void *cls,
146 const GNUNET_HashCode *
149 struct IndexInfo *ii;
151 if ( (res == NULL) ||
154 sizeof(GNUNET_HashCode))) )
156 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
157 _("Hash mismatch trying to index file `%s'\n"),
159 GNUNET_SERVER_transmit_context_append (ii->tc,
161 GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED);
162 GNUNET_SERVER_transmit_context_run (ii->tc,
163 GNUNET_TIME_UNIT_MINUTES);
167 signal_index_ok (ii);
172 * Handle INDEX_START-message.
175 * @param client identification of the client
176 * @param message the actual message
179 handle_index_start (void *cls,
180 struct GNUNET_SERVER_Client *client,
181 const struct GNUNET_MessageHeader *message)
183 const struct IndexStartMessage *ism;
186 struct IndexInfo *ii;
193 msize = ntohs(message->size);
194 if ( (msize <= sizeof (struct IndexStartMessage)) ||
195 ( ((const char *)message)[msize-1] != '\0') )
198 GNUNET_SERVER_receive_done (client,
203 fn = (const char*) &ism[1];
204 dev = ntohl (ism->device);
205 ino = GNUNET_ntohll (ism->inode);
206 ism = (const struct IndexStartMessage*) message;
207 slen = strlen (fn) + 1;
208 ii = GNUNET_malloc (sizeof (struct IndexInfo) + slen);
209 ii->filename = (const char*) &ii[1];
210 memcpy (&ii[1], fn, slen);
211 ii->file_id = ism->file_id;
212 ii->tc = GNUNET_SERVER_transmit_context_create (client);
215 (GNUNET_OK == GNUNET_DISK_file_get_identifiers (fn,
221 /* fast validation OK! */
222 signal_index_ok (ii);
225 /* slow validation, need to hash full file (again) */
226 GNUNET_CRYPTO_hash_file (sched,
227 GNUNET_SCHEDULER_PRIORITY_IDLE,
237 * Handle INDEX_LIST_GET-message.
240 * @param client identification of the client
241 * @param message the actual message
244 handle_index_list_get (void *cls,
245 struct GNUNET_SERVER_Client *client,
246 const struct GNUNET_MessageHeader *message)
248 struct GNUNET_SERVER_TransmitContext *tc;
249 struct IndexInfoMessage *iim;
250 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
253 struct GNUNET_MessageHeader *msg;
254 struct IndexInfo *pos;
256 tc = GNUNET_SERVER_transmit_context_create (client);
257 iim = (struct IndexInfoMessage*) buf;
263 iim->file_id = pos->file_id;
265 slen = strlen (fn) + 1;
266 if (slen + sizeof (struct IndexInfoMessage) >
267 GNUNET_SERVER_MAX_MESSAGE_SIZE)
272 memcpy (&iim[1], fn, slen);
273 GNUNET_SERVER_transmit_context_append
276 sizeof (struct IndexInfoMessage)
277 - sizeof (struct GNUNET_MessageHeader) + slen,
278 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY);
281 GNUNET_SERVER_transmit_context_append (tc,
283 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END);
284 GNUNET_SERVER_transmit_context_run (tc,
285 GNUNET_TIME_UNIT_MINUTES);
290 * Handle UNINDEX-message.
293 * @param client identification of the client
294 * @param message the actual message
297 handle_unindex (void *cls,
298 struct GNUNET_SERVER_Client *client,
299 const struct GNUNET_MessageHeader *message)
301 const struct UnindexMessage *um;
302 struct IndexInfo *pos;
303 struct IndexInfo *prev;
304 struct IndexInfo *next;
305 struct GNUNET_SERVER_TransmitContext *tc;
308 um = (const struct UnindexMessage*) message;
315 if (0 == memcmp (&pos->file_id,
317 sizeof (GNUNET_HashCode)))
320 indexed_files = pos->next;
322 prev->next = pos->next;
332 if (GNUNET_YES == found)
334 tc = GNUNET_SERVER_transmit_context_create (client);
335 GNUNET_SERVER_transmit_context_append (tc,
337 GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK);
338 GNUNET_SERVER_transmit_context_run (tc,
339 GNUNET_TIME_UNIT_MINUTES);
344 * Signature of a function that is called whenever a datastore
345 * request can be processed (or an entry put on the queue times out).
348 * @param ok GNUNET_OK if DS is ready, GNUNET_SYSERR on timeout
350 typedef void (*RequestFunction)(void *cls,
355 * Doubly-linked list of our requests for the datastore.
357 struct DatastoreRequestQueue
361 * This is a doubly-linked list.
363 struct DatastoreRequestQueue *next;
366 * This is a doubly-linked list.
368 struct DatastoreRequestQueue *prev;
371 * Function to call (will issue the request).
381 * When should this request time-out because we don't care anymore?
383 struct GNUNET_TIME_Absolute timeout;
386 * ID of task used for signaling timeout.
388 GNUNET_SCHEDULER_TaskIdentifier task;
394 * Head of request queue for the datastore, sorted by timeout.
396 static struct DatastoreRequestQueue *drq_head;
399 * Tail of request queue for the datastore.
401 static struct DatastoreRequestQueue *drq_tail;
405 * Run the next DS request in our
406 * queue, we're done with the current one.
411 struct DatastoreRequestQueue *e;
413 while (NULL != (e = drq_head))
415 if (0 != GNUNET_TIME_absolute_get_remaining (e->timeout).value)
417 if (e->task != GNUNET_SCHEDULER_NO_TASK)
418 GNUNET_SCHEDULER_cancel (sched, e->task);
419 GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
420 e->req (e->req_cls, GNUNET_NO);
425 if (e->task != GNUNET_SCHEDULER_NO_TASK)
426 GNUNET_SCHEDULER_cancel (sched, e->task);
427 e->task = GNUNET_SCHEDULER_NO_TASK;
428 e->req (e->req_cls, GNUNET_YES);
429 GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
435 * A datastore request had to be timed out.
437 * @param cls closure (of type "struct DatastoreRequestQueue*")
438 * @param tc task context, unused
441 timeout_ds_request (void *cls,
442 const struct GNUNET_SCHEDULER_TaskContext *tc)
444 struct DatastoreRequestQueue *e = cls;
446 e->task = GNUNET_SCHEDULER_NO_TASK;
447 GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
448 e->req (e->req_cls, GNUNET_NO);
454 * Queue a request for the datastore.
456 * @param deadline by when the request should run
457 * @param fun function to call once the request can be run
458 * @param fun_cls closure for fun
460 static struct DatastoreRequestQueue *
461 queue_ds_request (struct GNUNET_TIME_Relative deadline,
465 struct DatastoreRequestQueue *e;
466 struct DatastoreRequestQueue *bef;
468 if (drq_head == NULL)
470 /* no other requests pending, run immediately */
471 fun (fun_cls, GNUNET_OK);
474 e = GNUNET_malloc (sizeof (struct DatastoreRequestQueue));
475 e->timeout = GNUNET_TIME_relative_to_absolute (deadline);
477 e->req_cls = fun_cls;
478 if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
480 /* local request, highest prio, put at head of queue
481 regardless of deadline */
487 while ( (NULL != bef) &&
488 (e->timeout.value < bef->timeout.value) )
491 GNUNET_CONTAINER_DLL_insert_after (drq_head, drq_tail, bef, e);
492 if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
494 e->task = GNUNET_SCHEDULER_add_delayed (sched,
496 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
497 GNUNET_SCHEDULER_NO_TASK,
506 * Closure for processing START_SEARCH messages from a client.
508 struct LocalGetContext
512 * This is a doubly-linked list.
514 struct LocalGetContext *next;
517 * This is a doubly-linked list.
519 struct LocalGetContext *prev;
522 * Client that initiated the search.
524 struct GNUNET_SERVER_Client *client;
527 * Array of results that we've already received
530 GNUNET_HashCode *results;
533 * Bloomfilter over all results (for fast query construction);
534 * NULL if we don't have any results.
536 struct GNUNET_CONTAINER_BloomFilter *results_bf;
539 * DS request associated with this operation.
541 struct DatastoreRequestQueue *req;
544 * Current result message to transmit to client (or NULL).
546 struct ContentMessage *result;
549 * Type of the content that we're looking for.
555 * Desired anonymity level.
557 uint32_t anonymity_level;
560 * Number of results actually stored in the results array.
562 unsigned int results_used;
565 * Size of the results array in memory.
567 unsigned int results_size;
570 * If the request is for a DBLOCK or IBLOCK, this is the identity of
571 * the peer that is known to have a response. Set to all-zeros if
572 * such a target is not known (note that even if OUR anonymity
573 * level is >0 we may happen to know the responder's identity;
574 * nevertheless, we should probably not use it for a DHT-lookup
575 * or similar blunt actions in order to avoid exposing ourselves).
577 * If the request is for an SBLOCK, this is the identity of the
578 * pseudonym to which the SBLOCK belongs.
580 * If the request is for a KBLOCK, "target" must be all zeros.
582 GNUNET_HashCode target;
585 * Hash of the keyword (aka query) for KBLOCKs; Hash of
586 * the CHK-encoded block for DBLOCKS and IBLOCKS (aka query)
587 * and hash of the identifier XORed with the target for
588 * SBLOCKS (aka query).
590 GNUNET_HashCode query;
596 * Head of doubly-linked LGC list.
598 static struct LocalGetContext *lgc_head;
601 * Tail of doubly-linked LGC list.
603 static struct LocalGetContext *lgc_tail;
607 * Free the state associated with a local get context.
609 * @param lgc the lgc to free
612 local_get_context_free (struct LocalGetContext *lgc)
614 GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
615 GNUNET_SERVER_client_drop (lgc->client);
616 GNUNET_free_non_null (lgc->results);
617 if (lgc->results_bf != NULL)
618 GNUNET_CONTAINER_bloomfilter_free (lgc->results_bf);
619 if (lgc->req != NULL)
621 if (lgc->req->task != GNUNET_SCHEDULER_NO_TASK)
622 GNUNET_SCHEDULER_cancel (sched, lgc->req->task);
623 GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
624 GNUNET_free (lgc->req);
631 * We're able to transmit the next (local) result to the client.
632 * Do it and ask the datastore for more. Or, on error, tell
633 * the datastore to stop giving us more.
635 * @param cls our closure (struct LocalGetContext)
636 * @param max maximum number of bytes we can transmit
637 * @param buf where to copy our message
638 * @return number of bytes copied to buf
641 transmit_local_result (void *cls,
645 struct LocalGetContext *lgc = cls;
651 GNUNET_free (lgc->result);
653 GNUNET_DATASTORE_get_next (dsh, GNUNET_NO);
656 msize = ntohs (lgc->result->header.size);
657 GNUNET_assert (max >= msize);
658 memcpy (buf, lgc->result, msize);
659 GNUNET_free (lgc->result);
661 GNUNET_DATASTORE_get_next (dsh, GNUNET_YES);
667 * We're processing (local) results for a search request
668 * from a (local) client. Pass applicable results to the
669 * client and if we are done either clean up (operation
670 * complete) or switch to P2P search (more results possible).
672 * @param cls our closure (struct LocalGetContext)
673 * @param key key for the content
674 * @param size number of bytes in data
675 * @param data content stored
676 * @param type type of the content
677 * @param priority priority of the content
678 * @param anonymity anonymity-level for the content
679 * @param expiration expiration time for the content
680 * @param uid unique identifier for the datum;
681 * maybe 0 if no unique identifier is available
684 process_local_get_result (void *cls,
685 const GNUNET_HashCode * key,
691 struct GNUNET_TIME_Absolute
695 struct LocalGetContext *lgc = cls;
698 // FIXME: handle ONDEMAND blocks!
701 /* no further results from datastore; continue
702 processing further requests from the client and
703 allow the next task to use the datastore; also,
704 switch to P2P requests or clean up our state. */
706 GNUNET_SERVER_receive_done (lgc->client,
708 if ( (lgc->results_used == 0) ||
709 (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_KBLOCK) ||
710 (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SBLOCK) ||
711 (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SKBLOCK) )
713 // FIXME: initiate P2P search
716 /* got all possible results, clean up! */
717 local_get_context_free (lgc);
720 if (lgc->results_used == lgc->results_size)
722 GNUNET_array_grow (lgc->results,
724 lgc->results_size * 2 + 2);
725 if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
726 (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
728 // FIXME: possibly grow/create BF!
731 GNUNET_CRYPTO_hash (data,
733 &lgc->results[lgc->results_used++]);
734 if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
735 (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
737 // FIXME: add result to BF!
739 msize = size + sizeof (struct ContentMessage);
740 GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
741 lgc->result = GNUNET_malloc (msize);
742 lgc->result->header.size = htons (msize);
743 lgc->result->header.type = htons (GNUNET_MESSAGE_TYPE_FS_CONTENT);
744 lgc->result->type = htonl (type);
745 lgc->result->expiration = GNUNET_TIME_absolute_hton (expiration);
746 memcpy (&lgc->result[1],
749 GNUNET_SERVER_notify_transmit_ready (lgc->client,
751 GNUNET_TIME_UNIT_FOREVER_REL,
752 &transmit_local_result,
758 * We're processing a search request from a local
759 * client. Now it is our turn to query the datastore.
761 * @param cls our closure (struct LocalGetContext)
765 transmit_local_get (void *cls,
766 const struct GNUNET_SCHEDULER_TaskContext *tc)
768 struct LocalGetContext *lgc = cls;
770 // FIXME: handle ONDEMAND blocks (change type to ANY for DBLOCK/IBLOCK)
771 GNUNET_DATASTORE_get (dsh,
774 &process_local_get_result,
776 GNUNET_TIME_UNIT_FOREVER_REL);
781 * We're processing a search request from a local
782 * client. Now it is our turn to query the datastore.
784 * @param cls our closure (struct LocalGetContext)
785 * @param ok did we succeed to queue for datastore access, should always be GNUNET_OK
788 transmit_local_get_ready (void *cls,
791 struct LocalGetContext *lgc = cls;
793 GNUNET_assert (GNUNET_OK == ok);
794 GNUNET_SCHEDULER_add_continuation (sched,
798 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
803 * Handle START_SEARCH-message (search request from client).
806 * @param client identification of the client
807 * @param message the actual message
810 handle_start_search (void *cls,
811 struct GNUNET_SERVER_Client *client,
812 const struct GNUNET_MessageHeader *message)
814 const struct SearchMessage *sm;
815 struct LocalGetContext *lgc;
817 sm = (const struct SearchMessage*) message;
818 GNUNET_SERVER_client_keep (client);
819 lgc = GNUNET_malloc (sizeof (struct LocalGetContext));
820 lgc->client = client;
821 lgc->type = ntohl (sm->type);
822 lgc->anonymity_level = ntohl (sm->anonymity_level);
823 lgc->target = sm->target;
824 lgc->query = sm->query;
825 GNUNET_CONTAINER_DLL_insert (lgc_head, lgc_tail, lgc);
826 lgc->req = queue_ds_request (GNUNET_TIME_UNIT_FOREVER_REL,
827 &transmit_local_get_ready,
833 * List of handlers for the messages understood by this
836 static struct GNUNET_SERVER_MessageHandler handlers[] = {
837 {&handle_index_start, NULL,
838 GNUNET_MESSAGE_TYPE_FS_INDEX_START, 0},
839 {&handle_index_list_get, NULL,
840 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET, sizeof(struct GNUNET_MessageHeader) },
841 {&handle_unindex, NULL, GNUNET_MESSAGE_TYPE_FS_UNINDEX,
842 sizeof (struct UnindexMessage) },
843 {&handle_start_search, NULL, GNUNET_MESSAGE_TYPE_FS_START_SEARCH,
844 sizeof (struct SearchMessage) },
850 * A client disconnected. Remove all of its pending queries.
852 * @param cls closure, NULL
853 * @param client identification of the client
856 handle_client_disconnect (void *cls,
857 struct GNUNET_SERVER_Client
860 struct LocalGetContext *lgc;
863 while ( (NULL != lgc) &&
864 (lgc->client != client) )
867 return; /* not one of our clients */
868 local_get_context_free (lgc);
873 * Task run during shutdown.
879 shutdown_task (void *cls,
880 const struct GNUNET_SCHEDULER_TaskContext *tc)
882 GNUNET_DATASTORE_disconnect (dsh,
889 * Process fs requests.
892 * @param sched scheduler to use
893 * @param server the initialized server
894 * @param cfg configuration to use
898 struct GNUNET_SCHEDULER_Handle *s,
899 struct GNUNET_SERVER_Handle *server,
900 const struct GNUNET_CONFIGURATION_Handle *c)
905 dsh = GNUNET_DATASTORE_connect (cfg,
909 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
910 _("Failed to connect to datastore service.\n"));
913 GNUNET_SERVER_disconnect_notify (server,
914 &handle_client_disconnect,
916 GNUNET_SERVER_add_handlers (server, handlers);
917 // FIXME: also register with core to handle P2P messages!
918 GNUNET_SCHEDULER_add_delayed (sched,
920 GNUNET_SCHEDULER_PRIORITY_IDLE,
921 GNUNET_SCHEDULER_NO_TASK,
922 GNUNET_TIME_UNIT_FOREVER_REL,
929 * The main function for the fs service.
931 * @param argc number of arguments from the command line
932 * @param argv command line arguments
933 * @return 0 ok, 1 on error
936 main (int argc, char *const *argv)
939 GNUNET_SERVICE_run (argc,
941 "fs", &run, NULL, NULL, NULL)) ? 0 : 1;
944 /* end of gnunet-service-fs.c */