source: trunk/libtransmission/torrent.c @ 4209

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

if tr_torrentStat() is called again in the same second for a torrent, return the cached value.

  • Property svn:keywords set to Date Rev Author Id
File size: 34.8 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 4209 2007-12-19 02:44:21Z 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 * available = 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                s->desiredSize += tr_torPieceCountBytes( tor, i );
801                if( tr_bitfieldHas( available, i ) )
802                    s->desiredAvailable += tr_torPieceCountBytes( tor, i );
803            }
804        }
805
806        tr_bitfieldFree( available );
807    }
808   
809    s->ratio = ( s->downloadedEver || s->haveValid )
810      ? s->uploadedEver / (float)(MAX(s->downloadedEver,s->haveValid))
811      : TR_RATIO_NA;
812   
813    tr_torrentUnlock( tor );
814
815    return s;
816}
817
818/***
819****
820***/
821
822static uint64_t
823fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
824{
825    const tr_file * file     =  &tor->info.files[fileIndex];
826    const uint64_t firstBlock       =  file->offset / tor->blockSize;
827    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
828    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
829    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
830    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
831    uint64_t haveBytes = 0;
832
833    assert( tor != NULL );
834    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
835    assert( file->offset + file->length <= tor->info.totalSize );
836    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
837    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
838    assert( firstBlock <= lastBlock );
839    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
840    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
841
842    if( firstBlock == lastBlock )
843    {
844        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
845            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
846    }
847    else
848    {
849        uint64_t i;
850
851        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
852            haveBytes += tor->blockSize - firstBlockOffset;
853
854        for( i=firstBlock+1; i<lastBlock; ++i )
855            if( tr_cpBlockIsComplete( tor->completion, i ) )
856               haveBytes += tor->blockSize;
857
858        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
859            haveBytes += lastBlockOffset + 1;
860    }
861
862    return haveBytes;
863}
864
865tr_file_stat *
866tr_torrentFiles( const tr_torrent * tor, int * fileCount )
867{
868    int i;
869    const int n = tor->info.fileCount;
870    tr_file_stat * files = tr_new0( tr_file_stat, n );
871    tr_file_stat * walk = files;
872
873    for( i=0; i<n; ++i, ++walk )
874    {
875        const tr_file * file = tor->info.files + i;
876
877        walk->bytesCompleted = fileBytesCompleted( tor, i );
878
879        walk->progress = file->length
880            ? walk->bytesCompleted / (float)file->length
881            : 1.0;
882    }
883
884    *fileCount = n;
885
886    return files;
887}
888
889void
890tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
891{
892    tr_free( files );
893}
894
895/***
896****
897***/
898
899tr_peer_stat *
900tr_torrentPeers( const tr_torrent * tor, int * peerCount )
901{
902    tr_peer_stat * ret = NULL;
903
904    if( tor != NULL )
905        ret = tr_peerMgrPeerStats( tor->handle->peerMgr,
906                                   tor->info.hash, peerCount );
907
908    return ret;
909}
910
911void
912tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
913{
914    tr_free( peers );
915}
916
917void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
918{
919    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
920                                          tor->info.hash,
921                                          tab, size );
922}
923
924void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
925{
926    int i;
927    float interval;
928    tr_torrentLock( tor );
929
930    interval = (float)tor->info.pieceCount / (float)size;
931    for( i = 0; i < size; i++ )
932    {
933        int piece = i * interval;
934        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
935    }
936
937    tr_torrentUnlock( tor );
938}
939
940void
941tr_torrentResetTransferStats( tr_torrent * tor )
942{
943    tr_torrentLock( tor );
944
945    tor->downloadedPrev += tor->downloadedCur;
946    tor->downloadedCur   = 0;
947    tor->uploadedPrev   += tor->uploadedCur;
948    tor->uploadedCur     = 0;
949    tor->corruptPrev    += tor->corruptCur;
950    tor->corruptCur      = 0;
951
952    tr_torrentUnlock( tor );
953}
954
955
956void
957tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
958{
959    tr_torrentLock( tor );
960
961    if( has )
962        tr_cpPieceAdd( tor->completion, pieceIndex );
963    else
964        tr_cpPieceRem( tor->completion, pieceIndex );
965
966    tr_torrentUnlock( tor );
967}
968
969void tr_torrentRemoveSaved( tr_torrent * tor )
970{
971    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
972}
973
974/***
975****
976***/
977
978static void
979freeTorrent( tr_torrent * tor )
980{
981    tr_torrent * t;
982    tr_handle * h = tor->handle;
983    tr_info * inf = &tor->info;
984
985    assert( tor != NULL );
986    assert( !tor->isRunning );
987
988    tr_globalLock( h );
989
990    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
991
992    tr_cpClose( tor->completion );
993
994    tr_rcClose( tor->upload );
995    tr_rcClose( tor->download );
996    tr_rcClose( tor->swarmspeed );
997
998    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
999    tr_trackerFree( tor->tracker );
1000    tor->tracker = NULL;
1001
1002    tr_free( tor->destination );
1003
1004    if( tor == h->torrentList )
1005        h->torrentList = tor->next;
1006    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1007        if( t->next == tor ) {
1008            t->next = tor->next;
1009            break;
1010        }
1011    }
1012
1013    assert( h->torrentCount >= 1 );
1014    h->torrentCount--;
1015
1016    tr_inf( "closing torrent %s... %d torrents left",
1017            tor->info.name, h->torrentCount );
1018
1019    tr_metainfoFree( inf );
1020    tr_free( tor );
1021
1022    tr_globalUnlock( h );
1023}
1024
1025enum
1026{
1027    AFTER_RECHECK_NONE,
1028    AFTER_RECHECK_START,
1029    AFTER_RECHECK_STOP,
1030    AFTER_RECHECK_CLOSE
1031};
1032
1033static void
1034checkAndStartImpl( void * vtor )
1035{
1036    tr_torrent * tor = vtor;
1037
1038    tr_globalLock( tor->handle );
1039
1040    tor->isRunning  = 1;
1041    *tor->errorString = '\0';
1042    tr_torrentResetTransferStats( tor );
1043    tor->cpStatus = tr_cpGetStatus( tor->completion );
1044    saveFastResumeNow( tor );
1045    tor->startDate = tr_date( );
1046    tr_trackerStart( tor->tracker );
1047    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
1048
1049    tr_globalUnlock( tor->handle );
1050}
1051
1052static void
1053checkAndStartCB( tr_torrent * tor )
1054{
1055    tr_runInEventThread( tor->handle, checkAndStartImpl, tor );
1056}
1057
1058void
1059tr_torrentStart( tr_torrent * tor )
1060{
1061    tr_globalLock( tor->handle );
1062
1063    if( !tor->isRunning )
1064    {
1065        if( !tor->uncheckedPieces )
1066            tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1067        tr_fastResumeLoad( tor, TR_FR_PROGRESS, tor->uncheckedPieces, tor->destination, FALSE );
1068        tor->isRunning = 1;
1069        tr_ioRecheckAdd( tor, checkAndStartCB );
1070    }
1071
1072    tr_globalUnlock( tor->handle );
1073}
1074
1075static void
1076torrentRecheckDoneImpl( void * vtor )
1077{
1078    tr_torrentRecheckCompleteness( vtor );
1079}
1080static void
1081torrentRecheckDoneCB( tr_torrent * tor )
1082{
1083    tr_runInEventThread( tor->handle, torrentRecheckDoneImpl, tor );
1084}
1085void
1086tr_torrentRecheck( tr_torrent * tor )
1087{
1088    tr_globalLock( tor->handle );
1089
1090    if( !tor->uncheckedPieces )
1091        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1092    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1093
1094    tr_ioRecheckAdd( tor, torrentRecheckDoneCB );
1095
1096    tr_globalUnlock( tor->handle );
1097}
1098
1099
1100static void
1101stopTorrent( void * vtor )
1102{
1103    int i;
1104
1105    tr_torrent * tor = vtor;
1106    tr_ioRecheckRemove( tor );
1107    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1108    tr_trackerStop( tor->tracker );
1109
1110    for( i=0; i<tor->info.fileCount; ++i )
1111    {
1112        char path[MAX_PATH_LENGTH];
1113        const tr_file * file = &tor->info.files[i];
1114        tr_buildPath( path, sizeof(path), tor->destination, file->name, NULL );
1115        tr_fdFileClose( path );
1116    }
1117}
1118
1119void
1120tr_torrentStop( tr_torrent * tor )
1121{
1122    tr_globalLock( tor->handle );
1123
1124    saveFastResumeNow( tor );
1125    tor->isRunning = 0;
1126    tr_runInEventThread( tor->handle, stopTorrent, tor );
1127
1128    tr_globalUnlock( tor->handle );
1129}
1130
1131static void
1132closeTorrent( void * vtor )
1133{
1134    tr_torrent * tor = vtor;
1135    saveFastResumeNow( tor );
1136    tor->isRunning = 0;
1137    stopTorrent( tor );
1138    freeTorrent( tor );
1139}
1140
1141void
1142tr_torrentClose( tr_torrent * tor )
1143{
1144    tr_globalLock( tor->handle );
1145
1146    tr_torrentClearStatusCallback( tor );
1147    tr_runInEventThread( tor->handle, closeTorrent, tor );
1148
1149    tr_globalUnlock( tor->handle );
1150}
1151
1152/**
1153***  Completeness
1154**/
1155
1156static void
1157fireStatusChange( tr_torrent * tor, cp_status_t status )
1158{
1159    assert( tor != NULL );
1160    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1161
1162    if( tor->status_func != NULL )
1163        (tor->status_func)( tor, status, tor->status_func_user_data );
1164}
1165
1166void
1167tr_torrentSetStatusCallback( tr_torrent             * tor,
1168                             tr_torrent_status_func   func,
1169                             void                   * user_data )
1170{
1171    assert( tor != NULL );
1172    tor->status_func = func;
1173    tor->status_func_user_data = user_data;
1174}
1175
1176void
1177tr_torrentClearStatusCallback( tr_torrent * torrent )
1178{
1179    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1180}
1181
1182void
1183tr_torrentRecheckCompleteness( tr_torrent * tor )
1184{
1185    cp_status_t cpStatus;
1186
1187    tr_torrentLock( tor );
1188
1189    cpStatus = tr_cpGetStatus( tor->completion );
1190    if( cpStatus != tor->cpStatus ) {
1191        tor->cpStatus = cpStatus;
1192        fireStatusChange( tor, cpStatus );
1193        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1194            && tor->downloadedCur ) {        /* and it just happened */
1195            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1196        }
1197        saveFastResumeNow( tor );
1198    }
1199    tr_torrentUnlock( tor );
1200}
1201
1202int
1203tr_torrentIsSeed( const tr_torrent * tor )
1204{
1205    return tor->cpStatus==TR_CP_COMPLETE || tor->cpStatus==TR_CP_DONE;
1206}
1207
1208/**
1209***  File priorities
1210**/
1211
1212static void
1213setFilePriority( tr_torrent   * tor,
1214                 int            fileIndex,
1215                 tr_priority_t  priority )
1216{
1217    int i;
1218    tr_file * file;
1219
1220    assert( tor != NULL );
1221    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1222    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1223
1224    file = &tor->info.files[fileIndex];
1225    file->priority = priority;
1226    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1227      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1228
1229    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1230             fileIndex, file->firstPiece, file->lastPiece,
1231             priority, tor->info.files[fileIndex].name );
1232}
1233
1234void
1235tr_torrentSetFilePriorities( tr_torrent     * tor,
1236                             int            * files,
1237                             int              fileCount,
1238                             tr_priority_t    priority )
1239{
1240    int i;
1241    tr_torrentLock( tor );
1242
1243    for( i=0; i<fileCount; ++i )
1244        setFilePriority( tor, files[i], priority );
1245
1246    saveFastResumeNow( tor );
1247    tr_torrentUnlock( tor );
1248}
1249
1250tr_priority_t
1251tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1252{
1253    tr_priority_t ret;
1254
1255    tr_torrentLock( tor );
1256    assert( tor != NULL );
1257    assert( 0<=file && file<tor->info.fileCount );
1258    ret = tor->info.files[file].priority;
1259    tr_torrentUnlock( tor );
1260
1261    return ret;
1262}
1263
1264tr_priority_t*
1265tr_torrentGetFilePriorities( const tr_torrent * tor )
1266{
1267    int i;
1268    tr_priority_t * p;
1269
1270    tr_torrentLock( tor );
1271    p = tr_new0( tr_priority_t, tor->info.fileCount );
1272    for( i=0; i<tor->info.fileCount; ++i )
1273        p[i] = tor->info.files[i].priority;
1274    tr_torrentUnlock( tor );
1275
1276    return p;
1277}
1278
1279/**
1280***  File DND
1281**/
1282
1283int
1284tr_torrentGetFileDL( const tr_torrent * tor,
1285                     int                file )
1286{
1287    int doDownload;
1288    tr_torrentLock( tor );
1289
1290    assert( 0<=file && file<tor->info.fileCount );
1291    doDownload = !tor->info.files[file].dnd;
1292
1293    tr_torrentUnlock( tor );
1294    return doDownload != 0;
1295}
1296
1297static void
1298setFileDND( tr_torrent  * tor,
1299            int           fileIndex,
1300            int           doDownload )
1301{
1302    tr_file * file;
1303    const int dnd = !doDownload;
1304    int firstPiece, firstPieceDND;
1305    int lastPiece, lastPieceDND;
1306    int i;
1307
1308    file = &tor->info.files[fileIndex];
1309    file->dnd = dnd;
1310    firstPiece = file->firstPiece;
1311    lastPiece = file->lastPiece;
1312
1313    /* can't set the first piece to DND unless
1314       every file using that piece is DND */
1315    firstPieceDND = dnd;
1316    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1317        if( tor->info.files[i].lastPiece != firstPiece )
1318            break;
1319        firstPieceDND = tor->info.files[i].dnd;
1320    }
1321
1322    /* can't set the last piece to DND unless
1323       every file using that piece is DND */
1324    lastPieceDND = dnd;
1325    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1326        if( tor->info.files[i].firstPiece != lastPiece )
1327            break;
1328        lastPieceDND = tor->info.files[i].dnd;
1329    }
1330
1331    if( firstPiece == lastPiece )
1332    {
1333        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1334    }
1335    else
1336    {
1337        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1338        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1339        for( i=firstPiece+1; i<lastPiece; ++i )
1340            tor->info.pieces[i].dnd = dnd;
1341    }
1342}
1343
1344void
1345tr_torrentSetFileDLs ( tr_torrent  * tor,
1346                       int         * files,
1347                       int           fileCount,
1348                       int           doDownload )
1349{
1350    int i;
1351    tr_torrentLock( tor );
1352
1353    for( i=0; i<fileCount; ++i )
1354        setFileDND( tor, files[i], doDownload );
1355    tr_cpInvalidateDND ( tor->completion );
1356    saveFastResumeNow( tor );
1357
1358    tr_torrentUnlock( tor );
1359}
1360
1361/***
1362****
1363***/
1364
1365int _tr_block( const tr_torrent * tor, int index, int begin )
1366{
1367    const tr_info * inf = &tor->info;
1368    return index * ( inf->pieceSize / tor->blockSize ) +
1369        begin / tor->blockSize;
1370}
1371
1372uint64_t
1373tr_pieceOffset( const tr_torrent * tor, int index, int begin, int length )
1374{
1375    uint64_t ret;
1376    ret = tor->info.pieceSize;
1377    ret *= index;
1378    ret += begin;
1379    ret += length;
1380    return ret;
1381}
Note: See TracBrowser for help on using the repository browser.