source: trunk/libtransmission/torrent.c @ 2272

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

Move the initial fastresume check into the thread that creates the torrent, rather than in the torrent's worker thread

  • Property svn:keywords set to Date Rev Author Id
File size: 31.1 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2272 2007-07-01 19:10:14Z 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 "transmission.h"
26#include "shared.h"
27
28/***
29****  LOCKS
30***/
31
32void
33tr_torrentReaderLock( const tr_torrent_t * tor )
34{
35    tr_rwReaderLock ( (tr_rwlock_t*)&tor->lock );
36}
37
38void
39tr_torrentReaderUnlock( const tr_torrent_t * tor )
40{
41    tr_rwReaderUnlock ( (tr_rwlock_t*)&tor->lock );
42}
43
44void
45tr_torrentWriterLock( tr_torrent_t * tor )
46{
47    tr_rwWriterLock ( &tor->lock );
48}
49
50void
51tr_torrentWriterUnlock( tr_torrent_t * tor )
52{
53    tr_rwWriterUnlock ( &tor->lock );
54}
55
56/***
57****  PER-TORRENT UL / DL SPEEDS
58***/
59
60void
61tr_setUseCustomUpload( tr_torrent_t * tor, int limit )
62{
63    tr_torrentWriterLock( tor );
64    tor->customUploadLimit = limit;
65    tr_torrentWriterUnlock( tor );
66}
67
68void
69tr_setUseCustomDownload( tr_torrent_t * tor, int limit )
70{
71    tr_torrentWriterLock( tor );
72    tor->customDownloadLimit = limit;
73    tr_torrentWriterUnlock( tor );
74}
75
76void
77tr_setUploadLimit( tr_torrent_t * tor, int limit )
78{
79    tr_torrentWriterLock( tor );
80    tr_rcSetLimit( tor->upload, limit );
81    tr_torrentWriterUnlock( tor );
82}
83
84void
85tr_setDownloadLimit( tr_torrent_t * tor, int limit )
86{
87    tr_torrentWriterLock( tor );
88    tr_rcSetLimit( tor->download, limit );
89    tr_torrentWriterUnlock( tor );
90}
91
92/***
93****
94****  TORRENT INSTANTIATION
95****
96***/
97
98static int
99getBytePiece( const tr_info_t * info, uint64_t byteOffset )
100{
101    assert( info != NULL );
102    assert( info->pieceSize != 0 );
103
104    return byteOffset / info->pieceSize;
105}
106
107static void
108initFilePieces ( tr_info_t * info, int fileIndex )
109{
110    tr_file_t * file = &info->files[fileIndex];
111    uint64_t firstByte, lastByte;
112
113    assert( info != NULL );
114    assert( 0<=fileIndex && fileIndex<info->fileCount );
115
116    file = &info->files[fileIndex];
117    firstByte = file->offset;
118    lastByte = firstByte + (file->length ? file->length-1 : 0);
119    file->firstPiece = getBytePiece( info, firstByte );
120    file->lastPiece = getBytePiece( info, lastByte );
121    tr_dbg( "file #%d is in pieces [%d...%d] (%s)", fileIndex, file->firstPiece, file->lastPiece, file->name );
122}
123
124static tr_priority_t
125calculatePiecePriority ( const tr_torrent_t * tor,
126                         int                  piece )
127{
128    int i;
129    tr_priority_t priority = TR_PRI_DND;
130
131    for( i=0; i<tor->info.fileCount; ++i )
132    {
133        const tr_file_t * file = &tor->info.files[i];
134        if ( file->firstPiece <= piece
135          && file->lastPiece  >= piece
136          && file->priority   >  priority)
137              priority = file->priority;
138    }
139
140    return priority;
141}
142
143static void
144tr_torrentInitFilePieces( tr_torrent_t * tor )
145{
146    int i;
147    uint64_t offset = 0;
148
149    assert( tor != NULL );
150
151    for( i=0; i<tor->info.fileCount; ++i ) {
152      tor->info.files[i].offset = offset;
153      offset += tor->info.files[i].length;
154      initFilePieces( &tor->info, i );
155    }
156
157    for( i=0; i<tor->info.pieceCount; ++i )
158      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
159}
160
161static void torrentThreadLoop( void * );
162
163static void
164torrentRealInit( tr_handle_t   * h,
165                 tr_torrent_t  * tor,
166                 const char    * destination,
167                 int             flags )
168{
169    int i;
170    char name[512];
171   
172    tor->info.flags |= flags;
173
174    tr_sharedLock( h->shared );
175
176    tor->destination = tr_strdup( destination );
177
178    tr_torrentInitFilePieces( tor );
179
180    tor->handle   = h;
181    tor->id       = h->id;
182    tor->key      = h->key;
183    tor->azId     = h->azId;
184    tor->hasChangedState = -1;
185   
186    /* Escaped info hash for HTTP queries */
187    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
188    {
189        snprintf( &tor->escapedHashString[3*i],
190                  sizeof( tor->escapedHashString ) - 3 * i,
191                  "%%%02x", tor->info.hash[i] );
192    }
193
194    tor->pexDisabled = 0;
195
196    /* Block size: usually 16 ko, or less if we have to */
197    tor->blockSize  = MIN( tor->info.pieceSize, 1 << 14 );
198    tor->blockCount = ( tor->info.totalSize + tor->blockSize - 1 ) /
199                        tor->blockSize;
200    tor->completion = tr_cpInit( tor );
201
202    tor->thread = THREAD_EMPTY;
203    tr_rwInit( &tor->lock );
204
205    tor->upload         = tr_rcInit();
206    tor->download       = tr_rcInit();
207    tor->swarmspeed     = tr_rcInit();
208 
209    /* We have a new torrent */
210    tor->publicPort = tr_sharedGetPublicPort( h->shared );
211
212    tr_sharedUnlock( h->shared );
213
214    if( !h->isPortSet )
215        tr_setBindPort( h, TR_DEFAULT_PORT );
216
217    tor->downloadedPrev += tor->downloadedCur;
218    tor->downloadedCur   = 0;
219    tor->uploadedPrev   += tor->uploadedCur;
220    tor->uploadedCur     = 0;
221
222    tor->error   = TR_OK;
223    tor->runStatus = flags & TR_FLAG_PAUSED ? TR_RUN_STOPPED : TR_RUN_RUNNING;
224    tor->recheckFlag = tr_ioCheckFiles( tor, TR_RECHECK_FAST );
225
226    tr_sharedLock( h->shared );
227    tor->next = h->torrentList;
228    h->torrentList = tor;
229    h->torrentCount++;
230    tr_sharedUnlock( h->shared );
231
232    snprintf( name, sizeof( name ), "torrent %p (%s)", tor, tor->info.name );
233    tr_threadCreate( &tor->thread, torrentThreadLoop, tor, name );
234}
235
236static int
237pathIsInUse ( const tr_handle_t   * h,
238              const char          * destination,
239              const char          * name )
240{
241    const tr_torrent_t * tor;
242   
243    for( tor=h->torrentList; tor; tor=tor->next )
244        if( !strcmp( destination, tor->destination )
245         && !strcmp( name, tor->info.name ) )
246            return TRUE;
247
248    return FALSE;
249}
250
251static int
252hashExists( const tr_handle_t   * h,
253            const uint8_t       * hash )
254{
255    const tr_torrent_t * tor;
256
257    for( tor=h->torrentList; tor; tor=tor->next )
258        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
259            return TRUE;
260
261    return FALSE;
262}
263
264static int
265infoCanAdd( const tr_handle_t   * h,
266            const char          * destination,
267            const tr_info_t     * info )
268{
269    if( hashExists( h, info->hash ) )
270        return TR_EDUPLICATE;
271
272    if( destination && pathIsInUse( h, destination, info->name ) )
273        return TR_EDUPLICATE;
274
275    return TR_OK;
276}
277
278int
279tr_torrentParse( const tr_handle_t  * h,
280                 const char         * path,
281                 const char         * destination,
282                 tr_info_t          * setme_info )
283{
284    int ret;
285    tr_info_t tmp;
286
287    if( setme_info == NULL )
288        setme_info = &tmp;
289
290    memset( setme_info, 0, sizeof( tr_info_t ) );
291    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
292
293    if( ret == TR_OK )
294        ret = infoCanAdd( h, destination, setme_info );
295
296    if( setme_info == &tmp )
297        tr_metainfoFree( &tmp );
298
299    return ret;
300}
301 
302tr_torrent_t *
303tr_torrentInit( tr_handle_t   * h,
304                const char    * path,
305                const char    * destination,
306                int             flags,
307                int           * error )
308{
309    int val;
310    tr_torrent_t * tor = NULL;
311
312    if(( val = tr_torrentParse( h, path, destination, NULL )))
313        *error = val;
314    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
315        *error = TR_EOTHER;
316    else {
317        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
318        torrentRealInit( h, tor, destination, flags );
319    }
320
321    return tor;
322}
323
324int
325tr_torrentParseHash( const tr_handle_t  * h,
326                     const char         * hashStr,
327                     const char         * destination,
328                     tr_info_t          * setme_info )
329{
330    int ret;
331    tr_info_t tmp;
332
333    if( setme_info == NULL )
334        setme_info = &tmp;
335
336    memset( setme_info, 0, sizeof( tr_info_t ) );
337    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
338
339    if( ret == TR_OK )
340        ret = infoCanAdd( h, destination, setme_info );
341
342    if( setme_info == &tmp )
343        tr_metainfoFree( &tmp );
344
345    return ret;
346}
347
348
349tr_torrent_t *
350tr_torrentInitSaved( tr_handle_t    * h,
351                     const char     * hashStr,
352                     const char     * destination,
353                     int              flags,
354                     int            * error )
355{
356    int val;
357    tr_torrent_t * tor = NULL;
358
359    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
360        *error = val;
361    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
362        *error = TR_EOTHER;
363    else {
364        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
365        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
366    }
367
368    return tor;
369}
370
371static int
372tr_torrentParseData( const tr_handle_t  * h,
373                     const uint8_t      * data,
374                     size_t               size,
375                     const char         * destination,
376                     tr_info_t          * setme_info )
377{
378    int ret;
379    tr_info_t tmp;
380
381    if( setme_info == NULL )
382        setme_info = &tmp;
383
384    memset( setme_info, 0, sizeof( tr_info_t ) );
385    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
386
387    if( ret == TR_OK )
388        ret = infoCanAdd( h, destination, setme_info );
389
390    if( setme_info == &tmp )
391        tr_metainfoFree( &tmp );
392
393    return ret;
394}
395
396tr_torrent_t *
397tr_torrentInitData( tr_handle_t    * h,
398                    const uint8_t  * data,
399                    size_t           size,
400                    const char     * destination,
401                    int              flags,
402                    int            * error )
403{
404    int val;
405    tr_torrent_t * tor = NULL;
406
407    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
408        *error = val;
409    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
410        *error = TR_EOTHER;
411    else {
412        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
413        torrentRealInit( h, tor, destination, flags );
414    }
415
416    return tor;
417}
418
419const tr_info_t *
420tr_torrentInfo( const tr_torrent_t * tor )
421{
422    return &tor->info;
423}
424
425/***
426****
427***/
428
429int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
430{
431    return tr_trackerScrape( tor, s, l, d );
432}
433
434void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
435{
436    tr_free( tor->destination );
437    tor->destination = tr_strdup( path );
438
439    if( !tor->ioLoaded )
440         tor->ioLoaded = tr_ioLoadResume( tor ) == TR_OK;
441}
442
443const char* tr_torrentGetFolder( const tr_torrent_t * tor )
444{
445    return tor->destination;
446}
447
448
449/***********************************************************************
450 * torrentReallyStop
451 ***********************************************************************
452 * Joins the download thread and frees/closes everything related to it.
453 **********************************************************************/
454
455void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
456{
457    tr_torrentWriterLock( tor );
458
459    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
460    {
461        if( tor->pexDisabled != disable )
462        {
463            int i;
464            tor->pexDisabled = disable;
465            for( i=0; i<tor->peerCount; ++i )
466                tr_peerSetPrivate( tor->peers[i], disable );
467        }
468    }
469
470    tr_torrentWriterUnlock( tor );
471}
472
473static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
474{
475    int ret;
476
477    tr_torrentWriterLock( tor );
478    if (( ret = tor->hasChangedState == status ))
479        tor->hasChangedState = -1;
480    tr_torrentWriterUnlock( tor );
481
482    return ret;
483}
484
485int tr_getIncomplete( tr_torrent_t * tor )
486{
487    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
488}
489int tr_getDone( tr_torrent_t * tor )
490{
491    return tr_didStateChangeTo( tor, TR_CP_DONE );
492}
493int tr_getComplete( tr_torrent_t * tor )
494{
495    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
496}
497
498void tr_manualUpdate( tr_torrent_t * tor UNUSED )
499{
500#if 0
501    int peerCount, new;
502    uint8_t * peerCompact;
503
504    if( tor->status != TR_RUN_RUNNING )
505        return;
506   
507    tr_torrentWriterLock( tor );
508    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
509    new = 0;
510    if( peerCount > 0 )
511    {
512        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
513                                    peerCompact, peerCount );
514        free( peerCompact );
515    }
516    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
517    tr_torrentWriterUnlock( tor );
518#endif
519}
520
521tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
522{
523    tr_stat_t * s;
524    tr_tracker_t * tc;
525    int i;
526
527    tr_torrentReaderLock( tor );
528
529    tor->statCur = ( tor->statCur + 1 ) % 2;
530    s = &tor->stats[tor->statCur];
531
532    s->error  = tor->error;
533    memcpy( s->errorString, tor->errorString,
534            sizeof( s->errorString ) );
535
536    tc = tor->tracker;
537    s->cannotConnect = tr_trackerCannotConnect( tc );
538    s->tracker = tc
539        ? tr_trackerGet( tc )
540        : &tor->info.trackerList[0].list[0];
541
542    /* peers... */
543    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
544    s->peersTotal       = 0;
545    s->peersUploading   = 0;
546    s->peersDownloading = 0;
547    for( i=0; i<tor->peerCount; ++i ) {
548        const tr_peer_t * peer = tor->peers[i];
549            ++s->peersTotal;
550        if( tr_peerIsConnected( peer ) ) {
551            ++s->peersFrom[tr_peerIsFrom(peer)];
552            if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
553                ++s->peersUploading;
554            if( !tr_peerAmChoking( peer ) )
555                ++s->peersDownloading;
556        }
557    }
558
559    s->percentDone = tr_cpPercentDone( tor->completion );
560    s->percentComplete = tr_cpPercentComplete( tor->completion );
561    s->left     = tr_cpLeftUntilDone( tor->completion );
562
563
564    if( tor->recheckFlag )
565        s->status = TR_STATUS_CHECK_WAIT;
566    else switch( tor->runStatus ) {
567        case TR_RUN_STOPPING: s->status = TR_STATUS_STOPPING; break;
568        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
569        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
570        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
571            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
572            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
573            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
574        }
575    }
576
577    s->cpStatus = tor->cpStatus;
578
579    /* tr_rcRate() doesn't make the difference between 'piece'
580       messages and other messages, which causes a non-zero
581       download rate even tough we are not downloading. So we
582       force it to zero not to confuse the user. */
583    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
584        ? tr_rcRate( tor->download )
585        : 0.0;
586    s->rateUpload = tr_rcRate( tor->upload );
587   
588    s->seeders  = tr_trackerSeeders( tc );
589    s->leechers = tr_trackerLeechers( tc );
590    s->completedFromTracker = tr_trackerDownloaded( tc );
591
592    s->swarmspeed = tr_rcRate( tor->swarmspeed );
593   
594    s->startDate = tor->startDate;
595    s->activityDate = tor->activityDate;
596
597    s->eta = s->rateDownload < 0.1
598        ? -1.0f
599        : (s->left / s->rateDownload / 1024.0);
600
601    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
602    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
603    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
604   
605    s->ratio = s->downloaded || s->downloadedValid
606      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
607      : TR_RATIO_NA; 
608   
609    tr_torrentReaderUnlock( tor );
610
611    return s;
612}
613
614tr_peer_stat_t *
615tr_torrentPeers( const tr_torrent_t * tor, int * peerCount )
616{
617    tr_peer_stat_t * peers;
618
619    tr_torrentReaderLock( tor );
620
621    *peerCount = tor->peerCount;
622   
623    peers = tr_new0( tr_peer_stat_t, tor->peerCount ); 
624    if (peers != NULL)
625    {
626        tr_peer_t * peer;
627        struct in_addr * addr;
628        int i;
629        for( i = 0; i < tor->peerCount; i++ )
630        {
631            peer = tor->peers[i];
632           
633            addr = tr_peerAddress( peer );
634            if( NULL != addr )
635            {
636                tr_netNtop( addr, peers[i].addr,
637                           sizeof( peers[i].addr ) );
638            }
639           
640            peers[i].client        = tr_peerClient( peer );
641            peers[i].isConnected   = tr_peerIsConnected( peer );
642            peers[i].from          = tr_peerIsFrom( peer );
643            peers[i].progress      = tr_peerProgress( peer );
644            peers[i].port          = tr_peerPort( peer );
645           
646            if( ( peers[i].isDownloading = !tr_peerAmChoking( peer ) ) )
647            {
648                peers[i].uploadToRate = tr_peerUploadRate( peer );
649            }
650            if( ( peers[i].isUploading = ( tr_peerAmInterested( peer ) &&
651                                           !tr_peerIsChoking( peer ) ) ) )
652            {
653                peers[i].downloadFromRate = tr_peerDownloadRate( peer );
654            }
655        }
656    }
657   
658    tr_torrentReaderUnlock( tor );
659   
660    return peers;
661}
662
663void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
664{
665    tr_free( peers );
666}
667
668void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size )
669{
670    int i, j, piece;
671    float interval;
672
673    tr_torrentReaderLock( tor );
674
675    interval = (float)tor->info.pieceCount / (float)size;
676    for( i = 0; i < size; i++ )
677    {
678        piece = i * interval;
679
680        if( tr_cpPieceIsComplete( tor->completion, piece ) )
681        {
682            tab[i] = -1;
683            continue;
684        }
685
686        tab[i] = 0;
687        for( j = 0; j < tor->peerCount; j++ )
688        {
689            if( tr_peerHasPiece( tor->peers[j], piece ) )
690            {
691                (tab[i])++;
692            }
693        }
694    }
695
696    tr_torrentReaderUnlock( tor );
697}
698
699uint64_t
700tr_torrentFileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
701{
702    const tr_file_t * file     =  &tor->info.files[fileIndex];
703    const uint64_t firstBlock       =  file->offset / tor->blockSize;
704    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
705    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
706    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
707    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
708    uint64_t haveBytes = 0;
709
710    assert( tor != NULL );
711    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
712    assert( file->offset + file->length <= tor->info.totalSize );
713    assert( (int)firstBlock < tor->blockCount );
714    assert( (int)lastBlock < tor->blockCount );
715    assert( firstBlock <= lastBlock );
716    assert( tr_blockPiece( firstBlock ) == file->firstPiece );
717    assert( tr_blockPiece( lastBlock ) == file->lastPiece );
718
719    if( firstBlock == lastBlock )
720    {
721        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
722            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
723    }
724    else
725    {
726        uint64_t i;
727
728        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
729            haveBytes += tor->blockSize - firstBlockOffset;
730
731        for( i=firstBlock+1; i<lastBlock; ++i )
732            if( tr_cpBlockIsComplete( tor->completion, i ) )
733               haveBytes += tor->blockSize;
734
735        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
736            haveBytes += lastBlockOffset + 1;
737    }
738
739    return haveBytes;
740}
741
742float
743tr_torrentFileCompletion ( const tr_torrent_t * tor, int fileIndex )
744{
745    const uint64_t c = tr_torrentFileBytesCompleted ( tor, fileIndex );
746    uint64_t length = tor->info.files[fileIndex].length;
747   
748    if( !length )
749        return 1.0;
750    return (double)c / length;
751}
752
753float*
754tr_torrentCompletion( const tr_torrent_t * tor )
755{
756    int i;
757    float * f;
758    tr_torrentReaderLock( tor );
759
760    f = tr_new0( float, tor->info.fileCount );
761    for( i=0; i<tor->info.fileCount; ++i )
762       f[i] = tr_torrentFileCompletion ( tor, i );
763
764    tr_torrentReaderUnlock( tor );
765    return f;
766}
767
768void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size )
769{
770    int i;
771    float interval;
772    tr_torrentReaderLock( tor );
773
774    interval = (float)tor->info.pieceCount / (float)size;
775    for( i = 0; i < size; i++ )
776    {
777        int piece = i * interval;
778        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
779    }
780
781    tr_torrentReaderUnlock( tor );
782}
783
784void
785tr_torrentResetTransferStats( tr_torrent_t * tor )
786{
787    tr_torrentWriterLock( tor );
788
789    tor->downloadedPrev += tor->downloadedCur;
790    tor->downloadedCur   = 0;
791    tor->uploadedPrev   += tor->uploadedCur;
792    tor->uploadedCur     = 0;
793
794    tr_torrentWriterUnlock( tor );
795}
796
797
798void
799tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has )
800{
801    tr_torrentWriterLock( tor );
802
803    if( has )
804        tr_cpPieceAdd( tor->completion, pieceIndex );
805    else
806        tr_cpPieceRem( tor->completion, pieceIndex );
807
808    tr_torrentWriterUnlock( tor );
809}
810
811void tr_torrentRemoveSaved( tr_torrent_t * tor )
812{
813    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
814}
815
816void tr_torrentRecheck( tr_torrent_t * tor )
817{
818    tor->recheckFlag = TRUE;
819}
820
821
822int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
823{
824    int i;
825    tr_peer_t * otherPeer;
826
827    assert( tor != NULL );
828    assert( peer != NULL );
829
830    if( tor->peerCount >= TR_MAX_PEER_COUNT )
831    {
832        tr_peerDestroy(  peer );
833        return 0;
834    }
835
836    /* Don't accept two connections from the same IP */
837    for( i = 0; i < tor->peerCount; i++ )
838    {
839        otherPeer = tor->peers[i];
840        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
841        {
842            tr_peerDestroy(  peer );
843            return 0;
844        }
845    }
846
847    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
848                       tor->pexDisabled );
849    tr_peerSetTorrent( peer, tor );
850    tor->peers[tor->peerCount++] = peer;
851
852    return 1;
853}
854
855int tr_torrentAddCompact( tr_torrent_t * tor, int from,
856                           uint8_t * buf, int count )
857{
858    struct in_addr addr;
859    in_port_t port;
860    int i, added;
861    tr_peer_t * peer;
862
863    added = 0;
864    for( i = 0; i < count; i++ )
865    {
866        memcpy( &addr, buf, 4 ); buf += 4;
867        memcpy( &port, buf, 2 ); buf += 2;
868
869        peer = tr_peerInit( addr, port, -1, from );
870        added += tr_torrentAttachPeer( tor, peer );
871    }
872
873    return added;
874}
875
876/***
877****
878***/
879
880static void setRunState( tr_torrent_t * tor, run_status_t run )
881{
882    tr_torrentWriterLock( tor );
883    tor->runStatus = run;
884    tr_torrentWriterUnlock( tor );
885}
886
887void tr_torrentStart( tr_torrent_t * tor )
888{
889    setRunState( tor, TR_RUN_RUNNING );
890}
891
892void tr_torrentStop( tr_torrent_t * tor )
893{
894    setRunState( tor, TR_RUN_STOPPING );
895}
896
897void tr_torrentClose( tr_torrent_t * tor )
898{
899    tr_torrentStop( tor );
900    tor->dieFlag = TRUE;
901}
902
903static void
904tr_torrentFree( tr_torrent_t * tor )
905{
906    tr_torrent_t * t;
907    tr_handle_t * h = tor->handle;
908    tr_info_t * inf = &tor->info;
909
910    tr_sharedLock( h->shared );
911
912    tr_rwClose( &tor->lock );
913    tr_cpClose( tor->completion );
914
915    tr_rcClose( tor->upload );
916    tr_rcClose( tor->download );
917    tr_rcClose( tor->swarmspeed );
918
919    tr_free( tor->destination );
920
921    tr_metainfoFree( inf );
922
923    if( tor == h->torrentList )
924        h->torrentList = tor->next;
925    else for( t=h->torrentList; t!=NULL; t=t->next ) {
926        if( t->next == tor ) {
927            t->next = tor->next;
928            break;
929        }
930    }
931
932    tr_free( tor );
933
934    h->torrentCount--;
935
936    tr_sharedUnlock( h->shared );
937}
938
939static void
940torrentThreadLoop ( void * _tor )
941{
942    static tr_lock_t checkFilesLock;
943    static int checkFilesLockInited = FALSE;
944    tr_torrent_t * tor = _tor;
945
946    /* create the check-files mutex */
947    if( !checkFilesLockInited ) {
948         checkFilesLockInited = TRUE;
949         tr_lockInit( &checkFilesLock );
950    }
951
952    /* loop until the torrent is being deleted */
953    while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) )
954    {
955        cp_status_t cpStatus;
956
957        /* sleep a little while */
958        tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 800 );
959
960        /* if we're stopping... */
961        if( tor->runStatus == TR_RUN_STOPPING )
962        {
963            int i;
964            int peerCount;
965            uint8_t * peerCompact;
966            tr_torrentWriterLock( tor );
967
968            /* close the IO */
969            tr_ioClose( tor->io );
970            tor->io = NULL;
971
972            /* close the tracker */
973            tr_trackerStopped( tor->tracker );
974            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
975            tr_trackerClose( tor->tracker );
976            tor->tracker = NULL;
977
978            /* close the peers */
979            for( i=0; i<tor->peerCount; ++i )
980                tr_peerDestroy( tor->peers[i] );
981            tor->peerCount = 0;
982
983            /* resest the transfer rates */
984            tr_rcReset( tor->download );
985            tr_rcReset( tor->upload );
986            tr_rcReset( tor->swarmspeed );
987
988            tor->stopDate = tr_date();
989            tor->runStatus = TR_RUN_STOPPED;
990
991            tr_torrentWriterUnlock( tor );
992        }
993
994        /* do we need to check files? */
995        if( tor->recheckFlag )
996        {
997            if( !tr_lockTryLock( &checkFilesLock ) )
998            {
999                run_status_t realStatus;
1000
1001                tr_torrentWriterLock( tor );
1002                realStatus = tor->runStatus;
1003                tor->recheckFlag = FALSE;
1004                tor->runStatus = TR_RUN_CHECKING;
1005                tr_torrentWriterUnlock( tor );
1006
1007                tr_ioCheckFiles( tor, TR_RECHECK_FORCE );
1008                setRunState( tor, realStatus );
1009
1010                tr_lockUnlock( &checkFilesLock );
1011            }
1012            continue;
1013        }
1014
1015        /* if we're paused or stopped, not much to do... */
1016        if( tor->runStatus == TR_RUN_STOPPED )
1017            continue;
1018
1019        /* ping our peers if we're running... */
1020        if( tor->runStatus == TR_RUN_RUNNING )
1021        {
1022            int i;
1023            int peerCount;
1024            uint8_t * peerCompact;
1025
1026            /* starting to run... */
1027            if( tor->io == NULL ) {
1028                *tor->errorString = '\0';
1029                tr_torrentResetTransferStats( tor );
1030                tor->io = tr_ioInitFast( tor );
1031                if( tor->io == NULL ) {
1032                    tor->recheckFlag = TRUE;
1033                    continue;
1034                }
1035                tor->tracker = tr_trackerInit( tor );
1036                tor->startDate = tr_date();
1037            }
1038
1039            /* refresh our completion state */
1040            tr_torrentWriterLock( tor );
1041            cpStatus = tr_cpGetStatus( tor->completion );
1042            if( cpStatus != tor->cpStatus ) {
1043                tor->cpStatus = cpStatus;
1044                if( (cpStatus == TR_CP_COMPLETE)        /* if we're complete */
1045                    && tor->tracker!=NULL           /* and we have a tracker */
1046                    && tor->downloadedCur ) {        /* and it just happened */
1047                    tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1048                    tor->hasChangedState = tor->cpStatus;  /* and the client */
1049                }
1050                tr_ioSync( tor->io );
1051            }
1052            tr_torrentWriterUnlock( tor );
1053
1054            /* ping the tracker... */
1055            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1056            if( peerCount > 0 ) {
1057                int used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
1058                                                 peerCompact, peerCount );
1059                tr_dbg( "got %i peers from announce, used %i", peerCount, used );
1060                free( peerCompact );
1061            }
1062
1063            /* Shuffle peers */
1064            if ( tor->peerCount > 1 ) {
1065                tr_peer_t * tmp = tor->peers[0];
1066                memmove( tor->peers, tor->peers+1,
1067                        (tor->peerCount-1) * sizeof(void*) );
1068                tor->peers[tor->peerCount - 1] = tmp;
1069            }
1070
1071            /* receive/send messages */
1072            tr_torrentWriterLock( tor );
1073            for( i=0; i<tor->peerCount; ) {
1074                tr_peer_t * peer = tor->peers[i];
1075                int ret = tr_peerPulse( peer );
1076                if( ret & TR_ERROR_IO_MASK ) {
1077                    tr_err( "Fatal error, stopping download (%d)", ret );
1078                    tor->runStatus = TR_RUN_STOPPING;
1079                    tor->error = ret;
1080                    strlcpy( tor->errorString,
1081                             tr_errorString(ret),
1082                             sizeof(tor->errorString) );
1083                    break;
1084                }
1085                if( ret ) {
1086                    tr_peerDestroy( peer );
1087                    tor->peerCount--;
1088                    memmove( &tor->peers[i], &tor->peers[i+1],
1089                             (tor->peerCount-i)*sizeof(void*) );
1090                    continue;
1091                }
1092                i++;
1093            }
1094            tr_torrentWriterUnlock( tor );
1095        }
1096    }
1097
1098    tr_ioClose( tor->io );
1099    tr_torrentFree( tor );
1100}
1101
1102
1103/***
1104****
1105****  File prioritization
1106****
1107***/
1108
1109void
1110tr_torrentSetFilePriority( tr_torrent_t   * tor,
1111                           int              fileIndex,
1112                           tr_priority_t    priority )
1113{
1114    int i;
1115    tr_file_t * file;
1116
1117    tr_torrentWriterLock( tor );
1118
1119    assert( tor != NULL );
1120    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1121    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL
1122         || priority==TR_PRI_HIGH || priority==TR_PRI_DND );
1123
1124    file = &tor->info.files[fileIndex];
1125    file->priority = priority;
1126    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1127      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1128
1129    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1130             fileIndex, file->firstPiece, file->lastPiece,
1131             priority, tor->info.files[fileIndex].name );
1132
1133    tr_torrentWriterUnlock( tor );
1134}
1135
1136tr_priority_t
1137tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1138{
1139    tr_priority_t ret;
1140
1141    tr_torrentReaderLock( tor );
1142    assert( tor != NULL );
1143    assert( 0<=file && file<tor->info.fileCount );
1144    ret = tor->info.files[file].priority;
1145    tr_torrentReaderUnlock( tor );
1146
1147    return ret;
1148}
1149
1150
1151void
1152tr_torrentSetFilePriorities( tr_torrent_t         * tor,
1153                             const tr_priority_t  * filePriorities )
1154{
1155    int i;
1156    for( i=0; i<tor->info.pieceCount; ++i )
1157      tr_torrentSetFilePriority( tor, i, filePriorities[i] );
1158}
1159
1160tr_priority_t*
1161tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1162{
1163    int i;
1164    tr_priority_t * p;
1165
1166    tr_torrentReaderLock( tor );
1167    p = tr_new0( tr_priority_t, tor->info.fileCount );
1168    for( i=0; i<tor->info.fileCount; ++i )
1169        p[i] = tor->info.files[i].priority;
1170    tr_torrentReaderUnlock( tor );
1171
1172    return p;
1173}
Note: See TracBrowser for help on using the repository browser.