source: trunk/libtransmission/torrent.c @ 2509

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

speed improvements to tr_torrentStat() by folding two expensive & redundant functions together.

  • Property svn:keywords set to Date Rev Author Id
File size: 38.0 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2509 2007-07-27 01:22:49Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#include "transmission.h"
31#include "fastresume.h"
32#include "trcompat.h" /* for strlcpy */
33#include "metainfo.h"
34#include "net.h" /* tr_netNtop */
35#include "shared.h"
36
37/***
38****  LOCKS
39***/
40
41void
42tr_torrentReaderLock( const tr_torrent_t * tor )
43{
44    tr_rwReaderLock ( (tr_rwlock_t*)&tor->lock );
45}
46
47void
48tr_torrentReaderUnlock( const tr_torrent_t * tor )
49{
50    tr_rwReaderUnlock ( (tr_rwlock_t*)&tor->lock );
51}
52
53void
54tr_torrentWriterLock( tr_torrent_t * tor )
55{
56    tr_rwWriterLock ( &tor->lock );
57}
58
59void
60tr_torrentWriterUnlock( tr_torrent_t * tor )
61{
62    tr_rwWriterUnlock ( &tor->lock );
63}
64
65/***
66****  PER-TORRENT UL / DL SPEEDS
67***/
68
69void
70tr_torrentSetSpeedMode( tr_torrent_t     * tor,
71                        int                up_or_down,
72                        tr_speedlimit_t    mode )
73{
74    tr_speedlimit_t * limit = up_or_down==TR_UP
75        ? &tor->uploadLimitMode
76        : &tor->downloadLimitMode;
77    *limit = mode;
78}
79
80tr_speedlimit_t
81tr_torrentGetSpeedMode( const tr_torrent_t * tor,
82                        int                  up_or_down)
83{
84    return up_or_down==TR_UP ? tor->uploadLimitMode
85                             : tor->downloadLimitMode;
86}
87
88void
89tr_torrentSetSpeedLimit( tr_torrent_t   * tor,
90                         int              up_or_down,
91                         int              single_KiB_sec )
92{
93    tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download;
94    tr_rcSetLimit( rc, single_KiB_sec );
95}
96
97int
98tr_torrentGetSpeedLimit( const tr_torrent_t  * tor,
99                         int                   up_or_down )
100{
101    tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download;
102    return tr_rcGetLimit( rc );
103}
104
105/***
106****
107****  TORRENT INSTANTIATION
108****
109***/
110
111static int
112getBytePiece( const tr_info_t * info, uint64_t byteOffset )
113{
114    assert( info != NULL );
115    assert( info->pieceSize != 0 );
116
117    return byteOffset / info->pieceSize;
118}
119
120static void
121initFilePieces ( tr_info_t * info, int fileIndex )
122{
123    tr_file_t * file = &info->files[fileIndex];
124    uint64_t firstByte, lastByte;
125
126    assert( info != NULL );
127    assert( 0<=fileIndex && fileIndex<info->fileCount );
128
129    file = &info->files[fileIndex];
130    firstByte = file->offset;
131    lastByte = firstByte + (file->length ? file->length-1 : 0);
132    file->firstPiece = getBytePiece( info, firstByte );
133    file->lastPiece = getBytePiece( info, lastByte );
134    tr_dbg( "file #%d is in pieces [%d...%d] (%s)", fileIndex, file->firstPiece, file->lastPiece, file->name );
135}
136
137static tr_priority_t
138calculatePiecePriority ( const tr_torrent_t * tor,
139                         int                  piece )
140{
141    int i;
142    tr_priority_t priority = TR_PRI_NORMAL;
143
144    for( i=0; i<tor->info.fileCount; ++i )
145    {
146        const tr_file_t * file = &tor->info.files[i];
147        if ( file->firstPiece <= piece
148          && file->lastPiece  >= piece
149          && file->priority   >  priority)
150              priority = file->priority;
151    }
152
153    return priority;
154}
155
156static void
157tr_torrentInitFilePieces( tr_torrent_t * tor )
158{
159    int i;
160    uint64_t offset = 0;
161
162    assert( tor != NULL );
163
164    for( i=0; i<tor->info.fileCount; ++i ) {
165      tor->info.files[i].offset = offset;
166      offset += tor->info.files[i].length;
167      initFilePieces( &tor->info, i );
168    }
169
170    for( i=0; i<tor->info.pieceCount; ++i )
171    {
172        tr_piece_t * piece = tor->info.pieces + i;
173        uint64_t lastByte;
174        int lastBlock;
175        if( i == (tor->info.pieceCount-1))
176            lastByte = tor->info.totalSize - 1;
177        else {
178            lastByte = i + 1;
179            lastByte *= tor->info.pieceSize;
180            --lastByte;
181        }
182        lastBlock = lastByte / tor->blockSize;
183
184        piece->priority = calculatePiecePriority( tor, i );
185        piece->firstBlock = i * (tor->info.pieceSize / tor->blockSize);
186        piece->blockCount = lastBlock + 1 - piece->firstBlock;
187
188        assert( 0 <= piece->firstBlock );
189        assert( piece->firstBlock <= lastBlock );
190        assert( lastBlock < tor->blockCount );
191        assert( piece->firstBlock + piece->blockCount <= tor->blockCount );
192    }
193}
194
195static void torrentThreadLoop( void * );
196
197static void
198torrentRealInit( tr_handle_t   * h,
199                 tr_torrent_t  * tor,
200                 const char    * destination,
201                 int             flags )
202{
203    int i;
204    uint64_t loaded;
205    char name[512];
206    tr_bitfield_t * uncheckedPieces;
207   
208    tor->info.flags |= flags;
209
210    tr_sharedLock( h->shared );
211
212    tor->destination = tr_strdup( destination );
213
214    tor->handle   = h;
215    tor->key      = h->key;
216    tor->azId     = h->azId;
217    tor->hasChangedState = -1;
218   
219    /* Escaped info hash for HTTP queries */
220    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
221    {
222        snprintf( &tor->escapedHashString[3*i],
223                  sizeof( tor->escapedHashString ) - 3 * i,
224                  "%%%02x", tor->info.hash[i] );
225    }
226
227    tor->pexDisabled = 0;
228
229    /* Block size: usually 16 ko, or less if we have to */
230    tor->blockSize  = MIN( tor->info.pieceSize, 1 << 14 );
231    tor->blockCount = ( tor->info.totalSize + tor->blockSize - 1 ) /
232                        tor->blockSize;
233    tor->completion = tr_cpInit( tor );
234
235    tr_torrentInitFilePieces( tor );
236
237    tor->thread = THREAD_EMPTY;
238    tr_rwInit( &tor->lock );
239
240    tor->upload         = tr_rcInit();
241    tor->download       = tr_rcInit();
242    tor->swarmspeed     = tr_rcInit();
243 
244    /* We have a new torrent */
245    tor->publicPort = tr_sharedGetPublicPort( h->shared );
246
247    tr_sharedUnlock( h->shared );
248
249    if( !h->isPortSet )
250        tr_setBindPort( h, TR_DEFAULT_PORT );
251
252    assert( !tor->downloadedCur );
253    assert( !tor->uploadedCur );
254
255    tor->error   = TR_OK;
256    tor->runStatus = flags & TR_FLAG_PAUSED ? TR_RUN_STOPPED : TR_RUN_RUNNING;
257
258    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
259    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
260    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
261        tr_bitfieldFree( uncheckedPieces );
262    else
263        tor->uncheckedPieces = uncheckedPieces;
264
265
266    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
267        int limit, enabled;
268        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
269        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
270        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
271        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
272    }
273
274    tor->cpStatus = tr_cpGetStatus( tor->completion );
275
276    tr_sharedLock( h->shared );
277    tor->next = h->torrentList;
278    h->torrentList = tor;
279    h->torrentCount++;
280    tr_sharedUnlock( h->shared );
281
282    snprintf( name, sizeof( name ), "torrent %p (%s)", tor, tor->info.name );
283    tr_threadCreate( &tor->thread, torrentThreadLoop, tor, name );
284}
285
286static int
287pathIsInUse ( const tr_handle_t   * h,
288              const char          * destination,
289              const char          * name )
290{
291    const tr_torrent_t * tor;
292   
293    for( tor=h->torrentList; tor; tor=tor->next )
294        if( !strcmp( destination, tor->destination )
295         && !strcmp( name, tor->info.name ) )
296            return TRUE;
297
298    return FALSE;
299}
300
301static int
302hashExists( const tr_handle_t   * h,
303            const uint8_t       * hash )
304{
305    const tr_torrent_t * tor;
306
307    for( tor=h->torrentList; tor; tor=tor->next )
308        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
309            return TRUE;
310
311    return FALSE;
312}
313
314static int
315infoCanAdd( const tr_handle_t   * h,
316            const char          * destination,
317            const tr_info_t     * info )
318{
319    if( hashExists( h, info->hash ) )
320        return TR_EDUPLICATE;
321
322    if( destination && pathIsInUse( h, destination, info->name ) )
323        return TR_EDUPLICATE;
324
325    return TR_OK;
326}
327
328int
329tr_torrentParse( const tr_handle_t  * h,
330                 const char         * path,
331                 const char         * destination,
332                 tr_info_t          * setme_info )
333{
334    int ret;
335    tr_info_t tmp;
336
337    if( setme_info == NULL )
338        setme_info = &tmp;
339
340    memset( setme_info, 0, sizeof( tr_info_t ) );
341    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
342
343    if( ret == TR_OK )
344        ret = infoCanAdd( h, destination, setme_info );
345
346    /* if we allocated a temporary info, free it */
347    if( !ret && ( setme_info == &tmp ) )
348        tr_metainfoFree( &tmp );
349
350    return ret;
351}
352 
353tr_torrent_t *
354tr_torrentInit( tr_handle_t   * h,
355                const char    * path,
356                const char    * destination,
357                int             flags,
358                int           * error )
359{
360    int val;
361    int tmpError;
362    tr_torrent_t * tor = NULL;
363
364    if( !error )
365         error = &tmpError;
366
367    if(( val = tr_torrentParse( h, path, destination, NULL )))
368        *error = val;
369    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
370        *error = TR_EOTHER;
371    else {
372        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
373        torrentRealInit( h, tor, destination, flags );
374    }
375
376    return tor;
377}
378
379int
380tr_torrentParseHash( const tr_handle_t  * h,
381                     const char         * hashStr,
382                     const char         * destination,
383                     tr_info_t          * setme_info )
384{
385    int ret;
386    tr_info_t tmp;
387
388    if( setme_info == NULL )
389        setme_info = &tmp;
390
391    memset( setme_info, 0, sizeof( tr_info_t ) );
392    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
393
394    if( ret == TR_OK )
395        ret = infoCanAdd( h, destination, setme_info );
396
397    if( setme_info == &tmp )
398        tr_metainfoFree( &tmp );
399
400    return ret;
401}
402
403
404tr_torrent_t *
405tr_torrentInitSaved( tr_handle_t    * h,
406                     const char     * hashStr,
407                     const char     * destination,
408                     int              flags,
409                     int            * error )
410{
411    int val;
412    int tmpError;
413    tr_torrent_t * tor = NULL;
414
415    if( !error )
416         error = &tmpError;
417
418    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
419        *error = val;
420    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
421        *error = TR_EOTHER;
422    else {
423        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
424        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
425    }
426
427    return tor;
428}
429
430static int
431tr_torrentParseData( const tr_handle_t  * h,
432                     const uint8_t      * data,
433                     size_t               size,
434                     const char         * destination,
435                     tr_info_t          * setme_info )
436{
437    int ret;
438    tr_info_t tmp;
439
440    if( setme_info == NULL )
441        setme_info = &tmp;
442
443    memset( setme_info, 0, sizeof( tr_info_t ) );
444    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
445
446    if( ret == TR_OK )
447        ret = infoCanAdd( h, destination, setme_info );
448
449    if( setme_info == &tmp )
450        tr_metainfoFree( &tmp );
451
452    return ret;
453}
454
455tr_torrent_t *
456tr_torrentInitData( tr_handle_t    * h,
457                    const uint8_t  * data,
458                    size_t           size,
459                    const char     * destination,
460                    int              flags,
461                    int            * error )
462{
463    int val;
464    int tmpError;
465    tr_torrent_t * tor = NULL;
466
467    if( !error )
468         error = &tmpError;
469
470    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
471        *error = val;
472    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
473        *error = TR_EOTHER;
474    else {
475        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
476        torrentRealInit( h, tor, destination, flags );
477    }
478
479    return tor;
480}
481
482const tr_info_t *
483tr_torrentInfo( const tr_torrent_t * tor )
484{
485    return &tor->info;
486}
487
488/***
489****
490***/
491
492#if 0
493int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
494{
495    return tr_trackerScrape( tor, s, l, d );
496}
497#endif
498
499static void
500fastResumeSave( tr_torrent_t * tor )
501{
502    tr_fastResumeSave( tor );
503    tor->fastResumeDirty = FALSE;
504}
505
506void
507tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
508{
509    tr_free( tor->destination );
510    tor->destination = tr_strdup( path );
511    fastResumeSave( tor );
512}
513
514const char*
515tr_torrentGetFolder( const tr_torrent_t * tor )
516{
517    return tor->destination;
518}
519
520
521/***********************************************************************
522 * torrentReallyStop
523 ***********************************************************************
524 * Joins the download thread and frees/closes everything related to it.
525 **********************************************************************/
526
527void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
528{
529    tr_torrentWriterLock( tor );
530
531    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
532    {
533        if( tor->pexDisabled != disable )
534        {
535            int i;
536            tor->pexDisabled = disable;
537            for( i=0; i<tor->peerCount; ++i )
538                tr_peerSetPrivate( tor->peers[i], disable );
539        }
540    }
541
542    tr_torrentWriterUnlock( tor );
543}
544
545static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
546{
547    int ret;
548
549    tr_torrentWriterLock( tor );
550    if (( ret = tor->hasChangedState == status ))
551        tor->hasChangedState = -1;
552    tr_torrentWriterUnlock( tor );
553
554    return ret;
555}
556
557int tr_getIncomplete( tr_torrent_t * tor )
558{
559    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
560}
561int tr_getDone( tr_torrent_t * tor )
562{
563    return tr_didStateChangeTo( tor, TR_CP_DONE );
564}
565int tr_getComplete( tr_torrent_t * tor )
566{
567    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
568}
569
570void tr_manualUpdate( tr_torrent_t * tor UNUSED )
571{
572#if 0
573    int peerCount, new;
574    uint8_t * peerCompact;
575
576    if( tor->status != TR_RUN_RUNNING )
577        return;
578   
579    tr_torrentWriterLock( tor );
580    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
581    new = 0;
582    if( peerCount > 0 )
583    {
584        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
585                                    peerCompact, peerCount );
586        free( peerCompact );
587    }
588    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
589    tr_torrentWriterUnlock( tor );
590#endif
591}
592
593const tr_stat_t *
594tr_torrentStat( tr_torrent_t * tor )
595{
596    tr_stat_t * s;
597    tr_tracker_t * tc;
598    int i;
599    uint64_t doneHave, doneTotal;
600
601    tr_torrentReaderLock( tor );
602
603    tor->statCur = ( tor->statCur + 1 ) % 2;
604    s = &tor->stats[tor->statCur];
605
606    s->error  = tor->error;
607    memcpy( s->errorString, tor->errorString,
608            sizeof( s->errorString ) );
609
610    tc = tor->tracker;
611    s->cannotConnect = tr_trackerCannotConnect( tc );
612    s->tracker = tc
613        ? tr_trackerGet( tc )
614        : &tor->info.trackerList[0].list[0];
615
616    /* peers... */
617    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
618    s->peersTotal          = tor->peerCount;
619    s->peersConnected      = 0;
620    s->peersSendingToUs    = 0;
621    s->peersGettingFromUs  = 0;
622
623    for( i=0; i<tor->peerCount; ++i )
624    {
625        const tr_peer_t * peer = tor->peers[i];
626
627        if( tr_peerIsConnected( peer ) )
628        {
629            ++s->peersConnected;
630            ++s->peersFrom[tr_peerIsFrom(peer)];
631
632            if( tr_peerDownloadRate( peer ) > 0.01 )
633                ++s->peersSendingToUs;
634
635            if( tr_peerUploadRate( peer ) > 0.01 )
636                ++s->peersGettingFromUs;
637        }
638    }
639
640    s->percentComplete = tr_cpPercentComplete ( tor->completion );
641
642    tr_cpDoneStats( tor->completion, &doneHave, &doneTotal );
643    s->percentDone = (float)doneHave / (float)doneTotal;
644    s->left = doneTotal - doneHave;
645
646    if( tor->uncheckedPieces )
647        s->status = tor->runStatus==TR_RUN_CHECKING
648            ? TR_STATUS_CHECK
649            : TR_STATUS_CHECK_WAIT;
650    else switch( tor->runStatus ) {
651        case TR_RUN_STOPPING: /* fallthrough */
652        case TR_RUN_STOPPING_NET_WAIT: s->status = TR_STATUS_STOPPING; break;
653        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
654        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
655        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
656            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
657            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
658            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
659        }
660    }
661
662    s->recheckProgress = (tor->uncheckedPieces == NULL)
663        ? 0.0
664        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
665
666    s->cpStatus = tor->cpStatus;
667
668    /* tr_rcRate() doesn't make the difference between 'piece'
669       messages and other messages, which causes a non-zero
670       download rate even tough we are not downloading. So we
671       force it to zero not to confuse the user. */
672    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
673        ? tr_rcRate( tor->download )
674        : 0.0;
675    s->rateUpload = tr_rcRate( tor->upload );
676   
677    s->seeders  = tr_trackerSeeders( tc );
678    s->leechers = tr_trackerLeechers( tc );
679    s->completedFromTracker = tr_trackerDownloaded( tc );
680
681    s->swarmspeed = tr_rcRate( tor->swarmspeed );
682   
683    s->startDate = tor->startDate;
684    s->activityDate = tor->activityDate;
685
686    s->eta = s->rateDownload < 0.1
687        ? -1.0f
688        : (s->left / s->rateDownload / 1024.0);
689
690    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
691    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
692    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
693   
694    s->ratio = s->downloaded || s->downloadedValid
695      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
696      : TR_RATIO_NA; 
697   
698    tr_torrentReaderUnlock( tor );
699
700    return s;
701}
702
703/***
704****
705***/
706
707static uint64_t
708fileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
709{
710    const tr_file_t * file     =  &tor->info.files[fileIndex];
711    const uint64_t firstBlock       =  file->offset / tor->blockSize;
712    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
713    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
714    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
715    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
716    uint64_t haveBytes = 0;
717
718    assert( tor != NULL );
719    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
720    assert( file->offset + file->length <= tor->info.totalSize );
721    assert( (int)firstBlock < tor->blockCount );
722    assert( (int)lastBlock < tor->blockCount );
723    assert( firstBlock <= lastBlock );
724    assert( (int)TOR_BLOCK_PIECE( tor, firstBlock ) == file->firstPiece );
725    assert( (int)TOR_BLOCK_PIECE( tor, lastBlock ) == file->lastPiece );
726
727    if( firstBlock == lastBlock )
728    {
729        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
730            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
731    }
732    else
733    {
734        uint64_t i;
735
736        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
737            haveBytes += tor->blockSize - firstBlockOffset;
738
739        for( i=firstBlock+1; i<lastBlock; ++i )
740            if( tr_cpBlockIsComplete( tor->completion, i ) )
741               haveBytes += tor->blockSize;
742
743        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
744            haveBytes += lastBlockOffset + 1;
745    }
746
747    return haveBytes;
748}
749
750tr_file_stat_t *
751tr_torrentFiles( const tr_torrent_t * tor, int * fileCount )
752{
753    int i;
754    const int n = tor->info.fileCount;
755    tr_file_stat_t * files = tr_new0( tr_file_stat_t, n );
756    tr_file_stat_t * walk = files;
757
758    for( i=0; i<n; ++i, ++walk )
759    {
760        const tr_file_t * file = tor->info.files + i;
761        cp_status_t cp;
762
763        walk->bytesCompleted = fileBytesCompleted( tor, i );
764
765        walk->progress = file->length
766            ? walk->bytesCompleted / (float)file->length
767            : 1.0;
768
769        if( walk->bytesCompleted >= file->length )
770            cp = TR_CP_COMPLETE;
771        else if( tor->info.files[i].dnd )
772            cp = TR_CP_DONE;
773        else
774            cp = TR_CP_INCOMPLETE;
775
776        walk->completionStatus = cp;
777    }
778
779    *fileCount = n;
780
781    return files;
782}
783
784void
785tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED )
786{
787    tr_free( files );
788}
789
790/***
791****
792***/
793
794tr_peer_stat_t *
795tr_torrentPeers( const tr_torrent_t * tor, int * peerCount )
796{
797    tr_peer_stat_t * peers;
798
799    tr_torrentReaderLock( tor );
800
801    *peerCount = tor->peerCount;
802   
803    peers = tr_new0( tr_peer_stat_t, tor->peerCount ); 
804    if (peers != NULL)
805    {
806        tr_peer_t * peer;
807        struct in_addr * addr;
808        int i;
809        for( i=0; i<tor->peerCount; ++i )
810        {
811            peer = tor->peers[i];
812           
813            addr = tr_peerAddress( peer );
814            if( NULL != addr )
815            {
816                tr_netNtop( addr, peers[i].addr,
817                           sizeof( peers[i].addr ) );
818            }
819           
820            peers[i].client           =  tr_peerClient( peer );
821            peers[i].isConnected      =  tr_peerIsConnected( peer );
822            peers[i].from             =  tr_peerIsFrom( peer );
823            peers[i].progress         =  tr_peerProgress( peer );
824            peers[i].port             =  tr_peerPort( peer );
825
826            peers[i].uploadToRate     =  tr_peerUploadRate( peer );
827            peers[i].downloadFromRate =  tr_peerDownloadRate( peer );
828           
829            peers[i].isDownloading    =  peers[i].uploadToRate > 0.01;
830            peers[i].isUploading      =  peers[i].downloadFromRate > 0.01;
831        }
832    }
833   
834    tr_torrentReaderUnlock( tor );
835   
836    return peers;
837}
838
839void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
840{
841    tr_free( peers );
842}
843
844void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size )
845{
846    int i, j, piece;
847    float interval;
848
849    tr_torrentReaderLock( tor );
850
851    interval = (float)tor->info.pieceCount / (float)size;
852    for( i = 0; i < size; i++ )
853    {
854        piece = i * interval;
855
856        if( tr_cpPieceIsComplete( tor->completion, piece ) )
857        {
858            tab[i] = -1;
859            continue;
860        }
861
862        tab[i] = 0;
863        for( j = 0; j < tor->peerCount; j++ )
864        {
865            if( tr_peerHasPiece( tor->peers[j], piece ) )
866            {
867                (tab[i])++;
868            }
869        }
870    }
871
872    tr_torrentReaderUnlock( tor );
873}
874
875void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size )
876{
877    int i;
878    float interval;
879    tr_torrentReaderLock( tor );
880
881    interval = (float)tor->info.pieceCount / (float)size;
882    for( i = 0; i < size; i++ )
883    {
884        int piece = i * interval;
885        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
886    }
887
888    tr_torrentReaderUnlock( tor );
889}
890
891void
892tr_torrentResetTransferStats( tr_torrent_t * tor )
893{
894    tr_torrentWriterLock( tor );
895
896    tor->downloadedPrev += tor->downloadedCur;
897    tor->downloadedCur   = 0;
898    tor->uploadedPrev   += tor->uploadedCur;
899    tor->uploadedCur     = 0;
900
901    tr_torrentWriterUnlock( tor );
902}
903
904
905void
906tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has )
907{
908    tr_torrentWriterLock( tor );
909
910    if( has )
911        tr_cpPieceAdd( tor->completion, pieceIndex );
912    else
913        tr_cpPieceRem( tor->completion, pieceIndex );
914
915    tr_torrentWriterUnlock( tor );
916}
917
918void tr_torrentRemoveSaved( tr_torrent_t * tor )
919{
920    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
921}
922
923void tr_torrentRecheck( tr_torrent_t * tor )
924{
925    if( !tor->uncheckedPieces )
926        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
927    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
928}
929
930
931int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
932{
933    int i;
934    tr_peer_t * otherPeer;
935
936    assert( tor != NULL );
937    assert( peer != NULL );
938
939    if( tor->peerCount >= TR_MAX_PEER_COUNT )
940    {
941        tr_peerDestroy(  peer );
942        return 0;
943    }
944
945    /* Don't accept two connections from the same IP */
946    for( i = 0; i < tor->peerCount; i++ )
947    {
948        otherPeer = tor->peers[i];
949        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
950        {
951            tr_peerDestroy(  peer );
952            return 0;
953        }
954    }
955
956    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
957                       tor->pexDisabled );
958    tr_peerSetTorrent( peer, tor );
959    tor->peers[tor->peerCount++] = peer;
960
961    return 1;
962}
963
964int tr_torrentAddCompact( tr_torrent_t * tor, int from,
965                           uint8_t * buf, int count )
966{
967    struct in_addr addr;
968    in_port_t port;
969    int i, added;
970    tr_peer_t * peer;
971
972    added = 0;
973    for( i = 0; i < count; i++ )
974    {
975        memcpy( &addr, buf, 4 ); buf += 4;
976        memcpy( &port, buf, 2 ); buf += 2;
977
978        peer = tr_peerInit( &addr, port, -1, from );
979        added += tr_torrentAttachPeer( tor, peer );
980    }
981
982    return added;
983}
984
985/***
986****
987***/
988
989static void setRunState( tr_torrent_t * tor, run_status_t run )
990{
991    tr_torrentWriterLock( tor );
992    tor->runStatus = run;
993    tr_torrentWriterUnlock( tor );
994}
995
996void tr_torrentStart( tr_torrent_t * tor )
997{
998    setRunState( tor, TR_RUN_RUNNING );
999}
1000
1001void tr_torrentStop( tr_torrent_t * tor )
1002{
1003    if((tor->runStatus != TR_RUN_STOPPING) && (tor->runStatus != TR_RUN_STOPPED) )
1004        setRunState( tor, TR_RUN_STOPPING );
1005}
1006
1007void tr_torrentClose( tr_torrent_t * tor )
1008{
1009    tr_torrentStop( tor );
1010    tor->dieFlag = TRUE;
1011}
1012
1013static void
1014tr_torrentFree( tr_torrent_t * tor )
1015{
1016    tr_torrent_t * t;
1017    tr_handle_t * h = tor->handle;
1018    tr_info_t * inf = &tor->info;
1019
1020    tr_sharedLock( h->shared );
1021
1022    tr_rwClose( &tor->lock );
1023    tr_cpClose( tor->completion );
1024
1025    tr_rcClose( tor->upload );
1026    tr_rcClose( tor->download );
1027    tr_rcClose( tor->swarmspeed );
1028
1029    tr_free( tor->destination );
1030
1031    tr_metainfoFree( inf );
1032
1033    if( tor == h->torrentList )
1034        h->torrentList = tor->next;
1035    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1036        if( t->next == tor ) {
1037            t->next = tor->next;
1038            break;
1039        }
1040    }
1041
1042    h->torrentCount--;
1043
1044    tr_inf( "closing torrent %s... %d torrents left",
1045            tor->info.name, h->torrentCount );
1046
1047    tr_free( tor );
1048
1049    tr_sharedUnlock( h->shared );
1050}
1051
1052static void
1053recheckCpState( tr_torrent_t * tor )
1054{
1055    cp_status_t cpStatus;
1056
1057    tr_torrentWriterLock( tor );
1058
1059    cpStatus = tr_cpGetStatus( tor->completion );
1060    if( cpStatus != tor->cpStatus ) {
1061        tor->cpStatus = cpStatus;
1062        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1063        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1064            && tor->tracker!=NULL           /* and we have a tracker */
1065            && tor->downloadedCur ) {        /* and it just happened */
1066            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1067        }
1068        tr_ioSync( tor->io );
1069        fastResumeSave( tor );
1070    }
1071    tr_torrentWriterUnlock( tor );
1072}
1073
1074static void
1075torrentThreadLoop ( void * _tor )
1076{
1077    static tr_lock_t checkFilesLock;
1078    static int checkFilesLockInited = FALSE;
1079    tr_torrent_t * tor = _tor;
1080
1081    /* create the check-files mutex */
1082    if( !checkFilesLockInited ) {
1083         checkFilesLockInited = TRUE;
1084         tr_lockInit( &checkFilesLock );
1085    }
1086
1087    /* loop until the torrent is being deleted */
1088    while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) )
1089    {
1090        /* sleep a little while */
1091        tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 600 );
1092
1093        if( tor->fastResumeDirty )
1094        {
1095            fastResumeSave( tor );
1096            recheckCpState( tor );
1097        }
1098
1099        /* if we're stopping... */
1100        if( tor->runStatus == TR_RUN_STOPPING )
1101        {
1102            int i;
1103            int peerCount;
1104            uint8_t * peerCompact;
1105            tr_torrentWriterLock( tor );
1106
1107            /* close the IO */
1108            tr_ioClose( tor->io );
1109            tor->io = NULL;
1110            fastResumeSave( tor );
1111
1112            /* close the peers */
1113            for( i=0; i<tor->peerCount; ++i )
1114                tr_peerDestroy( tor->peers[i] );
1115            tor->peerCount = 0;
1116
1117            /* resest the transfer rates */
1118            tr_rcReset( tor->download );
1119            tr_rcReset( tor->upload );
1120            tr_rcReset( tor->swarmspeed );
1121
1122            /* tell the tracker we're stopping */
1123            tr_trackerStopped( tor->tracker );
1124            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1125            tor->runStatus = TR_RUN_STOPPING_NET_WAIT;
1126            tor->stopDate = tr_date();
1127            tr_torrentWriterUnlock( tor );
1128        }
1129
1130        if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT )
1131        {
1132            uint64_t date;
1133            int peerCount;
1134            uint8_t * peerCompact;
1135            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1136
1137            /* have we finished telling the tracker that we're stopping? */
1138            date = tr_trackerLastResponseDate( tor->tracker );
1139            if( date > tor->stopDate )
1140            {
1141                tr_torrentWriterLock( tor );
1142                tr_trackerClose( tor->tracker );
1143                tor->tracker = NULL;
1144                tor->runStatus = TR_RUN_STOPPED;
1145                tr_torrentWriterUnlock( tor );
1146            }
1147            continue;
1148        }
1149
1150        /* do we need to check files? */
1151        if( tor->uncheckedPieces )
1152        {
1153            if( !tr_lockTryLock( &checkFilesLock ) )
1154            {
1155                run_status_t realStatus;
1156
1157                tr_torrentWriterLock( tor );
1158                realStatus = tor->runStatus;
1159                tor->runStatus = TR_RUN_CHECKING;
1160                tr_torrentWriterUnlock( tor );
1161
1162                tr_ioCheckFiles( tor );
1163                setRunState( tor, realStatus );
1164
1165                tr_torrentWriterLock( tor );
1166                tor->cpStatus = tr_cpGetStatus( tor->completion );
1167                tr_torrentWriterUnlock( tor );
1168
1169                tr_lockUnlock( &checkFilesLock );
1170            }
1171            continue;
1172        }
1173
1174        /* if we're paused or stopped, not much to do... */
1175        if( tor->runStatus == TR_RUN_STOPPED )
1176            continue;
1177
1178        /* ping our peers if we're running... */
1179        if( tor->runStatus == TR_RUN_RUNNING )
1180        {
1181            int i;
1182            int peerCount;
1183            uint8_t * peerCompact;
1184
1185            /* starting to run... */
1186            if( tor->io == NULL ) {
1187                *tor->errorString = '\0';
1188                tr_torrentResetTransferStats( tor );
1189                tor->io = tr_ioNew( tor );
1190                tr_peerIdNew ( tor->peer_id, sizeof(tor->peer_id) );
1191                tor->tracker = tr_trackerInit( tor );
1192                tor->startDate = tr_date();
1193            }
1194
1195            /* refresh our completion state */
1196            recheckCpState( tor );
1197
1198            /* ping the tracker... */
1199            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1200            if( peerCount > 0 ) {
1201                int used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
1202                                                 peerCompact, peerCount );
1203                tr_dbg( "got %i peers from announce, used %i", peerCount, used );
1204                free( peerCompact );
1205            }
1206
1207            /* Shuffle peers */
1208            if ( tor->peerCount > 1 ) {
1209                tr_peer_t * tmp = tor->peers[0];
1210                memmove( tor->peers, tor->peers+1,
1211                        (tor->peerCount-1) * sizeof(void*) );
1212                tor->peers[tor->peerCount - 1] = tmp;
1213            }
1214
1215            /* receive/send messages */
1216            tr_torrentWriterLock( tor );
1217            for( i=0; i<tor->peerCount; ) {
1218                tr_peer_t * peer = tor->peers[i];
1219                int ret = tr_peerPulse( peer );
1220                if( ret & TR_ERROR_IO_MASK ) {
1221                    tr_err( "Fatal error, stopping download (%d)", ret );
1222                    tor->runStatus = TR_RUN_STOPPING;
1223                    tor->error = ret;
1224                    strlcpy( tor->errorString,
1225                             tr_errorString(ret),
1226                             sizeof(tor->errorString) );
1227                    break;
1228                }
1229                if( ret ) {
1230                    tr_peerDestroy( peer );
1231                    tor->peerCount--;
1232                    memmove( &tor->peers[i], &tor->peers[i+1],
1233                             (tor->peerCount-i)*sizeof(void*) );
1234                    continue;
1235                }
1236                i++;
1237            }
1238            tr_torrentWriterUnlock( tor );
1239        }
1240    }
1241
1242    tr_ioClose( tor->io );
1243    tr_torrentFree( tor );
1244}
1245
1246
1247/**
1248***  File priorities
1249**/
1250
1251void
1252tr_torrentSetFilePriority( tr_torrent_t   * tor,
1253                           int              fileIndex,
1254                           tr_priority_t    priority )
1255{
1256    int i;
1257    tr_file_t * file;
1258
1259    tr_torrentWriterLock( tor );
1260
1261    assert( tor != NULL );
1262    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1263    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1264
1265    file = &tor->info.files[fileIndex];
1266    file->priority = priority;
1267    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1268      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1269
1270    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1271             fileIndex, file->firstPiece, file->lastPiece,
1272             priority, tor->info.files[fileIndex].name );
1273
1274    tor->fastResumeDirty = TRUE;
1275
1276    tr_torrentWriterUnlock( tor );
1277}
1278
1279void
1280tr_torrentSetFilePriorities( tr_torrent_t        * tor,
1281                             int                 * files,
1282                             int                   fileCount,
1283                             tr_priority_t         priority )
1284{
1285    int i;
1286    for( i=0; i<fileCount; ++i )
1287        tr_torrentSetFilePriority( tor, files[i], priority );
1288}
1289
1290tr_priority_t
1291tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1292{
1293    tr_priority_t ret;
1294
1295    tr_torrentReaderLock( tor );
1296    assert( tor != NULL );
1297    assert( 0<=file && file<tor->info.fileCount );
1298    ret = tor->info.files[file].priority;
1299    tr_torrentReaderUnlock( tor );
1300
1301    return ret;
1302}
1303
1304
1305tr_priority_t*
1306tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1307{
1308    int i;
1309    tr_priority_t * p;
1310
1311    tr_torrentReaderLock( tor );
1312    p = tr_new0( tr_priority_t, tor->info.fileCount );
1313    for( i=0; i<tor->info.fileCount; ++i )
1314        p[i] = tor->info.files[i].priority;
1315    tr_torrentReaderUnlock( tor );
1316
1317    return p;
1318}
1319
1320/**
1321***  File DND
1322**/
1323
1324int
1325tr_torrentGetFileDL( const tr_torrent_t * tor,
1326                     int                  file )
1327{
1328    int doDownload;
1329    tr_torrentReaderLock( tor );
1330
1331    assert( 0<=file && file<tor->info.fileCount );
1332    doDownload = !tor->info.files[file].dnd;
1333
1334    tr_torrentReaderUnlock( tor );
1335    return doDownload != 0;
1336}
1337
1338void
1339tr_torrentSetFileDL( tr_torrent_t  * tor,
1340                     int             fileIndex,
1341                     int             doDownload )
1342{
1343    tr_file_t * file;
1344    const int dnd = !doDownload;
1345    int firstPiece, firstPieceDND;
1346    int lastPiece, lastPieceDND;
1347    int i;
1348
1349    tr_torrentWriterLock( tor );
1350
1351    file = &tor->info.files[fileIndex];
1352    file->dnd = dnd;
1353    firstPiece = file->firstPiece;
1354    lastPiece = file->lastPiece;
1355
1356    /* can't set the first piece to DND unless
1357       every file using that piece is DND */
1358    firstPieceDND = dnd;
1359    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1360        if( tor->info.files[i].lastPiece != firstPiece )
1361            break;
1362        firstPieceDND = tor->info.files[i].dnd;
1363    }
1364
1365    /* can't set the last piece to DND unless
1366       every file using that piece is DND */
1367    lastPieceDND = dnd;
1368    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1369        if( tor->info.files[i].firstPiece != lastPiece )
1370            break;
1371        lastPieceDND = tor->info.files[i].dnd;
1372    }
1373
1374    if( firstPiece == lastPiece )
1375    {
1376        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1377    }
1378    else
1379    {
1380        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1381        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1382        for( i=firstPiece+1; i<lastPiece; ++i )
1383            tor->info.pieces[i].dnd = dnd;
1384    }
1385
1386    tor->fastResumeDirty = TRUE;
1387
1388    tr_torrentWriterUnlock( tor );
1389}
1390
1391void
1392tr_torrentSetFileDLs ( tr_torrent_t   * tor,
1393                       int            * files,
1394                       int              fileCount,
1395                       int              doDownload )
1396{
1397    int i;
1398    for( i=0; i<fileCount; ++i )
1399        tr_torrentSetFileDL( tor, files[i], doDownload );
1400}
1401
1402/***
1403****
1404***/
1405
1406int _tr_blockSize( const tr_torrent_t * tor, int block )
1407{
1408    const tr_info_t * inf = &tor->info;
1409    int dummy;
1410
1411    if( block != tor->blockCount - 1 ||
1412        !( dummy = inf->totalSize % tor->blockSize ) )
1413    {
1414        return tor->blockSize;
1415    }
1416
1417    return dummy;
1418}
1419
1420int _tr_pieceSize( const tr_torrent_t * tor, int piece )
1421{
1422    const tr_info_t * inf = &tor->info;
1423    if( piece < inf->pieceCount - 1 ||
1424        !( inf->totalSize % inf->pieceSize ) )
1425    {
1426        return inf->pieceSize;
1427    }
1428    return inf->totalSize % inf->pieceSize;
1429}
1430
1431int _tr_block( const tr_torrent_t * tor, int index, int begin )
1432{
1433    const tr_info_t * inf = &tor->info;
1434    return index * ( inf->pieceSize / tor->blockSize ) +
1435        begin / tor->blockSize;
1436}
Note: See TracBrowser for help on using the repository browser.