source: trunk/libtransmission/torrent.c @ 2555

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

add portability wrapper for in_port_t...

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