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 3, 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 * @brief Merkle-tree-ish-CHK file encoding for GNUnet
23 * @see http://gnunet.org/encoding.php3
24 * @author Krista Bennett
25 * @author Christian Grothoff
30 #define DEBUG_TREE GNUNET_NO
33 * Context for an ECRS-based file encoder that computes
34 * the Merkle-ish-CHK tree.
36 struct GNUNET_FS_TreeEncoder
42 struct GNUNET_FS_Handle *h;
45 * Closure for all callbacks.
50 * Function to call on encrypted blocks.
52 GNUNET_FS_TreeBlockProcessor proc;
55 * Function to call with progress information.
57 GNUNET_FS_TreeProgressCallback progress;
60 * Function to call to receive input data.
62 GNUNET_FS_DataReader reader;
65 * Function to call once we're done with processing.
67 GNUNET_SCHEDULER_Task cont;
70 * Set to an error message (if we had an error).
75 * Set to the URI (upon successful completion)
77 struct GNUNET_FS_Uri *uri;
87 uint64_t publish_offset;
92 unsigned int current_depth;
95 * How deep is the tree?
97 unsigned int chk_tree_depth;
100 * In-memory cache of the current CHK tree.
101 * This struct will contain the CHK values
102 * from the root to the currently processed
103 * node in the tree as identified by
104 * "current_depth" and "publish_offset".
105 * The "chktree" will be initially NULL,
106 * then allocated to a sufficient number of
107 * entries for the size of the file and
108 * finally freed once the upload is complete.
110 struct ContentHashKey *chk_tree;
113 * Are we currently in 'GNUNET_FS_tree_encoder_next'?
114 * Flag used to prevent recursion.
121 * Compute the depth of the CHK tree.
123 * @param flen file length for which to compute the depth
124 * @return depth of the tree
127 GNUNET_FS_compute_depth (uint64_t flen)
129 unsigned int treeDepth;
137 if (fl * CHK_PER_INODE < fl)
139 /* integer overflow, this is a HUGE file... */
142 fl = fl * CHK_PER_INODE;
149 * Initialize a tree encoder. This function will call "proc" and
150 * "progress" on each block in the tree. Once all blocks have been
151 * processed, "cont" will be scheduled. The "reader" will be called
152 * to obtain the (plaintext) blocks for the file. Note that this
153 * function will not actually call "proc". The client must
154 * call "GNUNET_FS_tree_encoder_next" to trigger encryption (and
155 * calling of "proc") for the each block.
157 * @param h the global FS context
158 * @param size overall size of the file to encode
159 * @param cls closure for reader, proc, progress and cont
160 * @param reader function to call to read plaintext data
161 * @param proc function to call on each encrypted block
162 * @param progress function to call with progress information
163 * @param cont function to call when done
165 struct GNUNET_FS_TreeEncoder *
166 GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h,
169 GNUNET_FS_DataReader reader,
170 GNUNET_FS_TreeBlockProcessor proc,
171 GNUNET_FS_TreeProgressCallback progress,
172 GNUNET_SCHEDULER_Task cont)
174 struct GNUNET_FS_TreeEncoder *te;
176 GNUNET_assert (size > 0);
177 te = GNUNET_malloc (sizeof (struct GNUNET_FS_TreeEncoder));
183 te->progress = progress;
185 te->chk_tree_depth = GNUNET_FS_compute_depth (size);
186 te->current_depth = te->chk_tree_depth;
187 te->chk_tree = GNUNET_malloc (te->chk_tree_depth *
189 sizeof (struct ContentHashKey));
195 * Compute the size of the current IBlock.
197 * @param height height of the IBlock in the tree (aka overall
198 * number of tree levels minus depth); 0 == DBlock
199 * @param offset current offset in the overall file
200 * @return size of the corresponding IBlock
203 GNUNET_FS_tree_compute_iblock_size (unsigned int height,
211 GNUNET_assert (height > 0);
212 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
214 for (i=0;i<height;i++)
215 bds *= CHK_PER_INODE;
219 /* we were triggered at the end of a full block */
224 /* we were triggered at the end of the file */
225 bds /= CHK_PER_INODE;
230 return (uint16_t) (ret * sizeof(struct ContentHashKey));
235 * Compute how many bytes of data should be stored in
236 * the specified node.
238 * @param fsize overall file size
239 * @param totaldepth depth of the entire tree
240 * @param offset offset of the node
241 * @param depth depth of the node
242 * @return number of bytes stored in this node
245 GNUNET_FS_tree_calculate_block_size (uint64_t fsize,
246 unsigned int totaldepth,
256 GNUNET_assert (offset < fsize);
257 if (depth == totaldepth)
260 if (offset + ret > fsize)
261 ret = (size_t) (fsize - offset);
264 /* FIXME: this code should be *equivalent* to the
265 GNUNET_FS_tree_compute_iblock_size function above! Remove duplication! */
267 for (i = totaldepth-1; i > depth; i--)
268 rsize *= CHK_PER_INODE;
269 epos = offset + rsize * CHK_PER_INODE;
270 GNUNET_assert (epos > offset);
273 /* round up when computing #CHKs in our IBlock */
274 chks = (epos - offset + rsize - 1) / rsize;
275 GNUNET_assert (chks <= CHK_PER_INODE);
276 return chks * sizeof (struct ContentHashKey);
281 * Compute the offset of the CHK for the
282 * current block in the IBlock above.
284 * @param height height of the IBlock in the tree (aka overall
285 * number of tree levels minus depth); 0 == DBlock
286 * @param offset current offset in the overall file
287 * @return (array of CHKs') offset in the above IBlock
290 compute_chk_offset (unsigned int height,
297 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
299 for (i=0;i<height;i++)
300 bds *= CHK_PER_INODE;
302 offset--; /* round down since for height > 0 offset is at the END of the block */
304 return ret % CHK_PER_INODE;
309 * Encrypt the next block of the file (and call proc and progress
310 * accordingly; or of course "cont" if we have already completed
311 * encoding of the entire file).
313 * @param te tree encoder to use
316 GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
318 struct ContentHashKey *mychk;
319 const void *pt_block;
321 char iob[DBLOCK_SIZE];
322 char enc[DBLOCK_SIZE];
323 struct GNUNET_CRYPTO_AesSessionKey sk;
324 struct GNUNET_CRYPTO_AesInitializationVector iv;
327 GNUNET_assert (GNUNET_NO == te->in_next);
328 te->in_next = GNUNET_YES;
329 if (te->current_depth == te->chk_tree_depth)
331 pt_size = GNUNET_MIN(DBLOCK_SIZE,
332 te->size - te->publish_offset);
340 GNUNET_SCHEDULER_add_continuation (te->cont,
342 GNUNET_SCHEDULER_REASON_TIMEOUT);
343 te->in_next = GNUNET_NO;
350 pt_size = GNUNET_FS_tree_compute_iblock_size (te->chk_tree_depth - te->current_depth,
352 pt_block = &te->chk_tree[te->current_depth *
355 if (0 == te->current_depth)
357 te->uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
359 te->uri->data.chk.chk = te->chk_tree[0];
360 te->uri->data.chk.file_length = GNUNET_htonll (te->size);
361 GNUNET_SCHEDULER_add_continuation (te->cont,
363 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
364 te->in_next = GNUNET_NO;
367 off = compute_chk_offset (te->chk_tree_depth - te->current_depth,
370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371 "TE is at offset %llu and depth %u with block size %u and target-CHK-offset %u\n",
372 (unsigned long long) te->publish_offset,
374 (unsigned int) pt_size,
377 mychk = &te->chk_tree[(te->current_depth-1)*CHK_PER_INODE+off];
378 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
379 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
380 GNUNET_CRYPTO_aes_encrypt (pt_block,
385 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388 "TE calculates query to be `%s'\n",
389 GNUNET_h2s (&mychk->query));
391 if (NULL != te->proc)
396 (te->current_depth == te->chk_tree_depth)
397 ? GNUNET_BLOCK_TYPE_FS_DBLOCK
398 : GNUNET_BLOCK_TYPE_FS_IBLOCK,
401 if (NULL != te->progress)
402 te->progress (te->cls,
407 if (te->current_depth == te->chk_tree_depth)
409 te->publish_offset += pt_size;
410 if ( (te->publish_offset == te->size) ||
411 (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
416 if ( (off == CHK_PER_INODE) ||
417 (te->publish_offset == te->size) )
420 te->current_depth = te->chk_tree_depth;
422 te->in_next = GNUNET_NO;
427 * Clean up a tree encoder and return information
428 * about the resulting URI or an error message.
430 * @param te the tree encoder to clean up
431 * @param uri set to the resulting URI (if encoding finished)
432 * @param emsg set to an error message (if an error occured
433 * within the tree encoder; if this function is called
434 * prior to completion and prior to an internal error,
435 * both "*uri" and "*emsg" will be set to NULL).
437 void GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder * te,
438 struct GNUNET_FS_Uri **uri,
445 GNUNET_FS_uri_destroy (te->uri);
449 GNUNET_free_non_null (te->emsg);
450 GNUNET_free (te->chk_tree);
454 /* end of fs_tree.c */