aboutsummaryrefslogtreecommitdiff
path: root/src/fs/fs_tree.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-01-20 14:20:30 +0000
committerChristian Grothoff <christian@grothoff.org>2011-01-20 14:20:30 +0000
commitc9432075d84d3c50171367e7d648a3a09f431994 (patch)
tree816f97d3601f6f255354267225c3b43b076fdac7 /src/fs/fs_tree.c
parent81dfe20c8ee82d202244766f2914683a63d078ca (diff)
downloadgnunet-c9432075d84d3c50171367e7d648a3a09f431994.tar.gz
gnunet-c9432075d84d3c50171367e7d648a3a09f431994.zip
major rewrite of fs_download, hopefully fixing 1641
Diffstat (limited to 'src/fs/fs_tree.c')
-rw-r--r--src/fs/fs_tree.c250
1 files changed, 139 insertions, 111 deletions
diff --git a/src/fs/fs_tree.c b/src/fs/fs_tree.c
index 05c9c55cd..e22ce8221 100644
--- a/src/fs/fs_tree.c
+++ b/src/fs/fs_tree.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors) 3 (C) 2009-2011 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -75,7 +75,7 @@ struct GNUNET_FS_TreeEncoder
75 * Set to the URI (upon successful completion) 75 * Set to the URI (upon successful completion)
76 */ 76 */
77 struct GNUNET_FS_Uri *uri; 77 struct GNUNET_FS_Uri *uri;
78 78
79 /** 79 /**
80 * Overall file size. 80 * Overall file size.
81 */ 81 */
@@ -87,12 +87,12 @@ struct GNUNET_FS_TreeEncoder
87 uint64_t publish_offset; 87 uint64_t publish_offset;
88 88
89 /** 89 /**
90 * How deep are we? 90 * How deep are we? Depth 0 is for the DBLOCKs.
91 */ 91 */
92 unsigned int current_depth; 92 unsigned int current_depth;
93 93
94 /** 94 /**
95 * How deep is the tree? 95 * How deep is the tree? Always > 0.
96 */ 96 */
97 unsigned int chk_tree_depth; 97 unsigned int chk_tree_depth;
98 98
@@ -121,7 +121,7 @@ struct GNUNET_FS_TreeEncoder
121 * Compute the depth of the CHK tree. 121 * Compute the depth of the CHK tree.
122 * 122 *
123 * @param flen file length for which to compute the depth 123 * @param flen file length for which to compute the depth
124 * @return depth of the tree 124 * @return depth of the tree, always > 0. A depth of 1 means only a DBLOCK.
125 */ 125 */
126unsigned int 126unsigned int
127GNUNET_FS_compute_depth (uint64_t flen) 127GNUNET_FS_compute_depth (uint64_t flen)
@@ -146,73 +146,53 @@ GNUNET_FS_compute_depth (uint64_t flen)
146 146
147 147
148/** 148/**
149 * Initialize a tree encoder. This function will call "proc" and 149 * Calculate how many bytes of payload a block tree of the given
150 * "progress" on each block in the tree. Once all blocks have been 150 * depth MAY correspond to at most (this function ignores the fact that
151 * processed, "cont" will be scheduled. The "reader" will be called 151 * some blocks will only be present partially due to the total file
152 * to obtain the (plaintext) blocks for the file. Note that this 152 * size cutting some blocks off at the end).
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.
156 * 153 *
157 * @param h the global FS context 154 * @param depth depth of the block. depth==0 is a DBLOCK.
158 * @param size overall size of the file to encode 155 * @return number of bytes of payload a subtree of this depth may correspond to
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
164 */ 156 */
165struct GNUNET_FS_TreeEncoder * 157uint64_t
166GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h, 158GNUNET_FS_tree_compute_tree_size (unsigned int depth)
167 uint64_t size,
168 void *cls,
169 GNUNET_FS_DataReader reader,
170 GNUNET_FS_TreeBlockProcessor proc,
171 GNUNET_FS_TreeProgressCallback progress,
172 GNUNET_SCHEDULER_Task cont)
173{ 159{
174 struct GNUNET_FS_TreeEncoder *te; 160 uint64_t rsize;
175 161 unsigned int i;
176 te = GNUNET_malloc (sizeof (struct GNUNET_FS_TreeEncoder)); 162
177 te->h = h; 163 rsize = DBLOCK_SIZE;
178 te->size = size; 164 for (i = 0; i<depth; i++)
179 te->cls = cls; 165 rsize *= CHK_PER_INODE;
180 te->reader = reader; 166 return rsize;
181 te->proc = proc;
182 te->progress = progress;
183 te->cont = cont;
184 te->chk_tree_depth = GNUNET_FS_compute_depth (size);
185 te->current_depth = te->chk_tree_depth;
186 te->chk_tree = GNUNET_malloc (te->chk_tree_depth *
187 CHK_PER_INODE *
188 sizeof (struct ContentHashKey));
189 return te;
190} 167}
191 168
192 169
193/** 170/**
194 * Compute the size of the current IBlock. 171 * Compute the size of the current IBLOCK. The encoder is
172 * triggering the calculation of the size of an IBLOCK at the
173 * *end* (hence end_offset) of its construction. The IBLOCK
174 * maybe a full or a partial IBLOCK, and this function is to
175 * calculate how long it should be.
195 * 176 *
196 * @param height height of the IBlock in the tree (aka overall 177 * @param depth depth of the IBlock in the tree, 0 would be a DBLOCK,
197 * number of tree levels minus depth); 0 == DBlock 178 * must be > 0 (this function is for IBLOCKs only!)
198 * @param offset current offset in the overall file 179 * @param end_offset current offset in the payload (!) of the overall file,
180 * must be > 0 (since this function is called at the
181 * end of a block).
199 * @return size of the corresponding IBlock 182 * @return size of the corresponding IBlock
200 */ 183 */
201uint16_t 184static uint16_t
202GNUNET_FS_tree_compute_iblock_size (unsigned int height, 185GNUNET_FS_tree_compute_iblock_size (unsigned int depth,
203 uint64_t offset) 186 uint64_t end_offset)
204{ 187{
205 unsigned int ret; 188 unsigned int ret;
206 unsigned int i;
207 uint64_t mod; 189 uint64_t mod;
208 uint64_t bds; 190 uint64_t bds;
209 191
210 GNUNET_assert (height > 0); 192 GNUNET_assert (depth > 0);
211 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i" 193 GNUNET_assert (end_offset > 0);
212 corresponds to */ 194 bds = GNUNET_FS_tree_compute_tree_size (depth);
213 for (i=0;i<height;i++) 195 mod = end_offset % bds;
214 bds *= CHK_PER_INODE;
215 mod = offset % bds;
216 if (0 == mod) 196 if (0 == mod)
217 { 197 {
218 /* we were triggered at the end of a full block */ 198 /* we were triggered at the end of a full block */
@@ -232,42 +212,40 @@ GNUNET_FS_tree_compute_iblock_size (unsigned int height,
232 212
233/** 213/**
234 * Compute how many bytes of data should be stored in 214 * Compute how many bytes of data should be stored in
235 * the specified node. 215 * the specified block.
236 * 216 *
237 * @param fsize overall file size 217 * @param fsize overall file size, must be > 0.
238 * @param totaldepth depth of the entire tree 218 * @param offset offset in the original data corresponding
239 * @param offset offset of the node 219 * to the beginning of the tree induced by the block;
240 * @param depth depth of the node 220 * must be <= fsize
221 * @param depth depth of the node in the tree, 0 for DBLOCK
241 * @return number of bytes stored in this node 222 * @return number of bytes stored in this node
242 */ 223 */
243size_t 224size_t
244GNUNET_FS_tree_calculate_block_size (uint64_t fsize, 225GNUNET_FS_tree_calculate_block_size (uint64_t fsize,
245 unsigned int totaldepth,
246 uint64_t offset, 226 uint64_t offset,
247 unsigned int depth) 227 unsigned int depth)
248{ 228{
249 unsigned int i;
250 size_t ret; 229 size_t ret;
251 uint64_t rsize; 230 uint64_t rsize;
252 uint64_t epos; 231 uint64_t epos;
253 unsigned int chks; 232 unsigned int chks;
254 233
234 GNUNET_assert (fsize > 0);
255 GNUNET_assert (offset <= fsize); 235 GNUNET_assert (offset <= fsize);
256 if (depth == totaldepth) 236 if (depth == 0)
257 { 237 {
258 ret = DBLOCK_SIZE; 238 ret = DBLOCK_SIZE;
259 if (offset + ret > fsize) 239 if ( (offset + ret > fsize) ||
240 (offset + ret < offset) )
260 ret = (size_t) (fsize - offset); 241 ret = (size_t) (fsize - offset);
261 return ret; 242 return ret;
262 } 243 }
263 /* FIXME: this code should be *equivalent* to the 244
264 GNUNET_FS_tree_compute_iblock_size function above! Remove duplication! */ 245 rsize = GNUNET_FS_tree_compute_tree_size (depth - 1);
265 rsize = DBLOCK_SIZE;
266 for (i = totaldepth-1; i > depth; i--)
267 rsize *= CHK_PER_INODE;
268 epos = offset + rsize * CHK_PER_INODE; 246 epos = offset + rsize * CHK_PER_INODE;
269 GNUNET_assert (epos > offset); 247 if ( (epos < offset) ||
270 if (epos > fsize) 248 (epos > fsize) )
271 epos = fsize; 249 epos = fsize;
272 /* round up when computing #CHKs in our IBlock */ 250 /* round up when computing #CHKs in our IBlock */
273 chks = (epos - offset + rsize - 1) / rsize; 251 chks = (epos - offset + rsize - 1) / rsize;
@@ -277,29 +255,71 @@ GNUNET_FS_tree_calculate_block_size (uint64_t fsize,
277 255
278 256
279/** 257/**
258 * Initialize a tree encoder. This function will call "proc" and
259 * "progress" on each block in the tree. Once all blocks have been
260 * processed, "cont" will be scheduled. The "reader" will be called
261 * to obtain the (plaintext) blocks for the file. Note that this
262 * function will not actually call "proc". The client must
263 * call "GNUNET_FS_tree_encoder_next" to trigger encryption (and
264 * calling of "proc") for the each block.
265 *
266 * @param h the global FS context
267 * @param size overall size of the file to encode
268 * @param cls closure for reader, proc, progress and cont
269 * @param reader function to call to read plaintext data
270 * @param proc function to call on each encrypted block
271 * @param progress function to call with progress information
272 * @param cont function to call when done
273 */
274struct GNUNET_FS_TreeEncoder *
275GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h,
276 uint64_t size,
277 void *cls,
278 GNUNET_FS_DataReader reader,
279 GNUNET_FS_TreeBlockProcessor proc,
280 GNUNET_FS_TreeProgressCallback progress,
281 GNUNET_SCHEDULER_Task cont)
282{
283 struct GNUNET_FS_TreeEncoder *te;
284
285 te = GNUNET_malloc (sizeof (struct GNUNET_FS_TreeEncoder));
286 te->h = h;
287 te->size = size;
288 te->cls = cls;
289 te->reader = reader;
290 te->proc = proc;
291 te->progress = progress;
292 te->cont = cont;
293 te->chk_tree_depth = GNUNET_FS_compute_depth (size);
294 te->chk_tree = GNUNET_malloc (te->chk_tree_depth *
295 CHK_PER_INODE *
296 sizeof (struct ContentHashKey));
297 return te;
298}
299
300
301/**
280 * Compute the offset of the CHK for the 302 * Compute the offset of the CHK for the
281 * current block in the IBlock above. 303 * current block in the IBlock above.
282 * 304 *
283 * @param height height of the IBlock in the tree (aka overall 305 * @param depth depth of the IBlock in the tree (aka overall
284 * number of tree levels minus depth); 0 == DBlock 306 * number of tree levels minus depth); 0 == DBlock
285 * @param offset current offset in the overall file 307 * @param end_offset current offset in the overall file,
308 * at the *beginning* of the block for DBLOCKs (depth==0),
309 * otherwise at the *end* of the block (exclusive)
286 * @return (array of CHKs') offset in the above IBlock 310 * @return (array of CHKs') offset in the above IBlock
287 */ 311 */
288static unsigned int 312static unsigned int
289compute_chk_offset (unsigned int height, 313compute_chk_offset (unsigned int depth,
290 uint64_t offset) 314 uint64_t end_offset)
291{ 315{
292 uint64_t bds; 316 uint64_t bds;
293 unsigned int ret; 317 unsigned int ret;
294 unsigned int i;
295 318
296 bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i" 319 bds = GNUNET_FS_tree_compute_tree_size (depth);
297 corresponds to */ 320 if (depth > 0)
298 for (i=0;i<height;i++) 321 end_offset--; /* round down since for depth > 0 offset is at the END of the block */
299 bds *= CHK_PER_INODE; 322 ret = end_offset / bds;
300 if (height > 0)
301 offset--; /* round down since for height > 0 offset is at the END of the block */
302 ret = offset / bds;
303 return ret % CHK_PER_INODE; 323 return ret % CHK_PER_INODE;
304} 324}
305 325
@@ -325,8 +345,26 @@ GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
325 345
326 GNUNET_assert (GNUNET_NO == te->in_next); 346 GNUNET_assert (GNUNET_NO == te->in_next);
327 te->in_next = GNUNET_YES; 347 te->in_next = GNUNET_YES;
328 if (te->current_depth == te->chk_tree_depth) 348 if (te->chk_tree_depth == te->current_depth)
349 {
350 off = CHK_PER_INODE * (te->chk_tree_depth - 1);
351#if DEBUG_TREE
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "TE done, reading CHK `%s' from %u\n",
354 GNUNET_h2s (&te->chk_tree[off].query),
355 off);
356#endif
357 te->uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
358 te->uri->type = chk;
359 te->uri->data.chk.chk = te->chk_tree[off];
360 te->uri->data.chk.file_length = GNUNET_htonll (te->size);
361 te->in_next = GNUNET_NO;
362 te->cont (te->cls, NULL);
363 return;
364 }
365 if (0 == te->current_depth)
329 { 366 {
367 /* read DBLOCK */
330 pt_size = GNUNET_MIN(DBLOCK_SIZE, 368 pt_size = GNUNET_MIN(DBLOCK_SIZE,
331 te->size - te->publish_offset); 369 te->size - te->publish_offset);
332 if (pt_size != 370 if (pt_size !=
@@ -346,24 +384,12 @@ GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
346 } 384 }
347 else 385 else
348 { 386 {
349 pt_size = GNUNET_FS_tree_compute_iblock_size (te->chk_tree_depth - te->current_depth, 387 pt_size = GNUNET_FS_tree_compute_iblock_size (te->current_depth,
350 te->publish_offset); 388 te->publish_offset);
351 pt_block = &te->chk_tree[te->current_depth * 389 pt_block = &te->chk_tree[(te->current_depth - 1) *
352 CHK_PER_INODE]; 390 CHK_PER_INODE];
353 } 391 }
354 if (0 == te->current_depth) 392 off = compute_chk_offset (te->current_depth,
355 {
356 te->uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
357 te->uri->type = chk;
358 te->uri->data.chk.chk = te->chk_tree[0];
359 te->uri->data.chk.file_length = GNUNET_htonll (te->size);
360 GNUNET_SCHEDULER_add_continuation (te->cont,
361 te->cls,
362 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
363 te->in_next = GNUNET_NO;
364 return;
365 }
366 off = compute_chk_offset (te->chk_tree_depth - te->current_depth,
367 te->publish_offset); 393 te->publish_offset);
368#if DEBUG_TREE 394#if DEBUG_TREE
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 395 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -373,7 +399,7 @@ GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
373 (unsigned int) pt_size, 399 (unsigned int) pt_size,
374 (unsigned int) off); 400 (unsigned int) off);
375#endif 401#endif
376 mychk = &te->chk_tree[(te->current_depth-1)*CHK_PER_INODE+off]; 402 mychk = &te->chk_tree[te->current_depth*CHK_PER_INODE+off];
377 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key); 403 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
378 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv); 404 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
379 GNUNET_CRYPTO_aes_encrypt (pt_block, 405 GNUNET_CRYPTO_aes_encrypt (pt_block,
@@ -384,15 +410,16 @@ GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
384 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query); 410 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
385#if DEBUG_TREE 411#if DEBUG_TREE
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387 "TE calculates query to be `%s'\n", 413 "TE calculates query to be `%s', stored at %u\n",
388 GNUNET_h2s (&mychk->query)); 414 GNUNET_h2s (&mychk->query),
415 te->current_depth * CHK_PER_INODE + off);
389#endif 416#endif
390 if (NULL != te->proc) 417 if (NULL != te->proc)
391 te->proc (te->cls, 418 te->proc (te->cls,
392 &mychk->query, 419 mychk,
393 te->publish_offset, 420 te->publish_offset,
394 te->current_depth, 421 te->current_depth,
395 (te->current_depth == te->chk_tree_depth) 422 (0 == te->current_depth)
396 ? GNUNET_BLOCK_TYPE_FS_DBLOCK 423 ? GNUNET_BLOCK_TYPE_FS_DBLOCK
397 : GNUNET_BLOCK_TYPE_FS_IBLOCK, 424 : GNUNET_BLOCK_TYPE_FS_IBLOCK,
398 enc, 425 enc,
@@ -403,20 +430,20 @@ GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
403 pt_block, 430 pt_block,
404 pt_size, 431 pt_size,
405 te->current_depth); 432 te->current_depth);
406 if (te->current_depth == te->chk_tree_depth) 433 if (0 == te->current_depth)
407 { 434 {
408 te->publish_offset += pt_size; 435 te->publish_offset += pt_size;
409 if ( (te->publish_offset == te->size) || 436 if ( (te->publish_offset == te->size) ||
410 (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) ) 437 (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
411 te->current_depth--; 438 te->current_depth++;
412 } 439 }
413 else 440 else
414 { 441 {
415 if ( (off == CHK_PER_INODE) || 442 if ( (off == CHK_PER_INODE) ||
416 (te->publish_offset == te->size) ) 443 (te->publish_offset == te->size) )
417 te->current_depth--; 444 te->current_depth++;
418 else 445 else
419 te->current_depth = te->chk_tree_depth; 446 te->current_depth = 0;
420 } 447 }
421 te->in_next = GNUNET_NO; 448 te->in_next = GNUNET_NO;
422} 449}
@@ -433,10 +460,11 @@ GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder * te)
433 * prior to completion and prior to an internal error, 460 * prior to completion and prior to an internal error,
434 * both "*uri" and "*emsg" will be set to NULL). 461 * both "*uri" and "*emsg" will be set to NULL).
435 */ 462 */
436void GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder * te, 463void GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder *te,
437 struct GNUNET_FS_Uri **uri, 464 struct GNUNET_FS_Uri **uri,
438 char **emsg) 465 char **emsg)
439{ 466{
467 GNUNET_assert (GNUNET_NO == te->in_next);
440 if (uri != NULL) 468 if (uri != NULL)
441 *uri = te->uri; 469 *uri = te->uri;
442 else 470 else