2 This file is part of GNUnet.
3 (C) 2004, 2005, 2006, 2008 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_collection.c
23 * @brief Helper functions for building a collection
24 * @author Christian Grothoff
26 * A collection is a special kind of namespace. A collection is the
27 * set of files provided by the same user, but unlike namespaces it is
28 * automatically managed by the GNUnet UI. A collection is a single
29 * directory in a namespace that is automatically updated each time
30 * the user updates or deletes a file. That is, once the user starts
31 * a collection the gnunet-tools will always keep the corresponding
32 * directory and namespace entries up-to-date.
34 * A good way of thinking about a collection is a lazy user's
39 #include "gnunet_directories.h"
40 #include "gnunet_fs_service.h"
44 * Initialize collection.
46 * @param h handle to the file sharing subsystem
47 * @param namespace namespace to use for the collection
48 * @return GNUNET_OK on success, GNUNET_SYSERR if another
49 * namespace is already set for our collection
52 GNUNET_FS_collection_start (struct GNUNET_FS_Handle *h,
53 struct GNUNET_FS_Namespace *namespace)
62 * @param h handle to the file sharing subsystem
63 * @return GNUNET_OK on success, GNUNET_SYSERR if no collection is active
66 GNUNET_FS_collection_stop (struct GNUNET_FS_Handle *h)
73 * Are we using a collection?
75 * @param h handle to the file sharing subsystem
76 * @return NULL if there is no collection,
78 struct GNUNET_FS_Namespace *
79 GNUNET_FS_collection_get(struct GNUNET_FS_Handle *h)
86 * Publish an update of the current collection information to the
87 * network now. The function has no effect if the collection has not
88 * changed since the last publication. If we are currently not
89 * collecting, this function does nothing.
91 * @param h handle to the file sharing subsystem
93 void GNUNET_FS_collection_publish (struct GNUNET_FS_Handle *h)
99 * If we are currently building a collection, publish the given file
100 * information in that collection. If we are currently not
101 * collecting, this function does nothing.
103 * @param h handle to the file sharing subsystem
104 * @param uri uri to add to the collection
105 * @param meta metadata for the uri
107 void GNUNET_FS_collection_add (const struct GNUNET_FS_Handle *h,
108 const struct GNUNET_FS_Uri *uri,
109 const struct GNUNET_CONTAINER_MetaData *meta)
117 * Filename used to store collection information
119 #define COLLECTION "collection"
121 #define COLLECTION_ROOT "root"
124 * How long does a collection advertisement live?
126 #define COLLECTION_ADV_LIFETIME (12 * GNUNET_CRON_MONTHS)
129 * @brief information about a collection
131 typedef struct CollectionData
135 * What is the pseudonym ID for the publication?
140 * Anonymity level for the collection. (NBO)
142 uint32_t anonymityLevel;
145 * Priority of the collection (NBO).
150 * Has this collection changed since the last publication?
157 unsigned int revision;
168 * Metadata describing the collection
170 struct GNUNET_MetaData *meta;
173 * Files in the collection.
175 GNUNET_ECRS_FileInfo *files;
178 * How many files are in files?
180 unsigned int file_count;
185 static CollectionInfo *collectionData;
187 static struct GNUNET_Mutex *lock;
189 static struct GNUNET_GE_Context *ectx;
191 static struct GNUNET_GC_Configuration *cfg;
194 getCollectionFileName ()
196 return GNUNET_get_home_filename (ectx, cfg, GNUNET_NO, COLLECTION, NULL);
200 * Initialize collection module.
203 GNUNET_CO_init (struct GNUNET_GE_Context *e,
204 struct GNUNET_GC_Configuration *c)
209 unsigned long long size;
219 lock = GNUNET_mutex_create (GNUNET_YES);
220 fn = getCollectionFileName ();
221 if (!GNUNET_disk_file_test (ectx, fn))
226 /* read collection data */
227 if (GNUNET_OK != GNUNET_disk_file_size (ectx, fn, &size, GNUNET_YES))
232 if ((size > 0x7FFFFFFF) ||
233 (size < sizeof (CollectionData) + 2 * sizeof (int)))
235 GNUNET_GE_BREAK (ectx, 0);
240 fd = OPEN (fn, O_RDONLY | O_LARGEFILE);
243 GNUNET_GE_BREAK (ectx, 0);
248 rsize = (size_t) size;
249 buf = MMAP (NULL, rsize, PROT_READ, MAP_SHARED, fd, 0);
250 if (buf == MAP_FAILED)
252 GNUNET_GE_LOG_STRERROR_FILE (ectx,
253 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
254 GNUNET_GE_USER | GNUNET_GE_BULK, "mmap",
260 collectionData = GNUNET_malloc (sizeof (CollectionInfo));
261 memset (collectionData, 0, sizeof (CollectionInfo));
262 memcpy (&collectionData->data, buf, sizeof (CollectionData));
263 pos = &buf[sizeof (CollectionData)];
264 rsize -= sizeof (CollectionData);
265 len = ntohl (*(int *) pos);
266 if (len > 1024 * 1024 * 4)
269 mlen = ntohl (*(unsigned int *) pos);
270 pos += sizeof (unsigned int);
271 rsize -= 2 * sizeof (int);
274 GNUNET_array_grow (collectionData->files, collectionData->file_count, len);
275 collectionData->meta = GNUNET_meta_data_deserialize (ectx, pos, mlen);
278 GNUNET_GE_BREAK (ectx, collectionData->meta != NULL);
279 for (i = 0; i < collectionData->file_count; i++)
281 if (rsize < 2 * sizeof (int))
283 GNUNET_GE_BREAK (ectx, 0);
286 len = ntohl (*(int *) pos);
288 mlen = ntohl (*(int *) pos);
290 rsize -= 2 * sizeof (int);
291 if (rsize < mlen + len)
293 GNUNET_GE_BREAK (ectx, 0);
298 GNUNET_GE_BREAK (ectx, 0);
301 tmp = GNUNET_malloc (len + 1);
303 memcpy (tmp, pos, len);
306 collectionData->files[i].uri = GNUNET_ECRS_string_to_uri (ectx, tmp);
307 GNUNET_GE_ASSERT (ectx, collectionData->files[i].uri != NULL);
309 collectionData->files[i].meta
310 = GNUNET_meta_data_deserialize (ectx, pos, mlen);
311 GNUNET_GE_ASSERT (ectx, collectionData->files[i].meta != NULL);
315 GNUNET_GE_ASSERT (ectx, rsize == 0);
316 MUNMAP (buf, (size_t) size);
319 /* kill invalid entries (meta or uri == NULL) */
320 for (i = 0; i < collectionData->file_count; i++)
322 if ((collectionData->files[i].uri != NULL) &&
323 (collectionData->files[i].meta != NULL))
325 if (collectionData->files[i].uri != NULL)
326 GNUNET_ECRS_uri_destroy (collectionData->files[i].uri);
327 if (collectionData->files[i].meta != NULL)
328 GNUNET_meta_data_destroy (collectionData->files[i].meta);
329 collectionData->files[i]
330 = collectionData->files[collectionData->file_count - 1];
331 GNUNET_array_grow (collectionData->files,
332 collectionData->file_count,
333 collectionData->file_count - 1);
337 MUNMAP (buf, (size_t) size);
340 GNUNET_free (collectionData);
341 collectionData = NULL;
345 WRITEINT (int fd, int val)
350 WRITE (fd, &bval, sizeof (int));
363 if (collectionData == NULL)
366 /* write collection data */
368 GNUNET_meta_data_get_serialized_size (collectionData->meta, GNUNET_NO);
369 buf = GNUNET_malloc (mlen);
370 if (mlen != GNUNET_meta_data_serialize (ectx,
371 collectionData->meta, buf,
374 GNUNET_GE_BREAK (ectx, 0);
379 fn = getCollectionFileName ();
381 O_CREAT | O_LARGEFILE | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
384 GNUNET_GE_LOG_STRERROR_FILE (ectx,
385 GNUNET_GE_USER | GNUNET_GE_ADMIN |
386 GNUNET_GE_ERROR | GNUNET_GE_BULK, "open",
392 GNUNET_GE_BREAK (ectx, collectionData->file_count <= 1024 * 1024 * 4);
393 WRITE (fd, collectionData, sizeof (CollectionData));
394 WRITEINT (fd, collectionData->file_count);
396 WRITE (fd, buf, mlen);
398 for (i = 0; i < collectionData->file_count; i++)
401 GNUNET_meta_data_get_serialized_size (collectionData->files[i].meta,
403 buf = GNUNET_malloc (mlen);
404 if (mlen != GNUNET_meta_data_serialize (ectx,
405 collectionData->files[i].meta,
406 buf, mlen, GNUNET_NO))
408 GNUNET_GE_BREAK (ectx, 0);
412 tmp = GNUNET_ECRS_uri_to_string (collectionData->files[i].uri);
413 WRITEINT (fd, strlen (tmp));
415 GNUNET_GE_BREAK (ectx, strlen (tmp) < 16 * 1024);
416 WRITE (fd, tmp, strlen (tmp));
418 WRITE (fd, buf, mlen);
426 free_collection_data ()
430 if (collectionData == NULL)
432 GNUNET_meta_data_destroy (collectionData->meta);
433 for (i = 0; i < collectionData->file_count; i++)
435 GNUNET_meta_data_destroy (collectionData->files[i].meta);
436 GNUNET_ECRS_uri_destroy (collectionData->files[i].uri);
438 GNUNET_array_grow (collectionData->files, collectionData->file_count, 0);
439 GNUNET_free (collectionData);
440 collectionData = NULL;
444 * Shutdown collection module.
450 free_collection_data ();
451 GNUNET_mutex_destroy (lock);
461 * @param updateInterval of GNUNET_ECRS_SBLOCK_UPDATE_NONE
462 * means to update _immediately_ on any change,
463 * wherease GNUNET_ECRS_SBLOCK_UPDATE_SPORADIC means
464 * to publish updates when the CO_Context
465 * is destroyed (i.e. on exit from the UI).
468 GNUNET_CO_collection_start (uint32_t anonymityLevel,
470 const struct GNUNET_MetaData *meta)
472 struct GNUNET_ECRS_URI *advertisement;
473 struct GNUNET_ECRS_URI *rootURI;
474 const char *root = COLLECTION_ROOT;
476 GNUNET_mutex_lock (lock);
477 GNUNET_CO_collection_stop (); /* cancel old collection */
478 advertisement = GNUNET_ECRS_keyword_string_to_uri (ectx, COLLECTION);
479 GNUNET_GE_ASSERT (ectx, advertisement != NULL);
480 rootURI = GNUNET_ECRS_namespace_create (ectx,
486 COLLECTION_ADV_LIFETIME,
487 advertisement, root);
488 GNUNET_ECRS_uri_destroy (advertisement);
491 GNUNET_mutex_unlock (lock);
492 return GNUNET_SYSERR;
494 collectionData = GNUNET_malloc (sizeof (CollectionInfo));
495 memset (collectionData, 0, sizeof (CollectionInfo));
496 GNUNET_ECRS_uri_get_namespace_from_sks (rootURI, &collectionData->data.pid);
497 GNUNET_ECRS_uri_destroy (rootURI);
498 collectionData->data.priority = htonl (prio);
499 collectionData->data.anonymityLevel = htonl (anonymityLevel);
500 collectionData->meta = GNUNET_meta_data_duplicate (meta);
501 GNUNET_mutex_unlock (lock);
508 * @return GNUNET_OK on success, GNUNET_SYSERR if no collection is active
511 GNUNET_CO_collection_stop ()
513 GNUNET_mutex_lock (lock);
514 if (collectionData == NULL)
516 GNUNET_mutex_unlock (lock);
517 return GNUNET_SYSERR;
519 GNUNET_ECRS_namespace_delete (ectx, cfg, &collectionData->data.pid);
520 free_collection_data ();
521 GNUNET_mutex_unlock (lock);
526 * Are we using a collection?
528 * @return NULL if there is no collection, otherwise its metadata
530 struct GNUNET_MetaData *
531 GNUNET_CO_collection_get_name ()
533 struct GNUNET_MetaData *meta;
535 GNUNET_mutex_lock (lock);
536 if (collectionData == NULL)
538 GNUNET_mutex_unlock (lock);
541 meta = GNUNET_meta_data_duplicate (collectionData->meta);
542 GNUNET_mutex_unlock (lock);
547 * Publish an update of the current collection information to the
548 * network now. The function has no effect if the collection has not
549 * changed since the last publication. If we are currently not
550 * collecting, this function does nothing.
553 GNUNET_CO_collection_publish_now ()
555 struct GNUNET_ECRS_URI *uri;
556 struct GNUNET_ECRS_URI *directoryURI;
557 unsigned long long dirLen;
561 char this_revision_string[128];
562 char next_revision_string[128];
565 GNUNET_mutex_lock (lock);
566 if ((collectionData == NULL) ||
567 (ntohl (collectionData->data.changed) == GNUNET_NO))
569 GNUNET_mutex_unlock (lock);
573 tmpdir = getenv ("TMPDIR");
574 tmpdir = tmpdir ? tmpdir : "/tmp";
576 #define TEMPLATE "/gnunet-collectionXXXXXX"
577 tmpName = GNUNET_malloc (strlen (tmpdir) + sizeof (TEMPLATE) + 1);
578 strcpy (tmpName, tmpdir);
579 strcat (tmpName, TEMPLATE);
581 fd = mkstemp (tmpName);
584 GNUNET_GE_LOG_STRERROR (ectx,
585 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
586 GNUNET_GE_BULK, "mkstemp");
587 GNUNET_free (tmpName);
588 GNUNET_mutex_unlock (lock);
592 GNUNET_GE_ASSERT (ectx,
593 GNUNET_OK == GNUNET_ECRS_directory_create (ectx,
596 collectionData->file_count,
597 collectionData->files,
598 collectionData->meta));
599 if (-1 == WRITE (fd, dirData, dirLen))
601 GNUNET_GE_LOG_STRERROR (ectx,
602 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
603 GNUNET_GE_BULK, "write");
604 GNUNET_free (tmpName);
605 GNUNET_free (dirData);
606 GNUNET_mutex_unlock (lock);
609 GNUNET_free (dirData);
611 if (GNUNET_OK != GNUNET_ECRS_file_upload (ectx, cfg, tmpName, GNUNET_NO, /* indexing */
612 ntohl (collectionData->
613 data.anonymityLevel),
614 ntohl (collectionData->
617 COLLECTION_ADV_LIFETIME, NULL,
618 NULL, NULL, NULL, &directoryURI))
621 GNUNET_free (tmpName);
622 GNUNET_mutex_unlock (lock);
626 GNUNET_free (tmpName);
627 if (ntohl (collectionData->data.revision) == 0)
628 strcpy (this_revision_string, COLLECTION_ROOT);
630 GNUNET_snprintf (this_revision_string,
631 sizeof (this_revision_string),
632 _("Revision %u"), ntohl (collectionData->data.revision));
633 GNUNET_snprintf (next_revision_string,
634 sizeof (next_revision_string),
636 ntohl (collectionData->data.revision) + 1);
637 uri = GNUNET_ECRS_namespace_add_content (ectx,
639 &collectionData->data.pid,
640 ntohl (collectionData->
641 data.anonymityLevel),
642 ntohl (collectionData->
645 COLLECTION_ADV_LIFETIME,
646 this_revision_string,
647 next_revision_string, directoryURI,
648 collectionData->meta);
651 collectionData->data.revision =
652 htonl (ntohl (collectionData->data.revision) + 1);
653 collectionData->data.changed = htonl (GNUNET_NO);
654 GNUNET_ECRS_uri_destroy (uri);
656 GNUNET_mutex_unlock (lock);
660 * If we are currently building a collection, publish
661 * the given file information in that collection.
662 * If we are currently not collecting, this function
666 GNUNET_CO_collection_add_item (const GNUNET_ECRS_FileInfo * fi)
669 GNUNET_ECRS_FileInfo fc;
671 if ((GNUNET_ECRS_uri_test_ksk (fi->uri)))
673 GNUNET_GE_BREAK (ectx, 0);
678 GNUNET_GE_BREAK (ectx, 0);
681 GNUNET_mutex_lock (lock);
682 if (collectionData == NULL)
684 GNUNET_mutex_unlock (lock);
687 for (i = 0; i < collectionData->file_count; i++)
689 if (GNUNET_ECRS_uri_test_equal (fi->uri, collectionData->files[i].uri))
691 GNUNET_mutex_unlock (lock);
695 fc.uri = GNUNET_ECRS_uri_duplicate (fi->uri);
696 fc.meta = GNUNET_meta_data_duplicate (fi->meta);
697 GNUNET_array_append (collectionData->files, collectionData->file_count, fc);
698 collectionData->data.changed = htonl (GNUNET_YES);
699 GNUNET_mutex_unlock (lock);
704 /* end of fs_collection.c */