source: trunk/libtransmission/torrent.c @ 3254

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

simplify libT locks now that it's (more-or-less) single-threaded. fix deadlocks. make tr_locks nestable.

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