2 * This file Copyright (C) Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: torrent.c 14156 2013-08-05 13:07:23Z jordan $
13 #include <signal.h> /* signal () */
14 #include <sys/types.h> /* stat */
15 #include <sys/stat.h> /* stat */
17 #include <sys/wait.h> /* wait () */
21 #include <unistd.h> /* stat */
27 #include <string.h> /* memcmp */
28 #include <stdlib.h> /* qsort */
30 #include <event2/util.h> /* evutil_vsnprintf () */
32 #include "transmission.h"
33 #include "announcer.h"
34 #include "bandwidth.h"
36 #include "completion.h"
37 #include "crypto.h" /* for tr_sha1 */
39 #include "fdlimit.h" /* tr_fdTorrentClose */
40 #include "inout.h" /* tr_ioTestPiece () */
44 #include "peer-common.h" /* MAX_BLOCK_SIZE */
46 #include "platform.h" /* TR_PATH_DELIMITER_STR */
50 #include "torrent-magnet.h"
51 #include "trevent.h" /* tr_runInEventThread () */
61 #define tr_deeplog_tor(tor, ...) \
64 if (tr_logGetDeepEnabled ()) \
65 tr_logAddDeep (__FILE__, __LINE__, tr_torrentName (tor), __VA_ARGS__); \
74 tr_torrentName (const tr_torrent
* tor
)
76 return tor
? tor
->info
.name
: "";
80 tr_torrentId (const tr_torrent
* tor
)
82 return tor
? tor
->uniqueId
: -1;
86 tr_torrentFindFromId (tr_session
* session
, int id
)
88 tr_torrent
* tor
= NULL
;
90 while ((tor
= tr_torrentNext (session
, tor
)))
91 if (tor
->uniqueId
== id
)
98 tr_torrentFindFromHashString (tr_session
* session
, const char * str
)
100 tr_torrent
* tor
= NULL
;
102 while ((tor
= tr_torrentNext (session
, tor
)))
103 if (!evutil_ascii_strcasecmp (str
, tor
->info
.hashString
))
110 tr_torrentFindFromHash (tr_session
* session
, const uint8_t * torrentHash
)
112 tr_torrent
* tor
= NULL
;
114 while ((tor
= tr_torrentNext (session
, tor
)))
115 if (*tor
->info
.hash
== *torrentHash
)
116 if (!memcmp (tor
->info
.hash
, torrentHash
, SHA_DIGEST_LENGTH
))
123 tr_torrentFindFromMagnetLink (tr_session
* session
, const char * magnet
)
125 tr_magnet_info
* info
;
126 tr_torrent
* tor
= NULL
;
128 if ((info
= tr_magnetParse (magnet
)))
130 tor
= tr_torrentFindFromHash (session
, info
->hash
);
131 tr_magnetFree (info
);
138 tr_torrentFindFromObfuscatedHash (tr_session
* session
,
139 const uint8_t * obfuscatedTorrentHash
)
141 tr_torrent
* tor
= NULL
;
143 while ((tor
= tr_torrentNext (session
, tor
)))
144 if (!memcmp (tor
->obfuscatedHash
, obfuscatedTorrentHash
, SHA_DIGEST_LENGTH
))
151 tr_torrentIsPieceTransferAllowed (const tr_torrent
* tor
,
152 tr_direction direction
)
157 assert (tr_isTorrent (tor
));
158 assert (tr_isDirection (direction
));
160 if (tr_torrentUsesSpeedLimit (tor
, direction
))
161 if (tr_torrentGetSpeedLimit_Bps (tor
, direction
) <= 0)
164 if (tr_torrentUsesSessionLimits (tor
))
165 if (tr_sessionGetActiveSpeedLimit_Bps (tor
->session
, direction
, &limit
))
177 tr_torrentUnsetPeerId (tr_torrent
* tor
)
179 /* triggers a rebuild next time tr_torrentGetPeerId() is called */
180 *tor
->peer_id
= '\0';
184 peerIdTTL (const tr_torrent
* tor
)
188 if (!tor
->peer_id_creation_time
)
191 ttl
= (int)difftime(tor
->peer_id_creation_time
+(tor
->session
->peer_id_ttl_hours
*3600), tr_time());
196 const unsigned char *
197 tr_torrentGetPeerId (tr_torrent
* tor
)
199 bool needs_new_peer_id
= false;
202 needs_new_peer_id
= true;
204 if (!needs_new_peer_id
)
205 if (!tr_torrentIsPrivate (tor
))
206 if (peerIdTTL (tor
) <= 0)
207 needs_new_peer_id
= true;
209 if (needs_new_peer_id
)
211 tr_peerIdInit (tor
->peer_id
);
212 tor
->peer_id_creation_time
= tr_time ();
218 **** PER-TORRENT UL / DL SPEEDS
222 tr_torrentSetSpeedLimit_Bps (tr_torrent
* tor
, tr_direction dir
, unsigned int Bps
)
224 assert (tr_isTorrent (tor
));
225 assert (tr_isDirection (dir
));
227 if (tr_bandwidthSetDesiredSpeed_Bps (&tor
->bandwidth
, dir
, Bps
))
228 tr_torrentSetDirty (tor
);
231 tr_torrentSetSpeedLimit_KBps (tr_torrent
* tor
, tr_direction dir
, unsigned int KBps
)
233 tr_torrentSetSpeedLimit_Bps (tor
, dir
, toSpeedBytes (KBps
));
237 tr_torrentGetSpeedLimit_Bps (const tr_torrent
* tor
, tr_direction dir
)
239 assert (tr_isTorrent (tor
));
240 assert (tr_isDirection (dir
));
242 return tr_bandwidthGetDesiredSpeed_Bps (&tor
->bandwidth
, dir
);
245 tr_torrentGetSpeedLimit_KBps (const tr_torrent
* tor
, tr_direction dir
)
247 assert (tr_isTorrent (tor
));
248 assert (tr_isDirection (dir
));
250 return toSpeedKBps (tr_torrentGetSpeedLimit_Bps (tor
, dir
));
254 tr_torrentUseSpeedLimit (tr_torrent
* tor
, tr_direction dir
, bool do_use
)
256 assert (tr_isTorrent (tor
));
257 assert (tr_isDirection (dir
));
259 if (tr_bandwidthSetLimited (&tor
->bandwidth
, dir
, do_use
))
260 tr_torrentSetDirty (tor
);
264 tr_torrentUsesSpeedLimit (const tr_torrent
* tor
, tr_direction dir
)
266 assert (tr_isTorrent (tor
));
268 return tr_bandwidthIsLimited (&tor
->bandwidth
, dir
);
272 tr_torrentUseSessionLimits (tr_torrent
* tor
, bool doUse
)
276 assert (tr_isTorrent (tor
));
278 changed
= tr_bandwidthHonorParentLimits (&tor
->bandwidth
, TR_UP
, doUse
);
279 changed
|= tr_bandwidthHonorParentLimits (&tor
->bandwidth
, TR_DOWN
, doUse
);
282 tr_torrentSetDirty (tor
);
286 tr_torrentUsesSessionLimits (const tr_torrent
* tor
)
288 assert (tr_isTorrent (tor
));
290 return tr_bandwidthAreParentLimitsHonored (&tor
->bandwidth
, TR_UP
);
298 tr_torrentSetRatioMode (tr_torrent
* tor
, tr_ratiolimit mode
)
300 assert (tr_isTorrent (tor
));
301 assert (mode
==TR_RATIOLIMIT_GLOBAL
|| mode
==TR_RATIOLIMIT_SINGLE
|| mode
==TR_RATIOLIMIT_UNLIMITED
);
303 if (mode
!= tor
->ratioLimitMode
)
305 tor
->ratioLimitMode
= mode
;
307 tr_torrentSetDirty (tor
);
312 tr_torrentGetRatioMode (const tr_torrent
* tor
)
314 assert (tr_isTorrent (tor
));
316 return tor
->ratioLimitMode
;
320 tr_torrentSetRatioLimit (tr_torrent
* tor
, double desiredRatio
)
322 assert (tr_isTorrent (tor
));
324 if ((int)(desiredRatio
*100.0) != (int)(tor
->desiredRatio
*100.0))
326 tor
->desiredRatio
= desiredRatio
;
328 tr_torrentSetDirty (tor
);
333 tr_torrentGetRatioLimit (const tr_torrent
* tor
)
335 assert (tr_isTorrent (tor
));
337 return tor
->desiredRatio
;
341 tr_torrentGetSeedRatio (const tr_torrent
* tor
, double * ratio
)
345 assert (tr_isTorrent (tor
));
347 switch (tr_torrentGetRatioMode (tor
))
349 case TR_RATIOLIMIT_SINGLE
:
352 *ratio
= tr_torrentGetRatioLimit (tor
);
355 case TR_RATIOLIMIT_GLOBAL
:
356 isLimited
= tr_sessionIsRatioLimited (tor
->session
);
357 if (isLimited
&& ratio
)
358 *ratio
= tr_sessionGetRatioLimit (tor
->session
);
361 default: /* TR_RATIOLIMIT_UNLIMITED */
369 /* returns true if the seed ratio applies --
370 * it applies if the torrent's a seed AND it has a seed ratio set */
372 tr_torrentGetSeedRatioBytes (const tr_torrent
* tor
,
373 uint64_t * setmeLeft
,
374 uint64_t * setmeGoal
)
377 bool seedRatioApplies
= false;
379 assert (tr_isTorrent (tor
));
381 if (tr_torrentGetSeedRatio (tor
, &seedRatio
))
383 const uint64_t u
= tor
->uploadedCur
+ tor
->uploadedPrev
;
384 const uint64_t d
= tor
->downloadedCur
+ tor
->downloadedPrev
;
385 const uint64_t baseline
= d
? d
: tr_cpSizeWhenDone (&tor
->completion
);
386 const uint64_t goal
= baseline
* seedRatio
;
387 if (setmeLeft
) *setmeLeft
= goal
> u
? goal
- u
: 0;
388 if (setmeGoal
) *setmeGoal
= goal
;
389 seedRatioApplies
= tr_torrentIsSeed (tor
);
392 return seedRatioApplies
;
396 tr_torrentIsSeedRatioDone (const tr_torrent
* tor
)
399 return tr_torrentGetSeedRatioBytes (tor
, &bytesLeft
, NULL
) && !bytesLeft
;
407 tr_torrentSetIdleMode (tr_torrent
* tor
, tr_idlelimit mode
)
409 assert (tr_isTorrent (tor
));
410 assert (mode
==TR_IDLELIMIT_GLOBAL
|| mode
==TR_IDLELIMIT_SINGLE
|| mode
==TR_IDLELIMIT_UNLIMITED
);
412 if (mode
!= tor
->idleLimitMode
)
414 tor
->idleLimitMode
= mode
;
416 tr_torrentSetDirty (tor
);
421 tr_torrentGetIdleMode (const tr_torrent
* tor
)
423 assert (tr_isTorrent (tor
));
425 return tor
->idleLimitMode
;
429 tr_torrentSetIdleLimit (tr_torrent
* tor
, uint16_t idleMinutes
)
431 assert (tr_isTorrent (tor
));
435 tor
->idleLimitMinutes
= idleMinutes
;
437 tr_torrentSetDirty (tor
);
442 tr_torrentGetIdleLimit (const tr_torrent
* tor
)
444 assert (tr_isTorrent (tor
));
446 return tor
->idleLimitMinutes
;
450 tr_torrentGetSeedIdle (const tr_torrent
* tor
, uint16_t * idleMinutes
)
454 switch (tr_torrentGetIdleMode (tor
))
456 case TR_IDLELIMIT_SINGLE
:
458 if (idleMinutes
!= NULL
)
459 *idleMinutes
= tr_torrentGetIdleLimit (tor
);
462 case TR_IDLELIMIT_GLOBAL
:
463 isLimited
= tr_sessionIsIdleLimited (tor
->session
);
464 if (isLimited
&& idleMinutes
)
465 *idleMinutes
= tr_sessionGetIdleLimit (tor
->session
);
468 default: /* TR_IDLELIMIT_UNLIMITED */
477 tr_torrentIsSeedIdleLimitDone (tr_torrent
* tor
)
479 uint16_t idleMinutes
;
480 return tr_torrentGetSeedIdle (tor
, &idleMinutes
)
481 && difftime (tr_time (), MAX (tor
->startDate
, tor
->activityDate
)) >= idleMinutes
* 60u;
489 tr_torrentCheckSeedLimit (tr_torrent
* tor
)
491 assert (tr_isTorrent (tor
));
493 if (!tor
->isRunning
|| tor
->isStopping
|| !tr_torrentIsSeed (tor
))
496 /* if we're seeding and reach our seed ratio limit, stop the torrent */
497 if (tr_torrentIsSeedRatioDone (tor
))
499 tr_logAddTorInfo (tor
, "%s", "Seed ratio reached; pausing torrent");
501 tor
->isStopping
= true;
503 /* maybe notify the client */
504 if (tor
->ratio_limit_hit_func
!= NULL
)
505 tor
->ratio_limit_hit_func (tor
, tor
->ratio_limit_hit_func_user_data
);
507 /* if we're seeding and reach our inactiviy limit, stop the torrent */
508 else if (tr_torrentIsSeedIdleLimitDone (tor
))
510 tr_logAddTorInfo (tor
, "%s", "Seeding idle limit reached; pausing torrent");
512 tor
->isStopping
= true;
513 tor
->finishedSeedingByIdle
= true;
515 /* maybe notify the client */
516 if (tor
->idle_limit_hit_func
!= NULL
)
517 tor
->idle_limit_hit_func (tor
, tor
->idle_limit_hit_func_user_data
);
526 tr_torrentSetLocalError (tr_torrent
* tor
, const char * fmt
, ...)
530 assert (tr_isTorrent (tor
));
533 tor
->error
= TR_STAT_LOCAL_ERROR
;
534 tor
->errorTracker
[0] = '\0';
535 evutil_vsnprintf (tor
->errorString
, sizeof (tor
->errorString
), fmt
, ap
);
538 tr_logAddTorErr (tor
, "%s", tor
->errorString
);
541 tor
->isStopping
= true;
545 tr_torrentClearError (tr_torrent
* tor
)
547 tor
->error
= TR_STAT_OK
;
548 tor
->errorString
[0] = '\0';
549 tor
->errorTracker
[0] = '\0';
553 onTrackerResponse (tr_torrent
* tor
, const tr_tracker_event
* event
, void * unused UNUSED
)
555 switch (event
->messageType
)
557 case TR_TRACKER_PEERS
:
560 const int8_t seedProbability
= event
->seedProbability
;
561 const bool allAreSeeds
= seedProbability
== 100;
564 tr_logAddTorDbg (tor
, "Got %zu seeds from tracker", event
->pexCount
);
566 tr_logAddTorDbg (tor
, "Got %zu peers from tracker", event
->pexCount
);
568 for (i
= 0; i
< event
->pexCount
; ++i
)
569 tr_peerMgrAddPex (tor
, TR_PEER_FROM_TRACKER
, &event
->pex
[i
], seedProbability
);
574 case TR_TRACKER_WARNING
:
575 tr_logAddTorErr (tor
, _("Tracker warning: \"%s\""), event
->text
);
576 tor
->error
= TR_STAT_TRACKER_WARNING
;
577 tr_strlcpy (tor
->errorTracker
, event
->tracker
, sizeof (tor
->errorTracker
));
578 tr_strlcpy (tor
->errorString
, event
->text
, sizeof (tor
->errorString
));
581 case TR_TRACKER_ERROR
:
582 tr_logAddTorErr (tor
, _("Tracker error: \"%s\""), event
->text
);
583 tor
->error
= TR_STAT_TRACKER_ERROR
;
584 tr_strlcpy (tor
->errorTracker
, event
->tracker
, sizeof (tor
->errorTracker
));
585 tr_strlcpy (tor
->errorString
, event
->text
, sizeof (tor
->errorString
));
588 case TR_TRACKER_ERROR_CLEAR
:
589 if (tor
->error
!= TR_STAT_LOCAL_ERROR
)
590 tr_torrentClearError (tor
);
597 **** TORRENT INSTANTIATION
601 static tr_piece_index_t
602 getBytePiece (const tr_info
* info
, uint64_t byteOffset
)
604 tr_piece_index_t piece
;
607 assert (info
->pieceSize
!= 0);
609 piece
= byteOffset
/ info
->pieceSize
;
611 /* handle 0-byte files at the end of a torrent */
612 if (byteOffset
== info
->totalSize
)
613 piece
= info
->pieceCount
- 1;
619 initFilePieces (tr_info
* info
,
620 tr_file_index_t fileIndex
)
623 uint64_t firstByte
, lastByte
;
626 assert (fileIndex
< info
->fileCount
);
628 file
= &info
->files
[fileIndex
];
629 firstByte
= file
->offset
;
630 lastByte
= firstByte
+ (file
->length
? file
->length
- 1 : 0);
631 file
->firstPiece
= getBytePiece (info
, firstByte
);
632 file
->lastPiece
= getBytePiece (info
, lastByte
);
636 pieceHasFile (tr_piece_index_t piece
,
637 const tr_file
* file
)
639 return (file
->firstPiece
<= piece
) && (piece
<= file
->lastPiece
);
643 calculatePiecePriority (const tr_torrent
* tor
,
644 tr_piece_index_t piece
,
648 tr_priority_t priority
= TR_PRI_LOW
;
650 /* find the first file that has data in this piece */
654 while (i
> 0 && pieceHasFile (piece
, &tor
->info
.files
[i
- 1]))
659 for (i
=0; i
<tor
->info
.fileCount
; ++i
)
660 if (pieceHasFile (piece
, &tor
->info
.files
[i
]))
664 /* the piece's priority is the max of the priorities
665 * of all the files in that piece */
666 for (; i
<tor
->info
.fileCount
; ++i
)
668 const tr_file
* file
= &tor
->info
.files
[i
];
670 if (!pieceHasFile (piece
, file
))
673 priority
= MAX (priority
, file
->priority
);
675 /* when dealing with multimedia files, getting the first and
676 last pieces can sometimes allow you to preview it a bit
677 before it's fully downloaded... */
678 if (file
->priority
>= TR_PRI_NORMAL
)
679 if (file
->firstPiece
== piece
|| file
->lastPiece
== piece
)
680 priority
= TR_PRI_HIGH
;
687 tr_torrentInitFilePieces (tr_torrent
* tor
)
693 tr_info
* inf
= &tor
->info
;
695 /* assign the file offsets */
696 for (f
=0; f
<inf
->fileCount
; ++f
)
698 inf
->files
[f
].offset
= offset
;
699 offset
+= inf
->files
[f
].length
;
700 initFilePieces (inf
, f
);
703 /* build the array of first-file hints to give calculatePiecePriority */
704 firstFiles
= tr_new (int, inf
->pieceCount
);
705 for (p
=f
=0; p
<inf
->pieceCount
; ++p
)
707 while (inf
->files
[f
].lastPiece
< p
)
713 /* test to confirm the first-file hints are correct */
714 for (p
=0; p
<inf
->pieceCount
; ++p
)
717 assert (inf
->files
[f
].firstPiece
<= p
);
718 assert (inf
->files
[f
].lastPiece
>= p
);
720 assert (inf
->files
[f
-1].lastPiece
< p
);
722 for (f
=0; f
<inf
->fileCount
; ++f
)
723 if (pieceHasFile (p
, &inf
->files
[f
]))
726 assert ((int)f
== firstFiles
[p
]);
730 for (p
=0; p
<inf
->pieceCount
; ++p
)
731 inf
->pieces
[p
].priority
= calculatePiecePriority (tor
, p
, firstFiles
[p
]);
733 tr_free (firstFiles
);
736 static void torrentStart (tr_torrent
* tor
, bool bypass_queue
);
739 * Decide on a block size. Constraints:
740 * (1) most clients decline requests over 16 KiB
741 * (2) pieceSize must be a multiple of block size
744 tr_getBlockSize (uint32_t pieceSize
)
746 uint32_t b
= pieceSize
;
748 while (b
> MAX_BLOCK_SIZE
)
751 if (!b
|| (pieceSize
% b
)) /* not cleanly divisible */
757 static void refreshCurrentDir (tr_torrent
* tor
);
760 torrentInitFromInfo (tr_torrent
* tor
)
763 tr_info
* info
= &tor
->info
;
765 tor
->blockSize
= tr_getBlockSize (info
->pieceSize
);
768 tor
->lastPieceSize
= (uint32_t)(info
->totalSize
% info
->pieceSize
);
770 if (!tor
->lastPieceSize
)
771 tor
->lastPieceSize
= info
->pieceSize
;
774 tor
->lastBlockSize
= info
->totalSize
% tor
->blockSize
;
776 if (!tor
->lastBlockSize
)
777 tor
->lastBlockSize
= tor
->blockSize
;
779 tor
->blockCount
= tor
->blockSize
780 ? (info
->totalSize
+ tor
->blockSize
- 1) / tor
->blockSize
783 tor
->blockCountInPiece
= tor
->blockSize
784 ? info
->pieceSize
/ tor
->blockSize
787 tor
->blockCountInLastPiece
= tor
->blockSize
788 ? (tor
->lastPieceSize
+ tor
->blockSize
- 1) / tor
->blockSize
792 if (tor
->blockSize
!= 0)
793 assert ((info
->pieceSize
% tor
->blockSize
) == 0);
794 t
= info
->pieceCount
- 1;
795 t
*= info
->pieceSize
;
796 t
+= tor
->lastPieceSize
;
797 assert (t
== info
->totalSize
);
798 t
= tor
->blockCount
- 1;
800 t
+= tor
->lastBlockSize
;
801 assert (t
== info
->totalSize
);
802 t
= info
->pieceCount
- 1;
803 t
*= tor
->blockCountInPiece
;
804 t
+= tor
->blockCountInLastPiece
;
805 assert (t
== (uint64_t)tor
->blockCount
);
807 tr_cpConstruct (&tor
->completion
, tor
);
809 tr_torrentInitFilePieces (tor
);
811 tor
->completeness
= tr_cpGetStatus (&tor
->completion
);
814 static void tr_torrentFireMetadataCompleted (tr_torrent
* tor
);
817 tr_torrentGotNewInfoDict (tr_torrent
* tor
)
819 torrentInitFromInfo (tor
);
821 tr_peerMgrOnTorrentGotMetainfo (tor
);
823 tr_torrentFireMetadataCompleted (tor
);
827 hasAnyLocalData (const tr_torrent
* tor
)
831 for (i
=0; i
<tor
->info
.fileCount
; ++i
)
832 if (tr_torrentFindFile2 (tor
, i
, NULL
, NULL
, NULL
))
839 setLocalErrorIfFilesDisappeared (tr_torrent
* tor
)
841 const bool disappeared
= (tr_cpHaveTotal (&tor
->completion
) > 0) && !hasAnyLocalData (tor
);
845 tr_deeplog_tor (tor
, "%s", "[LAZY] uh oh, the files disappeared");
846 tr_torrentSetLocalError (tor
, "%s", _("No data found! Ensure your drives are connected or use \"Set Location\". To re-download, remove the torrent and re-add it."));
853 torrentInit (tr_torrent
* tor
, const tr_ctor
* ctor
)
860 tr_session
* session
= tr_ctorGetSession (ctor
);
861 static int nextUniqueId
= 1;
863 assert (session
!= NULL
);
865 tr_sessionLock (session
);
867 tor
->session
= session
;
868 tor
->uniqueId
= nextUniqueId
++;
869 tor
->magicNumber
= TORRENT_MAGIC_NUMBER
;
870 tor
->queuePosition
= session
->torrentCount
;
872 tr_sha1 (tor
->obfuscatedHash
, "req2", 4,
873 tor
->info
.hash
, SHA_DIGEST_LENGTH
,
876 if (!tr_ctorGetDownloadDir (ctor
, TR_FORCE
, &dir
) ||
877 !tr_ctorGetDownloadDir (ctor
, TR_FALLBACK
, &dir
))
878 tor
->downloadDir
= tr_strdup (dir
);
880 if (tr_ctorGetIncompleteDir (ctor
, &dir
))
881 dir
= tr_sessionGetIncompleteDir (session
);
882 if (tr_sessionIsIncompleteDirEnabled (session
))
883 tor
->incompleteDir
= tr_strdup (dir
);
885 tr_bandwidthConstruct (&tor
->bandwidth
, session
, &session
->bandwidth
);
887 tor
->bandwidth
.priority
= tr_ctorGetBandwidthPriority (ctor
);
889 tor
->error
= TR_STAT_OK
;
891 tor
->finishedSeedingByIdle
= false;
893 tr_peerMgrAddTorrent (session
->peerMgr
, tor
);
895 assert (!tor
->downloadedCur
);
896 assert (!tor
->uploadedCur
);
898 tr_torrentSetAddedDate (tor
, tr_time ()); /* this is a default value to be
899 overwritten by the resume file */
901 torrentInitFromInfo (tor
);
902 loaded
= tr_torrentLoadResume (tor
, ~0, ctor
);
903 tor
->completeness
= tr_cpGetStatus (&tor
->completion
);
904 setLocalErrorIfFilesDisappeared (tor
);
906 tr_ctorInitTorrentPriorities (ctor
, tor
);
907 tr_ctorInitTorrentWanted (ctor
, tor
);
909 refreshCurrentDir (tor
);
911 doStart
= tor
->isRunning
;
914 if (!(loaded
& TR_FR_SPEEDLIMIT
))
916 tr_torrentUseSpeedLimit (tor
, TR_UP
, false);
917 tr_torrentSetSpeedLimit_Bps (tor
, TR_UP
, tr_sessionGetSpeedLimit_Bps (tor
->session
, TR_UP
));
918 tr_torrentUseSpeedLimit (tor
, TR_DOWN
, false);
919 tr_torrentSetSpeedLimit_Bps (tor
, TR_DOWN
, tr_sessionGetSpeedLimit_Bps (tor
->session
, TR_DOWN
));
920 tr_torrentUseSessionLimits (tor
, true);
923 if (!(loaded
& TR_FR_RATIOLIMIT
))
925 tr_torrentSetRatioMode (tor
, TR_RATIOLIMIT_GLOBAL
);
926 tr_torrentSetRatioLimit (tor
, tr_sessionGetRatioLimit (tor
->session
));
929 if (!(loaded
& TR_FR_IDLELIMIT
))
931 tr_torrentSetIdleMode (tor
, TR_IDLELIMIT_GLOBAL
);
932 tr_torrentSetIdleLimit (tor
, tr_sessionGetIdleLimit (tor
->session
));
935 /* add the torrent to tr_session.torrentList */
936 session
->torrentCount
++;
937 if (session
->torrentList
== NULL
)
939 session
->torrentList
= tor
;
943 tr_torrent
* it
= session
->torrentList
;
944 while (it
->next
!= NULL
)
949 /* if we don't have a local .torrent file already, assume the torrent is new */
950 isNewTorrent
= stat (tor
->info
.torrent
, &st
);
952 /* maybe save our own copy of the metainfo */
953 if (tr_ctorGetSave (ctor
))
955 const tr_variant
* val
;
956 if (!tr_ctorGetMetainfo (ctor
, &val
))
958 const char * path
= tor
->info
.torrent
;
959 const int err
= tr_variantToFile (val
, TR_VARIANT_FMT_BENC
, path
);
961 tr_torrentSetLocalError (tor
, "Unable to save torrent file: %s", tr_strerror (err
));
962 tr_sessionSetTorrentFile (tor
->session
, tor
->info
.hashString
, path
);
966 tor
->tiers
= tr_announcerAddTorrent (tor
, onTrackerResponse
, NULL
);
970 tor
->startAfterVerify
= doStart
;
971 tr_torrentVerify (tor
, NULL
, NULL
);
975 tr_torrentStart (tor
);
978 tr_sessionUnlock (session
);
981 static tr_parse_result
982 torrentParseImpl (const tr_ctor
* ctor
,
986 int * setme_duplicate_id
)
990 bool hasInfo
= false;
992 const tr_variant
* metainfo
;
993 tr_session
* session
= tr_ctorGetSession (ctor
);
994 tr_parse_result result
= TR_PARSE_OK
;
996 if (setmeInfo
== NULL
)
998 memset (setmeInfo
, 0, sizeof (tr_info
));
1000 if (tr_ctorGetMetainfo (ctor
, &metainfo
))
1001 return TR_PARSE_ERR
;
1003 didParse
= tr_metainfoParse (session
, metainfo
, setmeInfo
,
1004 &hasInfo
, dictLength
);
1005 doFree
= didParse
&& (setmeInfo
== &tmp
);
1008 result
= TR_PARSE_ERR
;
1010 if (didParse
&& hasInfo
&& !tr_getBlockSize (setmeInfo
->pieceSize
))
1011 result
= TR_PARSE_ERR
;
1013 if (didParse
&& session
&& (result
== TR_PARSE_OK
))
1015 const tr_torrent
* const tor
= tr_torrentFindFromHash (session
, setmeInfo
->hash
);
1019 result
= TR_PARSE_DUPLICATE
;
1021 if (setme_duplicate_id
!= NULL
)
1022 *setme_duplicate_id
= tr_torrentId (tor
);
1027 tr_metainfoFree (setmeInfo
);
1029 if (setmeHasInfo
!= NULL
)
1030 *setmeHasInfo
= hasInfo
;
1036 tr_torrentParse (const tr_ctor
* ctor
, tr_info
* setmeInfo
)
1038 return torrentParseImpl (ctor
, setmeInfo
, NULL
, NULL
, NULL
);
1042 tr_torrentNew (const tr_ctor
* ctor
, int * setme_error
, int * setme_duplicate_id
)
1048 tr_torrent
* tor
= NULL
;
1050 assert (ctor
!= NULL
);
1051 assert (tr_isSession (tr_ctorGetSession (ctor
)));
1053 r
= torrentParseImpl (ctor
, &tmpInfo
, &hasInfo
, &len
, setme_duplicate_id
);
1054 if (r
== TR_PARSE_OK
)
1056 tor
= tr_new0 (tr_torrent
, 1);
1057 tor
->info
= tmpInfo
;
1060 tor
->infoDictLength
= len
;
1062 torrentInit (tor
, ctor
);
1066 if (r
== TR_PARSE_DUPLICATE
)
1067 tr_metainfoFree (&tmpInfo
);
1069 if (setme_error
!= NULL
)
1081 tr_torrentSetDownloadDir (tr_torrent
* tor
, const char * path
)
1083 assert (tr_isTorrent (tor
));
1085 if (!path
|| !tor
->downloadDir
|| strcmp (path
, tor
->downloadDir
))
1087 tr_free (tor
->downloadDir
);
1088 tor
->downloadDir
= tr_strdup (path
);
1089 tr_torrentSetDirty (tor
);
1092 refreshCurrentDir (tor
);
1096 tr_torrentGetDownloadDir (const tr_torrent
* tor
)
1098 assert (tr_isTorrent (tor
));
1100 return tor
->downloadDir
;
1104 tr_torrentGetCurrentDir (const tr_torrent
* tor
)
1106 assert (tr_isTorrent (tor
));
1108 return tor
->currentDir
;
1113 tr_torrentChangeMyPort (tr_torrent
* tor
)
1115 assert (tr_isTorrent (tor
));
1118 tr_announcerChangeMyPort (tor
);
1122 tr_torrentManualUpdateImpl (void * vtor
)
1124 tr_torrent
* tor
= vtor
;
1126 assert (tr_isTorrent (tor
));
1129 tr_announcerManualAnnounce (tor
);
1133 tr_torrentManualUpdate (tr_torrent
* tor
)
1135 assert (tr_isTorrent (tor
));
1137 tr_runInEventThread (tor
->session
, tr_torrentManualUpdateImpl
, tor
);
1141 tr_torrentCanManualUpdate (const tr_torrent
* tor
)
1143 return (tr_isTorrent (tor
))
1145 && (tr_announcerCanManualAnnounce (tor
));
1149 tr_torrentInfo (const tr_torrent
* tor
)
1151 return tr_isTorrent (tor
) ? &tor
->info
: NULL
;
1155 tr_torrentStatCached (tr_torrent
* tor
)
1157 const time_t now
= tr_time ();
1159 return tr_isTorrent (tor
) && (now
== tor
->lastStatTime
)
1161 : tr_torrentStat (tor
);
1165 tr_torrentSetVerifyState (tr_torrent
* tor
, tr_verify_state state
)
1167 assert (tr_isTorrent (tor
));
1168 assert (state
==TR_VERIFY_NONE
|| state
==TR_VERIFY_WAIT
|| state
==TR_VERIFY_NOW
);
1170 tor
->verifyState
= state
;
1171 tor
->anyDate
= tr_time ();
1175 tr_torrentGetActivity (const tr_torrent
* tor
)
1177 tr_torrent_activity ret
= TR_STATUS_STOPPED
;
1179 const bool is_seed
= tr_torrentIsSeed (tor
);
1181 if (tor
->verifyState
== TR_VERIFY_NOW
)
1183 ret
= TR_STATUS_CHECK
;
1185 else if (tor
->verifyState
== TR_VERIFY_WAIT
)
1187 ret
= TR_STATUS_CHECK_WAIT
;
1189 else if (tor
->isRunning
)
1191 ret
= is_seed
? TR_STATUS_SEED
: TR_STATUS_DOWNLOAD
;
1193 else if (tr_torrentIsQueued (tor
))
1195 if (is_seed
&& tr_sessionGetQueueEnabled (tor
->session
, TR_UP
))
1196 ret
= TR_STATUS_SEED_WAIT
;
1197 else if (!is_seed
&& tr_sessionGetQueueEnabled (tor
->session
, TR_DOWN
))
1198 ret
= TR_STATUS_DOWNLOAD_WAIT
;
1205 torrentGetIdleSecs (const tr_torrent
* tor
)
1208 const tr_torrent_activity activity
= tr_torrentGetActivity (tor
);
1210 if ((activity
== TR_STATUS_DOWNLOAD
|| activity
== TR_STATUS_SEED
) && tor
->startDate
!= 0)
1211 idle_secs
= difftime (tr_time (), MAX (tor
->startDate
, tor
->activityDate
));
1219 tr_torrentIsStalled (const tr_torrent
* tor
)
1221 return tr_sessionGetQueueStalledEnabled (tor
->session
)
1222 && (torrentGetIdleSecs (tor
) > (tr_sessionGetQueueStalledMinutes (tor
->session
) * 60));
1227 getVerifyProgress (const tr_torrent
* tor
)
1231 if (tr_torrentHasMetadata (tor
))
1233 tr_piece_index_t i
, n
;
1234 tr_piece_index_t checked
= 0;
1236 for (i
=0, n
=tor
->info
.pieceCount
; i
!=n
; ++i
)
1237 if (tor
->info
.pieces
[i
].timeChecked
)
1240 d
= checked
/ (double)tor
->info
.pieceCount
;
1247 tr_torrentStat (tr_torrent
* tor
)
1250 uint64_t seedRatioBytesLeft
;
1251 uint64_t seedRatioBytesGoal
;
1252 bool seedRatioApplies
;
1253 uint16_t seedIdleMinutes
;
1254 const uint64_t now
= tr_time_msec ();
1255 unsigned int pieceUploadSpeed_Bps
;
1256 unsigned int pieceDownloadSpeed_Bps
;
1257 struct tr_swarm_stats swarm_stats
;
1260 assert (tr_isTorrent (tor
));
1262 tor
->lastStatTime
= tr_time ();
1264 if (tor
->swarm
!= NULL
)
1265 tr_swarmGetStats (tor
->swarm
, &swarm_stats
);
1267 swarm_stats
= TR_SWARM_STATS_INIT
;
1270 s
->id
= tor
->uniqueId
;
1271 s
->activity
= tr_torrentGetActivity (tor
);
1272 s
->error
= tor
->error
;
1273 s
->queuePosition
= tor
->queuePosition
;
1274 s
->isStalled
= tr_torrentIsStalled (tor
);
1275 tr_strlcpy (s
->errorString
, tor
->errorString
, sizeof (s
->errorString
));
1277 s
->manualAnnounceTime
= tr_announcerNextManualAnnounce (tor
);
1278 s
->peersConnected
= swarm_stats
.peerCount
;
1279 s
->peersSendingToUs
= swarm_stats
.activePeerCount
[TR_DOWN
];
1280 s
->peersGettingFromUs
= swarm_stats
.activePeerCount
[TR_UP
];
1281 s
->webseedsSendingToUs
= swarm_stats
.activeWebseedCount
;
1282 for (i
=0; i
<TR_PEER_FROM__MAX
; i
++)
1283 s
->peersFrom
[i
] = swarm_stats
.peerFromCount
[i
];
1285 s
->rawUploadSpeed_KBps
= toSpeedKBps (tr_bandwidthGetRawSpeed_Bps (&tor
->bandwidth
, now
, TR_UP
));
1286 s
->rawDownloadSpeed_KBps
= toSpeedKBps (tr_bandwidthGetRawSpeed_Bps (&tor
->bandwidth
, now
, TR_DOWN
));
1287 pieceUploadSpeed_Bps
= tr_bandwidthGetPieceSpeed_Bps (&tor
->bandwidth
, now
, TR_UP
);
1288 pieceDownloadSpeed_Bps
= tr_bandwidthGetPieceSpeed_Bps (&tor
->bandwidth
, now
, TR_DOWN
);
1289 s
->pieceUploadSpeed_KBps
= toSpeedKBps (pieceUploadSpeed_Bps
);
1290 s
->pieceDownloadSpeed_KBps
= toSpeedKBps (pieceDownloadSpeed_Bps
);
1292 s
->percentComplete
= tr_cpPercentComplete (&tor
->completion
);
1293 s
->metadataPercentComplete
= tr_torrentGetMetadataPercent (tor
);
1295 s
->percentDone
= tr_cpPercentDone (&tor
->completion
);
1296 s
->leftUntilDone
= tr_cpLeftUntilDone (&tor
->completion
);
1297 s
->sizeWhenDone
= tr_cpSizeWhenDone (&tor
->completion
);
1298 s
->recheckProgress
= s
->activity
== TR_STATUS_CHECK
? getVerifyProgress (tor
) : 0;
1299 s
->activityDate
= tor
->activityDate
;
1300 s
->addedDate
= tor
->addedDate
;
1301 s
->doneDate
= tor
->doneDate
;
1302 s
->startDate
= tor
->startDate
;
1303 s
->secondsSeeding
= tor
->secondsSeeding
;
1304 s
->secondsDownloading
= tor
->secondsDownloading
;
1305 s
->idleSecs
= torrentGetIdleSecs (tor
);
1307 s
->corruptEver
= tor
->corruptCur
+ tor
->corruptPrev
;
1308 s
->downloadedEver
= tor
->downloadedCur
+ tor
->downloadedPrev
;
1309 s
->uploadedEver
= tor
->uploadedCur
+ tor
->uploadedPrev
;
1310 s
->haveValid
= tr_cpHaveValid (&tor
->completion
);
1311 s
->haveUnchecked
= tr_cpHaveTotal (&tor
->completion
) - s
->haveValid
;
1312 s
->desiredAvailable
= tr_peerMgrGetDesiredAvailable (tor
);
1314 s
->ratio
= tr_getRatio (s
->uploadedEver
,
1315 s
->downloadedEver
? s
->downloadedEver
: s
->haveValid
);
1317 seedRatioApplies
= tr_torrentGetSeedRatioBytes (tor
, &seedRatioBytesLeft
,
1318 &seedRatioBytesGoal
);
1320 switch (s
->activity
)
1322 /* etaXLSpeed exists because if we use the piece speed directly,
1323 * brief fluctuations cause the ETA to jump all over the place.
1324 * so, etaXLSpeed is a smoothed-out version of the piece speed
1325 * to dampen the effect of fluctuations */
1326 case TR_STATUS_DOWNLOAD
:
1327 if ((tor
->etaDLSpeedCalculatedAt
+ 800) < now
)
1329 tor
->etaDLSpeedCalculatedAt
= now
;
1330 tor
->etaDLSpeed_Bps
= ((tor
->etaDLSpeedCalculatedAt
+ 4000) < now
)
1331 ? pieceDownloadSpeed_Bps
/* if no recent previous speed, no need to smooth */
1332 : ((tor
->etaDLSpeed_Bps
*4.0) + pieceDownloadSpeed_Bps
)/5.0; /* smooth across 5 readings */
1335 if ((s
->leftUntilDone
> s
->desiredAvailable
) && (tor
->info
.webseedCount
< 1))
1336 s
->eta
= TR_ETA_NOT_AVAIL
;
1337 else if (tor
->etaDLSpeed_Bps
== 0)
1338 s
->eta
= TR_ETA_UNKNOWN
;
1340 s
->eta
= s
->leftUntilDone
/ tor
->etaDLSpeed_Bps
;
1342 s
->etaIdle
= TR_ETA_NOT_AVAIL
;
1345 case TR_STATUS_SEED
:
1346 if (!seedRatioApplies
)
1348 s
->eta
= TR_ETA_NOT_AVAIL
;
1352 if ((tor
->etaULSpeedCalculatedAt
+ 800) < now
)
1354 tor
->etaULSpeedCalculatedAt
= now
;
1355 tor
->etaULSpeed_Bps
= ((tor
->etaULSpeedCalculatedAt
+ 4000) < now
)
1356 ? pieceUploadSpeed_Bps
/* if no recent previous speed, no need to smooth */
1357 : ((tor
->etaULSpeed_Bps
*4.0) + pieceUploadSpeed_Bps
)/5.0; /* smooth across 5 readings */
1360 if (tor
->etaULSpeed_Bps
== 0)
1361 s
->eta
= TR_ETA_UNKNOWN
;
1363 s
->eta
= seedRatioBytesLeft
/ tor
->etaULSpeed_Bps
;
1366 if (tor
->etaULSpeed_Bps
< 1 && tr_torrentGetSeedIdle (tor
, &seedIdleMinutes
))
1367 s
->etaIdle
= seedIdleMinutes
* 60 - s
->idleSecs
;
1369 s
->etaIdle
= TR_ETA_NOT_AVAIL
;
1373 s
->eta
= TR_ETA_NOT_AVAIL
;
1374 s
->etaIdle
= TR_ETA_NOT_AVAIL
;
1378 /* s->haveValid is here to make sure a torrent isn't marked 'finished'
1379 * when the user hits "uncheck all" prior to starting the torrent... */
1380 s
->finished
= tor
->finishedSeedingByIdle
|| (seedRatioApplies
&& !seedRatioBytesLeft
&& s
->haveValid
);
1382 if (!seedRatioApplies
|| s
->finished
)
1383 s
->seedRatioPercentDone
= 1;
1384 else if (!seedRatioBytesGoal
) /* impossible? safeguard for div by zero */
1385 s
->seedRatioPercentDone
= 0;
1387 s
->seedRatioPercentDone
= (double)(seedRatioBytesGoal
- seedRatioBytesLeft
) / seedRatioBytesGoal
;
1389 /* test some of the constraints */
1390 assert (s
->sizeWhenDone
<= tor
->info
.totalSize
);
1391 assert (s
->leftUntilDone
<= s
->sizeWhenDone
);
1392 assert (s
->desiredAvailable
<= s
->leftUntilDone
);
1402 countFileBytesCompleted (const tr_torrent
* tor
, tr_file_index_t index
)
1405 const tr_file
* f
= &tor
->info
.files
[index
];
1409 tr_block_index_t first
;
1410 tr_block_index_t last
;
1411 tr_torGetFileBlockRange (tor
, index
, &first
, &last
);
1415 if (tr_cpBlockIsComplete (&tor
->completion
, first
))
1420 /* the first block */
1421 if (tr_cpBlockIsComplete (&tor
->completion
, first
))
1422 total
+= tor
->blockSize
- (f
->offset
% tor
->blockSize
);
1424 /* the middle blocks */
1425 if (first
+ 1 < last
)
1427 uint64_t u
= tr_bitfieldCountRange (&tor
->completion
.blockBitfield
, first
+1, last
);
1428 u
*= tor
->blockSize
;
1432 /* the last block */
1433 if (tr_cpBlockIsComplete (&tor
->completion
, last
))
1434 total
+= (f
->offset
+ f
->length
) - ((uint64_t)tor
->blockSize
* last
);
1442 tr_torrentFiles (const tr_torrent
* tor
,
1443 tr_file_index_t
* fileCount
)
1446 const tr_file_index_t n
= tor
->info
.fileCount
;
1447 tr_file_stat
* files
= tr_new0 (tr_file_stat
, n
);
1448 tr_file_stat
* walk
= files
;
1449 const bool isSeed
= tor
->completeness
== TR_SEED
;
1451 assert (tr_isTorrent (tor
));
1453 for (i
=0; i
<n
; ++i
, ++walk
)
1455 const uint64_t b
= isSeed
? tor
->info
.files
[i
].length
: countFileBytesCompleted (tor
, i
);
1456 walk
->bytesCompleted
= b
;
1457 walk
->progress
= tor
->info
.files
[i
].length
> 0 ? ((float)b
/ tor
->info
.files
[i
].length
) : 1.0f
;
1460 if (fileCount
!= NULL
)
1467 tr_torrentFilesFree (tr_file_stat
* files
,
1468 tr_file_index_t fileCount UNUSED
)
1478 tr_torrentWebSpeeds_KBps (const tr_torrent
* tor
)
1480 assert (tr_isTorrent (tor
));
1482 return tr_peerMgrWebSpeeds_KBps (tor
);
1486 tr_torrentPeers (const tr_torrent
* tor
, int * peerCount
)
1488 assert (tr_isTorrent (tor
));
1490 return tr_peerMgrPeerStats (tor
, peerCount
);
1494 tr_torrentPeersFree (tr_peer_stat
* peers
, int peerCount UNUSED
)
1500 tr_torrentTrackers (const tr_torrent
* tor
, int * setmeTrackerCount
)
1502 assert (tr_isTorrent (tor
));
1504 return tr_announcerStats (tor
, setmeTrackerCount
);
1508 tr_torrentTrackersFree (tr_tracker_stat
* trackers
, int trackerCount
)
1510 tr_announcerStatsFree (trackers
, trackerCount
);
1514 tr_torrentAvailability (const tr_torrent
* tor
, int8_t * tab
, int size
)
1516 assert (tr_isTorrent (tor
));
1518 if ((tab
!= NULL
) && (size
> 0))
1519 tr_peerMgrTorrentAvailability (tor
, tab
, size
);
1523 tr_torrentAmountFinished (const tr_torrent
* tor
, float * tab
, int size
)
1525 tr_cpGetAmountDone (&tor
->completion
, tab
, size
);
1529 tr_torrentResetTransferStats (tr_torrent
* tor
)
1531 tr_torrentLock (tor
);
1533 tor
->downloadedPrev
+= tor
->downloadedCur
;
1534 tor
->downloadedCur
= 0;
1535 tor
->uploadedPrev
+= tor
->uploadedCur
;
1536 tor
->uploadedCur
= 0;
1537 tor
->corruptPrev
+= tor
->corruptCur
;
1538 tor
->corruptCur
= 0;
1540 tr_torrentSetDirty (tor
);
1542 tr_torrentUnlock (tor
);
1546 tr_torrentSetHasPiece (tr_torrent
* tor
,
1547 tr_piece_index_t pieceIndex
,
1550 assert (tr_isTorrent (tor
));
1551 assert (pieceIndex
< tor
->info
.pieceCount
);
1554 tr_cpPieceAdd (&tor
->completion
, pieceIndex
);
1556 tr_cpPieceRem (&tor
->completion
, pieceIndex
);
1564 static bool queueIsSequenced (tr_session
*);
1568 freeTorrent (tr_torrent
* tor
)
1571 tr_session
* session
= tor
->session
;
1572 tr_info
* inf
= &tor
->info
;
1573 const time_t now
= tr_time ();
1575 assert (!tor
->isRunning
);
1577 tr_sessionLock (session
);
1579 tr_peerMgrRemoveTorrent (tor
);
1581 tr_announcerRemoveTorrent (session
->announcer
, tor
);
1583 tr_cpDestruct (&tor
->completion
);
1585 tr_free (tor
->downloadDir
);
1586 tr_free (tor
->incompleteDir
);
1588 if (tor
== session
->torrentList
)
1590 session
->torrentList
= tor
->next
;
1592 else for (t
= session
->torrentList
; t
!= NULL
; t
= t
->next
)
1596 t
->next
= tor
->next
;
1601 /* decrement the torrent count */
1602 assert (session
->torrentCount
>= 1);
1603 session
->torrentCount
--;
1605 /* resequence the queue positions */
1607 while ((t
= tr_torrentNext (session
, t
)))
1609 if (t
->queuePosition
> tor
->queuePosition
)
1615 assert (queueIsSequenced (session
));
1617 tr_bandwidthDestruct (&tor
->bandwidth
);
1619 tr_metainfoFree (inf
);
1620 memset (tor
, ~0, sizeof (tr_torrent
));
1623 tr_sessionUnlock (session
);
1627 *** Start/Stop Callback
1630 static void torrentSetQueued (tr_torrent
* tor
, bool queued
);
1633 torrentStartImpl (void * vtor
)
1636 tr_torrent
* tor
= vtor
;
1638 assert (tr_isTorrent (tor
));
1640 tr_sessionLock (tor
->session
);
1642 tr_torrentRecheckCompleteness (tor
);
1643 torrentSetQueued (tor
, false);
1646 tor
->isRunning
= true;
1647 tor
->completeness
= tr_cpGetStatus (&tor
->completion
);
1648 tor
->startDate
= tor
->anyDate
= now
;
1649 tr_torrentClearError (tor
);
1650 tor
->finishedSeedingByIdle
= false;
1652 tr_torrentResetTransferStats (tor
);
1653 tr_announcerTorrentStarted (tor
);
1654 tor
->dhtAnnounceAt
= now
+ tr_cryptoWeakRandInt (20);
1655 tor
->dhtAnnounce6At
= now
+ tr_cryptoWeakRandInt (20);
1656 tor
->lpdAnnounceAt
= now
;
1657 tr_peerMgrStartTorrent (tor
);
1659 tr_sessionUnlock (tor
->session
);
1663 tr_torrentGetCurrentSizeOnDisk (const tr_torrent
* tor
)
1666 uint64_t byte_count
= 0;
1667 const tr_file_index_t n
= tor
->info
.fileCount
;
1672 char * filename
= tr_torrentFindFile (tor
, i
);
1675 if (filename
&& !stat (filename
, &sb
))
1676 byte_count
+= sb
.st_size
;
1685 torrentShouldQueue (const tr_torrent
* tor
)
1687 const tr_direction dir
= tr_torrentGetQueueDirection (tor
);
1689 return tr_sessionCountQueueFreeSlots (tor
->session
, dir
) == 0;
1693 torrentStart (tr_torrent
* tor
, bool bypass_queue
)
1695 switch (tr_torrentGetActivity (tor
))
1697 case TR_STATUS_SEED
:
1698 case TR_STATUS_DOWNLOAD
:
1699 return; /* already started */
1702 case TR_STATUS_SEED_WAIT
:
1703 case TR_STATUS_DOWNLOAD_WAIT
:
1705 return; /* already queued */
1708 case TR_STATUS_CHECK
:
1709 case TR_STATUS_CHECK_WAIT
:
1710 /* verifying right now... wait until that's done so
1711 * we'll know what completeness to use/announce */
1712 tor
->startAfterVerify
= true;
1716 case TR_STATUS_STOPPED
:
1717 if (!bypass_queue
&& torrentShouldQueue (tor
))
1719 torrentSetQueued (tor
, true);
1725 /* don't allow the torrent to be started if the files disappeared */
1726 if (setLocalErrorIfFilesDisappeared (tor
))
1729 /* otherwise, start it now... */
1730 tr_sessionLock (tor
->session
);
1732 /* allow finished torrents to be resumed */
1733 if (tr_torrentIsSeedRatioDone (tor
))
1735 tr_logAddTorInfo (tor
, "%s", _("Restarted manually -- disabling its seed ratio"));
1736 tr_torrentSetRatioMode (tor
, TR_RATIOLIMIT_UNLIMITED
);
1739 /* corresponds to the peer_id sent as a tracker request parameter.
1740 * one tracker admin says: "When the same torrent is opened and
1741 * closed and opened again without quitting Transmission ...
1742 * change the peerid. It would help sometimes if a stopped event
1743 * was missed to ensure that we didn't think someone was cheating. */
1744 tr_torrentUnsetPeerId (tor
);
1745 tor
->isRunning
= true;
1746 tr_torrentSetDirty (tor
);
1747 tr_runInEventThread (tor
->session
, torrentStartImpl
, tor
);
1749 tr_sessionUnlock (tor
->session
);
1753 tr_torrentStart (tr_torrent
* tor
)
1755 if (tr_isTorrent (tor
))
1756 torrentStart (tor
, false);
1760 tr_torrentStartNow (tr_torrent
* tor
)
1762 if (tr_isTorrent (tor
))
1763 torrentStart (tor
, true);
1770 tr_verify_done_func callback_func
;
1771 void * callback_data
;
1775 onVerifyDoneThreadFunc (void * vdata
)
1777 struct verify_data
* data
= vdata
;
1778 tr_torrent
* tor
= data
->tor
;
1781 tr_torrentRecheckCompleteness (tor
);
1783 if (data
->callback_func
!= NULL
)
1784 (*data
->callback_func
)(tor
, data
->aborted
, data
->callback_data
);
1786 if (!data
->aborted
&& tor
->startAfterVerify
)
1788 tor
->startAfterVerify
= false;
1789 torrentStart (tor
, false);
1796 onVerifyDone (tr_torrent
* tor
, bool aborted
, void * vdata
)
1798 struct verify_data
* data
= vdata
;
1799 assert (data
->tor
== tor
);
1800 data
->aborted
= aborted
;
1801 tr_runInEventThread (tor
->session
, onVerifyDoneThreadFunc
, data
);
1805 verifyTorrent (void * vdata
)
1808 struct verify_data
* data
= vdata
;
1809 tr_torrent
* tor
= data
->tor
;
1810 tr_sessionLock (tor
->session
);
1812 /* if the torrent's already being verified, stop it */
1813 tr_verifyRemove (tor
);
1815 startAfter
= (tor
->isRunning
|| tor
->startAfterVerify
) && !tor
->isStopping
;
1817 tr_torrentStop (tor
);
1818 tor
->startAfterVerify
= startAfter
;
1820 if (setLocalErrorIfFilesDisappeared (tor
))
1821 tor
->startAfterVerify
= false;
1823 tr_verifyAdd (tor
, onVerifyDone
, data
);
1825 tr_sessionUnlock (tor
->session
);
1829 tr_torrentVerify (tr_torrent
* tor
,
1830 tr_verify_done_func callback_func
,
1831 void * callback_data
)
1833 struct verify_data
* data
;
1835 data
= tr_new (struct verify_data
, 1);
1837 data
->aborted
= false;
1838 data
->callback_func
= callback_func
;
1839 data
->callback_data
= callback_data
;
1840 tr_runInEventThread (tor
->session
, verifyTorrent
, data
);
1844 tr_torrentSave (tr_torrent
* tor
)
1846 assert (tr_isTorrent (tor
));
1850 tor
->isDirty
= false;
1851 tr_torrentSaveResume (tor
);
1856 stopTorrent (void * vtor
)
1858 tr_torrent
* tor
= vtor
;
1859 tr_logAddTorInfo (tor
, "%s", "Pausing");
1861 assert (tr_isTorrent (tor
));
1863 tr_torrentLock (tor
);
1865 tr_verifyRemove (tor
);
1866 torrentSetQueued (tor
, false);
1867 tr_peerMgrStopTorrent (tor
);
1868 tr_announcerTorrentStopped (tor
);
1869 tr_cacheFlushTorrent (tor
->session
->cache
, tor
);
1871 tr_fdTorrentClose (tor
->session
, tor
->uniqueId
);
1873 if (!tor
->isDeleting
)
1874 tr_torrentSave (tor
);
1876 tr_torrentUnlock (tor
);
1880 tr_torrentStop (tr_torrent
* tor
)
1882 assert (tr_isTorrent (tor
));
1884 if (tr_isTorrent (tor
))
1886 tr_sessionLock (tor
->session
);
1889 tor
->isStopping
= 0;
1890 tr_torrentSetDirty (tor
);
1891 tr_runInEventThread (tor
->session
, stopTorrent
, tor
);
1893 tr_sessionUnlock (tor
->session
);
1898 closeTorrent (void * vtor
)
1901 tr_torrent
* tor
= vtor
;
1903 assert (tr_isTorrent (tor
));
1905 d
= tr_variantListAddDict (&tor
->session
->removedTorrents
, 2);
1906 tr_variantDictAddInt (d
, TR_KEY_id
, tor
->uniqueId
);
1907 tr_variantDictAddInt (d
, TR_KEY_date
, tr_time ());
1909 tr_logAddTorInfo (tor
, "%s", _("Removing torrent"));
1913 if (tor
->isDeleting
)
1915 tr_metainfoRemoveSaved (tor
->session
, &tor
->info
);
1916 tr_torrentRemoveResume (tor
);
1924 tr_torrentFree (tr_torrent
* tor
)
1926 if (tr_isTorrent (tor
))
1928 tr_session
* session
= tor
->session
;
1929 assert (tr_isSession (session
));
1930 tr_sessionLock (session
);
1932 tr_torrentClearCompletenessCallback (tor
);
1933 tr_runInEventThread (session
, closeTorrent
, tor
);
1935 tr_sessionUnlock (session
);
1943 tr_fileFunc
* deleteFunc
;
1946 static void tr_torrentDeleteLocalData (tr_torrent
*, tr_fileFunc
);
1949 removeTorrent (void * vdata
)
1951 struct remove_data
* data
= vdata
;
1952 tr_session
* session
= data
->tor
->session
;
1953 tr_sessionLock (session
);
1955 if (data
->deleteFlag
)
1956 tr_torrentDeleteLocalData (data
->tor
, data
->deleteFunc
);
1958 tr_torrentClearCompletenessCallback (data
->tor
);
1959 closeTorrent (data
->tor
);
1962 tr_sessionUnlock (session
);
1966 tr_torrentRemove (tr_torrent
* tor
,
1968 tr_fileFunc deleteFunc
)
1970 struct remove_data
* data
;
1972 assert (tr_isTorrent (tor
));
1973 tor
->isDeleting
= 1;
1975 data
= tr_new0 (struct remove_data
, 1);
1977 data
->deleteFlag
= deleteFlag
;
1978 data
->deleteFunc
= deleteFunc
;
1979 tr_runInEventThread (tor
->session
, removeTorrent
, data
);
1987 getCompletionString (int type
)
1991 /* Translators: this is a minor point that's safe to skip over, but FYI:
1992 "Complete" and "Done" are specific, different terms in Transmission:
1993 "Complete" means we've downloaded every file in the torrent.
1994 "Done" means we're done downloading the files we wanted, but NOT all
1996 case TR_PARTIAL_SEED
:
2000 return _("Complete");
2003 return _("Incomplete");
2008 fireCompletenessChange (tr_torrent
* tor
,
2009 tr_completeness status
,
2012 assert ((status
== TR_LEECH
)
2013 || (status
== TR_SEED
)
2014 || (status
== TR_PARTIAL_SEED
));
2016 if (tor
->completeness_func
)
2017 tor
->completeness_func (tor
, status
, wasRunning
,
2018 tor
->completeness_func_user_data
);
2022 tr_torrentSetCompletenessCallback (tr_torrent
* tor
,
2023 tr_torrent_completeness_func func
,
2026 assert (tr_isTorrent (tor
));
2028 tor
->completeness_func
= func
;
2029 tor
->completeness_func_user_data
= user_data
;
2033 tr_torrentClearCompletenessCallback (tr_torrent
* torrent
)
2035 tr_torrentSetCompletenessCallback (torrent
, NULL
, NULL
);
2039 tr_torrentSetRatioLimitHitCallback (tr_torrent
* tor
,
2040 tr_torrent_ratio_limit_hit_func func
,
2043 assert (tr_isTorrent (tor
));
2045 tor
->ratio_limit_hit_func
= func
;
2046 tor
->ratio_limit_hit_func_user_data
= user_data
;
2050 tr_torrentClearRatioLimitHitCallback (tr_torrent
* torrent
)
2052 tr_torrentSetRatioLimitHitCallback (torrent
, NULL
, NULL
);
2056 tr_torrentSetIdleLimitHitCallback (tr_torrent
* tor
,
2057 tr_torrent_idle_limit_hit_func func
,
2060 assert (tr_isTorrent (tor
));
2062 tor
->idle_limit_hit_func
= func
;
2063 tor
->idle_limit_hit_func_user_data
= user_data
;
2067 tr_torrentClearIdleLimitHitCallback (tr_torrent
* torrent
)
2069 tr_torrentSetIdleLimitHitCallback (torrent
, NULL
, NULL
);
2073 onSigCHLD (int i UNUSED
)
2077 _cwait (NULL
, -1, WAIT_CHILD
);
2083 rc
= waitpid (-1, NULL
, WNOHANG
);
2084 while (rc
>0 || (rc
==-1 && errno
==EINTR
));
2090 torrentCallScript (const tr_torrent
* tor
, const char * script
)
2093 const time_t now
= tr_time ();
2095 tr_strlcpy (timeStr
, ctime (&now
), sizeof (timeStr
));
2096 *strchr (timeStr
,'\n') = '\0';
2098 if (script
&& *script
)
2101 char * cmd
[] = { tr_strdup (script
), NULL
};
2103 tr_strdup_printf ("TR_APP_VERSION=%s", SHORT_VERSION_STRING
),
2104 tr_strdup_printf ("TR_TIME_LOCALTIME=%s", timeStr
),
2105 tr_strdup_printf ("TR_TORRENT_DIR=%s", tor
->currentDir
),
2106 tr_strdup_printf ("TR_TORRENT_ID=%d", tr_torrentId (tor
)),
2107 tr_strdup_printf ("TR_TORRENT_HASH=%s", tor
->info
.hashString
),
2108 tr_strdup_printf ("TR_TORRENT_NAME=%s", tr_torrentName (tor
)),
2111 tr_logAddTorInfo (tor
, "Calling script \"%s\"", script
);
2114 if (_spawnvpe (_P_NOWAIT
, script
, (const char*)cmd
, env
) == -1)
2115 tr_logAddTorErr (tor
, "error executing script \"%s\": %s", cmd
[0], tr_strerror (errno
));
2117 signal (SIGCHLD
, onSigCHLD
);
2121 for (i
=0; env
[i
]; ++i
)
2124 if (execvp (script
, cmd
) == -1)
2125 tr_logAddTorErr (tor
, "error executing script \"%s\": %s", cmd
[0], tr_strerror (errno
));
2131 for (i
=0; cmd
[i
]; ++i
) tr_free (cmd
[i
]);
2132 for (i
=0; env
[i
]; ++i
) tr_free (env
[i
]);
2137 tr_torrentRecheckCompleteness (tr_torrent
* tor
)
2139 tr_completeness completeness
;
2141 tr_torrentLock (tor
);
2143 completeness
= tr_cpGetStatus (&tor
->completion
);
2144 if (completeness
!= tor
->completeness
)
2146 const bool recentChange
= tor
->downloadedCur
!= 0;
2147 const bool wasLeeching
= !tr_torrentIsSeed (tor
);
2148 const bool wasRunning
= tor
->isRunning
;
2151 tr_logAddTorInfo (tor
, _("State changed from \"%1$s\" to \"%2$s\""),
2152 getCompletionString (tor
->completeness
),
2153 getCompletionString (completeness
));
2155 tor
->completeness
= completeness
;
2156 tr_fdTorrentClose (tor
->session
, tor
->uniqueId
);
2158 if (tr_torrentIsSeed (tor
))
2162 tr_announcerTorrentCompleted (tor
);
2163 tor
->doneDate
= tor
->anyDate
= tr_time ();
2166 if (wasLeeching
&& wasRunning
)
2168 /* clear interested flag on all peers */
2169 tr_peerMgrClearInterest (tor
);
2172 if (tor
->currentDir
== tor
->incompleteDir
)
2173 tr_torrentSetLocation (tor
, tor
->downloadDir
, true, NULL
, NULL
);
2176 fireCompletenessChange (tor
, completeness
, wasRunning
);
2178 if (tr_torrentIsSeed (tor
))
2180 if (wasLeeching
&& wasRunning
)
2182 /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2183 tr_torrentCheckSeedLimit (tor
);
2186 if (tr_sessionIsTorrentDoneScriptEnabled (tor
->session
))
2187 torrentCallScript (tor
, tr_sessionGetTorrentDoneScript (tor
->session
));
2190 tr_torrentSetDirty (tor
);
2193 tr_torrentUnlock (tor
);
2201 tr_torrentFireMetadataCompleted (tr_torrent
* tor
)
2203 assert (tr_isTorrent (tor
));
2205 if (tor
->metadata_func
!= NULL
)
2206 tor
->metadata_func (tor
, tor
->metadata_func_user_data
);
2210 tr_torrentSetMetadataCallback (tr_torrent
* tor
,
2211 tr_torrent_metadata_func func
,
2214 assert (tr_isTorrent (tor
));
2216 tor
->metadata_func
= func
;
2217 tor
->metadata_func_user_data
= user_data
;
2226 tr_torrentInitFilePriority (tr_torrent
* tor
,
2227 tr_file_index_t fileIndex
,
2228 tr_priority_t priority
)
2233 assert (tr_isTorrent (tor
));
2234 assert (fileIndex
< tor
->info
.fileCount
);
2235 assert (tr_isPriority (priority
));
2237 file
= &tor
->info
.files
[fileIndex
];
2238 file
->priority
= priority
;
2239 for (i
=file
->firstPiece
; i
<=file
->lastPiece
; ++i
)
2240 tor
->info
.pieces
[i
].priority
= calculatePiecePriority (tor
, i
, fileIndex
);
2244 tr_torrentSetFilePriorities (tr_torrent
* tor
,
2245 const tr_file_index_t
* files
,
2246 tr_file_index_t fileCount
,
2247 tr_priority_t priority
)
2250 assert (tr_isTorrent (tor
));
2251 tr_torrentLock (tor
);
2253 for (i
=0; i
<fileCount
; ++i
)
2254 if (files
[i
] < tor
->info
.fileCount
)
2255 tr_torrentInitFilePriority (tor
, files
[i
], priority
);
2256 tr_torrentSetDirty (tor
);
2257 tr_peerMgrRebuildRequests (tor
);
2259 tr_torrentUnlock (tor
);
2263 tr_torrentGetFilePriorities (const tr_torrent
* tor
)
2268 assert (tr_isTorrent (tor
));
2270 p
= tr_new0 (tr_priority_t
, tor
->info
.fileCount
);
2272 for (i
=0; i
<tor
->info
.fileCount
; ++i
)
2273 p
[i
] = tor
->info
.files
[i
].priority
;
2283 setFileDND (tr_torrent
* tor
, tr_file_index_t fileIndex
, int doDownload
)
2285 const int8_t dnd
= !doDownload
;
2286 tr_piece_index_t firstPiece
;
2287 int8_t firstPieceDND
;
2288 tr_piece_index_t lastPiece
;
2289 int8_t lastPieceDND
;
2291 tr_file
* file
= &tor
->info
.files
[fileIndex
];
2294 firstPiece
= file
->firstPiece
;
2295 lastPiece
= file
->lastPiece
;
2297 /* can't set the first piece to DND unless
2298 every file using that piece is DND */
2299 firstPieceDND
= dnd
;
2302 for (i
=fileIndex
-1; firstPieceDND
; --i
)
2304 if (tor
->info
.files
[i
].lastPiece
!= firstPiece
)
2307 firstPieceDND
= tor
->info
.files
[i
].dnd
;
2313 /* can't set the last piece to DND unless
2314 every file using that piece is DND */
2316 for (i
=fileIndex
+1; lastPieceDND
&& i
<tor
->info
.fileCount
; ++i
)
2318 if (tor
->info
.files
[i
].firstPiece
!= lastPiece
)
2320 lastPieceDND
= tor
->info
.files
[i
].dnd
;
2323 if (firstPiece
== lastPiece
)
2325 tor
->info
.pieces
[firstPiece
].dnd
= firstPieceDND
&& lastPieceDND
;
2329 tr_piece_index_t pp
;
2330 tor
->info
.pieces
[firstPiece
].dnd
= firstPieceDND
;
2331 tor
->info
.pieces
[lastPiece
].dnd
= lastPieceDND
;
2332 for (pp
=firstPiece
+1; pp
<lastPiece
; ++pp
)
2333 tor
->info
.pieces
[pp
].dnd
= dnd
;
2338 tr_torrentInitFileDLs (tr_torrent
* tor
,
2339 const tr_file_index_t
* files
,
2340 tr_file_index_t fileCount
,
2345 assert (tr_isTorrent (tor
));
2347 tr_torrentLock (tor
);
2349 for (i
=0; i
<fileCount
; ++i
)
2350 if (files
[i
] < tor
->info
.fileCount
)
2351 setFileDND (tor
, files
[i
], doDownload
);
2353 tr_cpInvalidateDND (&tor
->completion
);
2355 tr_torrentUnlock (tor
);
2359 tr_torrentSetFileDLs (tr_torrent
* tor
,
2360 const tr_file_index_t
* files
,
2361 tr_file_index_t fileCount
,
2364 assert (tr_isTorrent (tor
));
2365 tr_torrentLock (tor
);
2367 tr_torrentInitFileDLs (tor
, files
, fileCount
, doDownload
);
2368 tr_torrentSetDirty (tor
);
2369 tr_torrentRecheckCompleteness (tor
);
2370 tr_peerMgrRebuildRequests (tor
);
2372 tr_torrentUnlock (tor
);
2380 tr_torrentGetPriority (const tr_torrent
* tor
)
2382 assert (tr_isTorrent (tor
));
2384 return tor
->bandwidth
.priority
;
2388 tr_torrentSetPriority (tr_torrent
* tor
, tr_priority_t priority
)
2390 assert (tr_isTorrent (tor
));
2391 assert (tr_isPriority (priority
));
2393 if (tor
->bandwidth
.priority
!= priority
)
2395 tor
->bandwidth
.priority
= priority
;
2397 tr_torrentSetDirty (tor
);
2406 tr_torrentSetPeerLimit (tr_torrent
* tor
,
2407 uint16_t maxConnectedPeers
)
2409 assert (tr_isTorrent (tor
));
2411 if (tor
->maxConnectedPeers
!= maxConnectedPeers
)
2413 tor
->maxConnectedPeers
= maxConnectedPeers
;
2415 tr_torrentSetDirty (tor
);
2420 tr_torrentGetPeerLimit (const tr_torrent
* tor
)
2422 assert (tr_isTorrent (tor
));
2424 return tor
->maxConnectedPeers
;
2432 tr_torrentGetBlockLocation (const tr_torrent
* tor
,
2433 tr_block_index_t block
,
2434 tr_piece_index_t
* piece
,
2438 uint64_t pos
= block
;
2439 pos
*= tor
->blockSize
;
2440 *piece
= pos
/ tor
->info
.pieceSize
;
2441 *offset
= pos
- (*piece
* tor
->info
.pieceSize
);
2442 *length
= tr_torBlockCountBytes (tor
, block
);
2447 _tr_block (const tr_torrent
* tor
,
2448 tr_piece_index_t index
,
2451 tr_block_index_t ret
;
2453 assert (tr_isTorrent (tor
));
2456 ret
*= (tor
->info
.pieceSize
/ tor
->blockSize
);
2457 ret
+= offset
/ tor
->blockSize
;
2462 tr_torrentReqIsValid (const tr_torrent
* tor
,
2463 tr_piece_index_t index
,
2469 assert (tr_isTorrent (tor
));
2471 if (index
>= tor
->info
.pieceCount
)
2473 else if (length
< 1)
2475 else if ((offset
+ length
) > tr_torPieceCountBytes (tor
, index
))
2477 else if (length
> MAX_BLOCK_SIZE
)
2479 else if (tr_pieceOffset (tor
, index
, offset
, length
) > tor
->info
.totalSize
)
2483 tr_logAddTorDbg (tor
, "index %lu offset %lu length %lu err %d\n",
2484 (unsigned long)index
,
2485 (unsigned long)offset
,
2486 (unsigned long)length
,
2493 tr_pieceOffset (const tr_torrent
* tor
,
2494 tr_piece_index_t index
,
2500 assert (tr_isTorrent (tor
));
2502 ret
= tor
->info
.pieceSize
;
2510 tr_torGetFileBlockRange (const tr_torrent
* tor
,
2511 const tr_file_index_t file
,
2512 tr_block_index_t
* first
,
2513 tr_block_index_t
* last
)
2515 const tr_file
* f
= &tor
->info
.files
[file
];
2516 uint64_t offset
= f
->offset
;
2518 *first
= offset
/ tor
->blockSize
;
2526 offset
+= f
->length
- 1;
2527 *last
= offset
/ tor
->blockSize
;
2532 tr_torGetPieceBlockRange (const tr_torrent
* tor
,
2533 const tr_piece_index_t piece
,
2534 tr_block_index_t
* first
,
2535 tr_block_index_t
* last
)
2537 uint64_t offset
= tor
->info
.pieceSize
;
2539 *first
= offset
/ tor
->blockSize
;
2540 offset
+= (tr_torPieceCountBytes (tor
, piece
) - 1);
2541 *last
= offset
/ tor
->blockSize
;
2550 tr_torrentSetPieceChecked (tr_torrent
* tor
, tr_piece_index_t pieceIndex
)
2552 assert (tr_isTorrent (tor
));
2553 assert (pieceIndex
< tor
->info
.pieceCount
);
2555 tor
->info
.pieces
[pieceIndex
].timeChecked
= tr_time ();
2559 tr_torrentSetChecked (tr_torrent
* tor
, time_t when
)
2561 tr_piece_index_t i
, n
;
2563 assert (tr_isTorrent (tor
));
2565 for (i
=0, n
=tor
->info
.pieceCount
; i
!=n
; ++i
)
2566 tor
->info
.pieces
[i
].timeChecked
= when
;
2570 tr_torrentCheckPiece (tr_torrent
* tor
, tr_piece_index_t pieceIndex
)
2572 const bool pass
= tr_ioTestPiece (tor
, pieceIndex
);
2574 tr_deeplog_tor (tor
, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex
, (int)pass
);
2575 tr_torrentSetHasPiece (tor
, pieceIndex
, pass
);
2576 tr_torrentSetPieceChecked (tor
, pieceIndex
);
2577 tor
->anyDate
= tr_time ();
2578 tr_torrentSetDirty (tor
);
2584 tr_torrentGetFileMTime (const tr_torrent
* tor
, tr_file_index_t i
)
2588 if (!tr_fdFileGetCachedMTime (tor
->session
, tor
->uniqueId
, i
, &mtime
))
2589 tr_torrentFindFile2 (tor
, i
, NULL
, NULL
, &mtime
);
2595 tr_torrentPieceNeedsCheck (const tr_torrent
* tor
, tr_piece_index_t p
)
2599 const tr_info
* inf
= tr_torrentInfo (tor
);
2601 /* if we've never checked this piece, then it needs to be checked */
2602 if (!inf
->pieces
[p
].timeChecked
)
2605 /* If we think we've completed one of the files in this piece,
2606 * but it's been modified since we last checked it,
2607 * then it needs to be rechecked */
2608 tr_ioFindFileLocation (tor
, p
, 0, &f
, &unused
);
2609 for (; f
< inf
->fileCount
&& pieceHasFile (p
, &inf
->files
[f
]); ++f
)
2610 if (tr_cpFileIsComplete (&tor
->completion
, f
))
2611 if (tr_torrentGetFileMTime (tor
, f
) > inf
->pieces
[p
].timeChecked
)
2622 compareTrackerByTier (const void * va
, const void * vb
)
2624 const tr_tracker_info
* a
= va
;
2625 const tr_tracker_info
* b
= vb
;
2628 if (a
->tier
!= b
->tier
)
2629 return a
->tier
- b
->tier
;
2631 /* get the effects of a stable sort by comparing the two elements' addresses */
2636 tr_torrentSetAnnounceList (tr_torrent
* tor
,
2637 const tr_tracker_info
* trackers_in
,
2641 tr_variant metainfo
;
2643 tr_tracker_info
* trackers
;
2645 tr_torrentLock (tor
);
2647 assert (tr_isTorrent (tor
));
2649 /* ensure the trackers' tiers are in ascending order */
2650 trackers
= tr_memdup (trackers_in
, sizeof (tr_tracker_info
) * trackerCount
);
2651 qsort (trackers
, trackerCount
, sizeof (tr_tracker_info
), compareTrackerByTier
);
2653 /* look for bad URLs */
2654 for (i
=0; ok
&& i
<trackerCount
; ++i
)
2655 if (!tr_urlIsValidTracker (trackers
[i
].announce
))
2658 /* save to the .torrent file */
2659 if (ok
&& !tr_variantFromFile (&metainfo
, TR_VARIANT_FMT_BENC
, tor
->info
.torrent
))
2664 /* remove the old fields */
2665 tr_variantDictRemove (&metainfo
, TR_KEY_announce
);
2666 tr_variantDictRemove (&metainfo
, TR_KEY_announce_list
);
2668 /* add the new fields */
2669 if (trackerCount
> 0)
2671 tr_variantDictAddStr (&metainfo
, TR_KEY_announce
, trackers
[0].announce
);
2673 if (trackerCount
> 1)
2677 tr_variant
* tier
= NULL
;
2678 tr_variant
* announceList
= tr_variantDictAddList (&metainfo
, TR_KEY_announce_list
, 0);
2680 for (i
=0; i
<trackerCount
; ++i
)
2682 if (prevTier
!= trackers
[i
].tier
)
2684 prevTier
= trackers
[i
].tier
;
2685 tier
= tr_variantListAddList (announceList
, 0);
2688 tr_variantListAddStr (tier
, trackers
[i
].announce
);
2692 /* try to parse it back again, to make sure it's good */
2693 memset (&tmpInfo
, 0, sizeof (tr_info
));
2694 if (tr_metainfoParse (tor
->session
, &metainfo
, &tmpInfo
,
2695 &hasInfo
, &tor
->infoDictLength
))
2697 /* it's good, so keep these new trackers and free the old ones */
2700 swap
.trackers
= tor
->info
.trackers
;
2701 swap
.trackerCount
= tor
->info
.trackerCount
;
2702 tor
->info
.trackers
= tmpInfo
.trackers
;
2703 tor
->info
.trackerCount
= tmpInfo
.trackerCount
;
2704 tmpInfo
.trackers
= swap
.trackers
;
2705 tmpInfo
.trackerCount
= swap
.trackerCount
;
2707 tr_metainfoFree (&tmpInfo
);
2708 tr_variantToFile (&metainfo
, TR_VARIANT_FMT_BENC
, tor
->info
.torrent
);
2712 tr_variantFree (&metainfo
);
2714 /* if we had a tracker-related error on this torrent,
2715 * and that tracker's been removed,
2716 * then clear the error */
2717 if ((tor
->error
== TR_STAT_TRACKER_WARNING
)
2718 || (tor
->error
== TR_STAT_TRACKER_ERROR
))
2722 for (i
=0; clear
&& i
<trackerCount
; ++i
)
2723 if (!strcmp (trackers
[i
].announce
, tor
->errorTracker
))
2727 tr_torrentClearError (tor
);
2730 /* tell the announcer to reload this torrent's tracker list */
2731 tr_announcerResetTorrent (tor
->session
->announcer
, tor
);
2734 tr_torrentUnlock (tor
);
2745 tr_torrentSetAddedDate (tr_torrent
* tor
,
2748 assert (tr_isTorrent (tor
));
2751 tor
->anyDate
= MAX (tor
->anyDate
, tor
->addedDate
);
2755 tr_torrentSetActivityDate (tr_torrent
* tor
, time_t t
)
2757 assert (tr_isTorrent (tor
));
2759 tor
->activityDate
= t
;
2760 tor
->anyDate
= MAX (tor
->anyDate
, tor
->activityDate
);
2764 tr_torrentSetDoneDate (tr_torrent
* tor
,
2767 assert (tr_isTorrent (tor
));
2770 tor
->anyDate
= MAX (tor
->anyDate
, tor
->doneDate
);
2778 tr_torrentGetBytesLeftToAllocate (const tr_torrent
* tor
)
2781 uint64_t bytesLeft
= 0;
2783 assert (tr_isTorrent (tor
));
2785 for (i
=0; i
<tor
->info
.fileCount
; ++i
)
2787 if (!tor
->info
.files
[i
].dnd
)
2790 const uint64_t length
= tor
->info
.files
[i
].length
;
2791 char * path
= tr_torrentFindFile (tor
, i
);
2793 bytesLeft
+= length
;
2795 if ((path
!= NULL
) && !stat (path
, &sb
)
2796 && S_ISREG (sb
.st_mode
)
2797 && ((uint64_t)sb
.st_size
<= length
))
2798 bytesLeft
-= sb
.st_size
;
2808 ***** Removing the torrent's local data
2812 isJunkFile (const char * base
)
2815 static const char * files
[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
2816 static const int file_count
= sizeof (files
) / sizeof (files
[0]);
2818 for (i
=0; i
<file_count
; ++i
)
2819 if (!strcmp (base
, files
[i
]))
2823 /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2824 if (!memcmp (base
, "._", 2))
2832 removeEmptyFoldersAndJunkFiles (const char * folder
)
2836 if ((odir
= opendir (folder
)))
2839 while ((d
= readdir (odir
)))
2841 if (strcmp (d
->d_name
, ".") && strcmp (d
->d_name
, ".."))
2844 char * filename
= tr_buildPath (folder
, d
->d_name
, NULL
);
2846 if (!stat (filename
, &sb
) && S_ISDIR (sb
.st_mode
))
2847 removeEmptyFoldersAndJunkFiles (filename
);
2848 else if (isJunkFile (d
->d_name
))
2849 tr_remove (filename
);
2861 * This convoluted code does something (seemingly) simple:
2862 * remove the torrent's local files.
2864 * Fun complications:
2865 * 1. Try to preserve the directory hierarchy in the recycle bin.
2866 * 2. If there are nontorrent files, don't delete them...
2867 * 3. ...unless the other files are "junk", such as .DS_Store
2870 deleteLocalData (tr_torrent
* tor
, tr_fileFunc func
)
2876 char * tmpdir
= NULL
;
2877 tr_ptrArray files
= TR_PTR_ARRAY_INIT
;
2878 tr_ptrArray folders
= TR_PTR_ARRAY_INIT
;
2879 const void * const vstrcmp
= strcmp
;
2880 const char * const top
= tor
->currentDir
;
2882 /* if it's a magnet link, there's nothing to move... */
2883 if (!tr_torrentHasMetadata (tor
))
2887 **** Move the local data to a new tmpdir
2890 base
= tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor
));
2891 tmpdir
= tr_buildPath (top
, base
, NULL
);
2892 tr_mkdtemp (tmpdir
);
2895 for (f
=0; f
<tor
->info
.fileCount
; ++f
)
2899 /* try to find the file, looking in the partial and download dirs */
2900 filename
= tr_buildPath (top
, tor
->info
.files
[f
].name
, NULL
);
2901 if (!tr_fileExists (filename
, NULL
))
2903 char * partial
= tr_torrentBuildPartial (tor
, f
);
2905 filename
= tr_buildPath (top
, partial
, NULL
);
2907 if (!tr_fileExists (filename
, NULL
))
2914 /* if we found the file, move it */
2915 if (filename
!= NULL
)
2917 char * target
= tr_buildPath (tmpdir
, tor
->info
.files
[f
].name
, NULL
);
2918 tr_moveFile (filename
, target
, NULL
);
2919 tr_ptrArrayAppend (&files
, target
);
2927 **** Try deleting the top-level files & folders to preserve
2928 **** the directory hierarchy in the recycle bin.
2929 **** If case that fails -- for example, rmdir () doesn't
2930 **** delete nonempty folders -- go from the bottom up too.
2933 /* try deleting the local data's top-level files & folders */
2934 if ((odir
= opendir (tmpdir
)))
2937 while ((d
= readdir (odir
)))
2939 if (strcmp (d
->d_name
, ".") && strcmp (d
->d_name
, ".."))
2941 char * file
= tr_buildPath (tmpdir
, d
->d_name
, NULL
);
2949 /* go from the bottom up */
2950 for (i
=0, n
=tr_ptrArraySize (&files
); i
<n
; ++i
)
2952 char * walk
= tr_strdup (tr_ptrArrayNth (&files
, i
));
2953 while (tr_fileExists (walk
, NULL
) && !tr_is_same_file (tmpdir
, walk
))
2955 char * tmp
= tr_dirname (walk
);
2964 **** The local data has been removed.
2965 **** What's left in top are empty folders, junk, and user-generated files.
2966 **** Remove the first two categories and leave the third.
2969 /* build a list of 'top's child directories that belong to this torrent */
2970 for (f
=0; f
<tor
->info
.fileCount
; ++f
)
2975 /* get the directory that this file goes in... */
2976 filename
= tr_buildPath (top
, tor
->info
.files
[f
].name
, NULL
);
2977 dir
= tr_dirname (filename
);
2980 /* walk up the directory tree until we reach 'top' */
2981 if (!tr_is_same_file (top
, dir
) && strcmp (top
, dir
))
2985 char * parent
= tr_dirname (dir
);
2986 if (tr_is_same_file (top
, parent
) || !strcmp (top
, parent
))
2988 if (tr_ptrArrayFindSorted (&folders
, dir
, vstrcmp
) == NULL
)
2989 tr_ptrArrayInsertSorted (&folders
, tr_strdup(dir
), vstrcmp
);
2994 /* walk upwards to parent */
3003 for (i
=0, n
=tr_ptrArraySize (&folders
); i
<n
; ++i
)
3004 removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders
, i
));
3009 tr_ptrArrayDestruct (&folders
, tr_free
);
3010 tr_ptrArrayDestruct (&files
, tr_free
);
3014 tr_torrentDeleteLocalData (tr_torrent
* tor
, tr_fileFunc func
)
3016 assert (tr_isTorrent (tor
));
3021 /* close all the files because we're about to delete them */
3022 tr_cacheFlushTorrent (tor
->session
->cache
, tor
);
3023 tr_fdTorrentClose (tor
->session
, tor
->uniqueId
);
3025 deleteLocalData (tor
, func
);
3034 bool move_from_old_location
;
3035 volatile int * setme_state
;
3036 volatile double * setme_progress
;
3042 setLocation (void * vdata
)
3045 struct LocationData
* data
= vdata
;
3046 tr_torrent
* tor
= data
->tor
;
3047 const bool do_move
= data
->move_from_old_location
;
3048 const char * location
= data
->location
;
3049 double bytesHandled
= 0;
3050 tr_torrentLock (tor
);
3052 assert (tr_isTorrent (tor
));
3054 tr_logAddDebug ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
3055 tr_torrentName (tor
), tor
->currentDir
, location
);
3057 tr_mkdirp (location
, 0777);
3059 if (!tr_is_same_file (location
, tor
->currentDir
))
3063 /* bad idea to move files while they're being verified... */
3064 tr_verifyRemove (tor
);
3066 /* try to move the files.
3067 * FIXME: there are still all kinds of nasty cases, like what
3068 * if the target directory runs out of space halfway through... */
3069 for (i
=0; !err
&& i
<tor
->info
.fileCount
; ++i
)
3072 const char * oldbase
;
3073 const tr_file
* f
= &tor
->info
.files
[i
];
3075 if (tr_torrentFindFile2 (tor
, i
, &oldbase
, &sub
, NULL
))
3077 char * oldpath
= tr_buildPath (oldbase
, sub
, NULL
);
3078 char * newpath
= tr_buildPath (location
, sub
, NULL
);
3080 tr_logAddDebug ("Found file #%d: %s", (int)i
, oldpath
);
3082 if (do_move
&& !tr_is_same_file (oldpath
, newpath
))
3084 bool renamed
= false;
3086 tr_logAddTorInfo (tor
, "moving \"%s\" to \"%s\"", oldpath
, newpath
);
3087 if (tr_moveFile (oldpath
, newpath
, &renamed
))
3090 tr_logAddTorErr (tor
, "error moving \"%s\" to \"%s\": %s",
3091 oldpath
, newpath
, tr_strerror (errno
));
3100 if (data
->setme_progress
!= NULL
)
3102 bytesHandled
+= f
->length
;
3103 *data
->setme_progress
= bytesHandled
/ tor
->info
.totalSize
;
3109 /* blow away the leftover subdirectories in the old location */
3111 tr_torrentDeleteLocalData (tor
, remove
);
3113 /* set the new location and reverify */
3114 tr_torrentSetDownloadDir (tor
, location
);
3118 if (!err
&& do_move
)
3120 tr_free (tor
->incompleteDir
);
3121 tor
->incompleteDir
= NULL
;
3122 tor
->currentDir
= tor
->downloadDir
;
3125 if (data
->setme_state
!= NULL
)
3126 *data
->setme_state
= err
? TR_LOC_ERROR
: TR_LOC_DONE
;
3129 tr_torrentUnlock (tor
);
3130 tr_free (data
->location
);
3135 tr_torrentSetLocation (tr_torrent
* tor
,
3136 const char * location
,
3137 bool move_from_old_location
,
3138 volatile double * setme_progress
,
3139 volatile int * setme_state
)
3141 struct LocationData
* data
;
3143 assert (tr_isTorrent (tor
));
3145 if (setme_state
!= NULL
)
3146 *setme_state
= TR_LOC_MOVING
;
3148 if (setme_progress
!= NULL
)
3149 *setme_progress
= 0;
3151 /* run this in the libtransmission thread */
3152 data
= tr_new (struct LocationData
, 1);
3154 data
->location
= tr_strdup (location
);
3155 data
->move_from_old_location
= move_from_old_location
;
3156 data
->setme_state
= setme_state
;
3157 data
->setme_progress
= setme_progress
;
3158 tr_runInEventThread (tor
->session
, setLocation
, data
);
3166 tr_torrentFileCompleted (tr_torrent
* tor
, tr_file_index_t fileIndex
)
3170 const tr_info
* inf
= &tor
->info
;
3171 const tr_file
* f
= &inf
->files
[fileIndex
];
3173 const tr_piece
* pend
;
3174 const time_t now
= tr_time ();
3176 /* close the file so that we can reopen in read-only mode as needed */
3177 tr_cacheFlushFile (tor
->session
->cache
, tor
, fileIndex
);
3178 tr_fdFileClose (tor
->session
, tor
, fileIndex
);
3180 /* now that the file is complete and closed, we can start watching its
3181 * mtime timestamp for changes to know if we need to reverify pieces */
3182 for (p
=&inf
->pieces
[f
->firstPiece
], pend
=&inf
->pieces
[f
->lastPiece
]; p
!=pend
; ++p
)
3183 p
->timeChecked
= now
;
3185 /* if the torrent's current filename isn't the same as the one in the
3186 * metadata -- for example, if it had the ".part" suffix appended to
3187 * it until now -- then rename it to match the one in the metadata */
3188 if (tr_torrentFindFile2 (tor
, fileIndex
, &base
, &sub
, NULL
))
3190 if (strcmp (sub
, f
->name
))
3192 char * oldpath
= tr_buildPath (base
, sub
, NULL
);
3193 char * newpath
= tr_buildPath (base
, f
->name
, NULL
);
3195 if (tr_rename (oldpath
, newpath
))
3196 tr_logAddTorErr (tor
, "Error moving \"%s\" to \"%s\": %s", oldpath
, newpath
, tr_strerror (errno
));
3207 tr_torrentPieceCompleted (tr_torrent
* tor
, tr_piece_index_t pieceIndex
)
3211 tr_peerMgrPieceCompleted (tor
, pieceIndex
);
3213 /* if this piece completes any file, invoke the fileCompleted func for it */
3214 for (i
=0; i
<tor
->info
.fileCount
; ++i
)
3216 const tr_file
* file
= &tor
->info
.files
[i
];
3218 if ((file
->firstPiece
<= pieceIndex
) && (pieceIndex
<= file
->lastPiece
))
3219 if (tr_cpFileIsComplete (&tor
->completion
, i
))
3220 tr_torrentFileCompleted (tor
, i
);
3225 tr_torrentGotBlock (tr_torrent
* tor
, tr_block_index_t block
)
3227 const bool block_is_new
= !tr_cpBlockIsComplete (&tor
->completion
, block
);
3229 assert (tr_isTorrent (tor
));
3230 assert (tr_amInEventThread (tor
->session
));
3236 tr_cpBlockAdd (&tor
->completion
, block
);
3237 tr_torrentSetDirty (tor
);
3239 p
= tr_torBlockPiece (tor
, block
);
3240 if (tr_cpPieceIsComplete (&tor
->completion
, p
))
3242 tr_logAddTorDbg (tor
, "[LAZY] checking just-completed piece %zu", (size_t)p
);
3244 if (tr_torrentCheckPiece (tor
, p
))
3246 tr_torrentPieceCompleted (tor
, p
);
3250 const uint32_t n
= tr_torPieceCountBytes (tor
, p
);
3251 tr_logAddTorErr (tor
, _("Piece %"PRIu32
", which was just downloaded, failed its checksum test"), p
);
3252 tor
->corruptCur
+= n
;
3253 tor
->downloadedCur
-= MIN (tor
->downloadedCur
, n
);
3254 tr_peerMgrGotBadPiece (tor
, p
);
3260 const uint32_t n
= tr_torBlockCountBytes (tor
, block
);
3261 tor
->downloadedCur
-= MIN (tor
->downloadedCur
, n
);
3262 tr_logAddTorDbg (tor
, "we have this block already...");
3271 tr_torrentFindFile2 (const tr_torrent
* tor
, tr_file_index_t fileNum
,
3272 const char ** base
, char ** subpath
, time_t * mtime
)
3275 const tr_file
* file
;
3276 const char * b
= NULL
;
3277 const char * s
= NULL
;
3279 assert (tr_isTorrent (tor
));
3280 assert (fileNum
< tor
->info
.fileCount
);
3282 file
= &tor
->info
.files
[fileNum
];
3284 /* look in the download dir... */
3287 char * filename
= tr_buildPath (tor
->downloadDir
, file
->name
, NULL
);
3288 if (tr_fileExists (filename
, mtime
))
3290 b
= tor
->downloadDir
;
3296 /* look in the incomplete dir... */
3297 if ((b
== NULL
) && (tor
->incompleteDir
!= NULL
))
3299 char * filename
= tr_buildPath (tor
->incompleteDir
, file
->name
, NULL
);
3300 if (tr_fileExists (filename
, mtime
))
3302 b
= tor
->incompleteDir
;
3309 part
= tr_torrentBuildPartial (tor
, fileNum
);
3311 /* look for a .part file in the incomplete dir... */
3312 if ((b
== NULL
) && (tor
->incompleteDir
!= NULL
))
3314 char * filename
= tr_buildPath (tor
->incompleteDir
, part
, NULL
);
3315 if (tr_fileExists (filename
, mtime
))
3317 b
= tor
->incompleteDir
;
3323 /* look for a .part file in the download dir... */
3326 char * filename
= tr_buildPath (tor
->downloadDir
, part
, NULL
);
3327 if (tr_fileExists (filename
, mtime
))
3329 b
= tor
->downloadDir
;
3335 /* return the results */
3338 if (subpath
!= NULL
)
3339 *subpath
= tr_strdup (s
);
3347 tr_torrentFindFile (const tr_torrent
* tor
, tr_file_index_t fileNum
)
3353 if (tr_torrentFindFile2 (tor
, fileNum
, &base
, &subpath
, NULL
))
3355 ret
= tr_buildPath (base
, subpath
, NULL
);
3362 /* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3364 refreshCurrentDir (tr_torrent
* tor
)
3366 const char * dir
= NULL
;
3368 if (tor
->incompleteDir
== NULL
)
3369 dir
= tor
->downloadDir
;
3370 else if (!tr_torrentHasMetadata (tor
)) /* no files to find */
3371 dir
= tor
->incompleteDir
;
3372 else if (!tr_torrentFindFile2 (tor
, 0, &dir
, NULL
, NULL
))
3373 dir
= tor
->incompleteDir
;
3375 assert (dir
!= NULL
);
3376 assert ((dir
== tor
->downloadDir
) || (dir
== tor
->incompleteDir
));
3377 tor
->currentDir
= dir
;
3381 tr_torrentBuildPartial (const tr_torrent
* tor
, tr_file_index_t fileNum
)
3383 return tr_strdup_printf ("%s.part", tor
->info
.files
[fileNum
].name
);
3391 compareTorrentByQueuePosition (const void * va
, const void * vb
)
3393 const tr_torrent
* a
= * (const tr_torrent
**) va
;
3394 const tr_torrent
* b
= * (const tr_torrent
**) vb
;
3396 return a
->queuePosition
- b
->queuePosition
;
3401 queueIsSequenced (tr_session
* session
)
3406 tr_torrent
** torrents
;
3409 torrents
= tr_sessionGetTorrents (session
, &n
);
3410 qsort (torrents
, n
, sizeof (tr_torrent
*), compareTorrentByQueuePosition
);
3413 fprintf (stderr
, "%s", "queue: ");
3415 fprintf (stderr
, "%d ", tmp
[i
]->queuePosition
);
3416 fputc ('\n', stderr
);
3420 is_sequenced
= true;
3421 for (i
=0; is_sequenced
&& i
<n
; ++i
)
3422 if (torrents
[i
]->queuePosition
!= i
)
3423 is_sequenced
= false;
3426 return is_sequenced
;
3431 tr_torrentGetQueuePosition (const tr_torrent
* tor
)
3433 return tor
->queuePosition
;
3437 tr_torrentSetQueuePosition (tr_torrent
* tor
, int pos
)
3441 const int old_pos
= tor
->queuePosition
;
3442 const time_t now
= tr_time ();
3447 tor
->queuePosition
= -1;
3450 while ((walk
= tr_torrentNext (tor
->session
, walk
)))
3454 if ((old_pos
<= walk
->queuePosition
) && (walk
->queuePosition
<= pos
))
3456 walk
->queuePosition
--;
3457 walk
->anyDate
= now
;
3463 if ((pos
<= walk
->queuePosition
) && (walk
->queuePosition
< old_pos
))
3465 walk
->queuePosition
++;
3466 walk
->anyDate
= now
;
3470 if (back
< walk
->queuePosition
)
3472 back
= walk
->queuePosition
;
3476 tor
->queuePosition
= MIN (pos
, (back
+1));
3479 assert (queueIsSequenced (tor
->session
));
3483 tr_torrentsQueueMoveTop (tr_torrent
** torrents_in
, int n
)
3486 tr_torrent
** torrents
= tr_memdup (torrents_in
, sizeof (tr_torrent
*) * n
);
3487 qsort (torrents
, n
, sizeof (tr_torrent
*), compareTorrentByQueuePosition
);
3488 for (i
=n
-1; i
>=0; --i
)
3489 tr_torrentSetQueuePosition (torrents
[i
], 0);
3494 tr_torrentsQueueMoveUp (tr_torrent
** torrents_in
, int n
)
3497 tr_torrent
** torrents
;
3499 torrents
= tr_memdup (torrents_in
, sizeof (tr_torrent
*) * n
);
3500 qsort (torrents
, n
, sizeof (tr_torrent
*), compareTorrentByQueuePosition
);
3502 tr_torrentSetQueuePosition (torrents
[i
], torrents
[i
]->queuePosition
- 1);
3508 tr_torrentsQueueMoveDown (tr_torrent
** torrents_in
, int n
)
3511 tr_torrent
** torrents
;
3513 torrents
= tr_memdup (torrents_in
, sizeof (tr_torrent
*) * n
);
3514 qsort (torrents
, n
, sizeof (tr_torrent
*), compareTorrentByQueuePosition
);
3515 for (i
=n
-1; i
>=0; --i
)
3516 tr_torrentSetQueuePosition (torrents
[i
], torrents
[i
]->queuePosition
+ 1);
3522 tr_torrentsQueueMoveBottom (tr_torrent
** torrents_in
, int n
)
3525 tr_torrent
** torrents
;
3527 torrents
= tr_memdup (torrents_in
, sizeof (tr_torrent
*) * n
);
3528 qsort (torrents
, n
, sizeof (tr_torrent
*), compareTorrentByQueuePosition
);
3530 tr_torrentSetQueuePosition (torrents
[i
], INT_MAX
);
3536 torrentSetQueued (tr_torrent
* tor
, bool queued
)
3538 assert (tr_isTorrent (tor
));
3539 assert (tr_isBool (queued
));
3541 if (tr_torrentIsQueued (tor
) != queued
)
3543 tor
->isQueued
= queued
;
3544 tor
->anyDate
= tr_time ();
3549 tr_torrentSetQueueStartCallback (tr_torrent
* torrent
, void (*callback
)(tr_torrent
*, void *), void * user_data
)
3551 torrent
->queue_started_callback
= callback
;
3552 torrent
->queue_started_user_data
= user_data
;
3563 renameArgsAreValid (const char * oldpath
, const char * newname
)
3565 return (oldpath
&& *oldpath
)
3566 && (newname
&& *newname
)
3567 && (strcmp (newname
, "."))
3568 && (strcmp (newname
, ".."))
3569 && (strchr (newname
, TR_PATH_DELIMITER
) == NULL
);
3572 static tr_file_index_t
*
3573 renameFindAffectedFiles (tr_torrent
* tor
, const char * oldpath
, size_t * setme_n
)
3578 tr_file_index_t
* indices
= tr_new0 (tr_file_index_t
, tor
->info
.fileCount
);
3581 oldpath_len
= strlen (oldpath
);
3582 for (i
=0; i
!=tor
->info
.fileCount
; ++i
)
3584 const char * name
= tor
->info
.files
[i
].name
;
3585 const size_t len
= strlen (name
);
3586 if ((len
>= oldpath_len
) && !memcmp (oldpath
, name
, oldpath_len
))
3595 renamePath (tr_torrent
* tor
,
3596 const char * oldpath
,
3597 const char * newname
)
3603 if (!tr_torrentIsSeed(tor
) && (tor
->incompleteDir
!= NULL
))
3604 base
= tor
->incompleteDir
;
3606 base
= tor
->downloadDir
;
3608 src
= tr_buildPath (base
, oldpath
, NULL
);
3609 if (!tr_fileExists (src
, NULL
)) /* check for it as a partial */
3611 char * tmp
= tr_strdup_printf ("%s.part", src
);
3616 if (tr_fileExists (src
, NULL
))
3620 char * parent
= tr_dirname (src
);
3623 if (tr_str_has_suffix (src
, ".part"))
3624 tgt
= tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR
"%s.part", parent
, newname
);
3626 tgt
= tr_buildPath (parent
, newname
, NULL
);
3629 tgt_exists
= tr_fileExists (tgt
, NULL
);
3637 rv
= tr_rename (src
, tgt
);
3653 renameTorrentFileString (tr_torrent
* tor
,
3654 const char * oldpath
,
3655 const char * newname
,
3656 tr_file_index_t fileIndex
)
3659 tr_file
* file
= &tor
->info
.files
[fileIndex
];
3660 const size_t oldpath_len
= strlen (oldpath
);
3662 if (strchr (oldpath
, TR_PATH_DELIMITER
) == NULL
)
3664 if (oldpath_len
>= strlen(file
->name
))
3665 name
= tr_buildPath (newname
, NULL
);
3667 name
= tr_buildPath (newname
, file
->name
+ oldpath_len
+ 1, NULL
);
3671 char * tmp
= tr_dirname (oldpath
);
3673 if (oldpath_len
>= strlen(file
->name
))
3674 name
= tr_buildPath (tmp
, newname
, NULL
);
3676 name
= tr_buildPath (tmp
, newname
, file
->name
+ oldpath_len
+ 1, NULL
);
3681 if (!strcmp (file
->name
, name
))
3687 tr_free (file
->name
);
3689 file
->is_renamed
= true;
3698 tr_torrent_rename_done_func
* callback
;
3699 void * callback_user_data
;
3703 torrentRenamePath (void * vdata
)
3706 struct rename_data
* data
= vdata
;
3707 tr_torrent
* const tor
= data
->tor
;
3708 const char * const oldpath
= data
->oldpath
;
3709 const char * const newname
= data
->newname
;
3715 assert (tr_isTorrent (tor
));
3717 if (!renameArgsAreValid (oldpath
, newname
))
3724 tr_file_index_t
* file_indices
;
3726 file_indices
= renameFindAffectedFiles (tor
, oldpath
, &n
);
3735 error
= renamePath (tor
, oldpath
, newname
);
3739 /* update tr_info.files */
3741 renameTorrentFileString(tor
, oldpath
, newname
, file_indices
[i
]);
3743 /* update tr_info.name if user changed the toplevel */
3744 if ((n
== tor
->info
.fileCount
) && (strchr(oldpath
,'/')==NULL
))
3746 tr_free (tor
->info
.name
);
3747 tor
->info
.name
= tr_strdup (newname
);
3750 tr_torrentSetDirty (tor
);
3754 tr_free (file_indices
);
3762 tor
->anyDate
= tr_time ();
3765 if (data
->callback
!= NULL
)
3766 (*data
->callback
)(tor
, data
->oldpath
, data
->newname
, error
, data
->callback_user_data
);
3769 tr_free (data
->oldpath
);
3770 tr_free (data
->newname
);
3775 tr_torrentRenamePath (tr_torrent
* tor
,
3776 const char * oldpath
,
3777 const char * newname
,
3778 tr_torrent_rename_done_func callback
,
3779 void * callback_user_data
)
3781 struct rename_data
* data
;
3783 data
= tr_new0 (struct rename_data
, 1);
3785 data
->oldpath
= tr_strdup (oldpath
);
3786 data
->newname
= tr_strdup (newname
);
3787 data
->callback
= callback
;
3788 data
->callback_user_data
= callback_user_data
;
3790 tr_runInEventThread (tor
->session
, torrentRenamePath
, data
);