annotate src/gt_share.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
rev   line source
paulo@0 1 /*
paulo@0 2 * $Id: gt_share.c,v 1.35 2004/03/26 11:53:18 hipnod Exp $
paulo@0 3 *
paulo@0 4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
paulo@0 5 *
paulo@0 6 * This program is free software; you can redistribute it and/or modify it
paulo@0 7 * under the terms of the GNU General Public License as published by the
paulo@0 8 * Free Software Foundation; either version 2, or (at your option) any
paulo@0 9 * later version.
paulo@0 10 *
paulo@0 11 * This program is distributed in the hope that it will be useful, but
paulo@0 12 * WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@0 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
paulo@0 14 * General Public License for more details.
paulo@0 15 */
paulo@0 16
paulo@0 17 #include "gt_gnutella.h"
paulo@0 18 #include "sha1.h"
paulo@0 19
paulo@0 20 #include "gt_share.h"
paulo@0 21 #include "gt_share_file.h"
paulo@0 22 #include "gt_query_route.h"
paulo@0 23 #include "gt_search.h"
paulo@0 24 #include "gt_search_exec.h"
paulo@0 25
paulo@0 26 #include "encoding/url.h"
paulo@0 27
paulo@0 28 /******************************************************************************/
paulo@0 29
paulo@0 30 #define SHARE_DEBUG gt_config_get_int("share/debug=0")
paulo@0 31
paulo@0 32 /*****************************************************************************/
paulo@0 33
paulo@0 34 /* each share is assigned an index here */
paulo@0 35 static Dataset *indices;
paulo@0 36
paulo@0 37 /* maps binary sha1 hashes -> FileShares */
paulo@0 38 static Dataset *sha1_hashes;
paulo@0 39
paulo@0 40 /* stored index of the last index assigned */
paulo@0 41 static uint32_t index_counter;
paulo@0 42
paulo@0 43 /* whether shares have been completely synchronized yet */
paulo@0 44 static BOOL sync_begun;
paulo@0 45
paulo@0 46 /* if shares are currently being synced */
paulo@0 47 static BOOL sync_done;
paulo@0 48
paulo@0 49 /******************************************************************************/
paulo@0 50
paulo@0 51 static void add_hash (FileShare *file);
paulo@0 52 static void remove_hash (FileShare *file);
paulo@0 53
paulo@0 54 /******************************************************************************/
paulo@0 55
paulo@0 56 /*
paulo@0 57 * Find the old index, using the SHA1 hash as a key.
paulo@0 58 */
paulo@0 59 static uint32_t get_old_index (Hash *hash)
paulo@0 60 {
paulo@0 61 Share *old_share;
paulo@0 62 uint32_t index = 0;
paulo@0 63
paulo@0 64 old_share = dataset_lookup (sha1_hashes, hash->data, SHA1_BINSIZE);
paulo@0 65 if (old_share != NULL)
paulo@0 66 {
paulo@0 67 GtShare *gt_share = share_get_udata (old_share, GT->name);
paulo@0 68
paulo@0 69 if (gt_share)
paulo@0 70 index = gt_share->index;
paulo@0 71 }
paulo@0 72
paulo@0 73 return index;
paulo@0 74 }
paulo@0 75
paulo@0 76 /*
paulo@0 77 * Find an unused index, but reuse the existing index if possible.
paulo@0 78 */
paulo@0 79 static uint32_t get_share_index (Share *share)
paulo@0 80 {
paulo@0 81 Hash *hash;
paulo@0 82 uint32_t index;
paulo@0 83
paulo@0 84 if ((hash = share_get_hash (share, "SHA1")) != NULL)
paulo@0 85 {
paulo@0 86 uint32_t hash_tmp;
paulo@0 87
paulo@0 88 /* if a Share with the same hash has another index, re-use that one */
paulo@0 89 if ((index = get_old_index (hash)) != 0)
paulo@0 90 return index;
paulo@0 91
paulo@0 92 memcpy (&hash_tmp, hash->data, 4);
paulo@0 93
paulo@0 94 /* use the first 24 bits of the SHA1 hash to seed the file index */
paulo@0 95 index_counter = hash_tmp & 0xfffffff;
paulo@0 96 }
paulo@0 97
paulo@0 98 if (!(index = dataset_uniq32 (indices, &index_counter)))
paulo@0 99 return 0;
paulo@0 100
paulo@0 101 return index;
paulo@0 102 }
paulo@0 103
paulo@0 104 static void add_index (Share *share, GtShare *gt_share)
paulo@0 105 {
paulo@0 106 uint32_t index;
paulo@0 107
paulo@0 108 if (SHARE_DEBUG)
paulo@0 109 GT->dbg (GT, "++[%d]->%s", gt_share->index, gt_share->filename);
paulo@0 110
paulo@0 111 index = get_share_index (share);
paulo@0 112 dataset_insert (&indices, &index, sizeof(index), share, 0);
paulo@0 113 }
paulo@0 114
paulo@0 115 static void remove_index (Share *share, GtShare *gt_share)
paulo@0 116 {
paulo@0 117 uint32_t index = gt_share->index;
paulo@0 118
paulo@0 119 assert (index > 0);
paulo@0 120
paulo@0 121 /*
paulo@0 122 * Check if the index is pointing at a different share. This case happens
paulo@0 123 * for every Share that is not removed on a resync, due to the weird way
paulo@0 124 * giftd 0.11.x use the new/add/remove/free interface.
paulo@0 125 */
paulo@0 126 if (dataset_lookup (indices, &index, sizeof(index)) != share)
paulo@0 127 return;
paulo@0 128
paulo@0 129 if (SHARE_DEBUG)
paulo@0 130 GT->dbg (GT, "--[%d]->%s", gt_share->index, gt_share->filename);
paulo@0 131
paulo@0 132 index = gt_share->index;
paulo@0 133 dataset_remove (indices, &index, sizeof(index));
paulo@0 134
paulo@0 135 if (dataset_length (indices) == 0)
paulo@0 136 {
paulo@0 137 dataset_clear (indices);
paulo@0 138 indices = NULL;
paulo@0 139 }
paulo@0 140 }
paulo@0 141
paulo@0 142 /******************************************************************************/
paulo@0 143
paulo@0 144 /* TODO: This duplicates memory thats already stored on the FileShare.
paulo@0 145 * Prevent this by maintaining a per-hash algorithm map of FileShares */
paulo@0 146 static void add_hash (FileShare *file)
paulo@0 147 {
paulo@0 148 Hash *hash;
paulo@0 149 ds_data_t key;
paulo@0 150 ds_data_t value;
paulo@0 151
paulo@0 152 if (!(hash = share_get_hash (file, "SHA1")))
paulo@0 153 return;
paulo@0 154
paulo@0 155 /* This shares the hash memory with the FileShare and also
paulo@0 156 * points directly at it. */
paulo@0 157 ds_data_init (&key, hash->data, hash->len, DS_NOCOPY);
paulo@0 158 ds_data_init (&value, file, 0, DS_NOCOPY);
paulo@0 159
paulo@0 160 /* We share the key with the FileShare, so remove the old key first
paulo@0 161 * so we don't end up sharing an old FileShare's hash. */
paulo@0 162 dataset_remove_ex (sha1_hashes, &key);
paulo@0 163 dataset_insert_ex (&sha1_hashes, &key, &value);
paulo@0 164 }
paulo@0 165
paulo@0 166 static void remove_hash (FileShare *file)
paulo@0 167 {
paulo@0 168 Hash *hash;
paulo@0 169
paulo@0 170 if (!(hash = share_get_hash (file, "SHA1")))
paulo@0 171 return;
paulo@0 172
paulo@0 173 /*
paulo@0 174 * If a FileShare is already present at this hash, and it isn't
paulo@0 175 * this FileShare, then don't remove it. This _will_ happen
paulo@0 176 * due to the way FileShares get added and removed on resyncs.
paulo@0 177 */
paulo@0 178 if (dataset_lookup (sha1_hashes, hash->data, hash->len) != file)
paulo@0 179 return;
paulo@0 180
paulo@0 181 dataset_remove (sha1_hashes, hash->data, hash->len);
paulo@0 182
paulo@0 183 if (dataset_length (sha1_hashes) == 0)
paulo@0 184 {
paulo@0 185 dataset_clear (sha1_hashes);
paulo@0 186 sha1_hashes = NULL;
paulo@0 187 }
paulo@0 188 }
paulo@0 189
paulo@0 190 /******************************************************************************/
paulo@0 191
paulo@0 192 static GtShare *gt_share_local_add (FileShare *file)
paulo@0 193 {
paulo@0 194 GtShare *share;
paulo@0 195 uint32_t index;
paulo@0 196
paulo@0 197 if (share_get_udata (file, GT->name))
paulo@0 198 return NULL;
paulo@0 199
paulo@0 200 index = get_share_index (file);
paulo@0 201
paulo@0 202 if (!(share = gt_share_new_data (file, index)))
paulo@0 203 return NULL;
paulo@0 204
paulo@0 205 add_hash (file);
paulo@0 206 add_index (file, share);
paulo@0 207
paulo@0 208 return share;
paulo@0 209 }
paulo@0 210
paulo@0 211 static void gt_share_local_remove (FileShare *file, GtShare *share)
paulo@0 212 {
paulo@0 213 remove_index (file, share);
paulo@0 214 remove_hash (file);
paulo@0 215
paulo@0 216 gt_share_free_data (file, share);
paulo@0 217 }
paulo@0 218
paulo@0 219 static int find_by_index (ds_data_t *key, ds_data_t *value, void **args)
paulo@0 220 {
paulo@0 221 uint32_t *index = args[0];
paulo@0 222 char *filename = args[1];
paulo@0 223 FileShare **ret = args[2];
paulo@0 224 FileShare *file = value->data;
paulo@0 225 GtShare *share;
paulo@0 226
paulo@0 227 if (!file || !(share = share_get_udata (file, GT->name)))
paulo@0 228 return DS_CONTINUE;
paulo@0 229
paulo@0 230 if (share->index == *index &&
paulo@0 231 (!filename || !strcmp (filename, share->filename)))
paulo@0 232 {
paulo@0 233 *ret = file;
paulo@0 234 return DS_BREAK;
paulo@0 235 }
paulo@0 236
paulo@0 237 return DS_CONTINUE;
paulo@0 238 }
paulo@0 239
paulo@0 240 FileShare *gt_share_local_lookup_by_index (uint32_t index, char *filename)
paulo@0 241 {
paulo@0 242 FileShare *ret = NULL;
paulo@0 243 void *args[] = { &index, filename, &ret };
paulo@0 244
paulo@0 245 share_foreach (DS_FOREACH_EX(find_by_index), args);
paulo@0 246
paulo@0 247 return ret;
paulo@0 248 }
paulo@0 249
paulo@0 250 static FileShare *lookup_sha1 (char *urn)
paulo@0 251 {
paulo@0 252 char *str, *str0;
paulo@0 253 char *prefix;
paulo@0 254 unsigned char *bin;
paulo@0 255 FileShare *file;
paulo@0 256
paulo@0 257 if (!(str0 = str = STRDUP (urn)))
paulo@0 258 return NULL;
paulo@0 259
paulo@0 260 /* TODO: consolidate with gt_protocol.c:parse_extended_data */
paulo@0 261 string_upper (str0);
paulo@0 262 string_sep (&str, "URN:");
paulo@0 263
paulo@0 264 prefix = string_sep (&str, ":");
paulo@0 265
paulo@0 266 /* Only support urn:sha1 or urn:sha-1 urns now */
paulo@0 267 if (STRCMP (prefix, "SHA1") != 0 && STRCMP (prefix, "SHA-1") != 0)
paulo@0 268 {
paulo@0 269 free (str0);
paulo@0 270 return NULL;
paulo@0 271 }
paulo@0 272
paulo@0 273 string_trim (str);
paulo@0 274
paulo@0 275 if (strlen (str) != 32)
paulo@0 276 {
paulo@0 277 free (str0);
paulo@0 278 return NULL;
paulo@0 279 }
paulo@0 280
paulo@0 281 if (!(bin = sha1_bin (str)))
paulo@0 282 {
paulo@0 283 free (str0);
paulo@0 284 return NULL;
paulo@0 285 }
paulo@0 286
paulo@0 287 file = dataset_lookup (sha1_hashes, bin, SHA1_BINSIZE);
paulo@0 288
paulo@0 289 free (str0);
paulo@0 290 free (bin);
paulo@0 291
paulo@0 292 return file;
paulo@0 293 }
paulo@0 294
paulo@0 295 FileShare *gt_share_local_lookup_by_urn (char *urn)
paulo@0 296 {
paulo@0 297 return lookup_sha1 (urn);
paulo@0 298 }
paulo@0 299
paulo@0 300 static char *get_sha1 (FileShare *file)
paulo@0 301 {
paulo@0 302 Hash *hash;
paulo@0 303 char *urn;
paulo@0 304 char *str;
paulo@0 305
paulo@0 306 if (!(hash = share_get_hash (file, "SHA1")))
paulo@0 307 return NULL;
paulo@0 308
paulo@0 309 assert (hash->len == SHA1_BINSIZE);
paulo@0 310
paulo@0 311 if (!(str = sha1_string (hash->data)))
paulo@0 312 return NULL;
paulo@0 313
paulo@0 314 urn = stringf_dup ("urn:sha1:%s", str);
paulo@0 315 free (str);
paulo@0 316
paulo@0 317 return urn;
paulo@0 318 }
paulo@0 319
paulo@0 320 char *gt_share_local_get_urns (FileShare *file)
paulo@0 321 {
paulo@0 322 char *urn;
paulo@0 323
paulo@0 324 urn = get_sha1 (file);
paulo@0 325
paulo@0 326 return urn;
paulo@0 327 }
paulo@0 328
paulo@0 329 int gt_share_local_sync_is_done (void)
paulo@0 330 {
paulo@0 331 return sync_done;
paulo@0 332 }
paulo@0 333
paulo@0 334 /******************************************************************************/
paulo@0 335
paulo@0 336 void *gnutella_share_new (Protocol *p, FileShare *file)
paulo@0 337 {
paulo@0 338 /* add this share to the data structures for searching */
paulo@0 339 gt_search_exec_add (file);
paulo@0 340
paulo@0 341 return gt_share_local_add (file);
paulo@0 342 }
paulo@0 343
paulo@0 344 void gnutella_share_free (Protocol *p, FileShare *file, void *data)
paulo@0 345 {
paulo@0 346 /* remove data structures for searching */
paulo@0 347 gt_search_exec_remove (file);
paulo@0 348
paulo@0 349 gt_share_local_remove (file, data);
paulo@0 350 }
paulo@0 351
paulo@0 352 int gnutella_share_add (Protocol *p, FileShare *file, void *data)
paulo@0 353 {
paulo@0 354 /* add to query routing tables */
paulo@0 355 gt_query_router_self_add (file);
paulo@0 356
paulo@0 357 return TRUE;
paulo@0 358 }
paulo@0 359
paulo@0 360 int gnutella_share_remove (Protocol *p, FileShare *file, void *data)
paulo@0 361 {
paulo@0 362 /* remove from query routing tables */
paulo@0 363 gt_query_router_self_remove (file);
paulo@0 364
paulo@0 365 return TRUE;
paulo@0 366 }
paulo@0 367
paulo@0 368 void gnutella_share_sync (Protocol *p, int begin)
paulo@0 369 {
paulo@0 370 gt_query_router_self_sync (begin);
paulo@0 371
paulo@0 372 if (begin)
paulo@0 373 {
paulo@0 374 sync_begun = TRUE;
paulo@0 375 }
paulo@0 376 else if (sync_begun)
paulo@0 377 {
paulo@0 378 sync_begun = FALSE;
paulo@0 379 sync_done = TRUE;
paulo@0 380
paulo@0 381 /* synchronize the search structures (possibly, to disk) */
paulo@0 382 gt_search_exec_sync ();
paulo@0 383 }
paulo@0 384 }