source: trunk/libtransmission/torrent.c @ 3289

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

tweak the `start torrent' code based on feedback from BentMyWookie?

  • Property svn:keywords set to Date Rev Author Id
File size: 33.0 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3289 2007-10-04 23:47:49Z 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->destination = tr_strdup( destination );
273
274    tor->handle   = h;
275    tor->pexDisabled = 0;
276
277    /**
278     * Decide on a block size.  constraints:
279     * (1) most clients decline requests over 16 KiB
280     * (2) pieceSize must be a multiple of block size
281     */
282    tor->blockSize = info->pieceSize;
283    while( tor->blockSize > (1024*16) )
284        tor->blockSize /= 2;
285
286    tor->lastPieceSize = info->totalSize % info->pieceSize;
287
288    if( !tor->lastPieceSize )
289         tor->lastPieceSize = info->pieceSize;
290
291    tor->lastBlockSize = info->totalSize % tor->blockSize;
292
293    if( !tor->lastBlockSize )
294         tor->lastBlockSize = tor->blockSize;
295
296    tor->blockCount =
297        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
298
299    tor->blockCountInPiece =
300        info->pieceSize / tor->blockSize;
301
302    tor->blockCountInLastPiece =
303        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
304
305    /* check our work */
306    assert( ( info->pieceSize % tor->blockSize ) == 0 );
307    t = info->pieceCount - 1;
308    t *= info->pieceSize;
309    t += tor->lastPieceSize;
310    assert( t == info->totalSize );
311    t = tor->blockCount - 1;
312    t *= tor->blockSize;
313    t += tor->lastBlockSize;
314    assert( t == info->totalSize );
315    t = info->pieceCount - 1;
316    t *= tor->blockCountInPiece;
317    t += tor->blockCountInLastPiece;
318    assert( t == (uint64_t)tor->blockCount );
319
320    tor->completion = tr_cpInit( tor );
321
322    tr_torrentInitFilePieces( tor );
323
324    tor->upload         = tr_rcInit();
325    tor->download       = tr_rcInit();
326    tor->swarmspeed     = tr_rcInit();
327
328    tr_sha1( tor->obfuscatedHash, "req2", 4,
329                                  info->hash, SHA_DIGEST_LENGTH,
330                                  NULL );
331
332    tr_peerMgrAddTorrent( h->peerMgr, tor );
333
334    if( !h->isPortSet )
335        tr_setBindPort( h, TR_DEFAULT_PORT );
336
337    assert( !tor->downloadedCur );
338    assert( !tor->uploadedCur );
339
340    tor->error   = TR_OK;
341
342    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
343    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
344
345    /* the `paused' flag has highest precedence...
346       after that, the fastresume setting is used...
347       if that's not found, default to RUNNING */
348    if( flags & TR_FLAG_PAUSED )
349        doStart = 0;
350    else if( loaded & TR_FR_RUN )
351        doStart = tor->isRunning;
352    else
353        doStart = 1;
354    tor->isRunning = 0;
355
356    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
357        tr_bitfieldFree( uncheckedPieces );
358    else
359        tor->uncheckedPieces = uncheckedPieces;
360
361
362    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
363        int limit, enabled;
364        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
365        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
366        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
367        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
368    }
369
370    tor->cpStatus = tr_cpGetStatus( tor->completion );
371
372    tor->tracker = tr_trackerNew( tor );
373    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
374
375    tor->next = h->torrentList;
376    h->torrentList = tor;
377    h->torrentCount++;
378
379    tr_globalUnlock( h );
380
381    if( doStart )
382        tr_torrentStart( tor );
383}
384
385static int
386pathIsInUse ( const tr_handle   * h,
387              const char        * destination,
388              const char        * name )
389{
390    const tr_torrent * tor;
391   
392    for( tor=h->torrentList; tor; tor=tor->next )
393        if( !strcmp( destination, tor->destination )
394         && !strcmp( name, tor->info.name ) )
395            return TRUE;
396
397    return FALSE;
398}
399
400static int
401hashExists( const tr_handle   * h,
402            const uint8_t     * hash )
403{
404    const tr_torrent * tor;
405
406    for( tor=h->torrentList; tor; tor=tor->next )
407        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
408            return TRUE;
409
410    return FALSE;
411}
412
413static int
414infoCanAdd( const tr_handle   * h,
415            const char        * destination,
416            const tr_info     * info )
417{
418    if( hashExists( h, info->hash ) )
419        return TR_EDUPLICATE;
420
421    if( destination && pathIsInUse( h, destination, info->name ) )
422        return TR_EDUPLICATE;
423
424    return TR_OK;
425}
426
427int
428tr_torrentParse( const tr_handle  * h,
429                 const char       * path,
430                 const char       * destination,
431                 tr_info          * setme_info )
432{
433    int ret, doFree;
434    tr_info tmp;
435
436    if( setme_info == NULL )
437        setme_info = &tmp;
438
439    memset( setme_info, 0, sizeof( tr_info ) );
440    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
441    doFree = !ret && (setme_info == &tmp);
442
443    if( ret == TR_OK )
444        ret = infoCanAdd( h, destination, setme_info );
445
446    if( doFree )
447        tr_metainfoFree( &tmp );
448
449    return ret;
450}
451 
452tr_torrent *
453tr_torrentInit( tr_handle   * h,
454                const char  * path,
455                const char  * destination,
456                int           flags,
457                int         * error )
458{
459    int val;
460    int tmpError;
461    tr_torrent * tor = NULL;
462
463    if( !error )
464         error = &tmpError;
465
466    if(( val = tr_torrentParse( h, path, destination, NULL )))
467        *error = val;
468    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
469        *error = TR_EOTHER;
470    else {
471        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
472        torrentRealInit( h, tor, destination, flags );
473    }
474
475    return tor;
476}
477
478int
479tr_torrentParseHash( const tr_handle  * h,
480                     const char       * hashStr,
481                     const char       * destination,
482                     tr_info          * setme_info )
483{
484    int ret, doFree;
485    tr_info tmp;
486
487    if( setme_info == NULL )
488        setme_info = &tmp;
489
490    memset( setme_info, 0, sizeof( tr_info ) );
491    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
492    doFree = !ret && (setme_info == &tmp);
493
494    if( ret == TR_OK )
495        ret = infoCanAdd( h, destination, setme_info );
496
497    if( doFree )
498        tr_metainfoFree( &tmp );
499
500    return ret;
501}
502
503tr_torrent *
504tr_torrentInitSaved( tr_handle    * h,
505                     const char   * hashStr,
506                     const char   * destination,
507                     int            flags,
508                     int          * error )
509{
510    int val;
511    int tmpError;
512    tr_torrent * tor = NULL;
513
514    if( !error )
515         error = &tmpError;
516
517    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
518        *error = val;
519    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
520        *error = TR_EOTHER;
521    else {
522        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
523        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
524    }
525
526    return tor;
527}
528
529static int
530tr_torrentParseData( const tr_handle  * h,
531                     const uint8_t    * data,
532                     size_t             size,
533                     const char       * destination,
534                     tr_info          * setme_info )
535{
536    int ret, doFree;
537    tr_info tmp;
538
539    if( setme_info == NULL )
540        setme_info = &tmp;
541
542    memset( setme_info, 0, sizeof( tr_info ) );
543    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
544    doFree = !ret && (setme_info == &tmp);
545
546    if( ret == TR_OK )
547        ret = infoCanAdd( h, destination, setme_info );
548
549    if( doFree )
550        tr_metainfoFree( &tmp );
551
552    return ret;
553}
554
555tr_torrent *
556tr_torrentInitData( tr_handle      * h,
557                    const uint8_t  * data,
558                    size_t           size,
559                    const char     * destination,
560                    int              flags,
561                    int            * error )
562{
563    int val;
564    int tmpError;
565    tr_torrent * tor = NULL;
566
567    if( !error )
568         error = &tmpError;
569
570    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
571        *error = val;
572    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
573        *error = TR_EOTHER;
574    else {
575        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
576        torrentRealInit( h, tor, destination, flags );
577    }
578
579    return tor;
580}
581
582const tr_info *
583tr_torrentInfo( const tr_torrent * tor )
584{
585    return &tor->info;
586}
587
588/***
589****
590***/
591
592#if 0
593int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
594{
595    return tr_trackerScrape( tor, s, l, d );
596}
597#endif
598
599static int
600saveFastResumeNow( void * vtor )
601{
602    tr_torrent * tor = (tr_torrent *) vtor;
603
604    tr_fastResumeSave( tor );
605    tr_torrentRecheckCompleteness( tor );
606
607    tr_timerFree( &tor->saveTimer );
608    return FALSE;
609}
610
611static void
612saveFastResumeSoon( void * vtor )
613{
614    tr_torrent * tor = (tr_torrent *) vtor;
615
616    if( tor->saveTimer == NULL )
617        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 100 );
618}
619
620/**
621***
622**/
623
624void
625tr_torrentSetFolder( tr_torrent * tor, const char * path )
626{
627    tr_free( tor->destination );
628    tor->destination = tr_strdup( path );
629    saveFastResumeSoon( tor );
630}
631
632const char*
633tr_torrentGetFolder( const tr_torrent * tor )
634{
635    return tor->destination;
636}
637
638void
639tr_torrentChangeMyPort( tr_torrent * tor )
640{
641    if( tor->tracker )
642        tr_trackerChangeMyPort( tor->tracker );
643}
644
645
646void
647tr_torrentDisablePex( tr_torrent * tor, int disable )
648{
649    assert( tor != NULL );
650    assert( disable==0 || disable==1 );
651
652    /* pex is ALWAYS disabled for private torrents */
653    if( tor->info.flags & TR_FLAG_PRIVATE )
654        disable = TRUE;
655
656    tor->pexDisabled = disable;
657}
658
659void
660tr_manualUpdate( tr_torrent * tor )
661{
662    if( tor->isRunning )
663        tr_trackerReannounce( tor->tracker );
664}
665int
666tr_torrentCanManualUpdate( const tr_torrent * tor )
667{
668    return ( tor != NULL )
669        && ( tor->isRunning )
670        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
671}
672
673const tr_stat *
674tr_torrentStat( tr_torrent * tor )
675{
676    tr_stat * s;
677    struct tr_tracker * tc;
678
679    tr_torrentLock( tor );
680
681    tor->statCur = !tor->statCur;
682    s = &tor->stats[tor->statCur];
683
684    s->error  = tor->error;
685    memcpy( s->errorString, tor->errorString,
686            sizeof( s->errorString ) );
687
688    tc = tor->tracker;
689    s->tracker = tr_trackerGetAddress( tor->tracker );
690
691    tr_peerMgrTorrentStats( tor->handle->peerMgr,
692                            tor->info.hash,
693                            &s->peersKnown,
694                            &s->peersConnected,
695                            &s->peersSendingToUs,
696                            &s->peersGettingFromUs,
697                             s->peersFrom );
698
699    s->percentComplete = tr_cpPercentComplete ( tor->completion );
700
701    s->percentDone = tr_cpPercentDone( tor->completion );
702    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
703
704    if( tor->recheckState == TR_RECHECK_NOW )
705        s->status = TR_STATUS_CHECK;
706    else if( tor->recheckState == TR_RECHECK_WAIT )
707        s->status = TR_STATUS_CHECK_WAIT;
708    else if( !tor->isRunning )
709        s->status = TR_STATUS_STOPPED;
710    else if( tor->cpStatus == TR_CP_INCOMPLETE )
711        s->status = TR_STATUS_DOWNLOAD;
712    else if( tor->cpStatus == TR_CP_DONE )
713        s->status = TR_STATUS_DONE;
714    else
715        s->status = TR_STATUS_SEED;
716
717    s->recheckProgress = (tor->uncheckedPieces == NULL)
718        ? 0.0
719        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
720
721    s->cpStatus = tor->cpStatus;
722
723    /* tr_rcRate() doesn't make the difference between 'piece'
724       messages and other messages, which causes a non-zero
725       download rate even tough we are not downloading. So we
726       force it to zero not to confuse the user. */
727    s->rateDownload = tor->isRunning
728        ? tr_rcRate( tor->download )
729        : 0.0;
730    s->rateUpload = tr_rcRate( tor->upload );
731   
732    tr_trackerGetCounts( tc,
733                         &s->completedFromTracker,
734                         &s->leechers, 
735                         &s->seeders );
736
737    s->swarmspeed = tr_rcRate( tor->swarmspeed );
738   
739    s->startDate = tor->startDate;
740    s->activityDate = tor->activityDate;
741
742    s->eta = s->rateDownload < 0.1
743        ? -1.0f
744        : (s->leftUntilDone / s->rateDownload / 1024.0);
745
746    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
747    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
748    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
749    s->haveValid       = tr_cpHaveValid( tor->completion );
750    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
751
752
753    {
754        int i;
755        tr_bitfield * available = tr_peerMgrGetAvailable( tor->handle->peerMgr,
756                                                          tor->info.hash );
757        s->desiredSize = 0;
758        s->desiredAvailable = 0;
759
760        for( i=0; i<tor->info.pieceCount; ++i ) {
761            if( !tor->info.pieces[i].dnd ) {
762                s->desiredSize += tor->info.pieceSize;
763                if( tr_bitfieldHas( available, i ) )
764                    s->desiredAvailable += tor->info.pieceSize;
765            }
766        }
767
768        tr_bitfieldFree( available );
769    }
770   
771    s->ratio = s->downloadedEver ? s->uploadedEver / (float)s->downloadedEver
772                                 : TR_RATIO_NA;
773   
774    tr_torrentUnlock( tor );
775
776    return s;
777}
778
779/***
780****
781***/
782
783static uint64_t
784fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
785{
786    const tr_file * file     =  &tor->info.files[fileIndex];
787    const uint64_t firstBlock       =  file->offset / tor->blockSize;
788    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
789    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
790    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
791    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
792    uint64_t haveBytes = 0;
793
794    assert( tor != NULL );
795    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
796    assert( file->offset + file->length <= tor->info.totalSize );
797    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
798    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
799    assert( firstBlock <= lastBlock );
800    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
801    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
802
803    if( firstBlock == lastBlock )
804    {
805        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
806            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
807    }
808    else
809    {
810        uint64_t i;
811
812        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
813            haveBytes += tor->blockSize - firstBlockOffset;
814
815        for( i=firstBlock+1; i<lastBlock; ++i )
816            if( tr_cpBlockIsComplete( tor->completion, i ) )
817               haveBytes += tor->blockSize;
818
819        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
820            haveBytes += lastBlockOffset + 1;
821    }
822
823    return haveBytes;
824}
825
826tr_file_stat *
827tr_torrentFiles( const tr_torrent * tor, int * fileCount )
828{
829    int i;
830    const int n = tor->info.fileCount;
831    tr_file_stat * files = tr_new0( tr_file_stat, n );
832    tr_file_stat * walk = files;
833
834    for( i=0; i<n; ++i, ++walk )
835    {
836        const tr_file * file = tor->info.files + i;
837        cp_status_t cp;
838
839        walk->bytesCompleted = fileBytesCompleted( tor, i );
840
841        walk->progress = file->length
842            ? walk->bytesCompleted / (float)file->length
843            : 1.0;
844
845        if( walk->bytesCompleted >= file->length )
846            cp = TR_CP_COMPLETE;
847        else if( tor->info.files[i].dnd )
848            cp = TR_CP_DONE;
849        else
850            cp = TR_CP_INCOMPLETE;
851
852        walk->completionStatus = cp;
853    }
854
855    *fileCount = n;
856
857    return files;
858}
859
860void
861tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
862{
863    tr_free( files );
864}
865
866/***
867****
868***/
869
870tr_peer_stat *
871tr_torrentPeers( const tr_torrent * tor, int * peerCount )
872{
873    return tr_peerMgrPeerStats( tor->handle->peerMgr,
874                                tor->info.hash, peerCount );
875}
876
877void
878tr_torrentPeersFree( tr_peer_stat * peers, int peerCount )
879{
880    int i;
881    for( i=0; i<peerCount; ++i )
882        tr_free( (char*) peers[i].client );
883    tr_free( peers );
884}
885
886void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
887{
888    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
889                                          tor->info.hash,
890                                          tab, size );
891}
892
893void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
894{
895    int i;
896    float interval;
897    tr_torrentLock( tor );
898
899    interval = (float)tor->info.pieceCount / (float)size;
900    for( i = 0; i < size; i++ )
901    {
902        int piece = i * interval;
903        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
904    }
905
906    tr_torrentUnlock( tor );
907}
908
909void
910tr_torrentResetTransferStats( tr_torrent * tor )
911{
912    tr_torrentLock( tor );
913
914    tor->downloadedPrev += tor->downloadedCur;
915    tor->downloadedCur   = 0;
916    tor->uploadedPrev   += tor->uploadedCur;
917    tor->uploadedCur     = 0;
918    tor->corruptPrev    += tor->corruptCur;
919    tor->corruptCur      = 0;
920
921    tr_torrentUnlock( tor );
922}
923
924
925void
926tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
927{
928    tr_torrentLock( tor );
929
930    if( has )
931        tr_cpPieceAdd( tor->completion, pieceIndex );
932    else
933        tr_cpPieceRem( tor->completion, pieceIndex );
934
935    tr_torrentUnlock( tor );
936}
937
938void tr_torrentRemoveSaved( tr_torrent * tor )
939{
940    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
941}
942
943/***
944****
945***/
946
947static void
948freeTorrent( tr_torrent * tor )
949{
950    tr_torrent * t;
951    tr_handle * h = tor->handle;
952    tr_info * inf = &tor->info;
953
954    assert( tor != NULL );
955    assert( !tor->isRunning );
956
957    tr_globalLock( h );
958
959    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
960
961    tr_cpClose( tor->completion );
962
963    tr_rcClose( tor->upload );
964    tr_rcClose( tor->download );
965    tr_rcClose( tor->swarmspeed );
966
967    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
968    tr_trackerFree( tor->tracker );
969    tor->tracker = NULL;
970
971    tr_free( tor->destination );
972
973    if( tor == h->torrentList )
974        h->torrentList = tor->next;
975    else for( t=h->torrentList; t!=NULL; t=t->next ) {
976        if( t->next == tor ) {
977            t->next = tor->next;
978            break;
979        }
980    }
981
982    assert( h->torrentCount >= 1 );
983    h->torrentCount--;
984
985    tr_inf( "closing torrent %s... %d torrents left",
986            tor->info.name, h->torrentCount );
987
988    tr_metainfoFree( inf );
989    tr_free( tor );
990
991    tr_globalUnlock( h );
992}
993
994enum
995{
996    AFTER_RECHECK_NONE,
997    AFTER_RECHECK_START,
998    AFTER_RECHECK_STOP,
999    AFTER_RECHECK_CLOSE
1000};
1001
1002static void
1003checkAndStartCB( tr_torrent * tor )
1004{
1005    tr_globalLock( tor->handle );
1006
1007    tr_torrentRecheckCompleteness( tor );
1008
1009    tor->isRunning  = 1;
1010    *tor->errorString = '\0';
1011    tr_torrentResetTransferStats( tor );
1012    tr_torrentRecheckCompleteness( tor );
1013    tor->startDate = tr_date( );
1014    tr_trackerStart( tor->tracker );
1015    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
1016
1017    tr_globalUnlock( tor->handle );
1018}
1019   
1020void
1021tr_torrentStart( tr_torrent * tor )
1022{
1023    tr_globalLock( tor->handle );
1024
1025    if( !tor->isRunning )
1026    {
1027        tor->isRunning = 1;
1028        tr_ioRecheckAdd( tor, checkAndStartCB );
1029    }
1030
1031    tr_globalUnlock( tor->handle );
1032}
1033
1034void
1035tr_torrentRecheck( tr_torrent * tor )
1036{
1037    tr_globalLock( tor->handle );
1038
1039    if( !tor->uncheckedPieces )
1040        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1041    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1042
1043    tr_ioRecheckAdd( tor, NULL );
1044
1045    tr_globalUnlock( tor->handle );
1046}
1047
1048
1049static void
1050stopTorrent( void * vtor )
1051{
1052    tr_torrent * tor = vtor;
1053    tr_ioRecheckRemove( tor );
1054    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1055    tr_trackerStop( tor->tracker );
1056    tr_ioClose( tor );
1057}
1058
1059void
1060tr_torrentStop( tr_torrent * tor )
1061{
1062    tr_globalLock( tor->handle );
1063
1064    saveFastResumeNow( tor );
1065    tor->isRunning = 0;
1066    tr_runInEventThread( tor->handle, stopTorrent, tor );
1067
1068    tr_globalUnlock( tor->handle );
1069}
1070
1071static void
1072closeTorrent( void * vtor )
1073{
1074    tr_torrent * tor = vtor;
1075    saveFastResumeNow( tor );
1076    tor->isRunning = 0;
1077    stopTorrent( tor );
1078    freeTorrent( tor );
1079}
1080
1081void
1082tr_torrentClose( tr_torrent * tor )
1083{
1084    tr_globalLock( tor->handle );
1085
1086    tr_runInEventThread( tor->handle, closeTorrent, tor );
1087
1088    tr_globalUnlock( tor->handle );
1089}
1090
1091/**
1092***  Completeness
1093**/
1094
1095static void
1096fireStatusChange( tr_torrent * tor, cp_status_t status )
1097{
1098    assert( tor != NULL );
1099    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1100
1101    if( tor->status_func != NULL )
1102        (tor->status_func)( tor, status, tor->status_func_user_data );
1103}
1104
1105void
1106tr_torrentSetStatusCallback( tr_torrent             * tor,
1107                             tr_torrent_status_func   func,
1108                             void                   * user_data )
1109{
1110    assert( tor != NULL );
1111    tor->status_func = func;
1112    tor->status_func_user_data = user_data;
1113}
1114
1115void
1116tr_torrentClearStatusCallback( tr_torrent * torrent )
1117{
1118    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1119}
1120
1121void
1122tr_torrentRecheckCompleteness( tr_torrent * tor )
1123{
1124    cp_status_t cpStatus;
1125
1126    tr_torrentLock( tor );
1127
1128    cpStatus = tr_cpGetStatus( tor->completion );
1129    if( cpStatus != tor->cpStatus ) {
1130        tor->cpStatus = cpStatus;
1131        fireStatusChange( tor, cpStatus );
1132        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1133            && tor->downloadedCur ) {        /* and it just happened */
1134            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1135        }
1136        tr_ioClose( tor );
1137        saveFastResumeSoon( tor );
1138    }
1139    tr_torrentUnlock( tor );
1140}
1141
1142/**
1143***  File priorities
1144**/
1145
1146void
1147tr_torrentSetFilePriority( tr_torrent   * tor,
1148                           int            fileIndex,
1149                           tr_priority_t  priority )
1150{
1151    int i;
1152    tr_file * file;
1153
1154    tr_torrentLock( tor );
1155
1156    assert( tor != NULL );
1157    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1158    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1159
1160    file = &tor->info.files[fileIndex];
1161    file->priority = priority;
1162    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1163      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1164
1165    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1166             fileIndex, file->firstPiece, file->lastPiece,
1167             priority, tor->info.files[fileIndex].name );
1168
1169    saveFastResumeSoon( tor );
1170
1171    tr_torrentUnlock( tor );
1172}
1173
1174void
1175tr_torrentSetFilePriorities( tr_torrent     * tor,
1176                             int            * files,
1177                             int              fileCount,
1178                             tr_priority_t    priority )
1179{
1180    int i;
1181    for( i=0; i<fileCount; ++i )
1182        tr_torrentSetFilePriority( tor, files[i], priority );
1183}
1184
1185tr_priority_t
1186tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1187{
1188    tr_priority_t ret;
1189
1190    tr_torrentLock( tor );
1191    assert( tor != NULL );
1192    assert( 0<=file && file<tor->info.fileCount );
1193    ret = tor->info.files[file].priority;
1194    tr_torrentUnlock( tor );
1195
1196    return ret;
1197}
1198
1199tr_priority_t*
1200tr_torrentGetFilePriorities( const tr_torrent * tor )
1201{
1202    int i;
1203    tr_priority_t * p;
1204
1205    tr_torrentLock( tor );
1206    p = tr_new0( tr_priority_t, tor->info.fileCount );
1207    for( i=0; i<tor->info.fileCount; ++i )
1208        p[i] = tor->info.files[i].priority;
1209    tr_torrentUnlock( tor );
1210
1211    return p;
1212}
1213
1214/**
1215***  File DND
1216**/
1217
1218int
1219tr_torrentGetFileDL( const tr_torrent * tor,
1220                     int                file )
1221{
1222    int doDownload;
1223    tr_torrentLock( tor );
1224
1225    assert( 0<=file && file<tor->info.fileCount );
1226    doDownload = !tor->info.files[file].dnd;
1227
1228    tr_torrentUnlock( tor );
1229    return doDownload != 0;
1230}
1231
1232void
1233tr_torrentSetFileDL( tr_torrent  * tor,
1234                     int           fileIndex,
1235                     int           doDownload )
1236{
1237    tr_file * file;
1238    const int dnd = !doDownload;
1239    int firstPiece, firstPieceDND;
1240    int lastPiece, lastPieceDND;
1241    int i;
1242
1243    tr_torrentLock( tor );
1244
1245    file = &tor->info.files[fileIndex];
1246    file->dnd = dnd;
1247    firstPiece = file->firstPiece;
1248    lastPiece = file->lastPiece;
1249
1250    /* can't set the first piece to DND unless
1251       every file using that piece is DND */
1252    firstPieceDND = dnd;
1253    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1254        if( tor->info.files[i].lastPiece != firstPiece )
1255            break;
1256        firstPieceDND = tor->info.files[i].dnd;
1257    }
1258
1259    /* can't set the last piece to DND unless
1260       every file using that piece is DND */
1261    lastPieceDND = dnd;
1262    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1263        if( tor->info.files[i].firstPiece != lastPiece )
1264            break;
1265        lastPieceDND = tor->info.files[i].dnd;
1266    }
1267
1268    if( firstPiece == lastPiece )
1269    {
1270        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1271    }
1272    else
1273    {
1274        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1275        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1276        for( i=firstPiece+1; i<lastPiece; ++i )
1277            tor->info.pieces[i].dnd = dnd;
1278    }
1279
1280    tr_cpInvalidateDND ( tor->completion );
1281
1282    saveFastResumeSoon( tor );
1283
1284    tr_torrentUnlock( tor );
1285}
1286
1287void
1288tr_torrentSetFileDLs ( tr_torrent  * tor,
1289                       int         * files,
1290                       int           fileCount,
1291                       int           doDownload )
1292{
1293    int i;
1294    for( i=0; i<fileCount; ++i )
1295        tr_torrentSetFileDL( tor, files[i], doDownload );
1296}
1297
1298/***
1299****
1300***/
1301
1302int _tr_block( const tr_torrent * tor, int index, int begin )
1303{
1304    const tr_info * inf = &tor->info;
1305    return index * ( inf->pieceSize / tor->blockSize ) +
1306        begin / tor->blockSize;
1307}
1308
1309uint64_t
1310tr_pieceOffset( const tr_torrent * tor, int index, int begin, int length )
1311{
1312    uint64_t ret;
1313    ret = tor->info.pieceSize;
1314    ret *= index;
1315    ret += begin;
1316    ret += length;
1317    return ret;
1318}
1319
Note: See TracBrowser for help on using the repository browser.