fs_tree.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2009-2011 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file fs/fs_tree.c
  19. * @brief Merkle-tree-ish-CHK file encoding for GNUnet
  20. * @see http://gnunet.org/encoding.php3
  21. * @author Krista Bennett
  22. * @author Christian Grothoff
  23. */
  24. #include "platform.h"
  25. #include "fs_tree.h"
  26. #define DEBUG_TREE GNUNET_NO
  27. /**
  28. * Context for an ECRS-based file encoder that computes
  29. * the Merkle-ish-CHK tree.
  30. */
  31. struct GNUNET_FS_TreeEncoder
  32. {
  33. /**
  34. * Global FS context.
  35. */
  36. struct GNUNET_FS_Handle *h;
  37. /**
  38. * Closure for all callbacks.
  39. */
  40. void *cls;
  41. /**
  42. * Function to call on encrypted blocks.
  43. */
  44. GNUNET_FS_TreeBlockProcessor proc;
  45. /**
  46. * Function to call with progress information.
  47. */
  48. GNUNET_FS_TreeProgressCallback progress;
  49. /**
  50. * Function to call to receive input data.
  51. */
  52. GNUNET_FS_DataReader reader;
  53. /**
  54. * Function to call once we're done with processing.
  55. */
  56. GNUNET_SCHEDULER_Task cont;
  57. /**
  58. * Set to an error message (if we had an error).
  59. */
  60. char *emsg;
  61. /**
  62. * Set to the URI (upon successful completion)
  63. */
  64. struct GNUNET_FS_Uri *uri;
  65. /**
  66. * Overall file size.
  67. */
  68. uint64_t size;
  69. /**
  70. * How far are we?
  71. */
  72. uint64_t publish_offset;
  73. /**
  74. * How deep are we? Depth 0 is for the DBLOCKs.
  75. */
  76. unsigned int current_depth;
  77. /**
  78. * How deep is the tree? Always > 0.
  79. */
  80. unsigned int chk_tree_depth;
  81. /**
  82. * In-memory cache of the current CHK tree.
  83. * This struct will contain the CHK values
  84. * from the root to the currently processed
  85. * node in the tree as identified by
  86. * "current_depth" and "publish_offset".
  87. * The "chktree" will be initially NULL,
  88. * then allocated to a sufficient number of
  89. * entries for the size of the file and
  90. * finally freed once the upload is complete.
  91. */
  92. struct ContentHashKey *chk_tree;
  93. /**
  94. * Are we currently in 'GNUNET_FS_tree_encoder_next'?
  95. * Flag used to prevent recursion.
  96. */
  97. int in_next;
  98. };
  99. /**
  100. * Compute the depth of the CHK tree.
  101. *
  102. * @param flen file length for which to compute the depth
  103. * @return depth of the tree, always > 0. A depth of 1 means only a DBLOCK.
  104. */
  105. unsigned int
  106. GNUNET_FS_compute_depth (uint64_t flen)
  107. {
  108. unsigned int treeDepth;
  109. uint64_t fl;
  110. treeDepth = 1;
  111. fl = DBLOCK_SIZE;
  112. while (fl < flen)
  113. {
  114. treeDepth++;
  115. if (fl * CHK_PER_INODE < fl)
  116. {
  117. /* integer overflow, this is a HUGE file... */
  118. return treeDepth;
  119. }
  120. fl = fl * CHK_PER_INODE;
  121. }
  122. return treeDepth;
  123. }
  124. /**
  125. * Calculate how many bytes of payload a block tree of the given
  126. * depth MAY correspond to at most (this function ignores the fact that
  127. * some blocks will only be present partially due to the total file
  128. * size cutting some blocks off at the end).
  129. *
  130. * @param depth depth of the block. depth==0 is a DBLOCK.
  131. * @return number of bytes of payload a subtree of this depth may correspond to
  132. */
  133. uint64_t
  134. GNUNET_FS_tree_compute_tree_size (unsigned int depth)
  135. {
  136. uint64_t rsize;
  137. unsigned int i;
  138. rsize = DBLOCK_SIZE;
  139. for (i = 0; i<depth; i++)
  140. rsize *= CHK_PER_INODE;
  141. return rsize;
  142. }
  143. /**
  144. * Compute the size of the current IBLOCK. The encoder is
  145. * triggering the calculation of the size of an IBLOCK at the
  146. * *end* (hence end_offset) of its construction. The IBLOCK
  147. * maybe a full or a partial IBLOCK, and this function is to
  148. * calculate how long it should be.
  149. *
  150. * @param depth depth of the IBlock in the tree, 0 would be a DBLOCK,
  151. * must be > 0 (this function is for IBLOCKs only!)
  152. * @param end_offset current offset in the payload (!) of the overall file,
  153. * must be > 0 (since this function is called at the
  154. * end of a block).
  155. * @return size of the corresponding IBlock
  156. */
  157. static uint16_t
  158. GNUNET_FS_tree_compute_iblock_size (unsigned int depth,
  159. uint64_t end_offset)
  160. {
  161. unsigned int ret;
  162. uint64_t mod;
  163. uint64_t bds;
  164. GNUNET_assert (depth > 0);
  165. GNUNET_assert (end_offset > 0);
  166. bds = GNUNET_FS_tree_compute_tree_size (depth);
  167. mod = end_offset % bds;
  168. if (0 == mod)
  169. {
  170. /* we were triggered at the end of a full block */
  171. ret = CHK_PER_INODE;
  172. }
  173. else
  174. {
  175. /* we were triggered at the end of the file */
  176. bds /= CHK_PER_INODE;
  177. ret = mod / bds;
  178. if (0 != mod % bds)
  179. ret++;
  180. }
  181. return (uint16_t) (ret * sizeof(struct ContentHashKey));
  182. }
  183. /**
  184. * Compute how many bytes of data should be stored in
  185. * the specified block.
  186. *
  187. * @param fsize overall file size, must be > 0.
  188. * @param offset offset in the original data corresponding
  189. * to the beginning of the tree induced by the block;
  190. * must be <= fsize
  191. * @param depth depth of the node in the tree, 0 for DBLOCK
  192. * @return number of bytes stored in this node
  193. */
  194. size_t
  195. GNUNET_FS_tree_calculate_block_size (uint64_t fsize,
  196. uint64_t offset,
  197. unsigned int depth)
  198. {
  199. size_t ret;
  200. uint64_t rsize;
  201. uint64_t epos;
  202. unsigned int chks;
  203. GNUNET_assert (fsize > 0);
  204. GNUNET_assert (offset <= fsize);
  205. if (depth == 0)
  206. {
  207. ret = DBLOCK_SIZE;
  208. if ( (offset + ret > fsize) ||
  209. (offset + ret < offset) )
  210. ret = (size_t) (fsize - offset);
  211. return ret;
  212. }
  213. rsize = GNUNET_FS_tree_compute_tree_size (depth - 1);
  214. epos = offset + rsize * CHK_PER_INODE;
  215. if ( (epos < offset) ||
  216. (epos > fsize) )
  217. epos = fsize;
  218. /* round up when computing #CHKs in our IBlock */
  219. chks = (epos - offset + rsize - 1) / rsize;
  220. GNUNET_assert (chks <= CHK_PER_INODE);
  221. return chks * sizeof (struct ContentHashKey);
  222. }
  223. /**
  224. * Initialize a tree encoder. This function will call "proc" and
  225. * "progress" on each block in the tree. Once all blocks have been
  226. * processed, "cont" will be scheduled. The "reader" will be called
  227. * to obtain the (plaintext) blocks for the file. Note that this
  228. * function will not actually call "proc". The client must
  229. * call "GNUNET_FS_tree_encoder_next" to trigger encryption (and
  230. * calling of "proc") for the each block.
  231. *
  232. * @param h the global FS context
  233. * @param size overall size of the file to encode
  234. * @param cls closure for reader, proc, progress and cont
  235. * @param reader function to call to read plaintext data
  236. * @param proc function to call on each encrypted block
  237. * @param progress function to call with progress information
  238. * @param cont function to call when done
  239. */
  240. struct GNUNET_FS_TreeEncoder *
  241. GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h,
  242. uint64_t size,
  243. void *cls,
  244. GNUNET_FS_DataReader reader,
  245. GNUNET_FS_TreeBlockProcessor proc,
  246. GNUNET_FS_TreeProgressCallback progress,
  247. GNUNET_SCHEDULER_Task cont)
  248. {
  249. struct GNUNET_FS_TreeEncoder *te;
  250. te = GNUNET_malloc (sizeof (struct GNUNET_FS_TreeEncoder));
  251. te->h = h;
  252. te->size = size;
  253. te->cls = cls;
  254. te->reader = reader;
  255. te->proc = proc;
  256. te->progress = progress;
  257. te->cont = cont;
  258. te->chk_tree_depth = GNUNET_FS_compute_depth (size);
  259. te->chk_tree = GNUNET_malloc (te->chk_tree_depth *
  260. CHK_PER_INODE *
  261. sizeof (struct ContentHashKey));
  262. return te;
  263. }
  264. /**
  265. * Compute the offset of the CHK for the
  266. * current block in the IBlock above.
  267. *
  268. * @param depth depth of the IBlock in the tree (aka overall
  269. * number of tree levels minus depth); 0 == DBlock
  270. * @param end_offset current offset in the overall file,
  271. * at the *beginning* of the block for DBLOCKs (depth==0),
  272. * otherwise at the *end* of the block (exclusive)
  273. * @return (array of CHKs') offset in the above IBlock
  274. */
  275. static unsigned int
  276. compute_chk_offset (unsigned int depth,
  277. uint64_t end_offset)
  278. {
  279. uint64_t bds;
  280. unsigned int ret;
  281. bds = GNUNET_FS_tree_compute_tree_size (depth);
  282. if (depth > 0)
  283. end_offset--; /* round down since for depth > 0 offset is at the END of the block */
  284. ret = end_offset / bds;
  285. return ret % CHK_PER_INODE;
  286. }
  287. /**
  288. * Encrypt the next block of the file (and call proc and progress
  289. * accordingly; or of course "cont" if we have already completed
  290. * encoding of the entire file).
  291. *
  292. * @param te tree encoder to use
  293. */
  294. void
  295. GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
  296. {
  297. struct ContentHashKey *mychk;
  298. const void *pt_block;
  299. uint16_t pt_size;
  300. char iob[DBLOCK_SIZE];
  301. char enc[DBLOCK_SIZE];
  302. struct GNUNET_CRYPTO_AesSessionKey sk;
  303. struct GNUNET_CRYPTO_AesInitializationVector iv;
  304. unsigned int off;
  305. GNUNET_assert (GNUNET_NO == te->in_next);
  306. te->in_next = GNUNET_YES;
  307. if (te->chk_tree_depth == te->current_depth)
  308. {
  309. off = CHK_PER_INODE * (te->chk_tree_depth - 1);
  310. #if DEBUG_TREE
  311. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  312. "TE done, reading CHK `%s' from %u\n",
  313. GNUNET_h2s (&te->chk_tree[off].query),
  314. off);
  315. #endif
  316. te->uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
  317. te->uri->type = chk;
  318. te->uri->data.chk.chk = te->chk_tree[off];
  319. te->uri->data.chk.file_length = GNUNET_htonll (te->size);
  320. te->in_next = GNUNET_NO;
  321. te->cont (te->cls, NULL);
  322. return;
  323. }
  324. if (0 == te->current_depth)
  325. {
  326. /* read DBLOCK */
  327. pt_size = GNUNET_MIN(DBLOCK_SIZE,
  328. te->size - te->publish_offset);
  329. if (pt_size !=
  330. te->reader (te->cls,
  331. te->publish_offset,
  332. pt_size,
  333. iob,
  334. &te->emsg))
  335. {
  336. GNUNET_SCHEDULER_add_continuation (te->cont,
  337. te->cls,
  338. GNUNET_SCHEDULER_REASON_TIMEOUT);
  339. te->in_next = GNUNET_NO;
  340. return;
  341. }
  342. pt_block = iob;
  343. }
  344. else
  345. {
  346. pt_size = GNUNET_FS_tree_compute_iblock_size (te->current_depth,
  347. te->publish_offset);
  348. pt_block = &te->chk_tree[(te->current_depth - 1) *
  349. CHK_PER_INODE];
  350. }
  351. off = compute_chk_offset (te->current_depth,
  352. te->publish_offset);
  353. #if DEBUG_TREE
  354. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  355. "TE is at offset %llu and depth %u with block size %u and target-CHK-offset %u\n",
  356. (unsigned long long) te->publish_offset,
  357. te->current_depth,
  358. (unsigned int) pt_size,
  359. (unsigned int) off);
  360. #endif
  361. mychk = &te->chk_tree[te->current_depth*CHK_PER_INODE+off];
  362. GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
  363. GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
  364. GNUNET_CRYPTO_aes_encrypt (pt_block,
  365. pt_size,
  366. &sk,
  367. &iv,
  368. enc);
  369. GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
  370. #if DEBUG_TREE
  371. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  372. "TE calculates query to be `%s', stored at %u\n",
  373. GNUNET_h2s (&mychk->query),
  374. te->current_depth * CHK_PER_INODE + off);
  375. #endif
  376. if (NULL != te->proc)
  377. te->proc (te->cls,
  378. mychk,
  379. te->publish_offset,
  380. te->current_depth,
  381. (0 == te->current_depth)
  382. ? GNUNET_BLOCK_TYPE_FS_DBLOCK
  383. : GNUNET_BLOCK_TYPE_FS_IBLOCK,
  384. enc,
  385. pt_size);
  386. if (NULL != te->progress)
  387. te->progress (te->cls,
  388. te->publish_offset,
  389. pt_block,
  390. pt_size,
  391. te->current_depth);
  392. if (0 == te->current_depth)
  393. {
  394. te->publish_offset += pt_size;
  395. if ( (te->publish_offset == te->size) ||
  396. (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
  397. te->current_depth++;
  398. }
  399. else
  400. {
  401. if ( (off == CHK_PER_INODE) ||
  402. (te->publish_offset == te->size) )
  403. te->current_depth++;
  404. else
  405. te->current_depth = 0;
  406. }
  407. te->in_next = GNUNET_NO;
  408. }
  409. /**
  410. * Clean up a tree encoder and return information
  411. * about the resulting URI or an error message.
  412. *
  413. * @param te the tree encoder to clean up
  414. * @param uri set to the resulting URI (if encoding finished)
  415. * @param emsg set to an error message (if an error occured
  416. * within the tree encoder; if this function is called
  417. * prior to completion and prior to an internal error,
  418. * both "*uri" and "*emsg" will be set to NULL).
  419. */
  420. void GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder *te,
  421. struct GNUNET_FS_Uri **uri,
  422. char **emsg)
  423. {
  424. GNUNET_assert (GNUNET_NO == te->in_next);
  425. if (uri != NULL)
  426. *uri = te->uri;
  427. else
  428. if (NULL != te->uri)
  429. GNUNET_FS_uri_destroy (te->uri);
  430. if (emsg != NULL)
  431. *emsg = te->emsg;
  432. else
  433. GNUNET_free_non_null (te->emsg);
  434. GNUNET_free (te->chk_tree);
  435. GNUNET_free (te);
  436. }
  437. /* end of fs_tree.c */