2 This file is part of GNUnet.
3 Copyright (C) 2009-2011 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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
32 * Context for an ECRS-based file encoder that computes
33 * the Merkle-ish-CHK tree.
35 struct GNUNET_FS_TreeEncoder {
39 struct GNUNET_FS_Handle *h;
42 * Closure for all callbacks.
47 * Function to call on encrypted blocks.
49 GNUNET_FS_TreeBlockProcessor proc;
52 * Function to call with progress information.
54 GNUNET_FS_TreeProgressCallback progress;
57 * Function to call to receive input data.
59 GNUNET_FS_DataReader reader;
62 * Function to call once we're done with processing.
64 GNUNET_SCHEDULER_TaskCallback cont;
67 * Set to an error message (if we had an error).
72 * Set to the URI (upon successful completion)
74 struct GNUNET_FS_Uri *uri;
84 uint64_t publish_offset;
87 * How deep are we? Depth 0 is for the DBLOCKs.
89 unsigned int current_depth;
92 * How deep is the tree? Always > 0.
94 unsigned int chk_tree_depth;
97 * In-memory cache of the current CHK tree.
98 * This struct will contain the CHK values
99 * from the root to the currently processed
100 * node in the tree as identified by
101 * "current_depth" and "publish_offset".
102 * The "chktree" will be initially NULL,
103 * then allocated to a sufficient number of
104 * entries for the size of the file and
105 * finally freed once the upload is complete.
107 struct ContentHashKey *chk_tree;
110 * Are we currently in 'GNUNET_FS_tree_encoder_next'?
111 * Flag used to prevent recursion.
118 * Compute the depth of the CHK tree.
120 * @param flen file length for which to compute the depth
121 * @return depth of the tree, always > 0. A depth of 1 means only a DBLOCK.
124 GNUNET_FS_compute_depth(uint64_t flen)
126 unsigned int treeDepth;
134 if (fl * CHK_PER_INODE < fl)
136 /* integer overflow, this is a HUGE file... */
139 fl = fl * CHK_PER_INODE;
146 * Calculate how many bytes of payload a block tree of the given
147 * depth MAY correspond to at most (this function ignores the fact that
148 * some blocks will only be present partially due to the total file
149 * size cutting some blocks off at the end).
151 * @param depth depth of the block. depth==0 is a DBLOCK.
152 * @return number of bytes of payload a subtree of this depth may correspond to
155 GNUNET_FS_tree_compute_tree_size(unsigned int depth)
161 for (i = 0; i < depth; i++)
162 rsize *= CHK_PER_INODE;
168 * Compute the size of the current IBLOCK. The encoder is
169 * triggering the calculation of the size of an IBLOCK at the
170 * *end* (hence end_offset) of its construction. The IBLOCK
171 * maybe a full or a partial IBLOCK, and this function is to
172 * calculate how long it should be.
174 * @param depth depth of the IBlock in the tree, 0 would be a DBLOCK,
175 * must be > 0 (this function is for IBLOCKs only!)
176 * @param end_offset current offset in the payload (!) of the overall file,
177 * must be > 0 (since this function is called at the
179 * @return size of the corresponding IBlock
182 GNUNET_FS_tree_compute_iblock_size(unsigned int depth, uint64_t end_offset)
188 GNUNET_assert(depth > 0);
189 GNUNET_assert(end_offset > 0);
190 bds = GNUNET_FS_tree_compute_tree_size(depth);
191 mod = end_offset % bds;
194 /* we were triggered at the end of a full block */
199 /* we were triggered at the end of the file */
200 bds /= CHK_PER_INODE;
205 return (uint16_t)(ret * sizeof(struct ContentHashKey));
210 * Compute how many bytes of data should be stored in
211 * the specified block.
213 * @param fsize overall file size, must be > 0.
214 * @param offset offset in the original data corresponding
215 * to the beginning of the tree induced by the block;
217 * @param depth depth of the node in the tree, 0 for DBLOCK
218 * @return number of bytes stored in this node
221 GNUNET_FS_tree_calculate_block_size(uint64_t fsize, uint64_t offset,
229 GNUNET_assert(fsize > 0);
230 GNUNET_assert(offset <= fsize);
234 if ((offset + ret > fsize) || (offset + ret < offset))
235 ret = (size_t)(fsize - offset);
239 rsize = GNUNET_FS_tree_compute_tree_size(depth - 1);
240 epos = offset + rsize * CHK_PER_INODE;
241 if ((epos < offset) || (epos > fsize))
243 /* round up when computing #CHKs in our IBlock */
244 chks = (epos - offset + rsize - 1) / rsize;
245 GNUNET_assert(chks <= CHK_PER_INODE);
246 return chks * sizeof(struct ContentHashKey);
251 * Initialize a tree encoder. This function will call @a proc and
252 * "progress" on each block in the tree. Once all blocks have been
253 * processed, "cont" will be scheduled. The @a reader will be called
254 * to obtain the (plaintext) blocks for the file. Note that this
255 * function will not actually call @a proc. The client must
256 * call #GNUNET_FS_tree_encoder_next to trigger encryption (and
257 * calling of @a proc) for the each block.
259 * @param h the global FS context
260 * @param size overall size of the file to encode
261 * @param cls closure for reader, proc, progress and cont
262 * @param reader function to call to read plaintext data
263 * @param proc function to call on each encrypted block
264 * @param progress function to call with progress information
265 * @param cont function to call when done
267 struct GNUNET_FS_TreeEncoder *
268 GNUNET_FS_tree_encoder_create(struct GNUNET_FS_Handle *h, uint64_t size,
270 GNUNET_FS_DataReader reader,
271 GNUNET_FS_TreeBlockProcessor proc,
272 GNUNET_FS_TreeProgressCallback progress,
273 GNUNET_SCHEDULER_TaskCallback cont)
275 struct GNUNET_FS_TreeEncoder *te;
277 te = GNUNET_new(struct GNUNET_FS_TreeEncoder);
283 te->progress = progress;
285 te->chk_tree_depth = GNUNET_FS_compute_depth(size);
287 = GNUNET_new_array(te->chk_tree_depth * CHK_PER_INODE,
288 struct ContentHashKey);
289 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
290 "Created tree encoder for file with %llu bytes and depth %u\n",
291 (unsigned long long)size,
298 * Compute the offset of the CHK for the
299 * current block in the IBlock above.
301 * @param depth depth of the IBlock in the tree (aka overall
302 * number of tree levels minus depth); 0 == DBlock
303 * @param end_offset current offset in the overall file,
304 * at the *beginning* of the block for DBLOCKs (depth==0),
305 * otherwise at the *end* of the block (exclusive)
306 * @return (array of CHKs') offset in the above IBlock
309 compute_chk_offset(unsigned int depth, uint64_t end_offset)
314 bds = GNUNET_FS_tree_compute_tree_size(depth);
316 end_offset--; /* round down since for depth > 0 offset is at the END of the block */
317 ret = end_offset / bds;
318 return ret % CHK_PER_INODE;
323 * Encrypt the next block of the file (and call proc and progress
324 * accordingly; or of course "cont" if we have already completed
325 * encoding of the entire file).
327 * @param te tree encoder to use
330 GNUNET_FS_tree_encoder_next(struct GNUNET_FS_TreeEncoder *te)
332 struct ContentHashKey *mychk;
333 const void *pt_block;
335 char iob[DBLOCK_SIZE];
336 char enc[DBLOCK_SIZE];
337 struct GNUNET_CRYPTO_SymmetricSessionKey sk;
338 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
341 GNUNET_assert(GNUNET_NO == te->in_next);
342 te->in_next = GNUNET_YES;
343 if (te->chk_tree_depth == te->current_depth)
345 off = CHK_PER_INODE * (te->chk_tree_depth - 1);
346 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "TE done, reading CHK `%s' from %u\n",
347 GNUNET_h2s(&te->chk_tree[off].query), off);
348 te->uri = GNUNET_new(struct GNUNET_FS_Uri);
349 te->uri->type = GNUNET_FS_URI_CHK;
350 te->uri->data.chk.chk = te->chk_tree[off];
351 te->uri->data.chk.file_length = GNUNET_htonll(te->size);
352 te->in_next = GNUNET_NO;
356 if (0 == te->current_depth)
359 pt_size = GNUNET_MIN(DBLOCK_SIZE, te->size - te->publish_offset);
361 te->reader(te->cls, te->publish_offset, pt_size, iob, &te->emsg))
363 te->in_next = GNUNET_NO;
372 GNUNET_FS_tree_compute_iblock_size(te->current_depth,
374 pt_block = &te->chk_tree[(te->current_depth - 1) * CHK_PER_INODE];
376 off = compute_chk_offset(te->current_depth, te->publish_offset);
377 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
378 "TE is at offset %llu and depth %u with block size %u and target-CHK-offset %u\n",
379 (unsigned long long)te->publish_offset, te->current_depth,
380 (unsigned int)pt_size, (unsigned int)off);
381 mychk = &te->chk_tree[te->current_depth * CHK_PER_INODE + off];
382 GNUNET_CRYPTO_hash(pt_block, pt_size, &mychk->key);
383 GNUNET_CRYPTO_hash_to_aes_key(&mychk->key, &sk, &iv);
384 GNUNET_CRYPTO_symmetric_encrypt(pt_block, pt_size, &sk, &iv, enc);
385 GNUNET_CRYPTO_hash(enc, pt_size, &mychk->query);
386 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
387 "TE calculates query to be `%s', stored at %u\n",
388 GNUNET_h2s(&mychk->query),
389 te->current_depth * CHK_PER_INODE + off);
390 if (NULL != te->proc)
391 te->proc(te->cls, mychk, te->publish_offset, te->current_depth,
393 te->current_depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK :
394 GNUNET_BLOCK_TYPE_FS_IBLOCK, enc, pt_size);
395 if (NULL != te->progress)
396 te->progress(te->cls, te->publish_offset, pt_block, pt_size,
398 if (0 == te->current_depth)
400 te->publish_offset += pt_size;
401 if ((te->publish_offset == te->size) ||
402 (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE)))
407 if ((off == CHK_PER_INODE) || (te->publish_offset == te->size))
410 te->current_depth = 0;
412 te->in_next = GNUNET_NO;
417 * Get the resulting URI from the encoding.
419 * @param te the tree encoder to clean up
420 * @return uri set to the resulting URI (if encoding finished), NULL otherwise
422 struct GNUNET_FS_Uri *
423 GNUNET_FS_tree_encoder_get_uri(struct GNUNET_FS_TreeEncoder *te)
426 return GNUNET_FS_uri_dup(te->uri);
432 * Clean up a tree encoder and return information
433 * about possible errors.
435 * @param te the tree encoder to clean up
436 * @param emsg set to an error message (if an error occured
437 * within the tree encoder; if this function is called
438 * prior to completion and prior to an internal error,
439 * both "*emsg" will be set to NULL).
442 GNUNET_FS_tree_encoder_finish(struct GNUNET_FS_TreeEncoder *te,
445 if (NULL != te->reader)
447 (void)te->reader(te->cls, UINT64_MAX, 0, 0, NULL);
450 GNUNET_assert(GNUNET_NO == te->in_next);
452 GNUNET_FS_uri_destroy(te->uri);
456 GNUNET_free_non_null(te->emsg);
457 GNUNET_free(te->chk_tree);
461 /* end of fs_tree.c */