fs_tree.c 13 KB

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