From 33a7d131b4af2109d631b5abd0acd837f237c5d8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 18 Jan 2023 02:09:29 -0600 Subject: [PATCH] feat: add setting to choose between lazy-verify or full verify (#4611) --- docs/Editing-Configuration-Files.md | 1 + libtransmission/quark.cc | 3 +- libtransmission/quark.h | 1 + libtransmission/session-settings.h | 3 +- libtransmission/session.h | 5 + libtransmission/torrent.cc | 2 +- libtransmission/transmission.h | 10 ++ libtransmission/variant-converters.cc | 127 ++++++++++++++++--------- tests/libtransmission/settings-test.cc | 42 ++++++++ 9 files changed, 148 insertions(+), 46 deletions(-) diff --git a/docs/Editing-Configuration-Files.md b/docs/Editing-Configuration-Files.md index a93a88635..645ebda68 100644 --- a/docs/Editing-Configuration-Files.md +++ b/docs/Editing-Configuration-Files.md @@ -84,6 +84,7 @@ Here is a sample of the three basic types: respectively Boolean, Number and Stri * **script-torrent-done-filename:** String (default = "") Path to script. * **script-torrent-done-seeding-enabled:** Boolean (default = false) Run a script when a torrent is done seeding. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page * **script-torrent-done-seeding-filename:** String (default = "") Path to script. + * **torrent-added-verify-mode:** String ("fast", "full", default: "fast") Whether newly-added torrents' local data should be fully verified when added, or wait and verify them on-demand later. See [#2626](https://github.com/transmission/transmission/pull/2626) for more discussion. * **utp-enabled:** Boolean (default = true) Enable [Micro Transport Protocol (µTP)](https://en.wikipedia.org/wiki/Micro_Transport_Protocol) #### Peers diff --git a/libtransmission/quark.cc b/libtransmission/quark.cc index 824a43857..0fa0d4528 100644 --- a/libtransmission/quark.cc +++ b/libtransmission/quark.cc @@ -18,7 +18,7 @@ using namespace std::literals; namespace { -auto constexpr MyStatic = std::array{ ""sv, +auto constexpr MyStatic = std::array{ ""sv, "activeTorrentCount"sv, "activity-date"sv, "activityDate"sv, @@ -367,6 +367,7 @@ auto constexpr MyStatic = std::array{ ""sv, "torrent-added"sv, "torrent-added-notification-command"sv, "torrent-added-notification-enabled"sv, + "torrent-added-verify-mode"sv, "torrent-complete-notification-command"sv, "torrent-complete-notification-enabled"sv, "torrent-complete-sound-command"sv, diff --git a/libtransmission/quark.h b/libtransmission/quark.h index e0b84b239..48a07e9d0 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -370,6 +370,7 @@ enum TR_KEY_torrent_added, TR_KEY_torrent_added_notification_command, TR_KEY_torrent_added_notification_enabled, + TR_KEY_torrent_added_verify_mode, TR_KEY_torrent_complete_notification_command, TR_KEY_torrent_complete_notification_enabled, TR_KEY_torrent_complete_sound_command, diff --git a/libtransmission/session-settings.h b/libtransmission/session-settings.h index b43667408..3758dd21f 100644 --- a/libtransmission/session-settings.h +++ b/libtransmission/session-settings.h @@ -72,7 +72,8 @@ struct tr_variant; V(TR_KEY_trash_original_torrent_files, should_delete_source_torrents, bool, false, "") \ V(TR_KEY_umask, umask, tr_mode_t, 022, "") \ V(TR_KEY_upload_slots_per_torrent, upload_slots_per_torrent, size_t, 8U, "") \ - V(TR_KEY_utp_enabled, utp_enabled, bool, true, "") + V(TR_KEY_utp_enabled, utp_enabled, bool, true, "") \ + V(TR_KEY_torrent_added_verify_mode, torrent_added_verify_mode, tr_verify_added_mode, TR_VERIFY_ADDED_FAST, "") struct tr_session_settings { diff --git a/libtransmission/session.h b/libtransmission/session.h index 8c0acc23a..315c38352 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -707,6 +707,11 @@ public: return !settings_.should_start_added_torrents; } + [[nodiscard]] constexpr auto shouldFullyVerifyAddedTorrents() const noexcept + { + return settings_.torrent_added_verify_mode == TR_VERIFY_ADDED_FULL; + } + [[nodiscard]] constexpr auto shouldDeleteSource() const noexcept { return settings_.should_delete_source_torrents; diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc index ae5b59e50..8a7700c74 100644 --- a/libtransmission/torrent.cc +++ b/libtransmission/torrent.cc @@ -1188,7 +1188,7 @@ void torrentInit(tr_torrent* tor, tr_ctor const* ctor) opts.has_local_data = has_local_data; torrentStart(tor, opts); } - else if (isNewTorrentASeed(tor)) + else if (!session->shouldFullyVerifyAddedTorrents() && isNewTorrentASeed(tor)) { tor->completion.setHasAll(); tor->doneDate = tor->addedDate; diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 04f01325d..516694c52 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -60,6 +60,16 @@ using tr_priority_t = int8_t; #define TR_RPC_SESSION_ID_HEADER "X-Transmission-Session-Id" +enum tr_verify_added_mode +{ + // See discussion @ https://github.com/transmission/transmission/pull/2626 + // Let newly-added torrents skip upfront verify do it on-demand later. + TR_VERIFY_ADDED_FAST = 0, + + // Force torrents to be fully verified as they are added. + TR_VERIFY_ADDED_FULL = 1 +}; + enum tr_preallocation_mode { TR_PREALLOCATE_NONE = 0, diff --git a/libtransmission/variant-converters.cc b/libtransmission/variant-converters.cc index 24b67c5a9..4267e6284 100644 --- a/libtransmission/variant-converters.cc +++ b/libtransmission/variant-converters.cc @@ -14,9 +14,40 @@ using namespace std::literals; +namespace +{ +auto constexpr EncryptionKeys = std::array, 3>{ { + { "required", TR_ENCRYPTION_REQUIRED }, + { "preferred", TR_ENCRYPTION_PREFERRED }, + { "allowed", TR_CLEAR_PREFERRED }, +} }; + +auto constexpr LogKeys = std::array, 7>{ { + { "critical", TR_LOG_CRITICAL }, + { "debug", TR_LOG_DEBUG }, + { "error", TR_LOG_ERROR }, + { "info", TR_LOG_INFO }, + { "off", TR_LOG_OFF }, + { "trace", TR_LOG_TRACE }, + { "warn", TR_LOG_WARN }, +} }; + +auto constexpr PreallocationKeys = std::array, 5>{ { + { "off", TR_PREALLOCATE_NONE }, + { "none", TR_PREALLOCATE_NONE }, + { "fast", TR_PREALLOCATE_SPARSE }, + { "sparse", TR_PREALLOCATE_SPARSE }, + { "full", TR_PREALLOCATE_FULL }, +} }; + +auto constexpr VerifyModeKeys = std::array, 2>{ { + { "fast", TR_VERIFY_ADDED_FAST }, + { "full", TR_VERIFY_ADDED_FULL }, +} }; +} // namespace + namespace libtransmission { - template<> std::optional VariantConverter::load(tr_variant* src) { @@ -55,21 +86,10 @@ void VariantConverter::save(tr_variant* tgt, double const& val) // --- -namespace EncryptionHelpers -{ -// clang-format off -static auto constexpr Keys = std::array, 3>{{ - { "required", TR_ENCRYPTION_REQUIRED }, - { "preferred", TR_ENCRYPTION_PREFERRED }, - { "allowed", TR_CLEAR_PREFERRED } -}}; -// clang-format on -} // namespace EncryptionHelpers - template<> std::optional VariantConverter::load(tr_variant* src) { - using namespace EncryptionHelpers; + static constexpr auto Keys = EncryptionKeys; if (auto val = std::string_view{}; tr_variantGetStrView(src, &val)) { @@ -106,25 +126,10 @@ void VariantConverter::save(tr_variant* tgt, tr_encryption_m // --- -namespace LogLevelHelpers -{ -// clang-format off -static auto constexpr Keys = std::array, 7>{ { - { "critical", TR_LOG_CRITICAL }, - { "debug", TR_LOG_DEBUG }, - { "error", TR_LOG_ERROR }, - { "info", TR_LOG_INFO }, - { "off", TR_LOG_OFF }, - { "trace", TR_LOG_TRACE }, - { "warn", TR_LOG_WARN }, -}}; -// clang-format on -} // namespace LogLevelHelpers - template<> std::optional VariantConverter::load(tr_variant* src) { - using namespace LogLevelHelpers; + static constexpr auto Keys = LogKeys; if (auto val = std::string_view{}; tr_variantGetStrView(src, &val)) { @@ -207,23 +212,10 @@ void VariantConverter::save(tr_variant* tgt, tr_port const& val) // --- -namespace PreallocationModeHelpers -{ -// clang-format off -static auto constexpr Keys = std::array, 5>{{ - { "off", TR_PREALLOCATE_NONE }, - { "none", TR_PREALLOCATE_NONE }, - { "fast", TR_PREALLOCATE_SPARSE }, - { "sparse", TR_PREALLOCATE_SPARSE }, - { "full", TR_PREALLOCATE_FULL }, -}}; -// clang-format on -} // namespace PreallocationModeHelpers - template<> std::optional VariantConverter::load(tr_variant* src) { - using namespace PreallocationModeHelpers; + static constexpr auto Keys = PreallocationKeys; if (auto val = std::string_view{}; tr_variantGetStrView(src, &val)) { @@ -320,4 +312,53 @@ void VariantConverter::save(tr_variant* tgt, tr_tos_t const& val) tr_variantInitStr(tgt, val.toString()); } +// --- + +template<> +std::optional VariantConverter::load(tr_variant* src) +{ + static constexpr auto& Keys = VerifyModeKeys; + + if (auto val = std::string_view{}; tr_variantGetStrView(src, &val)) + { + auto const needle = tr_strlower(tr_strvStrip(val)); + + for (auto const& [name, value] : Keys) + { + if (name == needle) + { + return value; + } + } + } + + if (auto val = int64_t{}; tr_variantGetInt(src, &val)) + { + for (auto const& [name, value] : Keys) + { + if (value == val) + { + return value; + } + } + } + + return {}; +} + +template<> +void VariantConverter::save(tr_variant* tgt, tr_verify_added_mode const& val) +{ + for (auto const& [key, value] : VerifyModeKeys) + { + if (value == val) + { + tr_variantInitStrView(tgt, key); + return; + } + } + + tr_variantInitInt(tgt, val); +} + } // namespace libtransmission diff --git a/tests/libtransmission/settings-test.cc b/tests/libtransmission/settings-test.cc index 3707a2d20..fe860eae6 100644 --- a/tests/libtransmission/settings-test.cc +++ b/tests/libtransmission/settings-test.cc @@ -406,3 +406,45 @@ TEST_F(SettingsTest, canSaveTos) EXPECT_EQ(ChangedValue.toString(), val); tr_variantClear(&dict); } + +TEST_F(SettingsTest, canLoadVerify) +{ + static auto constexpr Key = TR_KEY_torrent_added_verify_mode; + static auto constexpr ChangedValue = TR_VERIFY_ADDED_FULL; + + auto settings = std::make_unique(); + auto const default_value = settings->torrent_added_verify_mode; + ASSERT_NE(ChangedValue, default_value); + + auto dict = tr_variant{}; + tr_variantInitDict(&dict, 1); + tr_variantDictAddStrView(&dict, Key, "full"); + settings->load(&dict); + tr_variantClear(&dict); + EXPECT_EQ(ChangedValue, settings->torrent_added_verify_mode); + + settings = std::make_unique(); + tr_variantInitDict(&dict, 1); + tr_variantDictAddInt(&dict, Key, ChangedValue); + settings->load(&dict); + tr_variantClear(&dict); + EXPECT_EQ(ChangedValue, settings->torrent_added_verify_mode); +} + +TEST_F(SettingsTest, canSaveVerify) +{ + static auto constexpr Key = TR_KEY_torrent_added_verify_mode; + static auto constexpr ChangedValue = TR_VERIFY_ADDED_FULL; + + auto settings = tr_session_settings{}; + ASSERT_NE(ChangedValue, settings.torrent_added_verify_mode); + + auto dict = tr_variant{}; + tr_variantInitDict(&dict, 100); + settings.torrent_added_verify_mode = ChangedValue; + settings.save(&dict); + auto val = std::string_view{}; + EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val)); + EXPECT_EQ("full", val); + tr_variantClear(&dict); +}