source: trunk/libtransmission/torrent.c @ 3393

Last change on this file since 3393 was 3393, checked in by charles, 15 years ago

merge in the parts of peerutils' peer pruning code that still make sense

  • Property svn:keywords set to Date Rev Author Id
File size: 32.9 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3393 2007-10-13 13:54:05Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include <sys/types.h>
31
32#include "transmission.h"
33#include "completion.h"
34#include "crypto.h" /* for tr_sha1 */
35#include "fastresume.h"
36#include "handshake.h"
37#include "inout.h"
38#include "metainfo.h"
39#include "net.h" /* tr_netNtop */
40#include "peer-mgr.h"
41#include "platform.h"
42#include "ratecontrol.h"
43#include "shared.h"
44#include "tracker.h"
45#include "trcompat.h" /* for strlcpy */
46#include "trevent.h"
47#include "utils.h"
48
49/***
50****
51***/
52
53int
54tr_torrentExists( tr_handle       * handle,
55                  const uint8_t   * torrentHash )
56{
57    return tr_torrentFindFromHash( handle, torrentHash ) != NULL;
58}
59
60tr_torrent*
61tr_torrentFindFromHash( tr_handle      * handle,
62                        const uint8_t  * torrentHash )
63{
64    tr_torrent * tor;
65
66    for( tor = handle->torrentList; tor; tor = tor->next )
67        if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
68            return tor;
69
70    return NULL;
71}
72
73tr_torrent*
74tr_torrentFindFromObfuscatedHash( tr_handle      * handle,
75                                  const uint8_t  * obfuscatedTorrentHash )
76{
77    tr_torrent * tor;
78
79    for( tor = handle->torrentList; tor; tor = tor->next )
80        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) )
81            return tor;
82
83    return NULL;
84}
85
86/***
87****  LOCKS
88***/
89
90void
91tr_torrentLock( const tr_torrent * tor )
92{
93    tr_globalLock( tor->handle );
94}
95
96void
97tr_torrentUnlock( const tr_torrent * tor )
98{
99    tr_globalUnlock( tor->handle );
100}
101
102/***
103****  PER-TORRENT UL / DL SPEEDS
104***/
105
106void
107tr_torrentSetSpeedMode( tr_torrent   * tor,
108                        int            up_or_down,
109                        tr_speedlimit  mode )
110{
111    tr_speedlimit * limit = up_or_down==TR_UP
112        ? &tor->uploadLimitMode
113        : &tor->downloadLimitMode;
114    *limit = mode;
115}
116
117tr_speedlimit
118tr_torrentGetSpeedMode( const tr_torrent * tor,
119                        int                up_or_down)
120{
121    return up_or_down==TR_UP ? tor->uploadLimitMode
122                             : tor->downloadLimitMode;
123}
124
125void
126tr_torrentSetSpeedLimit( tr_torrent   * tor,
127                         int            up_or_down,
128                         int            single_KiB_sec )
129{
130    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
131    tr_rcSetLimit( rc, single_KiB_sec );
132}
133
134int
135tr_torrentGetSpeedLimit( const tr_torrent  * tor,
136                         int                 up_or_down )
137{
138    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
139    return tr_rcGetLimit( rc );
140}
141
142/***
143****
144***/
145
146static void
147onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
148{
149    tr_torrent * tor = (tr_torrent *) user_data;
150    tr_tracker_event_t * event = (tr_tracker_event_t *) vevent;
151
152    switch( event->messageType )
153    {
154        case TR_TRACKER_PEERS:
155            tr_peerMgrAddPeers( tor->handle->peerMgr,
156                                tor->info.hash,
157                                TR_PEER_FROM_TRACKER,
158                                event->peerCompact,
159                                event->peerCount );
160            break;
161
162        case TR_TRACKER_WARNING:
163            tr_err( "Tracker: Warning - %s", event->text );
164            tor->error = TR_ERROR_TC_WARNING;
165            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
166            break;
167
168        case TR_TRACKER_ERROR:
169            tr_err( "Tracker: Error - %s", event->text );
170            tor->error = TR_ERROR_TC_ERROR;
171            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
172            break;
173
174        case TR_TRACKER_ERROR_CLEAR:
175            tor->error = 0;
176            tor->errorString[0] = '\0';
177            break;
178    }
179}
180
181/***
182****
183****  TORRENT INSTANTIATION
184****
185***/
186
187static int
188getBytePiece( const tr_info * info, uint64_t byteOffset )
189{
190    assert( info != NULL );
191    assert( info->pieceSize != 0 );
192
193    return byteOffset / info->pieceSize;
194}
195
196static void
197initFilePieces ( tr_info * info, int fileIndex )
198{
199    tr_file * file = &info->files[fileIndex];
200    uint64_t firstByte, lastByte;
201
202    assert( info != NULL );
203    assert( 0<=fileIndex && fileIndex<info->fileCount );
204
205    file = &info->files[fileIndex];
206    firstByte = file->offset;
207    lastByte = firstByte + (file->length ? file->length-1 : 0);
208    file->firstPiece = getBytePiece( info, firstByte );
209    file->lastPiece = getBytePiece( info, lastByte );
210}
211
212static tr_priority_t
213calculatePiecePriority ( const tr_torrent * tor,
214                         int                piece )
215{
216    int i;
217    tr_priority_t priority = TR_PRI_NORMAL;
218
219    for( i=0; i<tor->info.fileCount; ++i )
220    {
221        const tr_file * file = &tor->info.files[i];
222        if ( file->firstPiece <= piece
223          && file->lastPiece  >= piece
224          && file->priority   >  priority)
225              priority = file->priority;
226
227        /* when dealing with multimedia files, getting the first and
228           last pieces can sometimes allow you to preview it a bit
229           before it's fully downloaded... */
230        if ( file->priority >= TR_PRI_NORMAL )
231            if ( file->firstPiece == piece || file->lastPiece == piece )
232                priority = TR_PRI_HIGH;
233    }
234
235    return priority;
236}
237
238static void
239tr_torrentInitFilePieces( tr_torrent * tor )
240{
241    int i;
242    uint64_t offset = 0;
243
244    assert( tor != NULL );
245
246    for( i=0; i<tor->info.fileCount; ++i ) {
247      tor->info.files[i].offset = offset;
248      offset += tor->info.files[i].length;
249      initFilePieces( &tor->info, i );
250    }
251
252    for( i=0; i<tor->info.pieceCount; ++i )
253        tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
254}
255
256static void
257torrentRealInit( tr_handle  * h,
258                 tr_torrent * tor,
259                 const char * destination,
260                 int          flags )
261{
262    int doStart;
263    uint64_t loaded;
264    uint64_t t;
265    tr_bitfield * uncheckedPieces;
266    tr_info * info = &tor->info;
267   
268    tor->info.flags |= flags;
269
270    tr_globalLock( h );
271
272    tor->handle   = h;
273    tor->pexDisabled = 0;
274
275    /**
276     * Decide on a block size.  constraints:
277     * (1) most clients decline requests over 16 KiB
278     * (2) pieceSize must be a multiple of block size
279     */
280    tor->blockSize = info->pieceSize;
281    while( tor->blockSize > (1024*16) )
282        tor->blockSize /= 2;
283
284    tor->lastPieceSize = info->totalSize % info->pieceSize;
285
286    if( !tor->lastPieceSize )
287         tor->lastPieceSize = info->pieceSize;
288
289    tor->lastBlockSize = info->totalSize % tor->blockSize;
290
291    if( !tor->lastBlockSize )
292         tor->lastBlockSize = tor->blockSize;
293
294    tor->blockCount =
295        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
296
297    tor->blockCountInPiece =
298        info->pieceSize / tor->blockSize;
299
300    tor->blockCountInLastPiece =
301        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
302
303    /* check our work */
304    assert( ( info->pieceSize % tor->blockSize ) == 0 );
305    t = info->pieceCount - 1;
306    t *= info->pieceSize;
307    t += tor->lastPieceSize;
308    assert( t == info->totalSize );
309    t = tor->blockCount - 1;
310    t *= tor->blockSize;
311    t += tor->lastBlockSize;
312    assert( t == info->totalSize );
313    t = info->pieceCount - 1;
314    t *= tor->blockCountInPiece;
315    t += tor->blockCountInLastPiece;
316    assert( t == (uint64_t)tor->blockCount );
317
318    tor->completion = tr_cpInit( tor );
319
320    tr_torrentInitFilePieces( tor );
321
322    tor->upload         = tr_rcInit();
323    tor->download       = tr_rcInit();
324    tor->swarmspeed     = tr_rcInit();
325
326    tr_sha1( tor->obfuscatedHash, "req2", 4,
327                                  info->hash, SHA_DIGEST_LENGTH,
328                                  NULL );
329
330    tr_peerMgrAddTorrent( h->peerMgr, tor );
331
332    if( !h->isPortSet )
333        tr_setBindPort( h, TR_DEFAULT_PORT );
334
335    assert( !tor->downloadedCur );
336    assert( !tor->uploadedCur );
337
338    tor->error   = TR_OK;
339
340    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
341
342    loaded = tr_fastResumeLoad( tor, destination, uncheckedPieces );
343
344    assert( tor->destination != NULL );
345
346    /* the `paused' flag has highest precedence...
347       after that, the fastresume setting is used...
348       if that's not found, default to RUNNING */
349    if( flags & TR_FLAG_PAUSED )
350        doStart = 0;
351    else if( loaded & TR_FR_RUN )
352        doStart = tor->isRunning;
353    else
354        doStart = 1;
355    tor->isRunning = 0;
356
357    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
358        tr_bitfieldFree( uncheckedPieces );
359    else
360        tor->uncheckedPieces = uncheckedPieces;
361
362
363    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
364        int limit, enabled;
365        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
366        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
367        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
368        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
369    }
370
371    tor->cpStatus = tr_cpGetStatus( tor->completion );
372
373    tor->tracker = tr_trackerNew( tor );
374    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
375
376    tor->next = h->torrentList;
377    h->torrentList = tor;
378    h->torrentCount++;
379
380    tr_globalUnlock( h );
381
382    if( doStart )
383        tr_torrentStart( tor );
384}
385
386static int
387pathIsInUse ( const tr_handle   * h,
388              const char        * destination,
389              const char        * name )
390{
391    const tr_torrent * tor;
392   
393    for( tor=h->torrentList; tor; tor=tor->next )
394        if( !strcmp( destination, tor->destination )
395         && !strcmp( name, tor->info.name ) )
396            return TRUE;
397
398    return FALSE;
399}
400
401static int
402hashExists( const tr_handle   * h,
403            const uint8_t     * hash )
404{
405    const tr_torrent * tor;
406
407    for( tor=h->torrentList; tor; tor=tor->next )
408        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
409            return TRUE;
410
411    return FALSE;
412}
413
414static int
415infoCanAdd( const tr_handle   * h,
416            const char        * destination,
417            const tr_info     * info )
418{
419    if( hashExists( h, info->hash ) )
420        return TR_EDUPLICATE;
421
422    if( destination && pathIsInUse( h, destination, info->name ) )
423        return TR_EDUPLICATE;
424
425    return TR_OK;
426}
427
428int
429tr_torrentParse( const tr_handle  * h,
430                 const char       * path,
431                 const char       * destination,
432                 tr_info          * setme_info )
433{
434    int ret, doFree;
435    tr_info tmp;
436
437    if( setme_info == NULL )
438        setme_info = &tmp;
439
440    memset( setme_info, 0, sizeof( tr_info ) );
441    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
442    doFree = !ret && (setme_info == &tmp);
443
444    if( ret == TR_OK )
445        ret = infoCanAdd( h, destination, setme_info );
446
447    if( doFree )
448        tr_metainfoFree( &tmp );
449
450    return ret;
451}
452 
453tr_torrent *
454tr_torrentInit( tr_handle   * h,
455                const char  * path,
456                const char  * destination,
457                int           flags,
458                int         * error )
459{
460    int val;
461    int tmpError;
462    tr_torrent * tor = NULL;
463
464    if( !error )
465         error = &tmpError;
466
467    if(( val = tr_torrentParse( h, path, destination, NULL )))
468        *error = val;
469    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
470        *error = TR_EOTHER;
471    else {
472        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
473        torrentRealInit( h, tor, destination, flags );
474    }
475
476    return tor;
477}
478
479int
480tr_torrentParseHash( const tr_handle  * h,
481                     const char       * hashStr,
482                     const char       * destination,
483                     tr_info          * setme_info )
484{
485    int ret, doFree;
486    tr_info tmp;
487
488    if( setme_info == NULL )
489        setme_info = &tmp;
490
491    memset( setme_info, 0, sizeof( tr_info ) );
492    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
493    doFree = !ret && (setme_info == &tmp);
494
495    if( ret == TR_OK )
496        ret = infoCanAdd( h, destination, setme_info );
497
498    if( doFree )
499        tr_metainfoFree( &tmp );
500
501    return ret;
502}
503
504tr_torrent *
505tr_torrentInitSaved( tr_handle    * h,
506                     const char   * hashStr,
507                     const char   * destination,
508                     int            flags,
509                     int          * error )
510{
511    int val;
512    int tmpError;
513    tr_torrent * tor = NULL;
514
515    if( !error )
516         error = &tmpError;
517
518    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
519        *error = val;
520    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
521        *error = TR_EOTHER;
522    else {
523        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
524        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
525    }
526
527    return tor;
528}
529
530static int
531tr_torrentParseData( const tr_handle  * h,
532                     const uint8_t    * data,
533                     size_t             size,
534                     const char       * destination,
535                     tr_info          * setme_info )
536{
537    int ret, doFree;
538    tr_info tmp;
539
540    if( setme_info == NULL )
541        setme_info = &tmp;
542
543    memset( setme_info, 0, sizeof( tr_info ) );
544    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
545    doFree = !ret && (setme_info == &tmp);
546
547    if( ret == TR_OK )
548        ret = infoCanAdd( h, destination, setme_info );
549
550    if( doFree )
551        tr_metainfoFree( &tmp );
552
553    return ret;
554}
555
556tr_torrent *
557tr_torrentInitData( tr_handle      * h,
558                    const uint8_t  * data,
559                    size_t           size,
560                    const char     * destination,
561                    int              flags,
562                    int            * error )
563{
564    int val;
565    int tmpError;
566    tr_torrent * tor = NULL;
567
568    if( !error )
569         error = &tmpError;
570
571    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
572        *error = val;
573    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
574        *error = TR_EOTHER;
575    else {
576        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
577        torrentRealInit( h, tor, destination, flags );
578    }
579
580    return tor;
581}
582
583const tr_info *
584tr_torrentInfo( const tr_torrent * tor )
585{
586    return &tor->info;
587}
588
589/***
590****
591***/
592
593#if 0
594int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
595{
596    return tr_trackerScrape( tor, s, l, d );
597}
598#endif
599
600static int
601saveFastResumeNow( void * vtor )
602{
603    tr_torrent * tor = (tr_torrent *) vtor;
604
605    tr_fastResumeSave( tor );
606    tr_torrentRecheckCompleteness( tor );
607
608    tr_timerFree( &tor->saveTimer );
609    return FALSE;
610}
611
612static void
613saveFastResumeSoon( void * vtor )
614{
615    tr_torrent * tor = (tr_torrent *) vtor;
616
617    if( tor->saveTimer == NULL )
618        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 100 );
619}
620
621/**
622***
623**/
624
625void
626tr_torrentSetFolder( tr_torrent * tor, const char * path )
627{
628    tr_free( tor->destination );
629    tor->destination = tr_strdup( path );
630    saveFastResumeSoon( tor );
631}
632
633const char*
634tr_torrentGetFolder( const tr_torrent * tor )
635{
636    return tor->destination;
637}
638
639void
640tr_torrentChangeMyPort( tr_torrent * tor )
641{
642    if( tor->tracker )
643        tr_trackerChangeMyPort( tor->tracker );
644}
645
646
647void
648tr_torrentDisablePex( tr_torrent * tor, int disable )
649{
650    assert( tor != NULL );
651    assert( disable==0 || disable==1 );
652
653    /* pex is ALWAYS disabled for private torrents */
654    if( tor->info.flags & TR_FLAG_PRIVATE )
655        disable = TRUE;
656
657    tor->pexDisabled = disable;
658}
659
660void
661tr_manualUpdate( tr_torrent * tor )
662{
663    if( tor->isRunning )
664        tr_trackerReannounce( tor->tracker );
665}
666int
667tr_torrentCanManualUpdate( const tr_torrent * tor )
668{
669    return ( tor != NULL )
670        && ( tor->isRunning )
671        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
672}
673
674const tr_stat *
675tr_torrentStat( tr_torrent * tor )
676{
677    tr_stat * s;
678    struct tr_tracker * tc;
679
680    tr_torrentLock( tor );
681
682    tor->statCur = !tor->statCur;
683    s = &tor->stats[tor->statCur];
684
685    s->error  = tor->error;
686    memcpy( s->errorString, tor->errorString,
687            sizeof( s->errorString ) );
688
689    tc = tor->tracker;
690    s->tracker = tr_trackerGetAddress( tor->tracker );
691
692    tr_peerMgrTorrentStats( tor->handle->peerMgr,
693                            tor->info.hash,
694                            &s->peersKnown,
695                            &s->peersConnected,
696                            &s->peersSendingToUs,
697                            &s->peersGettingFromUs,
698                             s->peersFrom );
699
700    s->percentComplete = tr_cpPercentComplete ( tor->completion );
701
702    s->percentDone = tr_cpPercentDone( tor->completion );
703    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
704
705    if( tor->recheckState == TR_RECHECK_NOW )
706        s->status = TR_STATUS_CHECK;
707    else if( tor->recheckState == TR_RECHECK_WAIT )
708        s->status = TR_STATUS_CHECK_WAIT;
709    else if( !tor->isRunning )
710        s->status = TR_STATUS_STOPPED;
711    else if( tor->cpStatus == TR_CP_INCOMPLETE )
712        s->status = TR_STATUS_DOWNLOAD;
713    else if( tor->cpStatus == TR_CP_DONE )
714        s->status = TR_STATUS_DONE;
715    else
716        s->status = TR_STATUS_SEED;
717
718    s->recheckProgress = (tor->uncheckedPieces == NULL)
719        ? 0.0
720        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
721
722    s->cpStatus = tor->cpStatus;
723
724    /* tr_rcRate() doesn't make the difference between 'piece'
725       messages and other messages, which causes a non-zero
726       download rate even tough we are not downloading. So we
727       force it to zero not to confuse the user. */
728    s->rateDownload = tor->isRunning
729        ? tr_rcRate( tor->download )
730        : 0.0;
731    s->rateUpload = tr_rcRate( tor->upload );
732   
733    tr_trackerGetCounts( tc,
734                         &s->completedFromTracker,
735                         &s->leechers, 
736                         &s->seeders );
737
738    s->swarmspeed = tr_rcRate( tor->swarmspeed );
739   
740    s->startDate = tor->startDate;
741    s->activityDate = tor->activityDate;
742
743    s->eta = s->rateDownload < 0.1
744        ? -1.0f
745        : (s->leftUntilDone / s->rateDownload / 1024.0);
746
747    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
748    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
749    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
750    s->haveValid       = tr_cpHaveValid( tor->completion );
751    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
752
753
754    {
755        int i;
756        tr_bitfield * available = tr_peerMgrGetAvailable( tor->handle->peerMgr,
757                                                          tor->info.hash );
758        s->desiredSize = 0;
759        s->desiredAvailable = 0;
760
761        for( i=0; i<tor->info.pieceCount; ++i ) {
762            if( !tor->info.pieces[i].dnd ) {
763                s->desiredSize += tor->info.pieceSize;
764                if( tr_bitfieldHas( available, i ) )
765                    s->desiredAvailable += tor->info.pieceSize;
766            }
767        }
768
769        tr_bitfieldFree( available );
770    }
771   
772    s->ratio = s->downloadedEver ? s->uploadedEver / (float)s->downloadedEver
773                                 : TR_RATIO_NA;
774   
775    tr_torrentUnlock( tor );
776
777    return s;
778}
779
780/***
781****
782***/
783
784static uint64_t
785fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
786{
787    const tr_file * file     =  &tor->info.files[fileIndex];
788    const uint64_t firstBlock       =  file->offset / tor->blockSize;
789    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
790    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
791    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
792    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
793    uint64_t haveBytes = 0;
794
795    assert( tor != NULL );
796    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
797    assert( file->offset + file->length <= tor->info.totalSize );
798    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
799    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
800    assert( firstBlock <= lastBlock );
801    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
802    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
803
804    if( firstBlock == lastBlock )
805    {
806        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
807            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
808    }
809    else
810    {
811        uint64_t i;
812
813        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
814            haveBytes += tor->blockSize - firstBlockOffset;
815
816        for( i=firstBlock+1; i<lastBlock; ++i )
817            if( tr_cpBlockIsComplete( tor->completion, i ) )
818               haveBytes += tor->blockSize;
819
820        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
821            haveBytes += lastBlockOffset + 1;
822    }
823
824    return haveBytes;
825}
826
827tr_file_stat *
828tr_torrentFiles( const tr_torrent * tor, int * fileCount )
829{
830    int i;
831    const int n = tor->info.fileCount;
832    tr_file_stat * files = tr_new0( tr_file_stat, n );
833    tr_file_stat * walk = files;
834
835    for( i=0; i<n; ++i, ++walk )
836    {
837        const tr_file * file = tor->info.files + i;
838        cp_status_t cp;
839
840        walk->bytesCompleted = fileBytesCompleted( tor, i );
841
842        walk->progress = file->length
843            ? walk->bytesCompleted / (float)file->length
844            : 1.0;
845
846        if( walk->bytesCompleted >= file->length )
847            cp = TR_CP_COMPLETE;
848        else if( tor->info.files[i].dnd )
849            cp = TR_CP_DONE;
850        else
851            cp = TR_CP_INCOMPLETE;
852
853        walk->completionStatus = cp;
854    }
855
856    *fileCount = n;
857
858    return files;
859}
860
861void
862tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
863{
864    tr_free( files );
865}
866
867/***
868****
869***/
870
871tr_peer_stat *
872tr_torrentPeers( const tr_torrent * tor, int * peerCount )
873{
874    return tr_peerMgrPeerStats( tor->handle->peerMgr,
875                                tor->info.hash, peerCount );
876}
877
878void
879tr_torrentPeersFree( tr_peer_stat * peers, int peerCount )
880{
881    int i;
882    for( i=0; i<peerCount; ++i )
883        tr_free( (char*) peers[i].client );
884    tr_free( peers );
885}
886
887void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
888{
889    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
890                                          tor->info.hash,
891                                          tab, size );
892}
893
894void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
895{
896    int i;
897    float interval;
898    tr_torrentLock( tor );
899
900    interval = (float)tor->info.pieceCount / (float)size;
901    for( i = 0; i < size; i++ )
902    {
903        int piece = i * interval;
904        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
905    }
906
907    tr_torrentUnlock( tor );
908}
909
910void
911tr_torrentResetTransferStats( tr_torrent * tor )
912{
913    tr_torrentLock( tor );
914
915    tor->downloadedPrev += tor->downloadedCur;
916    tor->downloadedCur   = 0;
917    tor->uploadedPrev   += tor->uploadedCur;
918    tor->uploadedCur     = 0;
919    tor->corruptPrev    += tor->corruptCur;
920    tor->corruptCur      = 0;
921
922    tr_torrentUnlock( tor );
923}
924
925
926void
927tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
928{
929    tr_torrentLock( tor );
930
931    if( has )
932        tr_cpPieceAdd( tor->completion, pieceIndex );
933    else
934        tr_cpPieceRem( tor->completion, pieceIndex );
935
936    tr_torrentUnlock( tor );
937}
938
939void tr_torrentRemoveSaved( tr_torrent * tor )
940{
941    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
942}
943
944/***
945****
946***/
947
948static void
949freeTorrent( tr_torrent * tor )
950{
951    tr_torrent * t;
952    tr_handle * h = tor->handle;
953    tr_info * inf = &tor->info;
954
955    assert( tor != NULL );
956    assert( !tor->isRunning );
957
958    tr_globalLock( h );
959
960    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
961
962    tr_cpClose( tor->completion );
963
964    tr_rcClose( tor->upload );
965    tr_rcClose( tor->download );
966    tr_rcClose( tor->swarmspeed );
967
968    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
969    tr_trackerFree( tor->tracker );
970    tor->tracker = NULL;
971
972    tr_free( tor->destination );
973
974    if( tor == h->torrentList )
975        h->torrentList = tor->next;
976    else for( t=h->torrentList; t!=NULL; t=t->next ) {
977        if( t->next == tor ) {
978            t->next = tor->next;
979            break;
980        }
981    }
982
983    assert( h->torrentCount >= 1 );
984    h->torrentCount--;
985
986    tr_inf( "closing torrent %s... %d torrents left",
987            tor->info.name, h->torrentCount );
988
989    tr_metainfoFree( inf );
990    tr_free( tor );
991
992    tr_globalUnlock( h );
993}
994
995enum
996{
997    AFTER_RECHECK_NONE,
998    AFTER_RECHECK_START,
999    AFTER_RECHECK_STOP,
1000    AFTER_RECHECK_CLOSE
1001};
1002
1003static void
1004checkAndStartCB( tr_torrent * tor )
1005{
1006    tr_globalLock( tor->handle );
1007
1008    tor->isRunning  = 1;
1009    *tor->errorString = '\0';
1010    tr_torrentResetTransferStats( tor );
1011    tr_torrentRecheckCompleteness( tor );
1012    tor->startDate = tr_date( );
1013    tr_trackerStart( tor->tracker );
1014    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
1015
1016    tr_globalUnlock( tor->handle );
1017}
1018   
1019void
1020tr_torrentStart( tr_torrent * tor )
1021{
1022    tr_globalLock( tor->handle );
1023
1024    if( !tor->isRunning )
1025    {
1026        tor->isRunning = 1;
1027        tr_ioRecheckAdd( tor, checkAndStartCB );
1028    }
1029
1030    tr_globalUnlock( tor->handle );
1031}
1032
1033void
1034tr_torrentRecheck( tr_torrent * tor )
1035{
1036    tr_globalLock( tor->handle );
1037
1038    if( !tor->uncheckedPieces )
1039        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1040    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1041
1042    tr_ioRecheckAdd( tor, tr_torrentRecheckCompleteness );
1043
1044    tr_globalUnlock( tor->handle );
1045}
1046
1047
1048static void
1049stopTorrent( void * vtor )
1050{
1051    tr_torrent * tor = vtor;
1052    tr_ioRecheckRemove( tor );
1053    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1054    tr_trackerStop( tor->tracker );
1055    tr_ioClose( tor );
1056}
1057
1058void
1059tr_torrentStop( tr_torrent * tor )
1060{
1061    tr_globalLock( tor->handle );
1062
1063    saveFastResumeNow( tor );
1064    tor->isRunning = 0;
1065    tr_runInEventThread( tor->handle, stopTorrent, tor );
1066
1067    tr_globalUnlock( tor->handle );
1068}
1069
1070static void
1071closeTorrent( void * vtor )
1072{
1073    tr_torrent * tor = vtor;
1074    saveFastResumeNow( tor );
1075    tor->isRunning = 0;
1076    stopTorrent( tor );
1077    freeTorrent( tor );
1078}
1079
1080void
1081tr_torrentClose( tr_torrent * tor )
1082{
1083    tr_globalLock( tor->handle );
1084
1085    tr_runInEventThread( tor->handle, closeTorrent, tor );
1086
1087    tr_globalUnlock( tor->handle );
1088}
1089
1090/**
1091***  Completeness
1092**/
1093
1094static void
1095fireStatusChange( tr_torrent * tor, cp_status_t status )
1096{
1097    assert( tor != NULL );
1098    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1099
1100    if( tor->status_func != NULL )
1101        (tor->status_func)( tor, status, tor->status_func_user_data );
1102}
1103
1104void
1105tr_torrentSetStatusCallback( tr_torrent             * tor,
1106                             tr_torrent_status_func   func,
1107                             void                   * user_data )
1108{
1109    assert( tor != NULL );
1110    tor->status_func = func;
1111    tor->status_func_user_data = user_data;
1112}
1113
1114void
1115tr_torrentClearStatusCallback( tr_torrent * torrent )
1116{
1117    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1118}
1119
1120void
1121tr_torrentRecheckCompleteness( tr_torrent * tor )
1122{
1123    cp_status_t cpStatus;
1124
1125    tr_torrentLock( tor );
1126
1127    cpStatus = tr_cpGetStatus( tor->completion );
1128    if( cpStatus != tor->cpStatus ) {
1129        tor->cpStatus = cpStatus;
1130        fireStatusChange( tor, cpStatus );
1131        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1132            && tor->downloadedCur ) {        /* and it just happened */
1133            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1134        }
1135        tr_ioClose( tor );
1136        saveFastResumeSoon( tor );
1137    }
1138    tr_torrentUnlock( tor );
1139}
1140
1141/**
1142***  File priorities
1143**/
1144
1145void
1146tr_torrentSetFilePriority( tr_torrent   * tor,
1147                           int            fileIndex,
1148                           tr_priority_t  priority )
1149{
1150    int i;
1151    tr_file * file;
1152
1153    tr_torrentLock( tor );
1154
1155    assert( tor != NULL );
1156    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1157    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1158
1159    file = &tor->info.files[fileIndex];
1160    file->priority = priority;
1161    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1162      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1163
1164    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1165             fileIndex, file->firstPiece, file->lastPiece,
1166             priority, tor->info.files[fileIndex].name );
1167
1168    saveFastResumeSoon( tor );
1169
1170    tr_torrentUnlock( tor );
1171}
1172
1173void
1174tr_torrentSetFilePriorities( tr_torrent     * tor,
1175                             int            * files,
1176                             int              fileCount,
1177                             tr_priority_t    priority )
1178{
1179    int i;
1180    for( i=0; i<fileCount; ++i )
1181        tr_torrentSetFilePriority( tor, files[i], priority );
1182}
1183
1184tr_priority_t
1185tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1186{
1187    tr_priority_t ret;
1188
1189    tr_torrentLock( tor );
1190    assert( tor != NULL );
1191    assert( 0<=file && file<tor->info.fileCount );
1192    ret = tor->info.files[file].priority;
1193    tr_torrentUnlock( tor );
1194
1195    return ret;
1196}
1197
1198tr_priority_t*
1199tr_torrentGetFilePriorities( const tr_torrent * tor )
1200{
1201    int i;
1202    tr_priority_t * p;
1203
1204    tr_torrentLock( tor );
1205    p = tr_new0( tr_priority_t, tor->info.fileCount );
1206    for( i=0; i<tor->info.fileCount; ++i )
1207        p[i] = tor->info.files[i].priority;
1208    tr_torrentUnlock( tor );
1209
1210    return p;
1211}
1212
1213/**
1214***  File DND
1215**/
1216
1217int
1218tr_torrentGetFileDL( const tr_torrent * tor,
1219                     int                file )
1220{
1221    int doDownload;
1222    tr_torrentLock( tor );
1223
1224    assert( 0<=file && file<tor->info.fileCount );
1225    doDownload = !tor->info.files[file].dnd;
1226
1227    tr_torrentUnlock( tor );
1228    return doDownload != 0;
1229}
1230
1231void
1232tr_torrentSetFileDL( tr_torrent  * tor,
1233                     int           fileIndex,
1234                     int           doDownload )
1235{
1236    tr_file * file;
1237    const int dnd = !doDownload;
1238    int firstPiece, firstPieceDND;
1239    int lastPiece, lastPieceDND;
1240    int i;
1241
1242    tr_torrentLock( tor );
1243
1244    file = &tor->info.files[fileIndex];
1245    file->dnd = dnd;
1246    firstPiece = file->firstPiece;
1247    lastPiece = file->lastPiece;
1248
1249    /* can't set the first piece to DND unless
1250       every file using that piece is DND */
1251    firstPieceDND = dnd;
1252    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1253        if( tor->info.files[i].lastPiece != firstPiece )
1254            break;
1255        firstPieceDND = tor->info.files[i].dnd;
1256    }
1257
1258    /* can't set the last piece to DND unless
1259       every file using that piece is DND */
1260    lastPieceDND = dnd;
1261    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1262        if( tor->info.files[i].firstPiece != lastPiece )
1263            break;
1264        lastPieceDND = tor->info.files[i].dnd;
1265    }
1266
1267    if( firstPiece == lastPiece )
1268    {
1269        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1270    }
1271    else
1272    {
1273        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1274        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1275        for( i=firstPiece+1; i<lastPiece; ++i )
1276            tor->info.pieces[i].dnd = dnd;
1277    }
1278
1279    tr_cpInvalidateDND ( tor->completion );
1280
1281    saveFastResumeSoon( tor );
1282
1283    tr_torrentUnlock( tor );
1284}
1285
1286void
1287tr_torrentSetFileDLs ( tr_torrent  * tor,
1288                       int         * files,
1289                       int           fileCount,
1290                       int           doDownload )
1291{
1292    int i;
1293    for( i=0; i<fileCount; ++i )
1294        tr_torrentSetFileDL( tor, files[i], doDownload );
1295}
1296
1297/***
1298****
1299***/
1300
1301int _tr_block( const tr_torrent * tor, int index, int begin )
1302{
1303    const tr_info * inf = &tor->info;
1304    return index * ( inf->pieceSize / tor->blockSize ) +
1305        begin / tor->blockSize;
1306}
1307
1308uint64_t
1309tr_pieceOffset( const tr_torrent * tor, int index, int begin, int length )
1310{
1311    uint64_t ret;
1312    ret = tor->info.pieceSize;
1313    ret *= index;
1314    ret += begin;
1315    ret += length;
1316    return ret;
1317}
1318
Note: See TracBrowser for help on using the repository browser.