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
29 * - directory creation
33 * - calling of progress function
34 * - handling of IO errors (emsg)
35 * - code-sharing with unindex
36 * - datastore reservation support
37 * - persistence support
41 #include "gnunet_constants.h"
42 #include "gnunet_util_lib.h"
43 #include "gnunet_fs_service.h"
46 #define DEBUG_PUBLISH GNUNET_YES
49 * Main function that performs the upload.
50 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
51 * @param tc task context
55 const struct GNUNET_SCHEDULER_TaskContext *tc);
59 * Context for "ds_put_cont".
64 * Publishing context for which the datastore
65 * PUT request was executed.
67 struct GNUNET_FS_PublishContext *sc;
70 * Specific file with the block.
72 struct GNUNET_FS_FileInformation *p;
75 * Function to run next, if any (can be NULL).
77 GNUNET_SCHEDULER_Task cont;
81 * Function called by the datastore API with
82 * the result from the PUT request.
84 * @param cls our closure
85 * @param success GNUNET_OK on success
86 * @param msg error message (or NULL)
89 ds_put_cont (void *cls,
93 struct PutContCtx *pcc = cls;
95 if (GNUNET_OK != success)
97 // FIXME: call progress CB with error
98 // FIXME: update pcc->p to indicate abort
99 GNUNET_FS_file_information_sync (pcc->p);
102 GNUNET_FS_file_information_sync (pcc->p);
103 if (NULL != pcc->cont)
105 = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
107 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
108 GNUNET_SCHEDULER_NO_TASK,
109 GNUNET_TIME_UNIT_ZERO,
117 * We need to publish a specific block. Do it. Then continue with
120 * @param sc overall upload data
121 * @param p file that the block belongs to (needed for options!)
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,
133 GNUNET_SCHEDULER_Task cont)
135 struct GNUNET_HashCode key;
137 // FIXME: GNUNET_FS_get_key (blk_type, blk, blk_size, &key);
138 // (or add "key" as argument to reduce hashing?)
139 dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
140 dpc_cls->cont = cont;
143 // FIXME: need to do something to "sc" to mark
144 // that "sc" can not be freed right now due to this
145 // pending, scheduled operation for which we don't have
147 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,
285 bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
287 for (i=0;i<height;i++)
288 bds *= GNUNET_FS_CHK_PER_INODE;
289 GNUNET_assert (0 == (offset % bds));
291 return ret % GNUNET_FS_CHK_PER_INODE;
296 * We are uploading a file or directory; load (if necessary) the next
297 * block into memory, encrypt it and send it to the FS service. Then
298 * continue with the main task.
300 * @param sc overall upload data
301 * @param p specific file or directory for which kblocks
305 publish_content (struct GNUNET_FS_PublishContext *sc,
306 struct GNUNET_FS_FileInformation *p)
308 struct ContentHashKey *chk;
309 const void *pt_block;
312 char iob[GNUNET_FS_DBLOCK_SIZE];
313 char enc[GNUNET_FS_DBLOCK_SIZE];
314 struct GNUNET_CRYPTO_AesSessionKey sk;
315 struct GNUNET_CRYPTO_AesInitializationVector iv;
319 // FIXME: figure out how to share this code
321 size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
322 if (NULL == p->chk_tree)
326 /* FIXME: create function to create directory
327 and use that API here! */
328 GNUNET_FS_directory_create (&p->data.dir.dir_size,
329 &p->data.dir.dir_data,
331 &directory_entry_lister,
332 p->data.dir.entries);
333 size = p->data.dir.data_size;
335 p->chk_tree_depth = compute_depth (size);
336 p->chk_tree = GNUNET_malloc (p->chk_tree_depth *
337 sizeof (struct ContentHashKey) *
338 GNUNET_FS_CHK_PER_INODE);
339 p->current_depth = p->chk_tree_depth;
341 if (p->current_depth == p->chk_tree_depth)
345 pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
346 p->data.dir.dir_size - p->publish_offset);
347 pt_block = &p->data.dir.dir_data[p->publish_offset];
351 pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
352 p->data.file.file_size - p->publish_offset);
353 p->data.file.reader (p->data.file.reader_cls,
363 pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
365 pt_block = &p->chk_tree[p->current_depth *
366 GNUNET_FS_CHK_PER_INODE];
368 off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
370 chk = &p->chk_tree[(p->current_depth-1)*GNUNET_FS_CHK_PER_INODE+off];
371 GNUNET_CRYPTO_hash (pt_block, pt_size, &chk->key);
372 GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
373 GNUNET_CRYPTO_aes_encrypt (pt_block,
378 // NOTE: this call (and progress below) is all that really differs
379 // between publish/unindex! Parameterize & move this code!
380 // FIXME: something around here would need to change
382 publish_block (sc, p, enc, pt_size,
383 (p->current_depth == p->chk_tree_depth)
384 ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK
385 : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
387 // FIXME: should call progress function somewhere here!
388 GNUNET_CRYPTO_hash (enc, pt_size, &chk->query);
389 if (p->current_depth == p->chk_tree_depth)
391 p->publish_offset += pt_size;
392 if ( (p->publish_offset == size) ||
393 (0 == p->publish_offset % (GNUNET_FS_CHK_PER_INODE * GNUNET_FS_DBLOCK_SIZE) ) )
398 if ( (off == GNUNET_FS_CHK_PER_INODE) ||
399 (p->publish_offset == size) )
402 p->current_depth = p->chk_tree_depth;
404 if (0 == p->current_depth)
406 p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
407 p->chk_uri.type = chk;
408 p->chk_uri.data.chk.chk = p->chk_tree[0];
409 p->chk_uri.data.chk.file_length = size;
410 GNUNET_free (p->chk_tree);
417 * Main function that performs the upload.
418 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
419 * @param tc task context
422 do_upload (void *cls,
423 const struct GNUNET_SCHEDULER_TaskContext *tc)
425 struct GNUNET_FS_PublishContext *sc = cls;
426 struct GNUNET_FS_FileInformation *p;
428 sc->upload_task = GNUNET_SCHEDULER_NO_TASK;
432 /* upload of entire hierarchy complete,
433 publish namespace entries */
437 if (NULL != p->chk_uri)
439 /* move on to next file */
441 sc->fi_pos = p->next;
444 /* upload of "p" complete, publish KBlocks! */
445 publish_kblocks (sc, p);
450 // FIXME: need to pre-compute hash over
451 // the entire file and ask FS to prepare
455 publish_content (sc, p);
460 * Publish a file or directory.
462 * @param h handle to the file sharing subsystem
463 * @param ctx initial value to use for the '*ctx'
464 * in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
465 * @param fi information about the file or directory structure to publish
466 * @param namespace namespace to publish the file in, NULL for no namespace
467 * @param nid identifier to use for the publishd content in the namespace
468 * (can be NULL, must be NULL if namespace is NULL)
469 * @param nuid update-identifier that will be used for future updates
470 * (can be NULL, must be NULL if namespace or nid is NULL)
471 * @return context that can be used to control the publish operation
473 struct GNUNET_FS_PublishContext *
474 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
476 struct GNUNET_FS_FileInformation *fi,
477 struct GNUNET_FS_Namespace *namespace,
481 struct GNUNET_FS_PublishContext *ret;
482 struct GNUNET_FS_FileInformation *p;
483 struct GNUNET_DATASTORE_Handle *dsh;
485 dsh = GNUNET_DATASTORE_connect (h->cfg,
489 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
492 ret->client_ctx = ctx;
494 ret->namespace = namespace;
495 if (namespace != NULL)
498 GNUNET_assert (NULL != nid);
499 ret->nid = GNUNET_strdup (nid);
501 ret->nuid = GNUNET_strdup (nuid);
503 // FIXME: make upload persistent!
505 /* find first leaf, DFS */
507 while ( (p->is_directory) &&
508 (NULL != p->data.dir.entries) )
509 p = p->data.dir.entries;
512 // FIXME: calculate space needed for "fi"
513 // and reserve as first task (then trigger
514 // "do_upload" from that continuation)!
516 = GNUNET_SCHEDULER_add_delayed (h->sched,
518 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
519 GNUNET_SCHEDULER_NO_TASK,
520 GNUNET_TIME_UNIT_ZERO,
528 * Stop an upload. Will abort incomplete uploads (but
529 * not remove blocks that have already been publishd) or
530 * simply clean up the state for completed uploads.
532 * @param sc context for the upload to stop
535 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
537 if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
538 GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
539 // FIXME: remove from persistence DB (?) --- think more about
540 // shutdown / persistent-resume APIs!!!
541 GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
542 GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
543 GNUNET_free_non_null (sc->nid);
544 GNUNET_free_non_null (sc->nuid);
545 GNUNET_DATASTORE_disconnect (sc->dsh);
549 /* end of fs_publish.c */