source: branches/encryption/libtransmission/torrent.c @ 2988

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

move the torrent recheck into its own thread. we're getting close to getting rid of all the torrent threads =)

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