aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-09-02 08:24:20 +0000
committerChristian Grothoff <christian@grothoff.org>2009-09-02 08:24:20 +0000
commit09118c85cd5200267784985900e4f83ea31b8622 (patch)
tree53fcbc1b6a38c3f4582769276243c485a7b98dff /src
parent815c76f4aeb141fa9654bc3abc16998c8188268f (diff)
downloadgnunet-09118c85cd5200267784985900e4f83ea31b8622.tar.gz
gnunet-09118c85cd5200267784985900e4f83ea31b8622.zip
refactoring publishing code
Diffstat (limited to 'src')
-rw-r--r--src/fs/Makefile.am1
-rw-r--r--src/fs/fs.h13
-rw-r--r--src/fs/fs_file_information.c2
-rw-r--r--src/fs/fs_publish.c450
-rw-r--r--src/fs/fs_tree.c381
-rw-r--r--src/fs/fs_tree.h167
6 files changed, 751 insertions, 263 deletions
diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am
index ebe6790c9..d4da86e6d 100644
--- a/src/fs/Makefile.am
+++ b/src/fs/Makefile.am
@@ -23,6 +23,7 @@ libgnunetfs_la_SOURCES = \
23 fs_publish.c \ 23 fs_publish.c \
24 fs_namespace.c \ 24 fs_namespace.c \
25 fs_search.c \ 25 fs_search.c \
26 fs_tree.c fs_tree.h \
26 fs_unindex.c \ 27 fs_unindex.c \
27 fs_uri.c 28 fs_uri.c
28 29
diff --git a/src/fs/fs.h b/src/fs/fs.h
index 956048cc5..c8712b492 100644
--- a/src/fs/fs.h
+++ b/src/fs/fs.h
@@ -250,7 +250,12 @@ struct GNUNET_FS_FileInformation
250 * entries for the size of the file and 250 * entries for the size of the file and
251 * finally freed once the upload is complete. 251 * finally freed once the upload is complete.
252 */ 252 */
253 struct ContentHashKey *chk_tree; 253 // struct ContentHashKey *chk_tree;
254
255 /**
256 * Encoder being used to publish this file.
257 */
258 struct GNUNET_FS_TreeEncoder *te;
254 259
255 /** 260 /**
256 * Error message (non-NULL if this operation 261 * Error message (non-NULL if this operation
@@ -261,20 +266,20 @@ struct GNUNET_FS_FileInformation
261 /** 266 /**
262 * Number of entries in "chk_tree". 267 * Number of entries in "chk_tree".
263 */ 268 */
264 unsigned int chk_tree_depth; 269 // unsigned int chk_tree_depth;
265 270
266 /** 271 /**
267 * Depth in the CHK-tree at which we are 272 * Depth in the CHK-tree at which we are
268 * currently publishing. 0 is the root 273 * currently publishing. 0 is the root
269 * of the tree. 274 * of the tree.
270 */ 275 */
271 unsigned int current_depth; 276 // unsigned int current_depth;
272 277
273 /** 278 /**
274 * How many bytes of this file or directory have been 279 * How many bytes of this file or directory have been
275 * published so far? 280 * published so far?
276 */ 281 */
277 uint64_t publish_offset; 282 // uint64_t publish_offset;
278 283
279 /** 284 /**
280 * Data describing either the file or the directory. 285 * Data describing either the file or the directory.
diff --git a/src/fs/fs_file_information.c b/src/fs/fs_file_information.c
index 984d56f30..5de9cbeb8 100644
--- a/src/fs/fs_file_information.c
+++ b/src/fs/fs_file_information.c
@@ -701,7 +701,6 @@ GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
701 ent->next = dir->data.dir.entries; 701 ent->next = dir->data.dir.entries;
702 dir->data.dir.entries = ent; 702 dir->data.dir.entries = ent;
703 dir->data.dir.dir_size = 0; 703 dir->data.dir.dir_size = 0;
704 dir->publish_offset = 0;
705 GNUNET_FS_file_information_sync (ent); 704 GNUNET_FS_file_information_sync (ent);
706 GNUNET_FS_file_information_sync (dir); 705 GNUNET_FS_file_information_sync (dir);
707 return GNUNET_OK; 706 return GNUNET_OK;
@@ -824,7 +823,6 @@ GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
824 &fi->client_info); 823 &fi->client_info);
825 } 824 }
826 GNUNET_free_non_null (fi->emsg); 825 GNUNET_free_non_null (fi->emsg);
827 GNUNET_free_non_null (fi->chk_tree);
828 /* clean up serialization */ 826 /* clean up serialization */
829 if (0 != UNLINK (fi->serialization)) 827 if (0 != UNLINK (fi->serialization))
830 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 828 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
diff --git a/src/fs/fs_publish.c b/src/fs/fs_publish.c
index 0c950f38a..6080fae03 100644
--- a/src/fs/fs_publish.c
+++ b/src/fs/fs_publish.c
@@ -26,8 +26,8 @@
26 * @author Christian Grothoff 26 * @author Christian Grothoff
27 * 27 *
28 * TODO: 28 * TODO:
29 * - code-sharing with unindex (write unindex code, clean up new FIXME's)
29 * - indexing cleanup: unindex on failure (can wait) 30 * - indexing cleanup: unindex on failure (can wait)
30 * - code-sharing with unindex (can wait)
31 * - persistence support (can wait) 31 * - persistence support (can wait)
32 * - datastore reservation support (optimization) 32 * - datastore reservation support (optimization)
33 * - location URIs (publish with anonymity-level zero) 33 * - location URIs (publish with anonymity-level zero)
@@ -39,6 +39,7 @@
39#include "gnunet_util_lib.h" 39#include "gnunet_util_lib.h"
40#include "gnunet_fs_service.h" 40#include "gnunet_fs_service.h"
41#include "fs.h" 41#include "fs.h"
42#include "fs_tree.h"
42 43
43#define DEBUG_PUBLISH GNUNET_YES 44#define DEBUG_PUBLISH GNUNET_YES
44 45
@@ -110,12 +111,14 @@ make_publish_status (struct GNUNET_FS_ProgressInfo *pi,
110 = (NULL == p->dir) ? NULL : p->dir->client_info; 111 = (NULL == p->dir) ? NULL : p->dir->client_info;
111 pi->value.publish.size 112 pi->value.publish.size
112 = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size; 113 = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
114#if FIXME
113 pi->value.publish.eta 115 pi->value.publish.eta
114 = GNUNET_TIME_calculate_eta (p->start_time, 116 = GNUNET_TIME_calculate_eta (p->start_time,
115 p->publish_offset, 117 p->publish_offset,
116 pi->value.publish.size); 118 pi->value.publish.size);
117 pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
118 pi->value.publish.completed = p->publish_offset; 119 pi->value.publish.completed = p->publish_offset;
120#endif
121 pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
119 pi->value.publish.anonymity = p->anonymity; 122 pi->value.publish.anonymity = p->anonymity;
120} 123}
121 124
@@ -302,7 +305,8 @@ publish_sblock (struct GNUNET_FS_PublishContext *sc)
302 * the result and continue the larger 305 * the result and continue the larger
303 * upload. 306 * upload.
304 * 307 *
305 * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload 308 * @param cls the "struct GNUNET_FS_PublishContext*"
309 * of the larger upload
306 * @param uri URI of the published blocks 310 * @param uri URI of the published blocks
307 * @param emsg NULL on success, otherwise error message 311 * @param emsg NULL on success, otherwise error message
308 */ 312 */
@@ -346,98 +350,198 @@ publish_kblocks_cont (void *cls,
346} 350}
347 351
348 352
349/** 353// FIXME: document
350 * Compute the depth of the CHK tree. 354static size_t
351 * 355block_reader (void *cls,
352 * @param flen file length for which to compute the depth 356 uint64_t offset,
353 * @return depth of the tree 357 size_t max,
354 */ 358 void *buf,
355static unsigned int 359 char **emsg)
356compute_depth (uint64_t flen)
357{ 360{
358 unsigned int treeDepth; 361 struct GNUNET_FS_PublishContext *sc = cls;
359 uint64_t fl; 362 struct GNUNET_FS_FileInformation *p;
363 uint16_t pt_size;
364 const char *dd;
365
366 p = sc->fi_pos;
367 if (p->is_directory)
368 {
369 pt_size = GNUNET_MIN(max,
370 p->data.dir.dir_size - offset);
371 dd = p->data.dir.dir_data;
372 memcpy (&buf,
373 &dd[offset],
374 pt_size);
375 }
376 else
377 {
378 pt_size = GNUNET_MIN(max,
379 p->data.file.file_size - offset);
380 if (pt_size !=
381 p->data.file.reader (p->data.file.reader_cls,
382 offset,
383 pt_size,
384 buf,
385 emsg))
386 return 0;
387 }
388 return pt_size;
389}
360 390
361 treeDepth = 1; 391
362 fl = DBLOCK_SIZE; 392// FIXME: document
363 while (fl < flen) 393static void
394encode_cont (void *cls,
395 const struct GNUNET_SCHEDULER_TaskContext *tc)
396{
397 struct GNUNET_FS_PublishContext *sc = cls;
398 struct GNUNET_FS_FileInformation *p;
399 struct GNUNET_FS_ProgressInfo pi;
400 char *emsg;
401
402 p = sc->fi_pos;
403 GNUNET_FS_tree_encoder_finish (p->te,
404 &p->chk_uri,
405 &emsg);
406 p->te = NULL;
407 if (NULL != emsg)
364 { 408 {
365 treeDepth++; 409 GNUNET_asprintf (&p->emsg,
366 if (fl * CHK_PER_INODE < fl) 410 _("Upload failed: %s"),
367 { 411 emsg);
368 /* integer overflow, this is a HUGE file... */ 412 GNUNET_free (emsg);
369 return treeDepth; 413 GNUNET_FS_file_information_sync (p);
370 } 414 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
371 fl = fl * CHK_PER_INODE; 415 make_publish_status (&pi, sc, p);
416 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
417 pi.value.publish.specifics.error.message = p->emsg;
418 p->client_info
419 = sc->h->upcb (sc->h->upcb_cls,
420 &pi);
372 } 421 }
373 return treeDepth; 422 /* continue with main */
423 sc->upload_task
424 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
425 GNUNET_NO,
426 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
427 GNUNET_SCHEDULER_NO_TASK,
428 GNUNET_TIME_UNIT_ZERO,
429 &do_upload,
430 sc);
374} 431}
375 432
376 433
377/** 434/**
378 * Compute the size of the current IBlock. 435 * Function called asking for the current (encoded)
436 * block to be processed. After processing the
437 * client should either call "GNUNET_FS_tree_encode_next"
438 * or (on error) "GNUNET_FS_tree_encode_finish".
379 * 439 *
380 * @param height height of the IBlock in the tree (aka overall 440 * @param cls closure
381 * number of tree levels minus depth); 0 == DBlock 441 * @param query the query for the block (key for lookup in the datastore)
382 * @param offset current offset in the overall file 442 * @param type type of the block (IBLOCK or DBLOCK)
383 * @return size of the corresponding IBlock 443 * @param block the (encrypted) block
444 * @param block_size size of block (in bytes)
384 */ 445 */
385static uint16_t 446static void
386compute_iblock_size (unsigned int height, 447block_proc (void *cls,
387 uint64_t offset) 448 const GNUNET_HashCode *query,
449 uint64_t offset,
450 unsigned int type,
451 const void *block,
452 uint16_t block_size)
388{ 453{
389 unsigned int ret; 454 struct GNUNET_FS_PublishContext *sc = cls;
390 unsigned int i; 455 struct GNUNET_FS_FileInformation *p;
391 uint64_t mod; 456 struct PutContCtx * dpc_cls;
392 uint64_t bds; 457 struct OnDemandBlock odb;
393 458
394 GNUNET_assert (height > 0); 459 p = sc->fi_pos;
395 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i" 460 if (NULL == sc->dsh)
396 corresponds to */
397 for (i=0;i<height;i++)
398 bds *= CHK_PER_INODE;
399 mod = offset % bds;
400 if (0 == mod)
401 { 461 {
402 /* we were triggered at the end of a full block */ 462 sc->upload_task
403 ret = CHK_PER_INODE; 463 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
464 GNUNET_NO,
465 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
466 GNUNET_SCHEDULER_NO_TASK,
467 GNUNET_TIME_UNIT_ZERO,
468 &do_upload,
469 sc);
470 return;
404 } 471 }
405 else 472
473 GNUNET_assert (GNUNET_NO == sc->in_network_wait);
474 sc->in_network_wait = GNUNET_YES;
475 dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
476 dpc_cls->cont = &do_upload;
477 dpc_cls->cont_cls = sc;
478 dpc_cls->p = p;
479 if ( (p->is_directory) &&
480 (p->data.file.do_index) &&
481 (type == GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) )
406 { 482 {
407 /* we were triggered at the end of the file */ 483 odb.offset = offset;
408 bds /= CHK_PER_INODE; 484 odb.file_id = p->data.file.file_id;
409 ret = mod / bds; 485 GNUNET_DATASTORE_put (sc->dsh,
410 if (0 != mod % bds) 486 sc->rid,
411 ret++; 487 query,
488 sizeof(struct OnDemandBlock),
489 &odb,
490 GNUNET_DATASTORE_BLOCKTYPE_ONDEMAND,
491 p->priority,
492 p->anonymity,
493 p->expirationTime,
494 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
495 &ds_put_cont,
496 dpc_cls);
497 return;
412 } 498 }
413 return (uint16_t) (ret * sizeof(struct ContentHashKey)); 499 GNUNET_DATASTORE_put (sc->dsh,
500 sc->rid,
501 query,
502 block_size,
503 block,
504 type,
505 p->priority,
506 p->anonymity,
507 p->expirationTime,
508 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
509 &ds_put_cont,
510 dpc_cls);
414} 511}
415 512
416 513
417/** 514/**
418 * Compute the offset of the CHK for the 515 * Function called with information about our
419 * current block in the IBlock above. 516 * progress in computing the tree encoding.
420 * 517 *
421 * @param height height of the IBlock in the tree (aka overall 518 * @param cls closure
422 * number of tree levels minus depth); 0 == DBlock 519 * @param offset where are we in the file
423 * @param offset current offset in the overall file 520 * @param pt_block plaintext of the currently processed block
424 * @return (array of CHKs') offset in the above IBlock 521 * @param pt_size size of pt_block
522 * @param depth depth of the block in the tree
425 */ 523 */
426static unsigned int 524static void
427compute_chk_offset (unsigned int height, 525progress_proc (void *cls,
428 uint64_t offset) 526 uint64_t offset,
429{ 527 const void *pt_block,
430 uint64_t bds; 528 size_t pt_size,
431 unsigned int ret; 529 unsigned int depth)
432 unsigned int i; 530{
531 struct GNUNET_FS_PublishContext *sc = cls;
532 struct GNUNET_FS_FileInformation *p;
533 struct GNUNET_FS_ProgressInfo pi;
433 534
434 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i" 535 p = sc->fi_pos;
435 corresponds to */ 536 pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
436 for (i=0;i<height;i++) 537 make_publish_status (&pi, sc, p);
437 bds *= CHK_PER_INODE; 538 pi.value.publish.specifics.progress.data = pt_block;
438 GNUNET_assert (0 == (offset % bds)); 539 pi.value.publish.specifics.progress.offset = offset;
439 ret = offset / bds; 540 pi.value.publish.specifics.progress.data_len = pt_size;
440 return ret % CHK_PER_INODE; 541 // FIXME: add depth to pi
542 p->client_info
543 = sc->h->upcb (sc->h->upcb_cls,
544 &pi);
441} 545}
442 546
443 547
@@ -450,32 +554,18 @@ compute_chk_offset (unsigned int height,
450 * @param p specific file or directory for which kblocks 554 * @param p specific file or directory for which kblocks
451 * should be created 555 * should be created
452 */ 556 */
557// FIXME: "p" argument is not needed!
453static void 558static void
454publish_content (struct GNUNET_FS_PublishContext *sc, 559publish_content (struct GNUNET_FS_PublishContext *sc,
455 struct GNUNET_FS_FileInformation *p) 560 struct GNUNET_FS_FileInformation *p)
456{ 561{
457 struct GNUNET_FS_ProgressInfo pi;
458 struct ContentHashKey *mychk;
459 const void *pt_block;
460 uint16_t pt_size;
461 char *emsg; 562 char *emsg;
462 char iob[DBLOCK_SIZE];
463 char enc[DBLOCK_SIZE];
464 struct GNUNET_CRYPTO_AesSessionKey sk;
465 struct GNUNET_CRYPTO_AesInitializationVector iv;
466 uint64_t size;
467 unsigned int off;
468 struct GNUNET_FS_DirectoryBuilder *db; 563 struct GNUNET_FS_DirectoryBuilder *db;
469 struct GNUNET_FS_FileInformation *dirpos; 564 struct GNUNET_FS_FileInformation *dirpos;
470 void *raw_data; 565 void *raw_data;
471 char *dd; 566 uint64_t size;
472 struct PutContCtx * dpc_cls;
473 struct OnDemandBlock odb;
474 567
475 // FIXME: figure out how to share this code 568 if (NULL == p->te)
476 // with unindex!
477 size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
478 if (NULL == p->chk_tree)
479 { 569 {
480 if (p->is_directory) 570 if (p->is_directory)
481 { 571 {
@@ -509,7 +599,7 @@ publish_content (struct GNUNET_FS_PublishContext *sc,
509 } 599 }
510 } 600 }
511 } 601 }
512 GNUNET_FS_directory_builder_add (db, 602 GNUNET_FS_directory_builder_add (db,
513 dirpos->chk_uri, 603 dirpos->chk_uri,
514 dirpos->meta, 604 dirpos->meta,
515 raw_data); 605 raw_data);
@@ -519,177 +609,23 @@ publish_content (struct GNUNET_FS_PublishContext *sc,
519 GNUNET_FS_directory_builder_finish (db, 609 GNUNET_FS_directory_builder_finish (db,
520 &p->data.dir.dir_size, 610 &p->data.dir.dir_size,
521 &p->data.dir.dir_data); 611 &p->data.dir.dir_data);
522 size = p->data.dir.dir_size;
523 }
524 p->chk_tree_depth = compute_depth (size);
525 p->chk_tree = GNUNET_malloc (p->chk_tree_depth *
526 sizeof (struct ContentHashKey) *
527 CHK_PER_INODE);
528 p->current_depth = p->chk_tree_depth;
529 }
530 if (p->current_depth == p->chk_tree_depth)
531 {
532 if (p->is_directory)
533 {
534 pt_size = GNUNET_MIN(DBLOCK_SIZE,
535 p->data.dir.dir_size - p->publish_offset);
536 dd = p->data.dir.dir_data;
537 pt_block = &dd[p->publish_offset];
538 }
539 else
540 {
541 pt_size = GNUNET_MIN(DBLOCK_SIZE,
542 p->data.file.file_size - p->publish_offset);
543 emsg = NULL;
544 if (pt_size !=
545 p->data.file.reader (p->data.file.reader_cls,
546 p->publish_offset,
547 pt_size,
548 iob,
549 &emsg))
550 {
551 GNUNET_asprintf (&p->emsg,
552 _("Upload failed: %s"),
553 emsg);
554 GNUNET_free (emsg);
555 GNUNET_FS_file_information_sync (p);
556 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
557 make_publish_status (&pi, sc, p);
558 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
559 pi.value.publish.specifics.error.message = p->emsg;
560 p->client_info
561 = sc->h->upcb (sc->h->upcb_cls,
562 &pi);
563 /* continue with main (to propagate error up) */
564 sc->upload_task
565 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
566 GNUNET_NO,
567 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
568 GNUNET_SCHEDULER_NO_TASK,
569 GNUNET_TIME_UNIT_ZERO,
570 &do_upload,
571 sc);
572 return;
573 }
574 pt_block = iob;
575 } 612 }
613 size = (p->is_directory)
614 ? p->data.dir.dir_size
615 : p->data.file.file_size;
616 p->te = GNUNET_FS_tree_encoder_create (sc->h,
617 size,
618 sc,
619 &block_reader,
620 &block_proc,
621 &progress_proc,
622 &encode_cont);
623
576 } 624 }
577 else 625 GNUNET_FS_tree_encoder_next (p->te);
578 {
579 pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
580 p->publish_offset);
581 pt_block = &p->chk_tree[p->current_depth *
582 CHK_PER_INODE];
583 }
584 off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
585 p->publish_offset);
586 mychk = &p->chk_tree[(p->current_depth-1)*CHK_PER_INODE+off];
587 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
588 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
589 GNUNET_CRYPTO_aes_encrypt (pt_block,
590 pt_size,
591 &sk,
592 &iv,
593 enc);
594 // NOTE: this block below is all that really differs
595 // between publish/unindex! Parameterize & move this code!
596 if (NULL == sc->dsh)
597 {
598 sc->upload_task
599 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
600 GNUNET_NO,
601 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
602 GNUNET_SCHEDULER_NO_TASK,
603 GNUNET_TIME_UNIT_ZERO,
604 &do_upload,
605 sc);
606 }
607 else
608 {
609 GNUNET_assert (GNUNET_NO == sc->in_network_wait);
610 sc->in_network_wait = GNUNET_YES;
611 dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
612 dpc_cls->cont = &do_upload;
613 dpc_cls->cont_cls = sc;
614 dpc_cls->p = p;
615 if ( (p->is_directory) &&
616 (p->data.file.do_index) &&
617 (p->current_depth == p->chk_tree_depth) )
618 {
619 odb.offset = p->publish_offset;
620 odb.file_id = p->data.file.file_id;
621 GNUNET_DATASTORE_put (sc->dsh,
622 sc->rid,
623 &mychk->query,
624 sizeof(struct OnDemandBlock),
625 &odb,
626 GNUNET_DATASTORE_BLOCKTYPE_ONDEMAND,
627 p->priority,
628 p->anonymity,
629 p->expirationTime,
630 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
631 &ds_put_cont,
632 dpc_cls);
633 }
634 else
635 {
636 GNUNET_DATASTORE_put (sc->dsh,
637 sc->rid,
638 &mychk->query,
639 pt_size,
640 enc,
641 (p->current_depth == p->chk_tree_depth)
642 ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK
643 : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
644 p->priority,
645 p->anonymity,
646 p->expirationTime,
647 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
648 &ds_put_cont,
649 dpc_cls);
650 }
651 }
652 if (p->current_depth == p->chk_tree_depth)
653 {
654 pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
655 make_publish_status (&pi, sc, p);
656 pi.value.publish.specifics.progress.data = pt_block;
657 pi.value.publish.specifics.progress.offset = p->publish_offset;
658 pi.value.publish.specifics.progress.data_len = pt_size;
659 p->client_info
660 = sc->h->upcb (sc->h->upcb_cls,
661 &pi);
662 }
663 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
664 if (p->current_depth == p->chk_tree_depth)
665 {
666 p->publish_offset += pt_size;
667 if ( (p->publish_offset == size) ||
668 (0 == p->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
669 p->current_depth--;
670 }
671 else
672 {
673 if ( (off == CHK_PER_INODE) ||
674 (p->publish_offset == size) )
675 p->current_depth--;
676 else
677 p->current_depth = p->chk_tree_depth;
678 }
679 if (0 == p->current_depth)
680 {
681 p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
682 p->chk_uri->type = chk;
683 p->chk_uri->data.chk.chk = p->chk_tree[0];
684 p->chk_uri->data.chk.file_length = size;
685 GNUNET_free (p->chk_tree);
686 p->chk_tree = NULL;
687 }
688} 626}
689 627
690 628
691
692
693/** 629/**
694 * Process the response (or lack thereof) from 630 * Process the response (or lack thereof) from
695 * the "fs" service to our 'start index' request. 631 * the "fs" service to our 'start index' request.
diff --git a/src/fs/fs_tree.c b/src/fs/fs_tree.c
new file mode 100644
index 000000000..5d6a4f1d9
--- /dev/null
+++ b/src/fs/fs_tree.c
@@ -0,0 +1,381 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19*/
20/**
21 * @file fs/fs_tree.c
22 * @brief Merkle-tree-ish-CHK file encoding for GNUnet
23 * @see http://gnunet.org/encoding.php3
24 * @author Krista Bennett
25 * @author Christian Grothoff
26 *
27 * TODO:
28 * - decide if this API should be made public (gnunet_fs_service.h)
29 * or remain "internal" (but with exported symbols?)
30 */
31#include "platform.h"
32#include "fs_tree.h"
33
34
35/**
36 * Context for an ECRS-based file encoder that computes
37 * the Merkle-ish-CHK tree.
38 */
39struct GNUNET_FS_TreeEncoder
40{
41
42 /**
43 * Global FS context.
44 */
45 struct GNUNET_FS_Handle *h;
46
47 /**
48 * Closure for all callbacks.
49 */
50 void *cls;
51
52 /**
53 * Function to call on encrypted blocks.
54 */
55 GNUNET_FS_TreeBlockProcessor proc;
56
57 /**
58 * Function to call with progress information.
59 */
60 GNUNET_FS_TreeProgressCallback progress;
61
62 /**
63 * Function to call to receive input data.
64 */
65 GNUNET_FS_DataReader reader;
66
67 /**
68 * Function to call once we're done with processing.
69 */
70 GNUNET_SCHEDULER_Task cont;
71
72 /**
73 * Set to an error message (if we had an error).
74 */
75 char *emsg;
76
77 /**
78 * Set to the URI (upon successful completion)
79 */
80 struct GNUNET_FS_Uri *uri;
81
82 /**
83 * Overall file size.
84 */
85 uint64_t size;
86
87 /**
88 * How far are we?
89 */
90 uint64_t publish_offset;
91
92 /**
93 * How deep are we?
94 */
95 unsigned int current_depth;
96
97 /**
98 * How deep is the tree?
99 */
100 unsigned int chk_tree_depth;
101
102 /**
103 * In-memory cache of the current CHK tree.
104 * This struct will contain the CHK values
105 * from the root to the currently processed
106 * node in the tree as identified by
107 * "current_depth" and "publish_offset".
108 * The "chktree" will be initially NULL,
109 * then allocated to a sufficient number of
110 * entries for the size of the file and
111 * finally freed once the upload is complete.
112 */
113 struct ContentHashKey *chk_tree;
114
115};
116
117
118/**
119 * Compute the depth of the CHK tree.
120 *
121 * @param flen file length for which to compute the depth
122 * @return depth of the tree
123 */
124static unsigned int
125compute_depth (uint64_t flen)
126{
127 unsigned int treeDepth;
128 uint64_t fl;
129
130 treeDepth = 1;
131 fl = DBLOCK_SIZE;
132 while (fl < flen)
133 {
134 treeDepth++;
135 if (fl * CHK_PER_INODE < fl)
136 {
137 /* integer overflow, this is a HUGE file... */
138 return treeDepth;
139 }
140 fl = fl * CHK_PER_INODE;
141 }
142 return treeDepth;
143}
144
145
146/**
147 * Initialize a tree encoder. This function will call "proc" and
148 * "progress" on each block in the tree. Once all blocks have been
149 * processed, "cont" will be scheduled. The "reader" will be called
150 * to obtain the (plaintext) blocks for the file. Note that this
151 * function will not actually call "proc". The client must
152 * call "GNUNET_FS_tree_encoder_next" to trigger encryption (and
153 * calling of "proc") for the each block.
154 *
155 * @param h the global FS context
156 * @param size overall size of the file to encode
157 * @param cls closure for reader, proc, progress and cont
158 * @param reader function to call to read plaintext data
159 * @param proc function to call on each encrypted block
160 * @param progress function to call with progress information
161 * @param cont function to call when done
162 */
163struct GNUNET_FS_TreeEncoder *
164GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h,
165 uint64_t size,
166 void *cls,
167 GNUNET_FS_DataReader reader,
168 GNUNET_FS_TreeBlockProcessor proc,
169 GNUNET_FS_TreeProgressCallback progress,
170 GNUNET_SCHEDULER_Task cont)
171{
172 struct GNUNET_FS_TreeEncoder *te;
173
174 te = GNUNET_malloc (sizeof (struct GNUNET_FS_TreeEncoder));
175 te->h = h;
176 te->size = size;
177 te->cls = cls;
178 te->reader = reader;
179 te->proc = proc;
180 te->progress = progress;
181 te->cont = cont;
182 te->chk_tree_depth = compute_depth (size);
183 te->current_depth = te->chk_tree_depth;
184 te->chk_tree = GNUNET_malloc (te->chk_tree_depth *
185 CHK_PER_INODE *
186 sizeof (struct ContentHashKey));
187 return te;
188}
189
190
191/**
192 * Compute the size of the current IBlock.
193 *
194 * @param height height of the IBlock in the tree (aka overall
195 * number of tree levels minus depth); 0 == DBlock
196 * @param offset current offset in the overall file
197 * @return size of the corresponding IBlock
198 */
199static uint16_t
200compute_iblock_size (unsigned int height,
201 uint64_t offset)
202{
203 unsigned int ret;
204 unsigned int i;
205 uint64_t mod;
206 uint64_t bds;
207
208 GNUNET_assert (height > 0);
209 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
210 corresponds to */
211 for (i=0;i<height;i++)
212 bds *= CHK_PER_INODE;
213 mod = offset % bds;
214 if (0 == mod)
215 {
216 /* we were triggered at the end of a full block */
217 ret = CHK_PER_INODE;
218 }
219 else
220 {
221 /* we were triggered at the end of the file */
222 bds /= CHK_PER_INODE;
223 ret = mod / bds;
224 if (0 != mod % bds)
225 ret++;
226 }
227 return (uint16_t) (ret * sizeof(struct ContentHashKey));
228}
229
230
231/**
232 * Compute the offset of the CHK for the
233 * current block in the IBlock above.
234 *
235 * @param height height of the IBlock in the tree (aka overall
236 * number of tree levels minus depth); 0 == DBlock
237 * @param offset current offset in the overall file
238 * @return (array of CHKs') offset in the above IBlock
239 */
240static unsigned int
241compute_chk_offset (unsigned int height,
242 uint64_t offset)
243{
244 uint64_t bds;
245 unsigned int ret;
246 unsigned int i;
247
248 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
249 corresponds to */
250 for (i=0;i<height;i++)
251 bds *= CHK_PER_INODE;
252 GNUNET_assert (0 == (offset % bds));
253 ret = offset / bds;
254 return ret % CHK_PER_INODE;
255}
256
257
258/**
259 * Encrypt the next block of the file (and
260 * call proc and progress accordingly; or
261 * of course "cont" if we have already completed
262 * encoding of the entire file).
263 *
264 * @param te tree encoder to use
265 */
266void GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
267{
268 struct ContentHashKey *mychk;
269 const void *pt_block;
270 uint16_t pt_size;
271 char iob[DBLOCK_SIZE];
272 char enc[DBLOCK_SIZE];
273 struct GNUNET_CRYPTO_AesSessionKey sk;
274 struct GNUNET_CRYPTO_AesInitializationVector iv;
275 unsigned int off;
276
277 if (te->current_depth == te->chk_tree_depth)
278 {
279 pt_size = GNUNET_MIN(DBLOCK_SIZE,
280 te->size - te->publish_offset);
281 if (pt_size !=
282 te->reader (te->cls,
283 te->publish_offset,
284 pt_size,
285 iob,
286 &te->emsg))
287 {
288 GNUNET_SCHEDULER_add_continuation (te->h->sched,
289 GNUNET_NO,
290 te->cont,
291 te->cls,
292 GNUNET_SCHEDULER_REASON_TIMEOUT);
293 return;
294 }
295 pt_block = iob;
296 }
297 else
298 {
299 pt_size = compute_iblock_size (te->chk_tree_depth - te->current_depth,
300 te->publish_offset);
301 pt_block = &te->chk_tree[te->current_depth *
302 CHK_PER_INODE];
303 }
304 off = compute_chk_offset (te->chk_tree_depth - te->current_depth,
305 te->publish_offset);
306 mychk = &te->chk_tree[(te->current_depth-1)*CHK_PER_INODE+off];
307 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
308 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
309 GNUNET_CRYPTO_aes_encrypt (pt_block,
310 pt_size,
311 &sk,
312 &iv,
313 enc);
314 if (NULL != te->proc)
315 te->proc (te->cls,
316 &mychk->query,
317 te->publish_offset,
318 pt_size,
319 enc,
320 (te->current_depth == te->chk_tree_depth)
321 ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK
322 : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK);
323 if (NULL != te->progress)
324 te->progress (te->cls,
325 te->publish_offset,
326 pt_block,
327 pt_size,
328 te->current_depth);
329 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
330 if (te->current_depth == te->chk_tree_depth)
331 {
332 te->publish_offset += pt_size;
333 if ( (te->publish_offset == te->size) ||
334 (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
335 te->current_depth--;
336 }
337 else
338 {
339 if ( (off == CHK_PER_INODE) ||
340 (te->publish_offset == te->size) )
341 te->current_depth--;
342 else
343 te->current_depth = te->chk_tree_depth;
344 }
345 if (0 == te->current_depth)
346 {
347 te->uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
348 te->uri->type = chk;
349 te->uri->data.chk.chk = te->chk_tree[0];
350 te->uri->data.chk.file_length = te->size;
351 GNUNET_SCHEDULER_add_continuation (te->h->sched,
352 GNUNET_NO,
353 te->cont,
354 te->cls,
355 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
356 }
357}
358
359
360/**
361 * Clean up a tree encoder and return information
362 * about the resulting URI or an error message.
363 *
364 * @param te the tree encoder to clean up
365 * @param uri set to the resulting URI (if encoding finished)
366 * @param emsg set to an error message (if an error occured
367 * within the tree encoder; if this function is called
368 * prior to completion and prior to an internal error,
369 * both "*uri" and "*emsg" will be set to NULL).
370 */
371void GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder * te,
372 struct GNUNET_FS_Uri **uri,
373 char **emsg)
374{
375 *uri = te->uri;
376 *emsg = te->emsg;
377 GNUNET_free (te->chk_tree);
378 GNUNET_free (te);
379}
380
381/* end of fs_tree.c */
diff --git a/src/fs/fs_tree.h b/src/fs/fs_tree.h
new file mode 100644
index 000000000..f24130a3c
--- /dev/null
+++ b/src/fs/fs_tree.h
@@ -0,0 +1,167 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19*/
20
21/**
22 * @file fs/fs_tree.h
23 * @brief Merkle-tree-ish-CHK file encoding for GNUnet
24 * @see http://gnunet.org/encoding.php3
25 * @author Krista Bennett
26 * @author Christian Grothoff
27 *
28 * TODO:
29 * - decide if this API should be made public (gnunet_fs_service.h)
30 * or remain "internal" (but with exported symbols?)
31 */
32#ifndef GNUNET_FS_TREE_H
33#define GNUNET_FS_TREE_H
34
35#include "fs.h"
36
37/**
38 * Context for an ECRS-based file encoder that computes
39 * the Merkle-ish-CHK tree.
40 */
41struct GNUNET_FS_TreeEncoder;
42
43
44/**
45 * Function called asking for the current (encoded)
46 * block to be processed. After processing the
47 * client should either call "GNUNET_FS_tree_encode_next"
48 * or (on error) "GNUNET_FS_tree_encode_finish".
49 *
50 * @param cls closure
51 * @param query the query for the block (key for lookup in the datastore)
52 * @param offset offset of the block
53 * @param type type of the block (IBLOCK or DBLOCK)
54 * @param block the (encrypted) block
55 * @param block_size size of block (in bytes)
56 */
57typedef void (*GNUNET_FS_TreeBlockProcessor)(void *cls,
58 const GNUNET_HashCode *query,
59 uint64_t offset,
60 unsigned int type,
61 const void *block,
62 uint16_t block_size);
63
64
65/**
66 * Function called with information about our
67 * progress in computing the tree encoding.
68 *
69 * @param cls closure
70 * @param offset where are we in the file
71 * @param pt_block plaintext of the currently processed block
72 * @param pt_size size of pt_block
73 * @param depth depth of the block in the tree
74 */
75typedef void (*GNUNET_FS_TreeProgressCallback)(void *cls,
76 uint64_t offset,
77 const void *pt_block,
78 size_t pt_size,
79 unsigned int depth);
80
81
82/**
83 * Initialize a tree encoder. This function will call "proc" and
84 * "progress" on each block in the tree. Once all blocks have been
85 * processed, "cont" will be scheduled. The "reader" will be called
86 * to obtain the (plaintext) blocks for the file. Note that this
87 * function will actually never call "proc"; the "proc" function must
88 * be triggered by calling "GNUNET_FS_tree_encoder_next" to trigger
89 * encryption (and calling of "proc") for each block.
90 *
91 * @param h the global FS context
92 * @param size overall size of the file to encode
93 * @param cls closure for reader, proc, progress and cont
94 * @param reader function to call to read plaintext data
95 * @param proc function to call on each encrypted block
96 * @param progress function to call with progress information
97 * @param cont function to call when done
98 */
99struct GNUNET_FS_TreeEncoder *
100GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h,
101 uint64_t size,
102 void *cls,
103 GNUNET_FS_DataReader reader,
104 GNUNET_FS_TreeBlockProcessor proc,
105 GNUNET_FS_TreeProgressCallback progress,
106 GNUNET_SCHEDULER_Task cont);
107
108
109/**
110 * Encrypt the next block of the file (and
111 * call proc and progress accordingly; or
112 * of course "cont" if we have already completed
113 * encoding of the entire file).
114 *
115 * @param te tree encoder to use
116 */
117void GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te);
118
119
120/**
121 * Clean up a tree encoder and return information
122 * about the resulting URI or an error message.
123 *
124 * @param te the tree encoder to clean up
125 * @param uri set to the resulting URI (if encoding finished)
126 * @param emsg set to an error message (if an error occured
127 * within the tree encoder; if this function is called
128 * prior to completion and prior to an internal error,
129 * both "*uri" and "*emsg" will be set to NULL).
130 */
131void GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder * te,
132 struct GNUNET_FS_Uri **uri,
133 char **emsg);
134
135
136#if 0
137/* the functions below will be needed for persistence
138 but are not yet implemented -- FIXME... */
139/**
140 * Get data that would be needed to resume
141 * the encoding later.
142 *
143 * @param te encoding to resume
144 * @param data set to the resume data
145 * @param size set to the size of the resume data
146 */
147void GNUNET_FS_tree_encoder_resume_get_data (const struct GNUNET_FS_TreeEncoder * te,
148 void **data,
149 size_t *size);
150
151
152/**
153 * Reset tree encoder to point previously
154 * obtained for resuming.
155 *
156 * @param te encoding to resume
157 * @param data the resume data
158 * @param size the size of the resume data
159 */
160void GNUNET_FS_tree_encoder_resume (struct GNUNET_FS_TreeEncoder * te,
161 const void *data,
162 size_t size);
163#endif
164
165#endif
166
167/* end of fs_tree.h */