source: trunk/libtransmission/torrent.c @ 5011

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

fix a possible crash when deleting a torrent.

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