fix corruption: torrentVerify on completion (#4178)

* torrentVerify on completion

* Make torrent verify on completion configurable via settings

* code review: replacing tr_verify_complete_mode with bool

* code review: sorting torrent_complete_verify_enabled with bool

* Update Application.cc

* code review: avoiding `session->onTorrentCompletenessChanged` before verification completion
This commit is contained in:
Cœur
2025-11-10 21:16:15 +01:00
committed by GitHub
parent d0e6b5519d
commit 78238ec3de
10 changed files with 60 additions and 5 deletions

View File

@@ -1359,6 +1359,10 @@ void Application::Impl::on_prefs_changed(tr_quark const key)
tr_sessionSetDeleteSource(tr, gtr_pref_flag_get(key)); tr_sessionSetDeleteSource(tr, gtr_pref_flag_get(key));
break; break;
case TR_KEY_torrent_complete_verify_enabled:
tr_sessionSetCompleteVerifyEnabled(tr, gtr_pref_flag_get(key));
break;
default: default:
break; break;
} }

View File

@@ -386,6 +386,7 @@ auto constexpr MyStatic = std::array<std::string_view, TR_N_KEYS>{
"torrent-complete-notification-enabled"sv, "torrent-complete-notification-enabled"sv,
"torrent-complete-sound-command"sv, "torrent-complete-sound-command"sv,
"torrent-complete-sound-enabled"sv, "torrent-complete-sound-enabled"sv,
"torrent-complete-verify-enabled"sv,
"torrent-duplicate"sv, "torrent-duplicate"sv,
"torrent-get"sv, "torrent-get"sv,
"torrent-set"sv, "torrent-set"sv,

View File

@@ -388,6 +388,7 @@ enum // NOLINT(performance-enum-size)
TR_KEY_torrent_complete_notification_enabled, TR_KEY_torrent_complete_notification_enabled,
TR_KEY_torrent_complete_sound_command, TR_KEY_torrent_complete_sound_command,
TR_KEY_torrent_complete_sound_enabled, TR_KEY_torrent_complete_sound_enabled,
TR_KEY_torrent_complete_verify_enabled,
TR_KEY_torrent_duplicate, TR_KEY_torrent_duplicate,
TR_KEY_torrent_get, TR_KEY_torrent_get,
TR_KEY_torrent_set, TR_KEY_torrent_set,

View File

@@ -1659,6 +1659,15 @@ size_t tr_sessionGetCacheLimit_MB(tr_session const* session)
// --- // ---
void tr_sessionSetCompleteVerifyEnabled(tr_session* session, bool enabled)
{
TR_ASSERT(session != nullptr);
session->settings_.torrent_complete_verify_enabled = enabled;
}
// ---
void tr_session::setDefaultTrackers(std::string_view trackers) void tr_session::setDefaultTrackers(std::string_view trackers)
{ {
auto const oldval = default_trackers_; auto const oldval = default_trackers_;

View File

@@ -416,6 +416,7 @@ public:
bool speed_limit_down_enabled = false; bool speed_limit_down_enabled = false;
bool speed_limit_up_enabled = false; bool speed_limit_up_enabled = false;
bool tcp_enabled = true; bool tcp_enabled = true;
bool torrent_complete_verify_enabled = false;
bool utp_enabled = true; bool utp_enabled = true;
double ratio_limit = 2.0; double ratio_limit = 2.0;
size_t cache_size_mbytes = 4U; size_t cache_size_mbytes = 4U;
@@ -513,6 +514,7 @@ public:
{ TR_KEY_start_added_torrents, &should_start_added_torrents }, { TR_KEY_start_added_torrents, &should_start_added_torrents },
{ TR_KEY_tcp_enabled, &tcp_enabled }, { TR_KEY_tcp_enabled, &tcp_enabled },
{ TR_KEY_torrent_added_verify_mode, &torrent_added_verify_mode }, { TR_KEY_torrent_added_verify_mode, &torrent_added_verify_mode },
{ TR_KEY_torrent_complete_verify_enabled, &torrent_complete_verify_enabled },
{ TR_KEY_trash_original_torrent_files, &should_delete_source_torrents }, { TR_KEY_trash_original_torrent_files, &should_delete_source_torrents },
{ TR_KEY_umask, &umask }, { TR_KEY_umask, &umask },
{ TR_KEY_upload_slots_per_torrent, &upload_slots_per_torrent }, { TR_KEY_upload_slots_per_torrent, &upload_slots_per_torrent },
@@ -963,6 +965,11 @@ public:
return settings().torrent_added_verify_mode == TR_VERIFY_ADDED_FULL; return settings().torrent_added_verify_mode == TR_VERIFY_ADDED_FULL;
} }
[[nodiscard]] constexpr auto shouldFullyVerifyCompleteTorrents() const noexcept
{
return settings().torrent_complete_verify_enabled;
}
[[nodiscard]] constexpr auto shouldDeleteSource() const noexcept [[nodiscard]] constexpr auto shouldDeleteSource() const noexcept
{ {
return settings().should_delete_source_torrents; return settings().should_delete_source_torrents;
@@ -1207,6 +1214,7 @@ private:
friend void tr_sessionSetAntiBruteForceEnabled(tr_session* session, bool is_enabled); friend void tr_sessionSetAntiBruteForceEnabled(tr_session* session, bool is_enabled);
friend void tr_sessionSetAntiBruteForceThreshold(tr_session* session, int max_bad_requests); friend void tr_sessionSetAntiBruteForceThreshold(tr_session* session, int max_bad_requests);
friend void tr_sessionSetCacheLimit_MB(tr_session* session, size_t mbytes); friend void tr_sessionSetCacheLimit_MB(tr_session* session, size_t mbytes);
friend void tr_sessionSetCompleteVerifyEnabled(tr_session* session, bool enabled);
friend void tr_sessionSetDHTEnabled(tr_session* session, bool enabled); friend void tr_sessionSetDHTEnabled(tr_session* session, bool enabled);
friend void tr_sessionSetDeleteSource(tr_session* session, bool delete_source); friend void tr_sessionSetDeleteSource(tr_session* session, bool delete_source);
friend void tr_sessionSetEncryption(tr_session* session, tr_encryption_mode mode); friend void tr_sessionSetEncryption(tr_session* session, tr_encryption_mode mode);

View File

@@ -1786,6 +1786,12 @@ void tr_torrent::recheck_completeness()
bool const recent_change = bytes_downloaded_.during_this_session() != 0U; bool const recent_change = bytes_downloaded_.during_this_session() != 0U;
bool const was_running = is_running(); bool const was_running = is_running();
if (new_completeness != TR_LEECH && was_running && session->shouldFullyVerifyCompleteTorrents())
{
tr_torrentVerify(this);
return;
}
tr_logAddTraceTor( tr_logAddTraceTor(
this, this,
fmt::format( fmt::format(
@@ -1794,10 +1800,11 @@ void tr_torrent::recheck_completeness()
get_completion_string(new_completeness))); get_completion_string(new_completeness)));
completeness_ = new_completeness; completeness_ = new_completeness;
session->close_torrent_files(id());
if (is_done()) if (is_done())
{ {
session->close_torrent_files(id());
if (recent_change) if (recent_change)
{ {
// https://www.bittorrent.org/beps/bep_0003.html // https://www.bittorrent.org/beps/bep_0003.html

View File

@@ -635,7 +635,7 @@ size_t tr_sessionGetQueueStalledMinutes(tr_session const* session);
Stalled torrents are left running but are not counted by `tr_sessionGetQueueSize()`. */ Stalled torrents are left running but are not counted by `tr_sessionGetQueueSize()`. */
void tr_sessionSetQueueStalledMinutes(tr_session* session, int minutes); void tr_sessionSetQueueStalledMinutes(tr_session* session, int minutes);
/** @return true if we're torrents idle for over N minutes will be flagged as 'stalled' */ /** @return true if torrents idle for over N minutes will be flagged as 'stalled' */
bool tr_sessionGetQueueStalledEnabled(tr_session const* session); bool tr_sessionGetQueueStalledEnabled(tr_session const* session);
/** @brief Set whether or not to count torrents idle for over N minutes as 'stalled' */ /** @brief Set whether or not to count torrents idle for over N minutes as 'stalled' */
@@ -646,10 +646,14 @@ void tr_sessionSetQueueStartCallback(tr_session* session, void (*callback)(tr_se
// --- // ---
/** @brief Set whether or not to verify data when torrent download is complete */
void tr_sessionSetCompleteVerifyEnabled(tr_session* session, bool enabled);
// ---
/** /**
* Load all the torrents in the session's torrent folder. * Load all the torrents in the session's torrent folder.
* This can be used at startup to kickstart all the torrents * This can be used at startup to kickstart all the torrents from the previous session.
* from the previous session.
* *
* @return the number of torrents in the session * @return the number of torrents in the session
*/ */

View File

@@ -1076,6 +1076,17 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<button verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Evu-rf-BPM">
<rect key="frame" x="76" y="99" width="257" height="18"/>
<buttonCell key="cell" type="check" title="Verify data when download completes" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="wM8-cD-WAB">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="setVerifyDataOnCompletion:" target="-2" id="bST-Mi-KsG"/>
<binding destination="365" name="value" keyPath="values.VerifyDataOnCompletion" id="zRv-3f-byJ"/>
</connections>
</button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="265" firstAttribute="centerY" secondItem="267" secondAttribute="centerY" id="1ye-ZZ-IHj"/> <constraint firstItem="265" firstAttribute="centerY" secondItem="267" secondAttribute="centerY" id="1ye-ZZ-IHj"/>
@@ -1084,9 +1095,9 @@
<constraint firstItem="264" firstAttribute="leading" secondItem="267" secondAttribute="trailing" constant="8" symbolic="YES" id="4kz-Pg-Gs1"/> <constraint firstItem="264" firstAttribute="leading" secondItem="267" secondAttribute="trailing" constant="8" symbolic="YES" id="4kz-Pg-Gs1"/>
<constraint firstItem="607" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="5Ok-rt-vHk"/> <constraint firstItem="607" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="5Ok-rt-vHk"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="606" secondAttribute="trailing" constant="20" symbolic="YES" id="5eC-JW-9sl"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="606" secondAttribute="trailing" constant="20" symbolic="YES" id="5eC-JW-9sl"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Evu-rf-BPM" secondAttribute="trailing" constant="20" symbolic="YES" id="5fw-Hm-Ccy"/>
<constraint firstItem="1959" firstAttribute="centerY" secondItem="1958" secondAttribute="centerY" id="68T-YZ-SBC"/> <constraint firstItem="1959" firstAttribute="centerY" secondItem="1958" secondAttribute="centerY" id="68T-YZ-SBC"/>
<constraint firstItem="263" firstAttribute="leading" secondItem="257" secondAttribute="trailing" constant="4" id="6mh-gV-D2p"/> <constraint firstItem="263" firstAttribute="leading" secondItem="257" secondAttribute="trailing" constant="4" id="6mh-gV-D2p"/>
<constraint firstItem="1298" firstAttribute="top" secondItem="639" secondAttribute="bottom" constant="20" symbolic="YES" id="9My-zU-rE4"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="1298" secondAttribute="trailing" constant="20" symbolic="YES" id="Ac7-Sc-pnt"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="1298" secondAttribute="trailing" constant="20" symbolic="YES" id="Ac7-Sc-pnt"/>
<constraint firstItem="606" firstAttribute="centerY" secondItem="607" secondAttribute="centerY" id="Ao0-Ui-5T8"/> <constraint firstItem="606" firstAttribute="centerY" secondItem="607" secondAttribute="centerY" id="Ao0-Ui-5T8"/>
<constraint firstItem="638" firstAttribute="leading" secondItem="636" secondAttribute="trailing" constant="4" id="Ap1-e8-iyn"/> <constraint firstItem="638" firstAttribute="leading" secondItem="636" secondAttribute="trailing" constant="4" id="Ap1-e8-iyn"/>
@@ -1099,6 +1110,7 @@
<constraint firstItem="1959" firstAttribute="leading" secondItem="1958" secondAttribute="trailing" constant="4" id="GBM-Dv-fBO"/> <constraint firstItem="1959" firstAttribute="leading" secondItem="1958" secondAttribute="trailing" constant="4" id="GBM-Dv-fBO"/>
<constraint firstItem="604" firstAttribute="centerY" secondItem="607" secondAttribute="centerY" id="GhR-LK-fsr"/> <constraint firstItem="604" firstAttribute="centerY" secondItem="607" secondAttribute="centerY" id="GhR-LK-fsr"/>
<constraint firstItem="1299" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="H9J-7l-cRN"/> <constraint firstItem="1299" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="H9J-7l-cRN"/>
<constraint firstItem="1301" firstAttribute="top" secondItem="Evu-rf-BPM" secondAttribute="bottom" constant="20" id="HFz-O8-K60"/>
<constraint firstItem="337" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="I19-F3-F5L"/> <constraint firstItem="337" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="I19-F3-F5L"/>
<constraint firstItem="1969" firstAttribute="leading" secondItem="1959" secondAttribute="trailing" constant="4" id="K65-ud-KmG"/> <constraint firstItem="1969" firstAttribute="leading" secondItem="1959" secondAttribute="trailing" constant="4" id="K65-ud-KmG"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="268" secondAttribute="trailing" constant="20" symbolic="YES" id="Kd0-RO-CDI"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="268" secondAttribute="trailing" constant="20" symbolic="YES" id="Kd0-RO-CDI"/>
@@ -1117,6 +1129,7 @@
<constraint firstItem="2043" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="WV1-Zh-WyT"/> <constraint firstItem="2043" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="WV1-Zh-WyT"/>
<constraint firstItem="264" firstAttribute="centerY" secondItem="267" secondAttribute="centerY" id="XGp-eS-3xI"/> <constraint firstItem="264" firstAttribute="centerY" secondItem="267" secondAttribute="centerY" id="XGp-eS-3xI"/>
<constraint firstItem="257" firstAttribute="leading" secondItem="337" secondAttribute="trailing" constant="4" id="XHe-qE-pza"/> <constraint firstItem="257" firstAttribute="leading" secondItem="337" secondAttribute="trailing" constant="4" id="XHe-qE-pza"/>
<constraint firstItem="Evu-rf-BPM" firstAttribute="top" secondItem="639" secondAttribute="bottom" constant="4" id="ZMf-g5-ItF"/>
<constraint firstItem="267" firstAttribute="leading" secondItem="4cS-54-dlc" secondAttribute="leading" id="ZXE-R2-LbH"/> <constraint firstItem="267" firstAttribute="leading" secondItem="4cS-54-dlc" secondAttribute="leading" id="ZXE-R2-LbH"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="265" secondAttribute="trailing" constant="20" symbolic="YES" id="a3K-zJ-Yl9"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="265" secondAttribute="trailing" constant="20" symbolic="YES" id="a3K-zJ-Yl9"/>
<constraint firstItem="268" firstAttribute="leading" secondItem="264" secondAttribute="leading" constant="20" id="aMi-iz-dj7"/> <constraint firstItem="268" firstAttribute="leading" secondItem="264" secondAttribute="leading" constant="20" id="aMi-iz-dj7"/>
@@ -1135,6 +1148,7 @@
<constraint firstItem="638" firstAttribute="centerY" secondItem="639" secondAttribute="centerY" id="lZM-1c-Rel"/> <constraint firstItem="638" firstAttribute="centerY" secondItem="639" secondAttribute="centerY" id="lZM-1c-Rel"/>
<constraint firstItem="1300" firstAttribute="top" secondItem="1299" secondAttribute="bottom" constant="-18" id="muf-Lk-n7G"/> <constraint firstItem="1300" firstAttribute="top" secondItem="1299" secondAttribute="bottom" constant="-18" id="muf-Lk-n7G"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="638" secondAttribute="trailing" constant="20" symbolic="YES" id="nga-Zx-3XL"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="638" secondAttribute="trailing" constant="20" symbolic="YES" id="nga-Zx-3XL"/>
<constraint firstItem="Evu-rf-BPM" firstAttribute="leading" secondItem="264" secondAttribute="leading" id="ocE-4C-4Pc"/>
<constraint firstItem="1300" firstAttribute="centerY" secondItem="1299" secondAttribute="centerY" id="pg2-8t-utk"/> <constraint firstItem="1300" firstAttribute="centerY" secondItem="1299" secondAttribute="centerY" id="pg2-8t-utk"/>
<constraint firstItem="263" firstAttribute="centerY" secondItem="337" secondAttribute="centerY" id="pnn-34-PEE"/> <constraint firstItem="263" firstAttribute="centerY" secondItem="337" secondAttribute="centerY" id="pnn-34-PEE"/>
<constraint firstAttribute="bottom" secondItem="2048" secondAttribute="bottom" constant="4" id="qBS-oe-tyG"/> <constraint firstAttribute="bottom" secondItem="2048" secondAttribute="bottom" constant="4" id="qBS-oe-tyG"/>

View File

@@ -497,6 +497,7 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
TR_KEY_incomplete_dir, TR_KEY_incomplete_dir,
[_fDefaults stringForKey:@"IncompleteDownloadFolder"].stringByExpandingTildeInPath.UTF8String); [_fDefaults stringForKey:@"IncompleteDownloadFolder"].stringByExpandingTildeInPath.UTF8String);
tr_variantDictAddBool(&settings, TR_KEY_incomplete_dir_enabled, [_fDefaults boolForKey:@"UseIncompleteDownloadFolder"]); tr_variantDictAddBool(&settings, TR_KEY_incomplete_dir_enabled, [_fDefaults boolForKey:@"UseIncompleteDownloadFolder"]);
tr_variantDictAddBool(&settings, TR_KEY_torrent_complete_verify_enabled, [_fDefaults boolForKey:@"VerifyDataOnCompletion"]);
tr_variantDictAddBool(&settings, TR_KEY_lpd_enabled, [_fDefaults boolForKey:@"LocalPeerDiscoveryGlobal"]); tr_variantDictAddBool(&settings, TR_KEY_lpd_enabled, [_fDefaults boolForKey:@"LocalPeerDiscoveryGlobal"]);
tr_variantDictAddInt(&settings, TR_KEY_message_level, TR_LOG_DEBUG); tr_variantDictAddInt(&settings, TR_KEY_message_level, TR_LOG_DEBUG);
tr_variantDictAddInt(&settings, TR_KEY_peer_limit_global, [_fDefaults integerForKey:@"PeersTotal"]); tr_variantDictAddInt(&settings, TR_KEY_peer_limit_global, [_fDefaults integerForKey:@"PeersTotal"]);

View File

@@ -916,6 +916,12 @@ static NSString* const kWebUIURLFormat = @"http://localhost:%ld/";
[self updateShowAddMagnetWindowField]; [self updateShowAddMagnetWindowField];
} }
- (IBAction)setVerifyDataOnCompletion:(id)sender
{
bool enabled = [self.fDefaults boolForKey:@"VerifyDataOnCompletion"];
tr_sessionSetCompleteVerifyEnabled(self.fHandle, enabled);
}
- (void)folderSheetShow:(id)sender - (void)folderSheetShow:(id)sender
{ {
NSOpenPanel* panel = [NSOpenPanel openPanel]; NSOpenPanel* panel = [NSOpenPanel openPanel];