source: trunk/libtransmission/torrent.c @ 3387

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

fix r3379 bug that forced us to recheck the torrents every startup

  • Property svn:keywords set to Date Rev Author Id
File size: 32.9 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3387 2007-10-13 03:58:54Z 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
32#include "transmission.h"
33#include "completion.h"
34#include "crypto.h" /* for tr_sha1 */
35#include "fastresume.h"
36#include "handshake.h"
37#include "inout.h"
38#include "metainfo.h"
39#include "net.h" /* tr_netNtop */
40#include "peer-mgr.h"
41#include "platform.h"
42#include "ratecontrol.h"
43#include "shared.h"
44#include "tracker.h"
45#include "trcompat.h" /* for strlcpy */
46#include "trevent.h"
47#include "utils.h"
48
49/***
50****
51***/
52
53int
54tr_torrentExists( tr_handle       * handle,
55                  const uint8_t   * torrentHash )
56{
57    return tr_torrentFindFromHash( handle, torrentHash ) != NULL;
58}
59
60tr_torrent*
61tr_torrentFindFromHash( tr_handle      * handle,
62                        const uint8_t  * torrentHash )
63{
64    tr_torrent * tor;
65
66    for( tor = handle->torrentList; tor; tor = tor->next )
67        if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
68            return tor;
69
70    return NULL;
71}
72
73tr_torrent*
74tr_torrentFindFromObfuscatedHash( tr_handle      * handle,
75                                  const uint8_t  * obfuscatedTorrentHash )
76{
77    tr_torrent * tor;
78
79    for( tor = handle->torrentList; tor; tor = tor->next )
80        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) )
81            return tor;
82
83    return NULL;
84}
85
86/***
87****  LOCKS
88***/
89
90void
91tr_torrentLock( const tr_torrent * tor )
92{
93    tr_globalLock( tor->handle );
94}
95
96void
97tr_torrentUnlock( const tr_torrent * tor )
98{
99    tr_globalUnlock( tor->handle );
100}
101
102/***
103****  PER-TORRENT UL / DL SPEEDS
104***/
105
106void
107tr_torrentSetSpeedMode( tr_torrent   * tor,
108                        int            up_or_down,
109                        tr_speedlimit  mode )
110{
111    tr_speedlimit * limit = up_or_down==TR_UP
112        ? &tor->uploadLimitMode
113        : &tor->downloadLimitMode;
114    *limit = mode;
115}
116
117tr_speedlimit
118tr_torrentGetSpeedMode( const tr_torrent * tor,
119                        int                up_or_down)
120{
121    return up_or_down==TR_UP ? tor->uploadLimitMode
122                             : tor->downloadLimitMode;
123}
124
125void
126tr_torrentSetSpeedLimit( tr_torrent   * tor,
127                         int            up_or_down,
128                         int            single_KiB_sec )
129{
130    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
131    tr_rcSetLimit( rc, single_KiB_sec );
132}
133
134int
135tr_torrentGetSpeedLimit( const tr_torrent  * tor,
136                         int                 up_or_down )
137{
138    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
139    return tr_rcGetLimit( rc );
140}
141
142/***
143****
144***/
145
146static void
147onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
148{
149    tr_torrent * tor = (tr_torrent *) user_data;
150    tr_tracker_event_t * event = (tr_tracker_event_t *) vevent;
151
152    switch( event->messageType )
153    {
154        case TR_TRACKER_PEERS:
155            tr_peerMgrAddPeers( tor->handle->peerMgr,
156                                tor->info.hash,
157                                TR_PEER_FROM_TRACKER,
158                                event->peerCompact,
159                                event->peerCount );
160            break;
161
162        case TR_TRACKER_WARNING:
163            tr_err( "Tracker: Warning - %s", event->text );
164            tor->error = TR_ERROR_TC_WARNING;
165            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
166            break;
167
168        case TR_TRACKER_ERROR:
169            tr_err( "Tracker: Error - %s", event->text );
170            tor->error = TR_ERROR_TC_ERROR;
171            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
172            break;
173
174        case TR_TRACKER_ERROR_CLEAR:
175            tor->error = 0;
176            tor->errorString[0] = '\0';
177            break;
178    }
179}
180
181/***
182****
183****  TORRENT INSTANTIATION
184****
185***/
186
187static int
188getBytePiece( const tr_info * info, uint64_t byteOffset )
189{
190    assert( info != NULL );
191    assert( info->pieceSize != 0 );
192
193    return byteOffset / info->pieceSize;
194}
195
196static void
197initFilePieces ( tr_info * info, int fileIndex )
198{
199    tr_file * file = &info->files[fileIndex];
200    uint64_t firstByte, lastByte;
201
202    assert( info != NULL );
203    assert( 0<=fileIndex && fileIndex<info->fileCount );
204
205    file = &info->files[fileIndex];
206    firstByte = file->offset;
207    lastByte = firstByte + (file->length ? file->length-1 : 0);
208    file->firstPiece = getBytePiece( info, firstByte );
209    file->lastPiece = getBytePiece( info, lastByte );
210}
211
212static tr_priority_t
213calculatePiecePriority ( const tr_torrent * tor,
214                         int                piece )
215{
216    int i;
217    tr_priority_t priority = TR_PRI_NORMAL;
218
219    for( i=0; i<tor->info.fileCount; ++i )
220    {
221        const tr_file * file = &tor->info.files[i];
222        if ( file->firstPiece <= piece
223          && file->lastPiece  >= piece
224          && file->priority   >  priority)
225              priority = file->priority;
226
227        /* when dealing with multimedia files, getting the first and
228           last pieces can sometimes allow you to preview it a bit
229           before it's fully downloaded... */
230        if ( file->priority >= TR_PRI_NORMAL )
231            if ( file->firstPiece == piece || file->lastPiece == piece )
232                priority = TR_PRI_HIGH;
233    }
234
235    return priority;
236}
237
238static void
239tr_torrentInitFilePieces( tr_torrent * tor )
240{
241    int i;
242    uint64_t offset = 0;
243
244    assert( tor != NULL );
245
246    for( i=0; i<tor->info.fileCount; ++i ) {
247      tor->info.files[i].offset = offset;
248      offset += tor->info.files[i].length;
249      initFilePieces( &tor->info, i );
250    }
251
252    for( i=0; i<tor->info.pieceCount; ++i )
253        tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
254}
255
256static void
257torrentRealInit( tr_handle  * h,
258                 tr_torrent * tor,
259                 const char * destination,
260                 int          flags )
261{
262    int doStart;
263    uint64_t loaded;
264    uint64_t t;
265    tr_bitfield * uncheckedPieces;
266    tr_info * info = &tor->info;
267   
268    tor->info.flags |= flags;
269
270    tr_globalLock( h );
271
272    tor->handle   = h;
273    tor->pexDisabled = 0;
274
275    /**
276     * Decide on a block size.  constraints:
277     * (1) most clients decline requests over 16 KiB
278     * (2) pieceSize must be a multiple of block size
279     */
280    tor->blockSize = info->pieceSize;
281    while( tor->blockSize > (1024*16) )
282        tor->blockSize /= 2;
283
284    tor->lastPieceSize = info->totalSize % info->pieceSize;
285
286    if( !tor->lastPieceSize )
287         tor->lastPieceSize = info->pieceSize;
288
289    tor->lastBlockSize = info->totalSize % tor->blockSize;
290
291    if( !tor->lastBlockSize )
292         tor->lastBlockSize = tor->blockSize;
293
294    tor->blockCount =
295        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
296
297    tor->blockCountInPiece =
298        info->pieceSize / tor->blockSize;
299
300    tor->blockCountInLastPiece =
301        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
302
303    /* check our work */
304    assert( ( info->pieceSize % tor->blockSize ) == 0 );
305    t = info->pieceCount - 1;
306    t *= info->pieceSize;
307    t += tor->lastPieceSize;
308    assert( t == info->totalSize );
309    t = tor->blockCount - 1;
310    t *= tor->blockSize;
311    t += tor->lastBlockSize;
312    assert( t == info->totalSize );
313    t = info->pieceCount - 1;
314    t *= tor->blockCountInPiece;
315    t += tor->blockCountInLastPiece;
316    assert( t == (uint64_t)tor->blockCount );
317
318    tor->completion = tr_cpInit( tor );
319
320    tr_torrentInitFilePieces( tor );
321
322    tor->upload         = tr_rcInit();
323    tor->download       = tr_rcInit();
324    tor->swarmspeed     = tr_rcInit();
325
326    tr_sha1( tor->obfuscatedHash, "req2", 4,
327                                  info->hash, SHA_DIGEST_LENGTH,
328                                  NULL );
329
330    tr_peerMgrAddTorrent( h->peerMgr, tor );
331
332    if( !h->isPortSet )
333        tr_setBindPort( h, TR_DEFAULT_PORT );
334
335    assert( !tor->downloadedCur );
336    assert( !tor->uploadedCur );
337
338    tor->error   = TR_OK;
339
340    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
341
342    loaded = tr_fastResumeLoad( tor, destination, uncheckedPieces );
343
344    /* the `paused' flag has highest precedence...
345       after that, the fastresume setting is used...
346       if that's not found, default to RUNNING */
347    if( flags & TR_FLAG_PAUSED )
348        doStart = 0;
349    else if( loaded & TR_FR_RUN )
350        doStart = tor->isRunning;
351    else
352        doStart = 1;
353    tor->isRunning = 0;
354
355    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
356        tr_bitfieldFree( uncheckedPieces );
357    else
358        tor->uncheckedPieces = uncheckedPieces;
359
360
361    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
362        int limit, enabled;
363        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
364        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
365        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
366        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
367    }
368
369    tor->cpStatus = tr_cpGetStatus( tor->completion );
370
371    tor->tracker = tr_trackerNew( tor );
372    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
373
374    tor->next = h->torrentList;
375    h->torrentList = tor;
376    h->torrentCount++;
377
378    tr_globalUnlock( h );
379
380    if( doStart )
381        tr_torrentStart( tor );
382}
383
384static int
385pathIsInUse ( const tr_handle   * h,
386              const char        * destination,
387              const char        * name )
388{
389    const tr_torrent * tor;
390   
391    for( tor=h->torrentList; tor; tor=tor->next )
392        if( !strcmp( destination, tor->destination )
393         && !strcmp( name, tor->info.name ) )
394            return TRUE;
395
396    return FALSE;
397}
398
399static int
400hashExists( const tr_handle   * h,
401            const uint8_t     * hash )
402{
403    const tr_torrent * tor;
404
405    for( tor=h->torrentList; tor; tor=tor->next )
406        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
407            return TRUE;
408
409    return FALSE;
410}
411
412static int
413infoCanAdd( const tr_handle   * h,
414            const char        * destination,
415            const tr_info     * info )
416{
417    if( hashExists( h, info->hash ) )
418        return TR_EDUPLICATE;
419
420    if( destination && pathIsInUse( h, destination, info->name ) )
421        return TR_EDUPLICATE;
422
423    return TR_OK;
424}
425
426int
427tr_torrentParse( const tr_handle  * h,
428                 const char       * path,
429                 const char       * destination,
430                 tr_info          * setme_info )
431{
432    int ret, doFree;
433    tr_info tmp;
434
435    if( setme_info == NULL )
436        setme_info = &tmp;
437
438    memset( setme_info, 0, sizeof( tr_info ) );
439    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
440    doFree = !ret && (setme_info == &tmp);
441
442    if( ret == TR_OK )
443        ret = infoCanAdd( h, destination, setme_info );
444
445    if( doFree )
446        tr_metainfoFree( &tmp );
447
448    return ret;
449}
450 
451tr_torrent *
452tr_torrentInit( tr_handle   * h,
453                const char  * path,
454                const char  * destination,
455                int           flags,
456                int         * error )
457{
458    int val;
459    int tmpError;
460    tr_torrent * tor = NULL;
461
462    if( !error )
463         error = &tmpError;
464
465    if(( val = tr_torrentParse( h, path, destination, NULL )))
466        *error = val;
467    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
468        *error = TR_EOTHER;
469    else {
470        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
471        torrentRealInit( h, tor, destination, flags );
472    }
473
474    return tor;
475}
476
477int
478tr_torrentParseHash( const tr_handle  * h,
479                     const char       * hashStr,
480                     const char       * destination,
481                     tr_info          * setme_info )
482{
483    int ret, doFree;
484    tr_info tmp;
485
486    if( setme_info == NULL )
487        setme_info = &tmp;
488
489    memset( setme_info, 0, sizeof( tr_info ) );
490    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
491    doFree = !ret && (setme_info == &tmp);
492
493    if( ret == TR_OK )
494        ret = infoCanAdd( h, destination, setme_info );
495
496    if( doFree )
497        tr_metainfoFree( &tmp );
498
499    return ret;
500}
501
502tr_torrent *
503tr_torrentInitSaved( tr_handle    * h,
504                     const char   * hashStr,
505                     const char   * destination,
506                     int            flags,
507                     int          * error )
508{
509    int val;
510    int tmpError;
511    tr_torrent * tor = NULL;
512
513    if( !error )
514         error = &tmpError;
515
516    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
517        *error = val;
518    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
519        *error = TR_EOTHER;
520    else {
521        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
522        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
523    }
524
525    return tor;
526}
527
528static int
529tr_torrentParseData( const tr_handle  * h,
530                     const uint8_t    * data,
531                     size_t             size,
532                     const char       * destination,
533                     tr_info          * setme_info )
534{
535    int ret, doFree;
536    tr_info tmp;
537
538    if( setme_info == NULL )
539        setme_info = &tmp;
540
541    memset( setme_info, 0, sizeof( tr_info ) );
542    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
543    doFree = !ret && (setme_info == &tmp);
544
545    if( ret == TR_OK )
546        ret = infoCanAdd( h, destination, setme_info );
547
548    if( doFree )
549        tr_metainfoFree( &tmp );
550
551    return ret;
552}
553
554tr_torrent *
555tr_torrentInitData( tr_handle      * h,
556                    const uint8_t  * data,
557                    size_t           size,
558                    const char     * destination,
559                    int              flags,
560                    int            * error )
561{
562    int val;
563    int tmpError;
564    tr_torrent * tor = NULL;
565
566    if( !error )
567         error = &tmpError;
568
569    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
570        *error = val;
571    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
572        *error = TR_EOTHER;
573    else {
574        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
575        torrentRealInit( h, tor, destination, flags );
576    }
577
578    return tor;
579}
580
581const tr_info *
582tr_torrentInfo( const tr_torrent * tor )
583{
584    return &tor->info;
585}
586
587/***
588****
589***/
590
591#if 0
592int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
593{
594    return tr_trackerScrape( tor, s, l, d );
595}
596#endif
597
598static int
599saveFastResumeNow( void * vtor )
600{
601    tr_torrent * tor = (tr_torrent *) vtor;
602
603    tr_fastResumeSave( tor );
604    tr_torrentRecheckCompleteness( tor );
605
606    tr_timerFree( &tor->saveTimer );
607    return FALSE;
608}
609
610static void
611saveFastResumeSoon( void * vtor )
612{
613    tr_torrent * tor = (tr_torrent *) vtor;
614
615    if( tor->saveTimer == NULL )
616        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 100 );
617}
618
619/**
620***
621**/
622
623void
624tr_torrentSetFolder( tr_torrent * tor, const char * path )
625{
626    tr_free( tor->destination );
627    tor->destination = tr_strdup( path );
628    saveFastResumeSoon( tor );
629}
630
631const char*
632tr_torrentGetFolder( const tr_torrent * tor )
633{
634    return tor->destination;
635}
636
637void
638tr_torrentChangeMyPort( tr_torrent * tor )
639{
640    if( tor->tracker )
641        tr_trackerChangeMyPort( tor->tracker );
642}
643
644
645void
646tr_torrentDisablePex( tr_torrent * tor, int disable )
647{
648    assert( tor != NULL );
649    assert( disable==0 || disable==1 );
650
651    /* pex is ALWAYS disabled for private torrents */
652    if( tor->info.flags & TR_FLAG_PRIVATE )
653        disable = TRUE;
654
655    tor->pexDisabled = disable;
656}
657
658void
659tr_manualUpdate( tr_torrent * tor )
660{
661    if( tor->isRunning )
662        tr_trackerReannounce( tor->tracker );
663}
664int
665tr_torrentCanManualUpdate( const tr_torrent * tor )
666{
667    return ( tor != NULL )
668        && ( tor->isRunning )
669        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
670}
671
672const tr_stat *
673tr_torrentStat( tr_torrent * tor )
674{
675    tr_stat * s;
676    struct tr_tracker * tc;
677
678    tr_torrentLock( tor );
679
680    tor->statCur = !tor->statCur;
681    s = &tor->stats[tor->statCur];
682
683    s->error  = tor->error;
684    memcpy( s->errorString, tor->errorString,
685            sizeof( s->errorString ) );
686
687    tc = tor->tracker;
688    s->tracker = tr_trackerGetAddress( tor->tracker );
689
690    tr_peerMgrTorrentStats( tor->handle->peerMgr,
691                            tor->info.hash,
692                            &s->peersKnown,
693                            &s->peersConnected,
694                            &s->peersSendingToUs,
695                            &s->peersGettingFromUs,
696                             s->peersFrom );
697
698    s->percentComplete = tr_cpPercentComplete ( tor->completion );
699
700    s->percentDone = tr_cpPercentDone( tor->completion );
701    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
702
703    if( tor->recheckState == TR_RECHECK_NOW )
704        s->status = TR_STATUS_CHECK;
705    else if( tor->recheckState == TR_RECHECK_WAIT )
706        s->status = TR_STATUS_CHECK_WAIT;
707    else if( !tor->isRunning )
708        s->status = TR_STATUS_STOPPED;
709    else if( tor->cpStatus == TR_CP_INCOMPLETE )
710        s->status = TR_STATUS_DOWNLOAD;
711    else if( tor->cpStatus == TR_CP_DONE )
712        s->status = TR_STATUS_DONE;
713    else
714        s->status = TR_STATUS_SEED;
715
716    s->recheckProgress = (tor->uncheckedPieces == NULL)
717        ? 0.0
718        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
719
720    s->cpStatus = tor->cpStatus;
721
722    /* tr_rcRate() doesn't make the difference between 'piece'
723       messages and other messages, which causes a non-zero
724       download rate even tough we are not downloading. So we
725       force it to zero not to confuse the user. */
726    s->rateDownload = tor->isRunning
727        ? tr_rcRate( tor->download )
728        : 0.0;
729    s->rateUpload = tr_rcRate( tor->upload );
730   
731    tr_trackerGetCounts( tc,
732                         &s->completedFromTracker,
733                         &s->leechers, 
734                         &s->seeders );
735
736    s->swarmspeed = tr_rcRate( tor->swarmspeed );
737   
738    s->startDate = tor->startDate;
739    s->activityDate = tor->activityDate;
740
741    s->eta = s->rateDownload < 0.1
742        ? -1.0f
743        : (s->leftUntilDone / s->rateDownload / 1024.0);
744
745    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
746    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
747    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
748    s->haveValid       = tr_cpHaveValid( tor->completion );
749    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
750
751
752    {
753        int i;
754        tr_bitfield * available = tr_peerMgrGetAvailable( tor->handle->peerMgr,
755                                                          tor->info.hash );
756        s->desiredSize = 0;
757        s->desiredAvailable = 0;
758
759        for( i=0; i<tor->info.pieceCount; ++i ) {
760            if( !tor->info.pieces[i].dnd ) {
761                s->desiredSize += tor->info.pieceSize;
762                if( tr_bitfieldHas( available, i ) )
763                    s->desiredAvailable += tor->info.pieceSize;
764            }
765        }
766
767        tr_bitfieldFree( available );
768    }
769   
770    s->ratio = s->downloadedEver ? s->uploadedEver / (float)s->downloadedEver
771                                 : TR_RATIO_NA;
772   
773    tr_torrentUnlock( tor );
774
775    return s;
776}
777
778/***
779****
780***/
781
782static uint64_t
783fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
784{
785    const tr_file * file     =  &tor->info.files[fileIndex];
786    const uint64_t firstBlock       =  file->offset / tor->blockSize;
787    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
788    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
789    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
790    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
791    uint64_t haveBytes = 0;
792
793    assert( tor != NULL );
794    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
795    assert( file->offset + file->length <= tor->info.totalSize );
796    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
797    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
798    assert( firstBlock <= lastBlock );
799    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
800    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
801
802    if( firstBlock == lastBlock )
803    {
804        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
805            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
806    }
807    else
808    {
809        uint64_t i;
810
811        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
812            haveBytes += tor->blockSize - firstBlockOffset;
813
814        for( i=firstBlock+1; i<lastBlock; ++i )
815            if( tr_cpBlockIsComplete( tor->completion, i ) )
816               haveBytes += tor->blockSize;
817
818        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
819            haveBytes += lastBlockOffset + 1;
820    }
821
822    return haveBytes;
823}
824
825tr_file_stat *
826tr_torrentFiles( const tr_torrent * tor, int * fileCount )
827{
828    int i;
829    const int n = tor->info.fileCount;
830    tr_file_stat * files = tr_new0( tr_file_stat, n );
831    tr_file_stat * walk = files;
832
833    for( i=0; i<n; ++i, ++walk )
834    {
835        const tr_file * file = tor->info.files + i;
836        cp_status_t cp;
837
838        walk->bytesCompleted = fileBytesCompleted( tor, i );
839
840        walk->progress = file->length
841            ? walk->bytesCompleted / (float)file->length
842            : 1.0;
843
844        if( walk->bytesCompleted >= file->length )
845            cp = TR_CP_COMPLETE;
846        else if( tor->info.files[i].dnd )
847            cp = TR_CP_DONE;
848        else
849            cp = TR_CP_INCOMPLETE;
850
851        walk->completionStatus = cp;
852    }
853
854    *fileCount = n;
855
856    return files;
857}
858
859void
860tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
861{
862    tr_free( files );
863}
864
865/***
866****
867***/
868
869tr_peer_stat *
870tr_torrentPeers( const tr_torrent * tor, int * peerCount )
871{
872    return tr_peerMgrPeerStats( tor->handle->peerMgr,
873                                tor->info.hash, peerCount );
874}
875
876void
877tr_torrentPeersFree( tr_peer_stat * peers, int peerCount )
878{
879    int i;
880    for( i=0; i<peerCount; ++i )
881        tr_free( (char*) peers[i].client );
882    tr_free( peers );
883}
884
885void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
886{
887    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
888                                          tor->info.hash,
889                                          tab, size );
890}
891
892void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
893{
894    int i;
895    float interval;
896    tr_torrentLock( tor );
897
898    interval = (float)tor->info.pieceCount / (float)size;
899    for( i = 0; i < size; i++ )
900    {
901        int piece = i * interval;
902        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
903    }
904
905    tr_torrentUnlock( tor );
906}
907
908void
909tr_torrentResetTransferStats( tr_torrent * tor )
910{
911    tr_torrentLock( tor );
912
913    tor->downloadedPrev += tor->downloadedCur;
914    tor->downloadedCur   = 0;
915    tor->uploadedPrev   += tor->uploadedCur;
916    tor->uploadedCur     = 0;
917    tor->corruptPrev    += tor->corruptCur;
918    tor->corruptCur      = 0;
919
920    tr_torrentUnlock( tor );
921}
922
923
924void
925tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
926{
927    tr_torrentLock( tor );
928
929    if( has )
930        tr_cpPieceAdd( tor->completion, pieceIndex );
931    else
932        tr_cpPieceRem( tor->completion, pieceIndex );
933
934    tr_torrentUnlock( tor );
935}
936
937void tr_torrentRemoveSaved( tr_torrent * tor )
938{
939    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
940}
941
942/***
943****
944***/
945
946static void
947freeTorrent( tr_torrent * tor )
948{
949    tr_torrent * t;
950    tr_handle * h = tor->handle;
951    tr_info * inf = &tor->info;
952
953    assert( tor != NULL );
954    assert( !tor->isRunning );
955
956    tr_globalLock( h );
957
958    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
959
960    tr_cpClose( tor->completion );
961
962    tr_rcClose( tor->upload );
963    tr_rcClose( tor->download );
964    tr_rcClose( tor->swarmspeed );
965
966    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
967    tr_trackerFree( tor->tracker );
968    tor->tracker = NULL;
969
970    tr_free( tor->destination );
971
972    if( tor == h->torrentList )
973        h->torrentList = tor->next;
974    else for( t=h->torrentList; t!=NULL; t=t->next ) {
975        if( t->next == tor ) {
976            t->next = tor->next;
977            break;
978        }
979    }
980
981    assert( h->torrentCount >= 1 );
982    h->torrentCount--;
983
984    tr_inf( "closing torrent %s... %d torrents left",
985            tor->info.name, h->torrentCount );
986
987    tr_metainfoFree( inf );
988    tr_free( tor );
989
990    tr_globalUnlock( h );
991}
992
993enum
994{
995    AFTER_RECHECK_NONE,
996    AFTER_RECHECK_START,
997    AFTER_RECHECK_STOP,
998    AFTER_RECHECK_CLOSE
999};
1000
1001static void
1002checkAndStartCB( tr_torrent * tor )
1003{
1004    tr_globalLock( tor->handle );
1005
1006    tor->isRunning  = 1;
1007    *tor->errorString = '\0';
1008    tr_torrentResetTransferStats( tor );
1009    tr_torrentRecheckCompleteness( tor );
1010    tor->startDate = tr_date( );
1011    tr_trackerStart( tor->tracker );
1012    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
1013
1014    tr_globalUnlock( tor->handle );
1015}
1016   
1017void
1018tr_torrentStart( tr_torrent * tor )
1019{
1020    tr_globalLock( tor->handle );
1021
1022    if( !tor->isRunning )
1023    {
1024        tor->isRunning = 1;
1025        tr_ioRecheckAdd( tor, checkAndStartCB );
1026    }
1027
1028    tr_globalUnlock( tor->handle );
1029}
1030
1031void
1032tr_torrentRecheck( tr_torrent * tor )
1033{
1034    tr_globalLock( tor->handle );
1035
1036    if( !tor->uncheckedPieces )
1037        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1038    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1039
1040    tr_ioRecheckAdd( tor, tr_torrentRecheckCompleteness );
1041
1042    tr_globalUnlock( tor->handle );
1043}
1044
1045
1046static void
1047stopTorrent( void * vtor )
1048{
1049    tr_torrent * tor = vtor;
1050    tr_ioRecheckRemove( tor );
1051    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1052    tr_trackerStop( tor->tracker );
1053    tr_ioClose( tor );
1054}
1055
1056void
1057tr_torrentStop( tr_torrent * tor )
1058{
1059    tr_globalLock( tor->handle );
1060
1061    saveFastResumeNow( tor );
1062    tor->isRunning = 0;
1063    tr_runInEventThread( tor->handle, stopTorrent, tor );
1064
1065    tr_globalUnlock( tor->handle );
1066}
1067
1068static void
1069closeTorrent( void * vtor )
1070{
1071    tr_torrent * tor = vtor;
1072    saveFastResumeNow( tor );
1073    tor->isRunning = 0;
1074    stopTorrent( tor );
1075    freeTorrent( tor );
1076}
1077
1078void
1079tr_torrentClose( tr_torrent * tor )
1080{
1081    tr_globalLock( tor->handle );
1082
1083    tr_runInEventThread( tor->handle, closeTorrent, tor );
1084
1085    tr_globalUnlock( tor->handle );
1086}
1087
1088/**
1089***  Completeness
1090**/
1091
1092static void
1093fireStatusChange( tr_torrent * tor, cp_status_t status )
1094{
1095    assert( tor != NULL );
1096    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1097
1098    if( tor->status_func != NULL )
1099        (tor->status_func)( tor, status, tor->status_func_user_data );
1100}
1101
1102void
1103tr_torrentSetStatusCallback( tr_torrent             * tor,
1104                             tr_torrent_status_func   func,
1105                             void                   * user_data )
1106{
1107    assert( tor != NULL );
1108    tor->status_func = func;
1109    tor->status_func_user_data = user_data;
1110}
1111
1112void
1113tr_torrentClearStatusCallback( tr_torrent * torrent )
1114{
1115    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1116}
1117
1118void
1119tr_torrentRecheckCompleteness( tr_torrent * tor )
1120{
1121    cp_status_t cpStatus;
1122
1123    tr_torrentLock( tor );
1124
1125    cpStatus = tr_cpGetStatus( tor->completion );
1126    if( cpStatus != tor->cpStatus ) {
1127        tor->cpStatus = cpStatus;
1128        fireStatusChange( tor, cpStatus );
1129        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1130            && tor->downloadedCur ) {        /* and it just happened */
1131            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1132        }
1133        tr_ioClose( tor );
1134        saveFastResumeSoon( tor );
1135    }
1136    tr_torrentUnlock( tor );
1137}
1138
1139/**
1140***  File priorities
1141**/
1142
1143void
1144tr_torrentSetFilePriority( tr_torrent   * tor,
1145                           int            fileIndex,
1146                           tr_priority_t  priority )
1147{
1148    int i;
1149    tr_file * file;
1150
1151    tr_torrentLock( tor );
1152
1153    assert( tor != NULL );
1154    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1155    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1156
1157    file = &tor->info.files[fileIndex];
1158    file->priority = priority;
1159    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1160      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1161
1162    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1163             fileIndex, file->firstPiece, file->lastPiece,
1164             priority, tor->info.files[fileIndex].name );
1165
1166    saveFastResumeSoon( tor );
1167
1168    tr_torrentUnlock( tor );
1169}
1170
1171void
1172tr_torrentSetFilePriorities( tr_torrent     * tor,
1173                             int            * files,
1174                             int              fileCount,
1175                             tr_priority_t    priority )
1176{
1177    int i;
1178    for( i=0; i<fileCount; ++i )
1179        tr_torrentSetFilePriority( tor, files[i], priority );
1180}
1181
1182tr_priority_t
1183tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1184{
1185    tr_priority_t ret;
1186
1187    tr_torrentLock( tor );
1188    assert( tor != NULL );
1189    assert( 0<=file && file<tor->info.fileCount );
1190    ret = tor->info.files[file].priority;
1191    tr_torrentUnlock( tor );
1192
1193    return ret;
1194}
1195
1196tr_priority_t*
1197tr_torrentGetFilePriorities( const tr_torrent * tor )
1198{
1199    int i;
1200    tr_priority_t * p;
1201
1202    tr_torrentLock( tor );
1203    p = tr_new0( tr_priority_t, tor->info.fileCount );
1204    for( i=0; i<tor->info.fileCount; ++i )
1205        p[i] = tor->info.files[i].priority;
1206    tr_torrentUnlock( tor );
1207
1208    return p;
1209}
1210
1211/**
1212***  File DND
1213**/
1214
1215int
1216tr_torrentGetFileDL( const tr_torrent * tor,
1217                     int                file )
1218{
1219    int doDownload;
1220    tr_torrentLock( tor );
1221
1222    assert( 0<=file && file<tor->info.fileCount );
1223    doDownload = !tor->info.files[file].dnd;
1224
1225    tr_torrentUnlock( tor );
1226    return doDownload != 0;
1227}
1228
1229void
1230tr_torrentSetFileDL( tr_torrent  * tor,
1231                     int           fileIndex,
1232                     int           doDownload )
1233{
1234    tr_file * file;
1235    const int dnd = !doDownload;
1236    int firstPiece, firstPieceDND;
1237    int lastPiece, lastPieceDND;
1238    int i;
1239
1240    tr_torrentLock( tor );
1241
1242    file = &tor->info.files[fileIndex];
1243    file->dnd = dnd;
1244    firstPiece = file->firstPiece;
1245    lastPiece = file->lastPiece;
1246
1247    /* can't set the first piece to DND unless
1248       every file using that piece is DND */
1249    firstPieceDND = dnd;
1250    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1251        if( tor->info.files[i].lastPiece != firstPiece )
1252            break;
1253        firstPieceDND = tor->info.files[i].dnd;
1254    }
1255
1256    /* can't set the last piece to DND unless
1257       every file using that piece is DND */
1258    lastPieceDND = dnd;
1259    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1260        if( tor->info.files[i].firstPiece != lastPiece )
1261            break;
1262        lastPieceDND = tor->info.files[i].dnd;
1263    }
1264
1265    if( firstPiece == lastPiece )
1266    {
1267        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1268    }
1269    else
1270    {
1271        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1272        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1273        for( i=firstPiece+1; i<lastPiece; ++i )
1274            tor->info.pieces[i].dnd = dnd;
1275    }
1276
1277    tr_cpInvalidateDND ( tor->completion );
1278
1279    saveFastResumeSoon( tor );
1280
1281    tr_torrentUnlock( tor );
1282}
1283
1284void
1285tr_torrentSetFileDLs ( tr_torrent  * tor,
1286                       int         * files,
1287                       int           fileCount,
1288                       int           doDownload )
1289{
1290    int i;
1291    for( i=0; i<fileCount; ++i )
1292        tr_torrentSetFileDL( tor, files[i], doDownload );
1293}
1294
1295/***
1296****
1297***/
1298
1299int _tr_block( const tr_torrent * tor, int index, int begin )
1300{
1301    const tr_info * inf = &tor->info;
1302    return index * ( inf->pieceSize / tor->blockSize ) +
1303        begin / tor->blockSize;
1304}
1305
1306uint64_t
1307tr_pieceOffset( const tr_torrent * tor, int index, int begin, int length )
1308{
1309    uint64_t ret;
1310    ret = tor->info.pieceSize;
1311    ret *= index;
1312    ret += begin;
1313    ret += length;
1314    return ret;
1315}
1316
Note: See TracBrowser for help on using the repository browser.