static bool request_tigertree_callback(const struct verify *ctx, enum verify_status status, void *user_data) { shared_file_t *sf = user_data; shared_file_check(sf); switch (status) { case VERIFY_START: if (!(SHARE_F_INDEXED & shared_file_flags(sf))) { /* * After a rescan, there might be files in the queue which are * no longer shared. */ if (GNET_PROPERTY(verify_debug) > 1) { g_debug("skipping TTH computation for %s: no longer shared", shared_file_path(sf)); } return FALSE; } if ( shared_file_tth(sf) && tth_cache_lookup(shared_file_tth(sf), shared_file_size(sf)) > 0 ) { if ( GNET_PROPERTY(tigertree_debug) > 1 || GNET_PROPERTY(verify_debug) > 1 ) { g_debug("TTH for %s is already cached (%s)", shared_file_path(sf), tth_base32(shared_file_tth(sf))); } return FALSE; } gnet_prop_set_boolean_val(PROP_TTH_REBUILDING, TRUE); return TRUE; case VERIFY_PROGRESS: return 0 != (SHARE_F_INDEXED & shared_file_flags(sf)); case VERIFY_DONE: { const struct tth *tth = verify_tth_digest(ctx); huge_update_hashes(sf, shared_file_sha1(sf), tth); tth_cache_insert(tth, verify_tth_leaves(ctx), verify_tth_leave_count(ctx)); } /* FALL THROUGH */ case VERIFY_ERROR: case VERIFY_SHUTDOWN: shared_file_unref(&sf); gnet_prop_set_boolean_val(PROP_TTH_REBUILDING, FALSE); return TRUE; case VERIFY_INVALID: break; } g_assert_not_reached(); return FALSE; }
size_t thex_upload_get_content_length(const shared_file_t *sf) { const struct tth *tth; size_t n_leaves, n_nodes; size_t size = 0; g_return_val_if_fail(sf, 0); tth = shared_file_tth(sf); g_return_val_if_fail(tth, 0); size += thex_upload_prepare_xml(NULL, tth, shared_file_size(sf)); n_nodes = 1; n_leaves = tt_good_node_count(shared_file_size(sf)); while (n_leaves > 1) { n_nodes += n_leaves; n_leaves = (n_leaves + 1) / 2; } size += thex_upload_prepare_tree(NULL, tth, NULL, n_nodes); return size; }
/** * Creates a new THEX upload context. The context must be freed with * thex_upload_close(). * * @param owner the owner of the TX stack (the upload) * @param host the host to which we're talking to * @param writable no document * @param link_cb callbacks for the link layer * @param wio no document * @param flags opening flags * * @return An initialized THEX upload context. */ struct special_upload * thex_upload_open( void *owner, const struct gnutella_host *host, const shared_file_t *sf, special_upload_writable_t writable, const struct tx_link_cb *link_cb, struct wrap_io *wio, int flags) { struct thex_upload *ctx; WALLOC(ctx); ctx->special_upload.read = thex_upload_read; ctx->special_upload.write = thex_upload_write; ctx->special_upload.flush = thex_upload_flush; ctx->special_upload.close = thex_upload_close; ctx->tth = atom_tth_get(shared_file_tth(sf)); ctx->filesize = shared_file_size(sf); ctx->data = NULL; ctx->size = 0; ctx->offset = 0; ctx->state = THEX_STATE_INITIAL; /* * Instantiate the TX stack. */ { struct tx_link_args args; args.cb = link_cb; args.wio = wio; args.bws = bsched_out_select_by_addr(gnet_host_get_addr(host)); ctx->tx = tx_make(owner, host, tx_link_get_ops(), &args); } if (flags & THEX_UPLOAD_F_CHUNKED) { ctx->tx = tx_make_above(ctx->tx, tx_chunk_get_ops(), 0); } /* * Put stack in "eager" mode: we want to be notified whenever * we can write something. */ tx_srv_register(ctx->tx, writable, owner); tx_eager_mode(ctx->tx, TRUE); /* * Update statistics. */ gnet_prop_incr_guint32(PROP_THEX_FILES_REQUESTED); return &ctx->special_upload; }
/** * Add file to the current query hit. * * @return TRUE if we kept the file, FALSE if we did not include it in the hit. */ static bool g2_build_qh2_add(struct g2_qh2_builder *ctx, const shared_file_t *sf) { const sha1_t *sha1; g2_tree_t *h, *c; shared_file_check(sf); /* * Make sure the file is still in the library. */ if (0 == shared_file_index(sf)) return FALSE; /* * On G2, the H/URN child is required, meaning we need the SHA1 at least. */ if (!sha1_hash_available(sf)) return FALSE; /* * Do not send duplicates, as determined by the SHA1 of the resource. * * A user may share several files with different names but the same SHA1, * and if all of them are hits, we only want to send one instance. * * When generating hits for host-browsing, we do not care about duplicates * and ctx->hs is NULL then. */ sha1 = shared_file_sha1(sf); /* This is an atom */ if (ctx->hs != NULL) { if (hset_contains(ctx->hs, sha1)) return FALSE; hset_insert(ctx->hs, sha1); } /* * Create the "H" child and attach it to the current tree. */ if (NULL == ctx->t) g2_build_qh2_start(ctx); h = g2_tree_alloc_empty("H"); g2_tree_add_child(ctx->t, h); /* * URN -- Universal Resource Name * * If there is a known TTH, then we can generate a bitprint, otherwise * we just convey the SHA1. */ { const tth_t * const tth = shared_file_tth(sf); char payload[SHA1_RAW_SIZE + TTH_RAW_SIZE + sizeof G2_URN_BITPRINT]; char *p = payload; if (NULL == tth) { p = mempcpy(p, G2_URN_SHA1, sizeof G2_URN_SHA1); p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload), sha1, SHA1_RAW_SIZE); } else { p = mempcpy(p, G2_URN_BITPRINT, sizeof G2_URN_BITPRINT); p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload), sha1, SHA1_RAW_SIZE); p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload), tth, TTH_RAW_SIZE); } g_assert(ptr_diff(p, payload) <= sizeof payload); c = g2_tree_alloc_copy("URN", payload, ptr_diff(p, payload)); g2_tree_add_child(h, c); } /* * URL -- empty to indicate that we share the file via uri-res. */ if (ctx->flags & QHIT_F_G2_URL) { uint known; uint16 csc; c = g2_tree_alloc_empty("URL"); g2_tree_add_child(h, c); /* * CSC -- if we know alternate sources, indicate how many in "CSC". * * This child is only emitted when they requested "URL". */ known = dmesh_count(sha1); csc = MIN(known, MAX_INT_VAL(uint16)); if (csc != 0) { char payload[2]; poke_le16(payload, csc); c = g2_tree_alloc_copy("CSC", payload, sizeof payload); g2_tree_add_child(h, c); } /* * PART -- if we only have a partial file, indicate how much we have. * * This child is only emitted when they requested "URL". */ if (shared_file_is_partial(sf) && !shared_file_is_finished(sf)) { filesize_t available = shared_file_available(sf); char payload[8]; /* If we have to encode file size as 64-bit */ uint32 av32; time_t mtime = shared_file_modification_time(sf); c = g2_tree_alloc_empty("PART"); g2_tree_add_child(h, c); av32 = available; if (av32 == available) { /* Fits within a 32-bit quantity */ poke_le32(payload, av32); g2_tree_set_payload(c, payload, sizeof av32, TRUE); } else { /* Encode as a 64-bit quantity then */ poke_le64(payload, available); g2_tree_set_payload(c, payload, sizeof payload, TRUE); } /* * GTKG extension: encode the last modification time of the * partial file in an "MT" child. This lets the other party * determine whether the host is still able to actively complete * the file. */ poke_le32(payload, (uint32) mtime); g2_tree_add_child(c, g2_tree_alloc_copy("MT", payload, sizeof(uint32))); } /* * CT -- creation time of the resource (GTKG extension). */ { time_t create_time = shared_file_creation_time(sf); if ((time_t) -1 != create_time) { char payload[8]; int n; create_time = MAX(0, create_time); n = vlint_encode(create_time, payload); g2_tree_add_child(h, g2_tree_alloc_copy("CT", payload, n)); /* No trailing 0s */ } } } /* * DN -- distinguished name. * * Note that the presence of DN also governs the presence of SZ if the * file length does not fit a 32-bit unsigned quantity. */ if (ctx->flags & QHIT_F_G2_DN) { char payload[8]; /* If we have to encode file size as 64-bit */ uint32 fs32; filesize_t fs = shared_file_size(sf); const char *name; const char *rp; c = g2_tree_alloc_empty("DN"); fs32 = fs; if (fs32 == fs) { /* Fits within a 32-bit quantity */ poke_le32(payload, fs32); g2_tree_set_payload(c, payload, sizeof fs32, TRUE); } else { /* Does not fit a 32-bit quantity, emit a SZ child */ poke_le64(payload, fs); g2_tree_add_child(h, g2_tree_alloc_copy("SZ", payload, sizeof payload)); } name = shared_file_name_nfc(sf); g2_tree_append_payload(c, name, shared_file_name_nfc_len(sf)); g2_tree_add_child(h, c); /* * GTKG extension: if there is a file path, expose it as a "P" child * under the DN node. */ rp = shared_file_relative_path(sf); if (rp != NULL) { g2_tree_add_child(c, g2_tree_alloc_copy("P", rp, strlen(rp))); } } /* * GTKG extension: if they requested alt-locs in the /Q2/I with "A", then * send them some known alt-locs in an "ALT" child. * * Note that these alt-locs can be for Gnutella hosts: since both Gnutella * and G2 share a common HTTP-based file transfer mechanism with compatible * extra headers, there is no need to handle them separately. */ if (ctx->flags & QHIT_F_G2_ALT) { gnet_host_t hvec[G2_BUILD_QH2_MAX_ALT]; int hcnt = 0; hcnt = dmesh_fill_alternate(sha1, hvec, N_ITEMS(hvec)); if (hcnt > 0) { int i; c = g2_tree_alloc_empty("ALT"); for (i = 0; i < hcnt; i++) { host_addr_t addr; uint16 port; addr = gnet_host_get_addr(&hvec[i]); port = gnet_host_get_port(&hvec[i]); if (host_addr_is_ipv4(addr)) { char payload[6]; host_ip_port_poke(payload, addr, port, NULL); g2_tree_append_payload(c, payload, sizeof payload); } } /* * If the payload is still empty, then drop the "ALT" child. * Otherwise, attach it to the "H" node. */ if (NULL == g2_tree_node_payload(c, NULL)) { g2_tree_free_null(&c); } else { g2_tree_add_child(h, c); } } } /* * Update the size of the query hit we're generating. */ ctx->current_size += g2_frame_serialize(h, NULL, 0); return TRUE; }
static bool request_tigertree_callback(const struct verify *ctx, enum verify_status status, void *user_data) { shared_file_t *sf = user_data; shared_file_check(sf); switch (status) { case VERIFY_START: if (!shared_file_is_servable(sf)) { /* * After a rescan, there might be files in the queue which are * no longer shared. */ if (GNET_PROPERTY(verify_debug) > 1) { g_debug("skipping TTH computation for %s: not a servable file", shared_file_path(sf)); } return FALSE; } if (shared_file_tth_is_available(sf)) { if ( GNET_PROPERTY(tigertree_debug) > 1 || GNET_PROPERTY(verify_debug) > 1 ) { g_debug("TTH for %s is already cached (%s)", shared_file_path(sf), tth_base32(shared_file_tth(sf))); } return FALSE; } gnet_prop_set_boolean_val(PROP_TTH_REBUILDING, TRUE); return TRUE; case VERIFY_PROGRESS: /* * Processing can continue whilst the library file is indexed or the * completed file is still beeing seeded. */ return shared_file_is_servable(sf); case VERIFY_DONE: { const struct tth *tth = verify_tth_digest(ctx); size_t n_leaves = verify_tth_leave_count(ctx); if (GNET_PROPERTY(verify_debug)) { g_debug("%s(): computed TTH %s (%zu lea%s) for %s", G_STRFUNC, tth_base32(tth), n_leaves, plural_f(n_leaves), shared_file_path(sf)); } /* * Write the TTH to the cache first, before updating the hashes. * That way, the logic behind huge_update_hashes() can rely on * the fact that the TTH is persisted already. * * This is important for seeded files for which we re-compute * the TTH once they are completed (to make sure we can serve * THEX requests at the proper good depth). In order to update * the GUI information, we'll need to probe the cache to determine * how large the TTH is exactly, since all we pass back to the * routines is the TTH root hash. * --RAM, 2017-10-20 */ tth_cache_insert(tth, verify_tth_leaves(ctx), n_leaves); huge_update_hashes(sf, shared_file_sha1(sf), tth); } goto done; case VERIFY_ERROR: if (GNET_PROPERTY(verify_debug)) { g_debug("%s(): unable to compute TTH for %s", G_STRFUNC, shared_file_path(sf)); } /* FALL THROUGH */ case VERIFY_SHUTDOWN: goto done; case VERIFY_INVALID: break; } g_assert_not_reached(); return FALSE; done: shared_file_unref(&sf); gnet_prop_set_boolean_val(PROP_TTH_REBUILDING, FALSE); return TRUE; }