2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 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_publish.c
23 * @brief publish a file or directory in GNUnet
24 * @see http://gnunet.org/encoding.php3
25 * @author Krista Bennett
26 * @author Christian Grothoff
30 #include "gnunet_util_lib.h"
31 #include "gnunet_fs_service.h"
34 #define DEBUG_PUBLISH GNUNET_YES
38 * Main function that performs the upload.
39 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
40 * @param tc task context
44 const struct GNUNET_SCHEDULER_TaskContext *tc)
46 struct GNUNET_FS_PublishContext *sc = cls;
48 sc->upload_task = GNUNET_SCHEDULER_NO_TASK;
50 // FIXME: find next block, process, schedule
51 // transmission to FS service
56 * Publish a file or directory.
58 * @param h handle to the file sharing subsystem
59 * @param ctx initial value to use for the '*ctx'
60 * in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
61 * @param fi information about the file or directory structure to publish
62 * @param namespace namespace to publish the file in, NULL for no namespace
63 * @param nid identifier to use for the publishd content in the namespace
64 * (can be NULL, must be NULL if namespace is NULL)
65 * @param nuid update-identifier that will be used for future updates
66 * (can be NULL, must be NULL if namespace or nid is NULL)
67 * @return context that can be used to control the publish operation
69 struct GNUNET_FS_PublishContext *
70 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
72 struct GNUNET_FS_FileInformation *fi,
73 struct GNUNET_FS_Namespace *namespace,
77 struct GNUNET_FS_PublishContext *ret;
79 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
81 ret->client_ctx = ctx;
83 ret->namespace = namespace;
84 if (namespace != NULL)
87 GNUNET_assert (NULL != nid);
88 ret->nid = GNUNET_strdup (nid);
90 ret->nuid = GNUNET_strdup (nuid);
92 // FIXME: make upload persistent!
94 = GNUNET_SCHEDULER_add_delayed (h->sched,
96 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
97 GNUNET_SCHEDULER_NO_TASK,
98 GNUNET_TIME_UNIT_ZERO,
106 * Stop an upload. Will abort incomplete uploads (but
107 * not remove blocks that have already been publishd) or
108 * simply clean up the state for completed uploads.
110 * @param sc context for the upload to stop
113 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
115 if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
116 GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
117 // FIXME: remove from persistence DB (?) --- think more about
118 // shutdown / persistent-resume APIs!!!
119 GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
120 GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
121 GNUNET_free_non_null (sc->nid);
122 GNUNET_free_non_null (sc->nuid);
130 * Append the given key and query to the iblock[level]. If
131 * iblock[level] is already full, compute its chk and push it to
132 * level+1 and clear the level. iblocks is guaranteed to be big
136 pushBlock (struct GNUNET_ClientServerConnection *sock,
137 const GNUNET_EC_ContentHashKey * chk,
139 GNUNET_DatastoreValue ** iblocks,
140 unsigned int prio, GNUNET_CronTime expirationTime)
143 unsigned int present;
144 GNUNET_DatastoreValue *value;
145 GNUNET_EC_DBlock *db;
146 GNUNET_EC_ContentHashKey ichk;
148 size = ntohl (iblocks[level]->size);
149 GNUNET_GE_ASSERT (NULL, size > sizeof (GNUNET_DatastoreValue));
150 size -= sizeof (GNUNET_DatastoreValue);
151 GNUNET_GE_ASSERT (NULL,
152 size - sizeof (GNUNET_EC_DBlock) <=
153 GNUNET_ECRS_IBLOCK_SIZE);
155 (size - sizeof (GNUNET_EC_DBlock)) / sizeof (GNUNET_EC_ContentHashKey);
156 db = (GNUNET_EC_DBlock *) & iblocks[level][1];
157 if (present == GNUNET_ECRS_CHK_PER_INODE)
159 GNUNET_EC_file_block_get_key (db, size, &ichk.key);
160 GNUNET_EC_file_block_get_query (db, size, &ichk.query);
161 if (GNUNET_OK != pushBlock (sock,
162 &ichk, level + 1, iblocks, prio,
164 return GNUNET_SYSERR;
165 GNUNET_EC_file_block_encode (db, size, &ichk.query, &value);
168 GNUNET_GE_BREAK (NULL, 0);
169 return GNUNET_SYSERR;
171 value->priority = htonl (prio);
172 value->expiration_time = GNUNET_htonll (expirationTime);
173 if (GNUNET_OK != GNUNET_FS_insert (sock, value))
176 return GNUNET_SYSERR;
179 size = sizeof (GNUNET_EC_DBlock); /* type */
181 /* append GNUNET_EC_ContentHashKey */
182 memcpy (&((char *) db)[size], chk, sizeof (GNUNET_EC_ContentHashKey));
183 size += sizeof (GNUNET_EC_ContentHashKey) + sizeof (GNUNET_DatastoreValue);
184 GNUNET_GE_ASSERT (NULL, size < GNUNET_MAX_BUFFER_SIZE);
185 iblocks[level]->size = htonl (size);
191 * Index or insert a file.
193 * @param priority what is the priority for OUR node to
194 * keep this file available? Use 0 for maximum anonymity and
195 * minimum reliability...
196 * @param doIndex GNUNET_YES for index, GNUNET_NO for insertion,
197 * GNUNET_SYSERR for simulation
198 * @param uri set to the URI of the uploaded file
199 * @return GNUNET_SYSERR if the upload failed (i.e. not enough space
200 * or gnunetd not running)
203 GNUNET_ECRS_file_upload (struct GNUNET_GE_Context *ectx,
204 struct GNUNET_GC_Configuration *cfg,
205 const char *filename,
207 unsigned int anonymityLevel,
208 unsigned int priority,
209 GNUNET_CronTime expirationTime,
210 GNUNET_ECRS_UploadProgressCallback upcb,
212 GNUNET_ECRS_TestTerminate tt,
213 void *ttClosure, struct GNUNET_ECRS_URI **uri)
215 unsigned long long filesize;
216 unsigned long long pos;
217 unsigned int treedepth;
222 GNUNET_DatastoreValue **iblocks;
223 GNUNET_DatastoreValue *dblock;
224 GNUNET_EC_DBlock *db;
225 GNUNET_DatastoreValue *value;
226 struct GNUNET_ClientServerConnection *sock;
227 GNUNET_HashCode fileId;
228 GNUNET_EC_ContentHashKey mchk;
230 GNUNET_CronTime start;
232 GNUNET_EC_FileIdentifier fid;
237 GNUNET_GE_ASSERT (ectx, cfg != NULL);
238 start = GNUNET_get_time ();
239 memset (&mchk, 0, sizeof (GNUNET_EC_ContentHashKey));
240 if (GNUNET_YES != GNUNET_disk_file_test (ectx, filename))
243 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
244 _("`%s' is not a file.\n"), filename);
245 return GNUNET_SYSERR;
248 GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES))
251 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
252 _("Cannot get size of file `%s'"), filename);
254 return GNUNET_SYSERR;
256 sock = GNUNET_client_connection_create (ectx, cfg);
260 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
261 _("Failed to connect to gnunetd."));
262 return GNUNET_SYSERR;
266 upcb (filesize, 0, eta, upcbClosure);
267 if (doIndex == GNUNET_YES)
269 if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId))
272 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
273 _("Cannot hash `%s'.\n"), filename);
275 GNUNET_client_connection_destroy (sock);
276 return GNUNET_SYSERR;
278 if (GNUNET_YES == GNUNET_FS_test_indexed (sock, &fileId))
280 /* file already indexed; simulate only to get the URI! */
281 doIndex = GNUNET_SYSERR;
284 if (doIndex == GNUNET_YES)
286 now = GNUNET_get_time ();
287 eta = now + 2 * (now - start);
288 /* very rough estimate: GNUNET_hash reads once through the file,
289 we'll do that once more and write it. But of course
290 the second read may be cached, and we have the encryption,
291 so a factor of two is really, really just a rough estimate */
293 /* reset the counter since the formula later does not
294 take the time for GNUNET_hash_file into account */
296 switch (GNUNET_FS_prepare_to_index (sock, &fileId, filename))
300 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
301 _("Initialization for indexing file `%s' failed.\n"),
303 GNUNET_client_connection_destroy (sock);
304 return GNUNET_SYSERR;
307 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
309 ("Indexing file `%s' failed. Suggestion: try to insert the file.\n"),
311 GNUNET_client_connection_destroy (sock);
312 return GNUNET_SYSERR;
317 treedepth = GNUNET_ECRS_compute_depth (filesize);
318 fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE);
322 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
323 _("Cannot open file `%s': `%s'"), filename,
326 GNUNET_client_connection_destroy (sock);
327 return GNUNET_SYSERR;
331 GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
332 sizeof (GNUNET_EC_DBlock));
334 htonl (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
335 sizeof (GNUNET_EC_DBlock));
336 dblock->anonymity_level = htonl (anonymityLevel);
337 dblock->priority = htonl (priority);
338 dblock->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
339 dblock->expiration_time = GNUNET_htonll (expirationTime);
340 db = (GNUNET_EC_DBlock *) & dblock[1];
341 db->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
343 GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1));
344 for (i = 0; i <= treedepth; i++)
347 GNUNET_malloc (sizeof (GNUNET_DatastoreValue) +
348 GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock));
350 htonl (sizeof (GNUNET_DatastoreValue) + sizeof (GNUNET_EC_DBlock));
351 iblocks[i]->anonymity_level = htonl (anonymityLevel);
352 iblocks[i]->priority = htonl (priority);
353 iblocks[i]->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
354 iblocks[i]->expiration_time = GNUNET_htonll (expirationTime);
355 ((GNUNET_EC_DBlock *) & iblocks[i][1])->type =
356 htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
360 while (pos < filesize)
363 upcb (filesize, pos, eta, upcbClosure);
365 if (GNUNET_OK != tt (ttClosure))
367 size = GNUNET_ECRS_DBLOCK_SIZE;
368 if (size > filesize - pos)
370 size = filesize - pos;
371 memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE);
373 GNUNET_GE_ASSERT (ectx,
374 sizeof (GNUNET_DatastoreValue) + size +
375 sizeof (GNUNET_EC_DBlock) < GNUNET_MAX_BUFFER_SIZE);
377 htonl (sizeof (GNUNET_DatastoreValue) + size +
378 sizeof (GNUNET_EC_DBlock));
379 if (size != READ (fd, &db[1], size))
381 GNUNET_GE_LOG_STRERROR_FILE (ectx,
382 GNUNET_GE_ERROR | GNUNET_GE_BULK |
383 GNUNET_GE_ADMIN | GNUNET_GE_USER,
388 if (GNUNET_OK != tt (ttClosure))
390 GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock),
392 GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock),
395 GNUNET_hash_to_enc (&mchk.query, &enc);
397 "Query for current block of size %u is `%s'\n", size,
398 (const char *) &enc);
400 if (doIndex == GNUNET_YES)
402 if (GNUNET_SYSERR == GNUNET_FS_index (sock, &fileId, dblock, pos))
405 GNUNET_GE_ERROR | GNUNET_GE_BULK |
408 ("Indexing data of file `%s' failed at position %llu.\n"),
417 GNUNET_EC_file_block_encode (db,
418 size + sizeof (GNUNET_EC_DBlock),
419 &mchk.query, &value))
421 GNUNET_GE_BREAK (ectx, 0);
424 GNUNET_GE_ASSERT (ectx, value != NULL);
425 *value = *dblock; /* copy options! */
426 if ((doIndex == GNUNET_NO) &&
427 (GNUNET_OK != (ret = GNUNET_FS_insert (sock, value))))
429 GNUNET_GE_BREAK (ectx, ret == GNUNET_NO);
436 now = GNUNET_get_time ();
439 eta = (GNUNET_CronTime) (start +
440 (((double) (now - start) / (double) pos))
441 * (double) filesize);
443 if (GNUNET_OK != pushBlock (sock, &mchk, 0, /* dblocks are on level 0 */
444 iblocks, priority, expirationTime))
448 if (GNUNET_OK != tt (ttClosure))
452 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
453 "Tree depth is %u, walking up tree.\n", treedepth);
455 for (i = 0; i < treedepth; i++)
457 size = ntohl (iblocks[i]->size) - sizeof (GNUNET_DatastoreValue);
458 GNUNET_GE_ASSERT (ectx, size < GNUNET_MAX_BUFFER_SIZE);
459 if (size == sizeof (GNUNET_EC_DBlock))
463 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
464 "Level %u is empty\n", i);
468 db = (GNUNET_EC_DBlock *) & iblocks[i][1];
469 GNUNET_EC_file_block_get_key (db, size, &mchk.key);
472 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
473 "Computing query for %u bytes content.\n", size);
475 GNUNET_EC_file_block_get_query (db, size, &mchk.query);
478 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
479 GNUNET_hash_to_enc (&mchk.query, &enc));
481 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
482 "Query for current block at level %u is `%s'.\n", i,
485 if (GNUNET_OK != pushBlock (sock,
486 &mchk, i + 1, iblocks, priority,
489 GNUNET_GE_BREAK (ectx, 0);
492 GNUNET_EC_file_block_encode (db, size, &mchk.query, &value);
495 GNUNET_GE_BREAK (ectx, 0);
498 value->expiration_time = GNUNET_htonll (expirationTime);
499 value->priority = htonl (priority);
500 if ((doIndex != GNUNET_SYSERR) &&
501 (GNUNET_SYSERR == GNUNET_FS_insert (sock, value)))
503 GNUNET_GE_BREAK (ectx, 0);
508 GNUNET_free (iblocks[i]);
513 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
514 GNUNET_hash_to_enc (&mchk.query, &enc));
515 GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
516 "Query for top block is %s\n", &enc);
519 fid.file_length = GNUNET_htonll (filesize);
520 db = (GNUNET_EC_DBlock *) & iblocks[treedepth][1];
522 fid.chk = *(GNUNET_EC_ContentHashKey *) & (db[1]);
523 *uri = GNUNET_malloc (sizeof (URI));
525 (*uri)->data.fi = fid;
528 GNUNET_free_non_null (iblocks[treedepth]);
529 GNUNET_free (iblocks);
530 GNUNET_free (dblock);
532 upcb (filesize, filesize, eta, upcbClosure);
534 GNUNET_client_connection_destroy (sock);
537 for (i = 0; i <= treedepth; i++)
538 GNUNET_free_non_null (iblocks[i]);
539 GNUNET_free (iblocks);
540 GNUNET_free (dblock);
542 GNUNET_client_connection_destroy (sock);
543 return GNUNET_SYSERR;
548 /* end of fs_publish.c */