source: trunk/libtransmission/torrent.c @ 3572

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

Quick & dirty fix to crasher ticket #415.

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