diff --git a/Transmission.xcodeproj/project.pbxproj b/Transmission.xcodeproj/project.pbxproj index c57c65938..6cbea2bc9 100644 --- a/Transmission.xcodeproj/project.pbxproj +++ b/Transmission.xcodeproj/project.pbxproj @@ -152,8 +152,7 @@ A23F4FF20D1D98AD002FCB97 /* PrefsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = A23F4FF00D1D98AD002FCB97 /* PrefsWindow.xib */; }; A23F50020D1D99D7002FCB97 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A23F50000D1D99D7002FCB97 /* MainMenu.xib */; }; A23F526F0F14395900AA02E3 /* PredicateEditorRowTemplateAny.mm in Sources */ = {isa = PBXBuildFile; fileRef = A23F526E0F14395900AA02E3 /* PredicateEditorRowTemplateAny.mm */; }; - A23FAE54178BC2950053DC5B /* platform-quota.cc in Sources */ = {isa = PBXBuildFile; fileRef = A23FAE52178BC2950053DC5B /* platform-quota.cc */; }; - A23FAE55178BC2950053DC5B /* platform-quota.h in Headers */ = {isa = PBXBuildFile; fileRef = A23FAE53178BC2950053DC5B /* platform-quota.h */; }; + A23FAE54178BC2950053DC5B /* file-capacity.cc in Sources */ = {isa = PBXBuildFile; fileRef = A23FAE52178BC2950053DC5B /* file-capacity.cc */; }; A242AD9315F05D23002B3A6C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A242AD9115F05D23002B3A6C /* Localizable.strings */; }; A2451E6916ACE4EB00586E0E /* FileRenameSheetController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2451E6716ACE4EB00586E0E /* FileRenameSheetController.mm */; }; A2451E6A16ACE4EB00586E0E /* FileRenameSheetController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A2451E6816ACE4EB00586E0E /* FileRenameSheetController.xib */; }; @@ -909,8 +908,7 @@ A23F29A0132A447400E9A83B /* announcer-http.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "announcer-http.cc"; sourceTree = ""; }; A23F526D0F14395900AA02E3 /* PredicateEditorRowTemplateAny.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PredicateEditorRowTemplateAny.h; sourceTree = ""; }; A23F526E0F14395900AA02E3 /* PredicateEditorRowTemplateAny.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PredicateEditorRowTemplateAny.mm; sourceTree = ""; }; - A23FAE52178BC2950053DC5B /* platform-quota.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "platform-quota.cc"; sourceTree = ""; }; - A23FAE53178BC2950053DC5B /* platform-quota.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "platform-quota.h"; sourceTree = ""; }; + A23FAE52178BC2950053DC5B /* file-capacity.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "file-capacity.cc"; sourceTree = ""; }; A242AD9215F05D23002B3A6C /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; A2451E6616ACE4EB00586E0E /* FileRenameSheetController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileRenameSheetController.h; sourceTree = ""; }; A2451E6716ACE4EB00586E0E /* FileRenameSheetController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FileRenameSheetController.mm; sourceTree = ""; }; @@ -1762,8 +1760,7 @@ 4D36BA6B0CA2F00800A63CA5 /* peer-msgs.h */, C1425B381EE9C805001DB851 /* peer-socket.h */, C1425B381EE9C805001DB853 /* peer-socket.cc */, - A23FAE52178BC2950053DC5B /* platform-quota.cc */, - A23FAE53178BC2950053DC5B /* platform-quota.h */, + A23FAE52178BC2950053DC5B /* file-capacity.cc */, BEFC1E030C07861A00B0BB3C /* platform.cc */, BEFC1E020C07861A00B0BB3C /* platform.h */, BEFC1DFD0C07861A00B0BB3C /* port-forwarding.cc */, @@ -2342,7 +2339,6 @@ A25BFD6E167BED3B0039D1AA /* variant.h in Headers */, A2EA52321686AC0D00180493 /* quark.h in Headers */, A2AF23C916B44FA0003BC59E /* log.h in Headers */, - A23FAE55178BC2950053DC5B /* platform-quota.h in Headers */, F11545ACA7C4D7A464F703AB /* block-info.h in Headers */, E23B55A5FC3B557F7746D510 /* interned-string.h in Headers */, ); @@ -3131,7 +3127,7 @@ A25BFD6D167BED3B0039D1AA /* variant.cc in Sources */, A2EA52311686AC0D00180493 /* quark.cc in Sources */, A2AF23C816B44FA0003BC59E /* log.cc in Sources */, - A23FAE54178BC2950053DC5B /* platform-quota.cc in Sources */, + A23FAE54178BC2950053DC5B /* file-capacity.cc in Sources */, 62F644738FE3D8788EBF73A9 /* block-info.cc in Sources */, E975121263DD973CAF4AEBA4 /* timer-ev.cc in Sources */, ); diff --git a/gtk/FreeSpaceLabel.cc b/gtk/FreeSpaceLabel.cc index b3494acbb..346a275e1 100644 --- a/gtk/FreeSpaceLabel.cc +++ b/gtk/FreeSpaceLabel.cc @@ -8,7 +8,7 @@ #include "Session.h" #include "Utils.h" -#include +#include #include #include @@ -50,8 +50,9 @@ bool FreeSpaceLabel::Impl::on_freespace_timer() return false; } - auto const bytes = tr_dirSpace(dir_).free; - auto const text = bytes < 0 ? _("Error") : fmt::format(_("{disk_space} free"), fmt::arg("disk_space", tr_strlsize(bytes))); + auto const capacity = tr_sys_path_get_capacity(dir_); + auto const text = capacity ? fmt::format(_("{disk_space} free"), fmt::arg("disk_space", tr_strlsize(capacity->free))) : + _("Error"); label_.set_markup(fmt::format(FMT_STRING("{:s}"), text)); return true; diff --git a/libtransmission/CMakeLists.txt b/libtransmission/CMakeLists.txt index 7a48df458..72e51c798 100644 --- a/libtransmission/CMakeLists.txt +++ b/libtransmission/CMakeLists.txt @@ -51,6 +51,7 @@ target_sources(${TR_NAME} error.cc error.h favicon-cache.h + file-capacity.cc file-piece-map.cc file-piece-map.h file-posix.cc @@ -91,8 +92,6 @@ target_sources(${TR_NAME} peer-msgs.h peer-socket.cc peer-socket.h - platform-quota.cc - platform-quota.h platform.cc platform.h port-forwarding-natpmp.cc diff --git a/libtransmission/platform-quota.cc b/libtransmission/file-capacity.cc similarity index 83% rename from libtransmission/platform-quota.cc rename to libtransmission/file-capacity.cc index 3ddbdb290..706824cd1 100644 --- a/libtransmission/platform-quota.cc +++ b/libtransmission/file-capacity.cc @@ -64,12 +64,20 @@ #include "libtransmission/transmission.h" +#include "libtransmission/error.h" +#include "libtransmission/file.h" #include "libtransmission/tr-macros.h" #include "libtransmission/utils.h" -#include "libtransmission/platform-quota.h" namespace { +struct tr_device_info +{ + std::string path; + std::string device; + std::string fstype; +}; + #ifndef _WIN32 [[nodiscard]] char const* getdev(std::string_view path) @@ -234,12 +242,12 @@ extern "C" #include } -[[nodiscard]] tr_disk_space getquota(char const* device) +[[nodiscard]] tr_sys_path_capacity getquota(char const* device) { struct quotahandle* qh; struct quotakey qk; struct quotaval qv; - struct tr_disk_space disk_space = { -1, -1 }; + struct tr_sys_path_capacity disk_space = { -1, -1 }; qh = quota_open(device); @@ -284,14 +292,14 @@ extern "C" #else -[[nodiscard]] tr_disk_space getquota(char const* device) +[[nodiscard]] tr_sys_path_capacity getquota(char const* device) { #if defined(__DragonFly__) struct ufs_dqblk dq = {}; #else struct dqblk dq = {}; #endif - struct tr_disk_space disk_space = { -1, -1 }; + auto disk_space = tr_sys_path_capacity{ -1, -1 }; #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) if (quotactl(device, QCMD(Q_GETQUOTA, USRQUOTA), getuid(), (caddr_t)&dq) != 0) @@ -370,9 +378,9 @@ extern "C" #ifdef HAVE_XQM -[[nodiscard]] tr_disk_space getxfsquota(char const* device) +[[nodiscard]] tr_sys_path_capacity getxfsquota(char const* device) { - struct tr_disk_space disk_space = { -1, -1 }; + struct tr_sys_path_capacity disk_space = { -1, -1 }; struct fs_disk_quota dq; if (quotactl(QCMD(Q_XGETQUOTA, USRQUOTA), device, getuid(), (caddr_t)&dq) == 0) @@ -409,9 +417,9 @@ extern "C" #endif /* _WIN32 */ -[[nodiscard]] tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info) +[[nodiscard]] tr_sys_path_capacity get_quota_space([[maybe_unused]] tr_device_info const& info) { - struct tr_disk_space ret = { -1, -1 }; + struct tr_sys_path_capacity ret = { -1, -1 }; #ifndef _WIN32 @@ -431,11 +439,11 @@ extern "C" return ret; } -[[nodiscard]] tr_disk_space getDiskSpace(char const* path) +[[nodiscard]] tr_sys_path_capacity getDiskSpace(char const* path) { #ifdef _WIN32 - struct tr_disk_space ret = { -1, -1 }; + struct tr_sys_path_capacity ret = { -1, -1 }; if (auto const wide_path = tr_win32_utf8_to_native(path); !std::empty(wide_path)) { @@ -454,9 +462,9 @@ extern "C" #elif defined(HAVE_STATVFS) struct statvfs buf = {}; - return statvfs(path, &buf) != 0 ? - (struct tr_disk_space){ -1, -1 } : - (struct tr_disk_space){ (int64_t)buf.f_bavail * (int64_t)buf.f_frsize, (int64_t)buf.f_blocks * (int64_t)buf.f_frsize }; + return statvfs(path, &buf) != 0 ? (struct tr_sys_path_capacity){ -1, -1 } : + (struct tr_sys_path_capacity){ (int64_t)buf.f_bavail * (int64_t)buf.f_frsize, + (int64_t)buf.f_blocks * (int64_t)buf.f_frsize }; #else @@ -467,8 +475,6 @@ extern "C" #endif } -} // namespace - tr_device_info tr_device_info_create(std::string_view path) { auto out = tr_device_info{}; @@ -481,7 +487,7 @@ tr_device_info tr_device_info_create(std::string_view path) return out; } -tr_disk_space tr_device_info_get_disk_space(struct tr_device_info const& info) +tr_sys_path_capacity tr_device_info_get_disk_space(struct tr_device_info const& info) { if (std::empty(info.path)) { @@ -489,7 +495,7 @@ tr_disk_space tr_device_info_get_disk_space(struct tr_device_info const& info) return { -1, -1 }; } - auto space = getQuotaSpace(info); + auto space = get_quota_space(info); if (space.free < 0 || space.total < 0) { @@ -498,3 +504,31 @@ tr_disk_space tr_device_info_get_disk_space(struct tr_device_info const& info) return space; } + +} // namespace + +std::optional tr_sys_path_get_capacity(std::string_view path, tr_error** error) +{ + auto const info = tr_sys_path_get_info(path, 0, error); + if (!info) + { + return {}; + } + + if (!info->isFolder()) + { + tr_error_set_from_errno(error, ENOTDIR); + return {}; + } + + auto const device = tr_device_info_create(path); + auto capacity = tr_device_info_get_disk_space(device); + + if (capacity.free < 0 || capacity.total < 0) + { + tr_error_set_from_errno(error, EINVAL); + return {}; + } + + return capacity; +} diff --git a/libtransmission/file.h b/libtransmission/file.h index 8c39b025b..73a35b7aa 100644 --- a/libtransmission/file.h +++ b/libtransmission/file.h @@ -118,6 +118,12 @@ struct tr_sys_path_info } }; +struct tr_sys_path_capacity +{ + int64_t free = -1; + int64_t total = -1; +}; + /** * @name Platform-specific wrapper functions * @@ -160,6 +166,15 @@ bool tr_sys_path_copy(char const* src_path, char const* dst_path, struct tr_erro int flags = 0, tr_error** error = nullptr); +/** + * @brief Get disk capacity and free disk space (in bytes) for the specified folder. + * + * @param[in] path Path to directory. + * @param[out] error Pointer to error object. Optional, pass `nullptr` if you + * are not interested in error details. + */ +[[nodiscard]] std::optional tr_sys_path_get_capacity(std::string_view path, tr_error** error = nullptr); + /** * @brief Portability wrapper for `access()`. * diff --git a/libtransmission/platform-quota.h b/libtransmission/platform-quota.h deleted file mode 100644 index 9235f377c..000000000 --- a/libtransmission/platform-quota.h +++ /dev/null @@ -1,37 +0,0 @@ -// This file Copyright © 2013-2023 Mnemosyne LLC. -// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), -// or any future license endorsed by Mnemosyne LLC. -// License text can be found in the licenses/ folder. - -#pragma once - -#include // int64_t -#include -#include - -/** - * @addtogroup tr_session Session - * @{ - */ - -struct tr_device_info -{ - std::string path; - std::string device; - std::string fstype; -}; - -struct tr_disk_space -{ - int64_t free; - int64_t total; -}; - -tr_device_info tr_device_info_create(std::string_view path); - -/** Values represents the total space on disk. - If the disk quota (free space) is enabled and readable, this returns how much is available in the quota. - Otherwise, it returns how much is available on the disk, or { -1, -1 } on error. */ -tr_disk_space tr_device_info_get_disk_space(tr_device_info const& info); - -/** @} */ diff --git a/libtransmission/rpcimpl.cc b/libtransmission/rpcimpl.cc index c27c559af..9e5bffa89 100644 --- a/libtransmission/rpcimpl.cc +++ b/libtransmission/rpcimpl.cc @@ -2181,7 +2181,14 @@ void addSessionField(tr_session const* s, tr_variant* d, tr_quark key) break; case TR_KEY_download_dir_free_space: - tr_variantDictAddInt(d, key, tr_dirSpace(s->downloadDir()).free); + if (auto const capacity = tr_sys_path_get_capacity(s->downloadDir()); capacity) + { + tr_variantDictAddInt(d, key, capacity->free); + } + else + { + tr_variantDictAddInt(d, key, -1); + } break; case TR_KEY_download_queue_enabled: @@ -2409,15 +2416,16 @@ char const* freeSpace(tr_session* /*session*/, tr_variant* args_in, tr_variant* /* get the free space */ auto const old_errno = errno; - errno = 0; - auto const dir_space = tr_dirSpace(path); - char const* const err = dir_space.free < 0 || dir_space.total < 0 ? tr_strerror(errno) : nullptr; + tr_error* error = nullptr; + auto const capacity = tr_sys_path_get_capacity(path, &error); + char const* const err = error != nullptr ? tr_strerror(error->code) : nullptr; + tr_error_clear(&error); errno = old_errno; /* response */ tr_variantDictAddStr(args_out, TR_KEY_path, path); - tr_variantDictAddInt(args_out, TR_KEY_size_bytes, dir_space.free); - tr_variantDictAddInt(args_out, TR_KEY_total_size, dir_space.total); + tr_variantDictAddInt(args_out, TR_KEY_size_bytes, capacity ? capacity->free : -1); + tr_variantDictAddInt(args_out, TR_KEY_total_size, capacity ? capacity->total : -1); return err; } diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index a22d5bdb6..e5fc54050 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -50,7 +50,6 @@ #include "libtransmission/log.h" #include "libtransmission/mime-types.h" #include "libtransmission/net.h" // ntohl() -#include "libtransmission/platform-quota.h" /* tr_device_info_create(), tr_device_info_get_disk_space(), tr_device_info_free() */ #include "libtransmission/tr-assert.h" #include "libtransmission/tr-strbuf.h" #include "libtransmission/utils.h" @@ -175,17 +174,6 @@ bool tr_saveFile(std::string_view filename, std::string_view contents, tr_error* return true; } -tr_disk_space tr_dirSpace(std::string_view directory) -{ - if (std::empty(directory)) - { - errno = EINVAL; - return { -1, -1 }; - } - - return tr_device_info_get_disk_space(tr_device_info_create(directory)); -} - // --- size_t tr_strvToBuf(std::string_view src, char* buf, size_t buflen) diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 5b1329cef..9533f84a4 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -16,7 +16,6 @@ #include #include -#include "platform-quota.h" #include "tr-macros.h" struct tr_error; @@ -76,12 +75,6 @@ constexpr auto tr_saveFile(std::string_view filename, ContiguousRange const& x, return tr_saveFile(filename, std::string_view{ std::data(x), std::size(x) }, error); } -/** - * @brief Get disk capacity and free disk space (in bytes) for the specified folder. - * @return struct with free and total as zero or positive integer on success, -1 in case of error. - */ -[[nodiscard]] tr_disk_space tr_dirSpace(std::string_view directory); - /** @brief return the current date in milliseconds */ [[nodiscard]] uint64_t tr_time_msec(); diff --git a/tests/libtransmission/announcer-udp-test.cc b/tests/libtransmission/announcer-udp-test.cc index ebb5a1c40..06a543f17 100644 --- a/tests/libtransmission/announcer-udp-test.cc +++ b/tests/libtransmission/announcer-udp-test.cc @@ -113,7 +113,7 @@ protected: } } - [[nodiscard]] static uint32_t parseConnectionRequest(std::vector data) + [[nodiscard]] static uint32_t parseConnectionRequest(std::vector const& data) { auto buf = MessageBuffer(data); EXPECT_EQ(ProtocolId, buf.to_uint64()); @@ -150,7 +150,7 @@ protected: return std::make_pair(buildScrapeRequestFromResponse(response), response); } - [[nodiscard]] static auto parseScrapeRequest(std::vector data, uint64_t expected_connection_id) + [[nodiscard]] static auto parseScrapeRequest(std::vector const& data, uint64_t expected_connection_id) { auto buf = MessageBuffer(data); EXPECT_EQ(expected_connection_id, buf.to_uint64()); @@ -254,7 +254,7 @@ protected: EXPECT_EQ(actual.external_ip, expected.external_ip); } - [[nodiscard]] static auto parseAnnounceRequest(std::vector data, uint64_t connection_id) + [[nodiscard]] static auto parseAnnounceRequest(std::vector const& data, uint64_t connection_id) { auto buf = MessageBuffer{ data }; auto req = UdpAnnounceReq{};