source: trunk/libtransmission/torrent.c @ 2361

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

replace tr_torrentGetFileStatus() with tr_torrentFiles() for BMW

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