source: trunk/libtransmission/torrent.c @ 4213

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

fix an obscure bug in tr_torrentStat() where (% verified + % unverified + % unavailable) could be > 1.0

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