static void tt_finish(TTH_CONTEXT *ctx) { if (0 == ctx->n || ctx->block_fill > 1) { tt_block(ctx); } if (ctx->bpl > 1) { fileoffset_t n_blocks; unsigned depth; n_blocks = ctx->n; depth = ctx->depth; while (0 == (n_blocks & 1)) { n_blocks /= 2; depth--; } while (n_blocks > 1) { if (0 == (n_blocks & 1)) { tt_compose(ctx); } depth--; n_blocks = (n_blocks + 1) / 2; if (depth == ctx->good_depth) { g_assert(ctx->li < G_N_ELEMENTS(ctx->leaves)); ctx->leaves[ctx->li] = ctx->stack[ctx->si - 1]; ctx->li++; } } } ctx->stack[0] = tt_root_hash(ctx->leaves, ctx->li); ctx->flags |= TTH_F_FINISHED; }
void tth_cache_insert(const struct tth *tth, const struct tth *leaves, int n_leaves) { int fd; g_return_if_fail(tth); g_return_if_fail(leaves); g_return_if_fail(n_leaves >= 1); { struct tth root; root = tt_root_hash(leaves, n_leaves); g_return_if_fail(tth_eq(tth, &root)); } if (1 == n_leaves) return; fd = tth_cache_file_create(tth); if (fd >= 0) { size_t size; ssize_t ret; STATIC_ASSERT(TTH_RAW_SIZE == sizeof(leaves[0])); size = TTH_RAW_SIZE * n_leaves; ret = write(fd, leaves, size); if ((ssize_t) -1 == ret) { g_warning("%s(%s): write() failed: %m", G_STRFUNC, tth_base32(tth)); } else if ((size_t) ret != size) { g_warning("%s(%s): incomplete write()", G_STRFUNC, tth_base32(tth)); } fd_forget_and_close(&fd); } }
static bool thex_download_handle_hashtree(struct thex_download *ctx, const char *data, size_t size) { bool success = FALSE; size_t n_nodes, n_leaves, n, start; unsigned good_depth; const struct tth *leaves; struct tth tth; if (size <= 0) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH hashtree record has no data"); } goto finish; } if (size < TTH_RAW_SIZE) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH hashtree record is too small"); } goto finish; } if (size % TTH_RAW_SIZE) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH hashtree has bad size"); } goto finish; } memcpy(tth.data, data, TTH_RAW_SIZE); if (!tth_eq(&tth, ctx->tth)) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH hashtree has different root hash %s", tth_base32(&tth)); } goto finish; } n_nodes = size / TTH_RAW_SIZE; n_leaves = tt_node_count_at_depth(ctx->filesize, ctx->depth); /* Shareaza uses a fixed depth of 9, allow one level less like others */ good_depth = tt_good_depth(ctx->filesize); ctx->depth = MIN(ctx->depth, good_depth); if (n_nodes < n_leaves * 2 - 1) { ctx->depth = good_depth; n = tt_node_count_at_depth(ctx->filesize, ctx->depth); n = n * 2 - 1; /* All nodes, not just leaves */ while (n > n_nodes) { n = (n + 1) / 2; ctx->depth--; } if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH calculated depth of hashtree: %u", ctx->depth); } n_leaves = tt_node_count_at_depth(ctx->filesize, ctx->depth); } if (ctx->depth < good_depth) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH tree depth (%u) is below the good depth (%u)", ctx->depth, good_depth); } } start = 0; n = n_leaves; while (n > 1) { n = (n + 1) / 2; start += n; } if (n_nodes < start + n_leaves) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH hashtree has too few nodes " "(filesize=%s depth=%u nodes=%zu expected=%zu)", filesize_to_string(ctx->filesize), ctx->depth, n_nodes, n_leaves * 2 - 1); } goto finish; } STATIC_ASSERT(TTH_RAW_SIZE == sizeof(struct tth)); leaves = (const struct tth *) &data[start * TTH_RAW_SIZE]; tth = tt_root_hash(leaves, n_leaves); if (!tth_eq(&tth, ctx->tth)) { if (GNET_PROPERTY(tigertree_debug)) { g_debug("TTH hashtree does not match root hash %s", tth_base32(&tth)); } goto finish; } ctx->leaves = g_memdup(leaves, TTH_RAW_SIZE * n_leaves); ctx->num_leaves = n_leaves; success = TRUE; finish: return success; }