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/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
32 * - calling of progress function
33 * - handling of IO errors (emsg)
34 * - code-sharing with unindex
35 * - datastore reservation support
36 * - persistence support
40 #include "gnunet_constants.h"
41 #include "gnunet_util_lib.h"
42 #include "gnunet_fs_service.h"
45 #define DEBUG_PUBLISH GNUNET_YES
48 * Main function that performs the upload.
49 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
50 * @param tc task context
54 const struct GNUNET_SCHEDULER_TaskContext *tc);
58 * Context for "ds_put_cont".
63 * Publishing context for which the datastore
64 * PUT request was executed.
66 struct GNUNET_FS_PublishContext *sc;
69 * Specific file with the block.
71 struct GNUNET_FS_FileInformation *p;
74 * Function to run next, if any (can be NULL).
76 GNUNET_SCHEDULER_Task cont;
80 * Function called by the datastore API with
81 * the result from the PUT request.
83 * @param cls our closure
84 * @param success GNUNET_OK on success
85 * @param msg error message (or NULL)
88 ds_put_cont (void *cls,
92 struct PutContCtx *pcc = cls;
94 if (GNUNET_OK != success)
96 // FIXME: call progress CB with error
97 // FIXME: update pcc->p to indicate abort
98 GNUNET_FS_file_information_sync (pcc->p);
101 GNUNET_FS_file_information_sync (pcc->p);
102 if (NULL != pcc->cont)
104 = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
106 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
107 GNUNET_SCHEDULER_NO_TASK,
108 GNUNET_TIME_UNIT_ZERO,
116 * We need to publish a specific block. Do it. Then continue with
119 * @param sc overall upload data
120 * @param p file that the block belongs to (needed for options!)
121 * @param query what the block should be indexed under
122 * @param blk encoded block to publish
123 * @param blk_size size of the block
124 * @param blk_type type of the block
125 * @param cont function to run when done
128 publish_block (struct GNUNET_FS_PublishContext *sc,
129 struct GNUNET_FS_FileInformation *p,
130 const GNUNET_HashCode *query,
134 GNUNET_SCHEDULER_Task cont)
136 struct PutContCtx * dpc_cls;
138 dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
139 dpc_cls->cont = cont;
142 // FIXME: need to do something to "sc" to mark
143 // that "sc" can not be freed right now due to this
144 // pending, scheduled operation for which we don't have
146 GNUNET_DATASTORE_put (sc->dsh,
155 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
162 * We are almost done publishing the structure,
163 * add SBlocks (if needed).
165 * @param sc overall upload data
168 publish_sblock (struct GNUNET_FS_PublishContext *sc)
170 struct GNUNET_FS_FileInformation *p;
173 // FIXME: build sblock & call publish_block!
175 // FIXME: continuation should
176 // be releasing the datastore reserve
177 // (once implemented)
182 * We have uploaded a file or directory; now publish
183 * the KBlocks in the global keyword space so that
184 * it can be found. Then continue with the
187 * @param sc overall upload data
188 * @param p specific file or directory for which kblocks
192 publish_kblocks (struct GNUNET_FS_PublishContext *sc,
193 struct GNUNET_FS_FileInformation *p)
195 // FIXME: build all kblocks
196 // call publish_kblock on each
197 // last continuation should then call the main continuation again
202 * Compute the depth of the CHK tree.
204 * @param flen file length for which to compute the depth
205 * @return depth of the tree
208 compute_depth (uint64_t flen)
210 unsigned int treeDepth;
214 fl = GNUNET_FS_DBLOCK_SIZE;
218 if (fl * GNUNET_FS_CHK_PER_INODE < fl)
220 /* integer overflow, this is a HUGE file... */
223 fl = fl * GNUNET_FS_CHK_PER_INODE;
230 * Compute the size of the current IBlock.
232 * @param height height of the IBlock in the tree (aka overall
233 * number of tree levels minus depth); 0 == DBlock
234 * @param offset current offset in the overall file
235 * @return size of the corresponding IBlock
238 compute_iblock_size (unsigned int height,
246 GNUNET_assert (height > 0);
247 bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
249 for (i=0;i<height;i++)
250 bds *= GNUNET_FS_CHK_PER_INODE;
254 /* we were triggered at the end of a full block */
255 ret = GNUNET_FS_CHK_PER_INODE;
259 /* we were triggered at the end of the file */
260 bds /= GNUNET_FS_CHK_PER_INODE;
265 return (uint16_t) (ret * sizeof(struct ContentHashKey));
270 * Compute the offset of the CHK for the
271 * current block in the IBlock above.
273 * @param height height of the IBlock in the tree (aka overall
274 * number of tree levels minus depth); 0 == DBlock
275 * @param offset current offset in the overall file
276 * @return (array of CHKs') offset in the above IBlock
279 compute_chk_offset (unsigned int height,
286 bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
288 for (i=0;i<height;i++)
289 bds *= GNUNET_FS_CHK_PER_INODE;
290 GNUNET_assert (0 == (offset % bds));
292 return ret % GNUNET_FS_CHK_PER_INODE;
297 * We are uploading a file or directory; load (if necessary) the next
298 * block into memory, encrypt it and send it to the FS service. Then
299 * continue with the main task.
301 * @param sc overall upload data
302 * @param p specific file or directory for which kblocks
306 publish_content (struct GNUNET_FS_PublishContext *sc,
307 struct GNUNET_FS_FileInformation *p)
309 struct ContentHashKey *mychk;
310 const void *pt_block;
313 char iob[GNUNET_FS_DBLOCK_SIZE];
314 char enc[GNUNET_FS_DBLOCK_SIZE];
315 struct GNUNET_CRYPTO_AesSessionKey sk;
316 struct GNUNET_CRYPTO_AesInitializationVector iv;
319 struct GNUNET_FS_DirectoryBuilder *db;
320 struct GNUNET_FS_FileInformation *dirpos;
324 // FIXME: figure out how to share this code
326 size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
327 if (NULL == p->chk_tree)
331 db = GNUNET_FS_directory_builder_create (p->meta);
332 dirpos = p->data.dir.entries;
333 while (NULL != dirpos)
335 if (dirpos->is_directory)
337 raw_data = dirpos->data.dir.dir_data;
338 dirpos->data.dir.dir_data = NULL;
343 if ( (dirpos->data.file.file_size < GNUNET_FS_MAX_INLINE_SIZE) &&
344 (dirpos->data.file.file_size > 0) )
346 raw_data = GNUNET_malloc (dirpos->data.file.file_size);
348 if (dirpos->data.file.file_size !=
349 dirpos->data.file.reader (dirpos->data.file.reader_cls,
351 dirpos->data.file.file_size,
355 GNUNET_free_non_null (emsg);
356 GNUNET_free (raw_data);
361 GNUNET_FS_directory_builder_add (db,
365 GNUNET_free_non_null (raw_data);
366 dirpos = dirpos->next;
368 GNUNET_FS_directory_builder_finish (db,
369 &p->data.dir.dir_size,
370 &p->data.dir.dir_data);
371 size = p->data.dir.dir_size;
373 p->chk_tree_depth = compute_depth (size);
374 p->chk_tree = GNUNET_malloc (p->chk_tree_depth *
375 sizeof (struct ContentHashKey) *
376 GNUNET_FS_CHK_PER_INODE);
377 p->current_depth = p->chk_tree_depth;
379 if (p->current_depth == p->chk_tree_depth)
383 pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
384 p->data.dir.dir_size - p->publish_offset);
385 dd = p->data.dir.dir_data;
386 pt_block = &dd[p->publish_offset];
390 pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
391 p->data.file.file_size - p->publish_offset);
394 p->data.file.reader (p->data.file.reader_cls,
400 // FIXME: abort with error "emsg"
408 pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
410 pt_block = &p->chk_tree[p->current_depth *
411 GNUNET_FS_CHK_PER_INODE];
413 off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
415 mychk = &p->chk_tree[(p->current_depth-1)*GNUNET_FS_CHK_PER_INODE+off];
416 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
417 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
418 GNUNET_CRYPTO_aes_encrypt (pt_block,
423 // NOTE: this call (and progress below) is all that really differs
424 // between publish/unindex! Parameterize & move this code!
425 // FIXME: something around here would need to change
427 publish_block (sc, p,
431 (p->current_depth == p->chk_tree_depth)
432 ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK
433 : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
435 // FIXME: should call progress function somewhere here!
436 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
437 if (p->current_depth == p->chk_tree_depth)
439 p->publish_offset += pt_size;
440 if ( (p->publish_offset == size) ||
441 (0 == p->publish_offset % (GNUNET_FS_CHK_PER_INODE * GNUNET_FS_DBLOCK_SIZE) ) )
446 if ( (off == GNUNET_FS_CHK_PER_INODE) ||
447 (p->publish_offset == size) )
450 p->current_depth = p->chk_tree_depth;
452 if (0 == p->current_depth)
454 p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
455 p->chk_uri->type = chk;
456 p->chk_uri->data.chk.chk = p->chk_tree[0];
457 p->chk_uri->data.chk.file_length = size;
458 GNUNET_free (p->chk_tree);
465 * Main function that performs the upload.
466 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
467 * @param tc task context
470 do_upload (void *cls,
471 const struct GNUNET_SCHEDULER_TaskContext *tc)
473 struct GNUNET_FS_PublishContext *sc = cls;
474 struct GNUNET_FS_FileInformation *p;
476 sc->upload_task = GNUNET_SCHEDULER_NO_TASK;
480 /* upload of entire hierarchy complete,
481 publish namespace entries */
485 if (NULL != p->chk_uri)
487 /* move on to next file */
489 sc->fi_pos = p->next;
492 /* upload of "p" complete, publish KBlocks! */
493 publish_kblocks (sc, p);
496 if ( (!p->is_directory) &&
497 (p->data.file.do_index) )
499 // FIXME: need to pre-compute hash over
500 // the entire file and ask FS to prepare
504 publish_content (sc, p);
509 * Publish a file or directory.
511 * @param h handle to the file sharing subsystem
512 * @param ctx initial value to use for the '*ctx'
513 * in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
514 * @param fi information about the file or directory structure to publish
515 * @param namespace namespace to publish the file in, NULL for no namespace
516 * @param nid identifier to use for the publishd content in the namespace
517 * (can be NULL, must be NULL if namespace is NULL)
518 * @param nuid update-identifier that will be used for future updates
519 * (can be NULL, must be NULL if namespace or nid is NULL)
520 * @return context that can be used to control the publish operation
522 struct GNUNET_FS_PublishContext *
523 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
525 struct GNUNET_FS_FileInformation *fi,
526 struct GNUNET_FS_Namespace *namespace,
530 struct GNUNET_FS_PublishContext *ret;
531 struct GNUNET_FS_FileInformation *p;
532 struct GNUNET_DATASTORE_Handle *dsh;
534 dsh = GNUNET_DATASTORE_connect (h->cfg,
538 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
541 ret->client_ctx = ctx;
543 ret->namespace = namespace;
544 if (namespace != NULL)
547 GNUNET_assert (NULL != nid);
548 ret->nid = GNUNET_strdup (nid);
550 ret->nuid = GNUNET_strdup (nuid);
552 // FIXME: make upload persistent!
554 /* find first leaf, DFS */
556 while ( (p->is_directory) &&
557 (NULL != p->data.dir.entries) )
558 p = p->data.dir.entries;
561 // FIXME: calculate space needed for "fi"
562 // and reserve as first task (then trigger
563 // "do_upload" from that continuation)!
565 = GNUNET_SCHEDULER_add_delayed (h->sched,
567 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
568 GNUNET_SCHEDULER_NO_TASK,
569 GNUNET_TIME_UNIT_ZERO,
577 * Stop an upload. Will abort incomplete uploads (but
578 * not remove blocks that have already been publishd) or
579 * simply clean up the state for completed uploads.
581 * @param sc context for the upload to stop
584 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
586 if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
587 GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
588 // FIXME: remove from persistence DB (?) --- think more about
589 // shutdown / persistent-resume APIs!!!
590 GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
591 GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
592 GNUNET_free_non_null (sc->nid);
593 GNUNET_free_non_null (sc->nuid);
594 GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
598 /* end of fs_publish.c */