source: trunk/libtransmission/torrent.c @ 2462

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

set default torrent ul/dl speed limits from global ul/dl speed. as a side effect, totally decouples fastresume from inout.

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