paulo@0: /* paulo@0: * $Id: gt_share.c,v 1.35 2004/03/26 11:53:18 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "sha1.h" paulo@0: paulo@0: #include "gt_share.h" paulo@0: #include "gt_share_file.h" paulo@0: #include "gt_query_route.h" paulo@0: #include "gt_search.h" paulo@0: #include "gt_search_exec.h" paulo@0: paulo@0: #include "encoding/url.h" paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: #define SHARE_DEBUG gt_config_get_int("share/debug=0") paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* each share is assigned an index here */ paulo@0: static Dataset *indices; paulo@0: paulo@0: /* maps binary sha1 hashes -> FileShares */ paulo@0: static Dataset *sha1_hashes; paulo@0: paulo@0: /* stored index of the last index assigned */ paulo@0: static uint32_t index_counter; paulo@0: paulo@0: /* whether shares have been completely synchronized yet */ paulo@0: static BOOL sync_begun; paulo@0: paulo@0: /* if shares are currently being synced */ paulo@0: static BOOL sync_done; paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: static void add_hash (FileShare *file); paulo@0: static void remove_hash (FileShare *file); paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Find the old index, using the SHA1 hash as a key. paulo@0: */ paulo@0: static uint32_t get_old_index (Hash *hash) paulo@0: { paulo@0: Share *old_share; paulo@0: uint32_t index = 0; paulo@0: paulo@0: old_share = dataset_lookup (sha1_hashes, hash->data, SHA1_BINSIZE); paulo@0: if (old_share != NULL) paulo@0: { paulo@0: GtShare *gt_share = share_get_udata (old_share, GT->name); paulo@0: paulo@0: if (gt_share) paulo@0: index = gt_share->index; paulo@0: } paulo@0: paulo@0: return index; paulo@0: } paulo@0: paulo@0: /* paulo@0: * Find an unused index, but reuse the existing index if possible. paulo@0: */ paulo@0: static uint32_t get_share_index (Share *share) paulo@0: { paulo@0: Hash *hash; paulo@0: uint32_t index; paulo@0: paulo@0: if ((hash = share_get_hash (share, "SHA1")) != NULL) paulo@0: { paulo@0: uint32_t hash_tmp; paulo@0: paulo@0: /* if a Share with the same hash has another index, re-use that one */ paulo@0: if ((index = get_old_index (hash)) != 0) paulo@0: return index; paulo@0: paulo@0: memcpy (&hash_tmp, hash->data, 4); paulo@0: paulo@0: /* use the first 24 bits of the SHA1 hash to seed the file index */ paulo@0: index_counter = hash_tmp & 0xfffffff; paulo@0: } paulo@0: paulo@0: if (!(index = dataset_uniq32 (indices, &index_counter))) paulo@0: return 0; paulo@0: paulo@0: return index; paulo@0: } paulo@0: paulo@0: static void add_index (Share *share, GtShare *gt_share) paulo@0: { paulo@0: uint32_t index; paulo@0: paulo@0: if (SHARE_DEBUG) paulo@0: GT->dbg (GT, "++[%d]->%s", gt_share->index, gt_share->filename); paulo@0: paulo@0: index = get_share_index (share); paulo@0: dataset_insert (&indices, &index, sizeof(index), share, 0); paulo@0: } paulo@0: paulo@0: static void remove_index (Share *share, GtShare *gt_share) paulo@0: { paulo@0: uint32_t index = gt_share->index; paulo@0: paulo@0: assert (index > 0); paulo@0: paulo@0: /* paulo@0: * Check if the index is pointing at a different share. This case happens paulo@0: * for every Share that is not removed on a resync, due to the weird way paulo@0: * giftd 0.11.x use the new/add/remove/free interface. paulo@0: */ paulo@0: if (dataset_lookup (indices, &index, sizeof(index)) != share) paulo@0: return; paulo@0: paulo@0: if (SHARE_DEBUG) paulo@0: GT->dbg (GT, "--[%d]->%s", gt_share->index, gt_share->filename); paulo@0: paulo@0: index = gt_share->index; paulo@0: dataset_remove (indices, &index, sizeof(index)); paulo@0: paulo@0: if (dataset_length (indices) == 0) paulo@0: { paulo@0: dataset_clear (indices); paulo@0: indices = NULL; paulo@0: } paulo@0: } paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: /* TODO: This duplicates memory thats already stored on the FileShare. paulo@0: * Prevent this by maintaining a per-hash algorithm map of FileShares */ paulo@0: static void add_hash (FileShare *file) paulo@0: { paulo@0: Hash *hash; paulo@0: ds_data_t key; paulo@0: ds_data_t value; paulo@0: paulo@0: if (!(hash = share_get_hash (file, "SHA1"))) paulo@0: return; paulo@0: paulo@0: /* This shares the hash memory with the FileShare and also paulo@0: * points directly at it. */ paulo@0: ds_data_init (&key, hash->data, hash->len, DS_NOCOPY); paulo@0: ds_data_init (&value, file, 0, DS_NOCOPY); paulo@0: paulo@0: /* We share the key with the FileShare, so remove the old key first paulo@0: * so we don't end up sharing an old FileShare's hash. */ paulo@0: dataset_remove_ex (sha1_hashes, &key); paulo@0: dataset_insert_ex (&sha1_hashes, &key, &value); paulo@0: } paulo@0: paulo@0: static void remove_hash (FileShare *file) paulo@0: { paulo@0: Hash *hash; paulo@0: paulo@0: if (!(hash = share_get_hash (file, "SHA1"))) paulo@0: return; paulo@0: paulo@0: /* paulo@0: * If a FileShare is already present at this hash, and it isn't paulo@0: * this FileShare, then don't remove it. This _will_ happen paulo@0: * due to the way FileShares get added and removed on resyncs. paulo@0: */ paulo@0: if (dataset_lookup (sha1_hashes, hash->data, hash->len) != file) paulo@0: return; paulo@0: paulo@0: dataset_remove (sha1_hashes, hash->data, hash->len); paulo@0: paulo@0: if (dataset_length (sha1_hashes) == 0) paulo@0: { paulo@0: dataset_clear (sha1_hashes); paulo@0: sha1_hashes = NULL; paulo@0: } paulo@0: } paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: static GtShare *gt_share_local_add (FileShare *file) paulo@0: { paulo@0: GtShare *share; paulo@0: uint32_t index; paulo@0: paulo@0: if (share_get_udata (file, GT->name)) paulo@0: return NULL; paulo@0: paulo@0: index = get_share_index (file); paulo@0: paulo@0: if (!(share = gt_share_new_data (file, index))) paulo@0: return NULL; paulo@0: paulo@0: add_hash (file); paulo@0: add_index (file, share); paulo@0: paulo@0: return share; paulo@0: } paulo@0: paulo@0: static void gt_share_local_remove (FileShare *file, GtShare *share) paulo@0: { paulo@0: remove_index (file, share); paulo@0: remove_hash (file); paulo@0: paulo@0: gt_share_free_data (file, share); paulo@0: } paulo@0: paulo@0: static int find_by_index (ds_data_t *key, ds_data_t *value, void **args) paulo@0: { paulo@0: uint32_t *index = args[0]; paulo@0: char *filename = args[1]; paulo@0: FileShare **ret = args[2]; paulo@0: FileShare *file = value->data; paulo@0: GtShare *share; paulo@0: paulo@0: if (!file || !(share = share_get_udata (file, GT->name))) paulo@0: return DS_CONTINUE; paulo@0: paulo@0: if (share->index == *index && paulo@0: (!filename || !strcmp (filename, share->filename))) paulo@0: { paulo@0: *ret = file; paulo@0: return DS_BREAK; paulo@0: } paulo@0: paulo@0: return DS_CONTINUE; paulo@0: } paulo@0: paulo@0: FileShare *gt_share_local_lookup_by_index (uint32_t index, char *filename) paulo@0: { paulo@0: FileShare *ret = NULL; paulo@0: void *args[] = { &index, filename, &ret }; paulo@0: paulo@0: share_foreach (DS_FOREACH_EX(find_by_index), args); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: static FileShare *lookup_sha1 (char *urn) paulo@0: { paulo@0: char *str, *str0; paulo@0: char *prefix; paulo@0: unsigned char *bin; paulo@0: FileShare *file; paulo@0: paulo@0: if (!(str0 = str = STRDUP (urn))) paulo@0: return NULL; paulo@0: paulo@0: /* TODO: consolidate with gt_protocol.c:parse_extended_data */ paulo@0: string_upper (str0); paulo@0: string_sep (&str, "URN:"); paulo@0: paulo@0: prefix = string_sep (&str, ":"); paulo@0: paulo@0: /* Only support urn:sha1 or urn:sha-1 urns now */ paulo@0: if (STRCMP (prefix, "SHA1") != 0 && STRCMP (prefix, "SHA-1") != 0) paulo@0: { paulo@0: free (str0); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: string_trim (str); paulo@0: paulo@0: if (strlen (str) != 32) paulo@0: { paulo@0: free (str0); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: if (!(bin = sha1_bin (str))) paulo@0: { paulo@0: free (str0); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: file = dataset_lookup (sha1_hashes, bin, SHA1_BINSIZE); paulo@0: paulo@0: free (str0); paulo@0: free (bin); paulo@0: paulo@0: return file; paulo@0: } paulo@0: paulo@0: FileShare *gt_share_local_lookup_by_urn (char *urn) paulo@0: { paulo@0: return lookup_sha1 (urn); paulo@0: } paulo@0: paulo@0: static char *get_sha1 (FileShare *file) paulo@0: { paulo@0: Hash *hash; paulo@0: char *urn; paulo@0: char *str; paulo@0: paulo@0: if (!(hash = share_get_hash (file, "SHA1"))) paulo@0: return NULL; paulo@0: paulo@0: assert (hash->len == SHA1_BINSIZE); paulo@0: paulo@0: if (!(str = sha1_string (hash->data))) paulo@0: return NULL; paulo@0: paulo@0: urn = stringf_dup ("urn:sha1:%s", str); paulo@0: free (str); paulo@0: paulo@0: return urn; paulo@0: } paulo@0: paulo@0: char *gt_share_local_get_urns (FileShare *file) paulo@0: { paulo@0: char *urn; paulo@0: paulo@0: urn = get_sha1 (file); paulo@0: paulo@0: return urn; paulo@0: } paulo@0: paulo@0: int gt_share_local_sync_is_done (void) paulo@0: { paulo@0: return sync_done; paulo@0: } paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: void *gnutella_share_new (Protocol *p, FileShare *file) paulo@0: { paulo@0: /* add this share to the data structures for searching */ paulo@0: gt_search_exec_add (file); paulo@0: paulo@0: return gt_share_local_add (file); paulo@0: } paulo@0: paulo@0: void gnutella_share_free (Protocol *p, FileShare *file, void *data) paulo@0: { paulo@0: /* remove data structures for searching */ paulo@0: gt_search_exec_remove (file); paulo@0: paulo@0: gt_share_local_remove (file, data); paulo@0: } paulo@0: paulo@0: int gnutella_share_add (Protocol *p, FileShare *file, void *data) paulo@0: { paulo@0: /* add to query routing tables */ paulo@0: gt_query_router_self_add (file); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: int gnutella_share_remove (Protocol *p, FileShare *file, void *data) paulo@0: { paulo@0: /* remove from query routing tables */ paulo@0: gt_query_router_self_remove (file); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: void gnutella_share_sync (Protocol *p, int begin) paulo@0: { paulo@0: gt_query_router_self_sync (begin); paulo@0: paulo@0: if (begin) paulo@0: { paulo@0: sync_begun = TRUE; paulo@0: } paulo@0: else if (sync_begun) paulo@0: { paulo@0: sync_begun = FALSE; paulo@0: sync_done = TRUE; paulo@0: paulo@0: /* synchronize the search structures (possibly, to disk) */ paulo@0: gt_search_exec_sync (); paulo@0: } paulo@0: }