fix: restore portable file path check (#6853)

* chore: change to snake_case naming

* fix: restore portable file path check

* fix: macosx build
This commit is contained in:
Yat Ho
2024-05-25 23:08:53 +08:00
committed by GitHub
parent adc405e5be
commit 9748f42c5a
14 changed files with 97 additions and 89 deletions

View File

@@ -134,7 +134,7 @@ tr_metainfo_builder::tr_metainfo_builder(std::string_view single_file_or_parent_
: top_{ single_file_or_parent_directory } : top_{ single_file_or_parent_directory }
{ {
files_ = findFiles(tr_sys_path_dirname(top_), tr_sys_path_basename(top_)); files_ = findFiles(tr_sys_path_dirname(top_), tr_sys_path_basename(top_));
block_info_ = tr_block_info{ files_.totalSize(), default_piece_size(files_.totalSize()) }; block_info_ = tr_block_info{ files_.total_size(), default_piece_size(files_.total_size()) };
} }
bool tr_metainfo_builder::set_piece_size(uint32_t piece_size) noexcept bool tr_metainfo_builder::set_piece_size(uint32_t piece_size) noexcept
@@ -144,7 +144,7 @@ bool tr_metainfo_builder::set_piece_size(uint32_t piece_size) noexcept
return false; return false;
} }
block_info_ = tr_block_info{ files_.totalSize(), piece_size }; block_info_ = tr_block_info{ files_.total_size(), piece_size };
return true; return true;
} }

View File

@@ -122,12 +122,12 @@ public:
[[nodiscard]] TR_CONSTEXPR20 auto file_count() const noexcept [[nodiscard]] TR_CONSTEXPR20 auto file_count() const noexcept
{ {
return files_.fileCount(); return files_.file_count();
} }
[[nodiscard]] TR_CONSTEXPR20 auto file_size(tr_file_index_t i) const noexcept [[nodiscard]] TR_CONSTEXPR20 auto file_size(tr_file_index_t i) const noexcept
{ {
return files_.fileSize(i); return files_.file_size(i);
} }
[[nodiscard]] constexpr auto is_private() const noexcept [[nodiscard]] constexpr auto is_private() const noexcept
@@ -167,7 +167,7 @@ public:
[[nodiscard]] constexpr auto total_size() const noexcept [[nodiscard]] constexpr auto total_size() const noexcept
{ {
return files_.totalSize(); return files_.total_size();
} }
[[nodiscard]] constexpr auto const& webseeds() const noexcept [[nodiscard]] constexpr auto const& webseeds() const noexcept

View File

@@ -489,7 +489,7 @@ void saveProgress(tr_variant* dict, tr_torrent const* tor, tr_torrent::ResumeHel
* pieces cleared from the bitset. * pieces cleared from the bitset.
* *
* Second approach (2.20 - 3.00): the 'progress' dict had a * Second approach (2.20 - 3.00): the 'progress' dict had a
* 'time_checked' entry which was a list with fileCount items. * 'time_checked' entry which was a list with file_count items.
* Each item was either a list of per-piece timestamps, or a * Each item was either a list of per-piece timestamps, or a
* single timestamp if either all or none of the pieces had been * single timestamp if either all or none of the pieces had been
* tested more recently than the file's mtime. * tested more recently than the file's mtime.

View File

@@ -32,15 +32,15 @@ namespace
using file_func_t = std::function<void(char const* filename)>; using file_func_t = std::function<void(char const* filename)>;
bool isFolder(std::string_view path) bool is_folder(std::string_view path)
{ {
auto const info = tr_sys_path_get_info(path); auto const info = tr_sys_path_get_info(path);
return info && info->isFolder(); return info && info->isFolder();
} }
bool isEmptyFolder(char const* path) bool is_empty_folder(char const* path)
{ {
if (!isFolder(path)) if (!is_folder(path))
{ {
return false; return false;
} }
@@ -63,9 +63,9 @@ bool isEmptyFolder(char const* path)
return true; return true;
} }
void depthFirstWalk(char const* path, file_func_t const& func, std::optional<int> max_depth = {}) void depth_first_walk(char const* path, file_func_t const& func, std::optional<int> max_depth = {})
{ {
if (isFolder(path) && (!max_depth || *max_depth > 0)) if (is_folder(path) && (!max_depth || *max_depth > 0))
{ {
if (auto const odir = tr_sys_dir_open(path); odir != TR_BAD_SYS_DIR) if (auto const odir = tr_sys_dir_open(path); odir != TR_BAD_SYS_DIR)
{ {
@@ -78,7 +78,7 @@ void depthFirstWalk(char const* path, file_func_t const& func, std::optional<int
continue; continue;
} }
depthFirstWalk(tr_pathbuf{ path, '/', name }.c_str(), func, max_depth ? *max_depth - 1 : max_depth); depth_first_walk(tr_pathbuf{ path, '/', name }.c_str(), func, max_depth ? *max_depth - 1 : max_depth);
} }
tr_sys_dir_close(odir); tr_sys_dir_close(odir);
@@ -88,7 +88,7 @@ void depthFirstWalk(char const* path, file_func_t const& func, std::optional<int
func(path); func(path);
} }
bool isJunkFile(std::string_view filename) bool is_junk_file(std::string_view filename)
{ {
auto const base = tr_sys_path_basename(filename); auto const base = tr_sys_path_basename(filename);
@@ -141,9 +141,9 @@ std::optional<tr_torrent_files::FoundFile> tr_torrent_files::find(
return {}; return {};
} }
bool tr_torrent_files::hasAnyLocalData(std::string_view const* paths, size_t n_paths) const bool tr_torrent_files::has_any_local_data(std::string_view const* paths, size_t n_paths) const
{ {
for (tr_file_index_t i = 0, n = fileCount(); i < n; ++i) for (tr_file_index_t i = 0, n = file_count(); i < n; ++i)
{ {
if (find(i, paths, n_paths)) if (find(i, paths, n_paths))
{ {
@@ -180,7 +180,7 @@ bool tr_torrent_files::move(
auto err = bool{}; auto err = bool{};
for (tr_file_index_t i = 0, n = fileCount(); i < n; ++i) for (tr_file_index_t i = 0, n = file_count(); i < n; ++i)
{ {
auto const found = find(i, std::data(paths), std::size(paths)); auto const found = find(i, std::data(paths), std::size(paths));
if (!found) if (!found)
@@ -210,7 +210,7 @@ bool tr_torrent_files::move(
{ {
auto const remove_empty_directories = [](char const* filename) auto const remove_empty_directories = [](char const* filename)
{ {
if (isEmptyFolder(filename)) if (is_empty_folder(filename))
{ {
tr_sys_path_remove(filename, nullptr); tr_sys_path_remove(filename, nullptr);
} }
@@ -249,7 +249,7 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
// move the local data to the tmpdir // move the local data to the tmpdir
auto const paths = std::array<std::string_view, 1>{ parent.sv() }; auto const paths = std::array<std::string_view, 1>{ parent.sv() };
for (tr_file_index_t idx = 0, n_files = fileCount(); idx < n_files; ++idx) for (tr_file_index_t idx = 0, n_files = file_count(); idx < n_files; ++idx)
{ {
if (auto const found = find(idx, std::data(paths), std::size(paths)); found) if (auto const found = find(idx, std::data(paths), std::size(paths)); found)
{ {
@@ -261,7 +261,7 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
// because we'll need it below in the 'remove junk' phase // because we'll need it below in the 'remove junk' phase
auto const path = tr_pathbuf{ parent, '/', tmpdir_prefix }; auto const path = tr_pathbuf{ parent, '/', tmpdir_prefix };
auto top_files = std::set<std::string>{ std::string{ path } }; auto top_files = std::set<std::string>{ std::string{ path } };
depthFirstWalk( depth_first_walk(
tmpdir, tmpdir,
[&parent, &tmpdir, &top_files](char const* filename) [&parent, &tmpdir, &top_files](char const* filename)
{ {
@@ -285,8 +285,8 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
// the folder hierarchy by removing top-level files & folders first. // the folder hierarchy by removing top-level files & folders first.
// But that can fail -- e.g. `func` might refuse to remove nonempty // But that can fail -- e.g. `func` might refuse to remove nonempty
// directories -- so plan B is to remove everything bottom-up. // directories -- so plan B is to remove everything bottom-up.
depthFirstWalk(tmpdir, func_wrapper, 1); depth_first_walk(tmpdir, func_wrapper, 1);
depthFirstWalk(tmpdir, func_wrapper); depth_first_walk(tmpdir, func_wrapper);
tr_sys_path_remove(tmpdir); tr_sys_path_remove(tmpdir);
// OK we've removed the local data. // OK we've removed the local data.
@@ -294,23 +294,23 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
// Remove the first two categories and leave the third alone. // Remove the first two categories and leave the third alone.
auto const remove_junk = [](char const* filename) auto const remove_junk = [](char const* filename)
{ {
if (isEmptyFolder(filename) || isJunkFile(filename)) if (is_empty_folder(filename) || is_junk_file(filename))
{ {
tr_sys_path_remove(filename); tr_sys_path_remove(filename);
} }
}; };
for (auto const& filename : top_files) for (auto const& filename : top_files)
{ {
depthFirstWalk(filename.c_str(), remove_junk); depth_first_walk(filename.c_str(), remove_junk);
} }
} }
namespace namespace
{ {
// `isUnixReservedFile` and `isWin32ReservedFile` kept as `maybe_unused` // `is_unix_reserved_file` and `is_win32_reserved_file` kept as `maybe_unused`
// for potential support of different filesystems on the same OS // for potential support of different filesystems on the same OS
[[nodiscard, maybe_unused]] bool isUnixReservedFile(std::string_view in) noexcept [[nodiscard, maybe_unused]] bool is_unix_reserved_file(std::string_view in) noexcept
{ {
static auto constexpr ReservedNames = std::array<std::string_view, 2>{ static auto constexpr ReservedNames = std::array<std::string_view, 2>{
"."sv, "."sv,
@@ -325,7 +325,7 @@ namespace
// COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. // COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
// Also avoid these names followed immediately by an extension; // Also avoid these names followed immediately by an extension;
// for example, NUL.txt is not recommended. // for example, NUL.txt is not recommended.
[[nodiscard, maybe_unused]] bool isWin32ReservedFile(std::string_view in) noexcept [[nodiscard, maybe_unused]] bool is_win32_reserved_file(std::string_view in) noexcept
{ {
if (std::empty(in)) if (std::empty(in))
{ {
@@ -365,18 +365,22 @@ namespace
[in_upper_sv](auto const& prefix) { return tr_strv_starts_with(in_upper_sv, prefix); }); [in_upper_sv](auto const& prefix) { return tr_strv_starts_with(in_upper_sv, prefix); });
} }
[[nodiscard]] bool isReservedFile(std::string_view in) noexcept [[nodiscard]] bool is_reserved_file(std::string_view in, bool os_specific) noexcept
{ {
if (!os_specific)
{
return is_unix_reserved_file(in) || is_win32_reserved_file(in);
}
#ifdef _WIN32 #ifdef _WIN32
return isWin32ReservedFile(in); return is_win32_reserved_file(in);
#else #else
return isUnixReservedFile(in); return is_unix_reserved_file(in);
#endif #endif
} }
// `isUnixReservedChar` and `isWin32ReservedChar` kept as `maybe_unused` // `is_unix_reserved_char` and `is_win32_reserved_char` kept as `maybe_unused`
// for potential support of different filesystems on the same OS // for potential support of different filesystems on the same OS
[[nodiscard, maybe_unused]] auto constexpr isUnixReservedChar(unsigned char ch) noexcept [[nodiscard, maybe_unused]] auto constexpr is_unix_reserved_char(unsigned char ch) noexcept
{ {
return ch == '/'; return ch == '/';
} }
@@ -385,7 +389,7 @@ namespace
// Use any character in the current code page for a name, including Unicode // Use any character in the current code page for a name, including Unicode
// characters and characters in the extended character set (128255), // characters and characters in the extended character set (128255),
// except for the following: // except for the following:
[[nodiscard, maybe_unused]] auto constexpr isWin32ReservedChar(unsigned char ch) noexcept [[nodiscard, maybe_unused]] auto constexpr is_win32_reserved_char(unsigned char ch) noexcept
{ {
switch (ch) switch (ch)
{ {
@@ -404,17 +408,21 @@ namespace
} }
} }
[[nodiscard]] auto constexpr isReservedChar(unsigned char ch) noexcept [[nodiscard]] auto constexpr is_reserved_char(unsigned char ch, bool os_specific) noexcept
{ {
if (!os_specific)
{
return is_unix_reserved_char(ch) || is_win32_reserved_char(ch);
}
#ifdef _WIN32 #ifdef _WIN32
return isWin32ReservedChar(ch); return is_win32_reserved_char(ch);
#else #else
return isUnixReservedChar(ch); return is_unix_reserved_char(ch);
#endif #endif
} }
// https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations // https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations
void appendSanitizedComponent(std::string_view in, tr_pathbuf& out) void append_sanitized_component(std::string_view in, tr_pathbuf& out, bool os_specific)
{ {
#ifdef _WIN32 #ifdef _WIN32
// remove leading and trailing spaces // remove leading and trailing spaces
@@ -428,27 +436,27 @@ void appendSanitizedComponent(std::string_view in, tr_pathbuf& out)
#endif #endif
// replace reserved filenames with an underscore // replace reserved filenames with an underscore
if (isReservedFile(in)) if (is_reserved_file(in, os_specific))
{ {
out.append('_'); out.append('_');
} }
// replace reserved characters with an underscore // replace reserved characters with an underscore
static auto constexpr AddChar = [](auto ch) auto const add_char = [os_specific](auto ch)
{ {
return isReservedChar(ch) ? '_' : ch; return is_reserved_char(ch, os_specific) ? '_' : ch;
}; };
std::transform(std::begin(in), std::end(in), std::back_inserter(out), AddChar); std::transform(std::begin(in), std::end(in), std::back_inserter(out), add_char);
} }
} // namespace } // namespace
void tr_torrent_files::makeSubpathPortable(std::string_view path, tr_pathbuf& append_me) void tr_torrent_files::sanitize_subpath(std::string_view path, tr_pathbuf& append_me, bool os_specific)
{ {
auto segment = std::string_view{}; auto segment = std::string_view{};
while (tr_strv_sep(&path, &segment, '/')) while (tr_strv_sep(&path, &segment, '/'))
{ {
appendSanitizedComponent(segment, append_me); append_sanitized_component(segment, append_me, os_specific);
append_me.append('/'); append_me.append('/');
} }

View File

@@ -35,17 +35,17 @@ public:
return std::empty(files_); return std::empty(files_);
} }
[[nodiscard]] TR_CONSTEXPR20 size_t fileCount() const noexcept [[nodiscard]] TR_CONSTEXPR20 size_t file_count() const noexcept
{ {
return std::size(files_); return std::size(files_);
} }
[[nodiscard]] TR_CONSTEXPR20 uint64_t fileSize(tr_file_index_t file_index) const [[nodiscard]] TR_CONSTEXPR20 uint64_t file_size(tr_file_index_t file_index) const
{ {
return files_.at(file_index).size_; return files_.at(file_index).size_;
} }
[[nodiscard]] constexpr auto totalSize() const noexcept [[nodiscard]] constexpr auto total_size() const noexcept
{ {
return total_size_; return total_size_;
} }
@@ -55,12 +55,12 @@ public:
return files_.at(file_index).path_; return files_.at(file_index).path_;
} }
void setPath(tr_file_index_t file_index, std::string_view path) void set_path(tr_file_index_t file_index, std::string_view path)
{ {
files_.at(file_index).setPath(path); files_.at(file_index).set_path(path);
} }
void insertSubpathPrefix(std::string_view path) void insert_subpath_prefix(std::string_view path)
{ {
auto const buf = tr_pathbuf{ path, '/' }; auto const buf = tr_pathbuf{ path, '/' };
@@ -76,7 +76,7 @@ public:
files_.reserve(n_files); files_.reserve(n_files);
} }
void shrinkToFit() void shrink_to_fit()
{ {
files_.shrink_to_fit(); files_.shrink_to_fit();
} }
@@ -87,7 +87,7 @@ public:
total_size_ = uint64_t{}; total_size_ = uint64_t{};
} }
[[nodiscard]] auto sortedByPath() const [[nodiscard]] auto sorted_by_path() const
{ {
auto ret = std::vector<std::pair<std::string /*path*/, uint64_t /*size*/>>{}; auto ret = std::vector<std::pair<std::string /*path*/, uint64_t /*size*/>>{};
ret.reserve(std::size(files_)); ret.reserve(std::size(files_));
@@ -153,20 +153,20 @@ public:
}; };
[[nodiscard]] std::optional<FoundFile> find(tr_file_index_t file, std::string_view const* paths, size_t n_paths) const; [[nodiscard]] std::optional<FoundFile> find(tr_file_index_t file, std::string_view const* paths, size_t n_paths) const;
[[nodiscard]] bool hasAnyLocalData(std::string_view const* paths, size_t n_paths) const; [[nodiscard]] bool has_any_local_data(std::string_view const* paths, size_t n_paths) const;
static void makeSubpathPortable(std::string_view path, tr_pathbuf& append_me); static void sanitize_subpath(std::string_view path, tr_pathbuf& append_me, bool os_specific = true);
[[nodiscard]] static auto makeSubpathPortable(std::string_view path) [[nodiscard]] static auto sanitize_subpath(std::string_view path, bool os_specific = true)
{ {
auto tmp = tr_pathbuf{}; auto tmp = tr_pathbuf{};
makeSubpathPortable(path, tmp); sanitize_subpath(path, tmp, os_specific);
return std::string{ tmp.sv() }; return std::string{ tmp.sv() };
} }
[[nodiscard]] static bool isSubpathPortable(std::string_view path) [[nodiscard]] static bool is_subpath_sanitized(std::string_view path, bool os_specific = true)
{ {
return makeSubpathPortable(path) == path; return sanitize_subpath(path, os_specific) == path;
} }
static constexpr std::string_view PartialFileSuffix = ".part"; static constexpr std::string_view PartialFileSuffix = ".part";
@@ -175,7 +175,7 @@ private:
struct file_t struct file_t
{ {
public: public:
void setPath(std::string_view subpath) void set_path(std::string_view subpath)
{ {
if (path_ != subpath) if (path_ != subpath)
{ {

View File

@@ -99,7 +99,7 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
{ {
file_subpath_ += '/'; file_subpath_ += '/';
} }
tr_torrent_files::makeSubpathPortable(currentKey(), file_subpath_); tr_torrent_files::sanitize_subpath(currentKey(), file_subpath_);
} }
else if (pathIs(InfoKey)) else if (pathIs(InfoKey))
{ {
@@ -298,7 +298,7 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
{ {
file_subpath_ += '/'; file_subpath_ += '/';
} }
tr_torrent_files::makeSubpathPortable(value, file_subpath_); tr_torrent_files::sanitize_subpath(value, file_subpath_);
} }
else if (current_key == AttrKey) else if (current_key == AttrKey)
{ {
@@ -488,10 +488,10 @@ private:
} }
auto root = tr_pathbuf{}; auto root = tr_pathbuf{};
tr_torrent_files::makeSubpathPortable(tm_.name_, root); tr_torrent_files::sanitize_subpath(tm_.name_, root);
if (!std::empty(root)) if (!std::empty(root))
{ {
tm_.files_.insertSubpathPrefix(root); tm_.files_.insert_subpath_prefix(root);
} }
TR_ASSERT(info_dict_begin_[0] == 'd'); TR_ASSERT(info_dict_begin_[0] == 'd');
@@ -522,7 +522,7 @@ private:
// In the single file case, length maps to the length of the file in bytes. // In the single file case, length maps to the length of the file in bytes.
if (tm_.file_count() == 0 && length_ != 0 && !std::empty(tm_.name_)) if (tm_.file_count() == 0 && length_ != 0 && !std::empty(tm_.name_))
{ {
tm_.files_.add(tr_torrent_files::makeSubpathPortable(tm_.name_), length_); tm_.files_.add(tr_torrent_files::sanitize_subpath(tm_.name_), length_);
} }
if (auto const has_metainfo = tm_.info_dict_size() != 0U; has_metainfo) if (auto const has_metainfo = tm_.info_dict_size() != 0U; has_metainfo)
@@ -546,7 +546,7 @@ private:
return false; return false;
} }
tm_.block_info_ = tr_block_info{ tm_.files_.totalSize(), piece_size_ }; tm_.block_info_ = tr_block_info{ tm_.files_.total_size(), piece_size_ };
return true; return true;
} }

View File

@@ -44,11 +44,11 @@ public:
} }
[[nodiscard]] TR_CONSTEXPR20 auto file_count() const noexcept [[nodiscard]] TR_CONSTEXPR20 auto file_count() const noexcept
{ {
return files().fileCount(); return files().file_count();
} }
[[nodiscard]] TR_CONSTEXPR20 auto file_size(tr_file_index_t i) const [[nodiscard]] TR_CONSTEXPR20 auto file_size(tr_file_index_t i) const
{ {
return files().fileSize(i); return files().file_size(i);
} }
[[nodiscard]] TR_CONSTEXPR20 auto const& file_subpath(tr_file_index_t i) const [[nodiscard]] TR_CONSTEXPR20 auto const& file_subpath(tr_file_index_t i) const
{ {
@@ -57,7 +57,7 @@ public:
void set_file_subpath(tr_file_index_t i, std::string_view subpath) void set_file_subpath(tr_file_index_t i, std::string_view subpath)
{ {
files_.setPath(i, subpath); files_.set_path(i, subpath);
} }
/// BLOCK INFO /// BLOCK INFO

View File

@@ -1212,7 +1212,7 @@ bool tr_torrent::has_any_local_data() const
auto paths = std::array<std::string_view, 4>{}; auto paths = std::array<std::string_view, 4>{};
auto const n_paths = buildSearchPathArray(this, std::data(paths)); auto const n_paths = buildSearchPathArray(this, std::data(paths));
return files().hasAnyLocalData(std::data(paths), n_paths); return files().has_any_local_data(std::data(paths), n_paths);
} }
void tr_torrentSetDownloadDir(tr_torrent* tor, char const* path) void tr_torrentSetDownloadDir(tr_torrent* tor, char const* path)
@@ -2548,7 +2548,7 @@ tr_bitfield const& tr_torrent::ResumeHelper::checked_pieces() const noexcept
return tor_.checked_pieces_; return tor_.checked_pieces_;
} }
void tr_torrent::ResumeHelper::load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*fileCount()*/) void tr_torrent::ResumeHelper::load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*file_count()*/)
{ {
TR_ASSERT(std::size(checked) == tor_.piece_count()); TR_ASSERT(std::size(checked) == tor_.piece_count());
tor_.checked_pieces_ = checked; tor_.checked_pieces_ = checked;

View File

@@ -70,7 +70,7 @@ struct tr_torrent
class ResumeHelper class ResumeHelper
{ {
public: public:
void load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*fileCount()*/); void load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*file_count()*/);
void load_blocks(tr_bitfield blocks); void load_blocks(tr_bitfield blocks);
void load_date_added(time_t when) noexcept; void load_date_added(time_t when) noexcept;
void load_date_done(time_t when) noexcept; void load_date_done(time_t when) noexcept;

View File

@@ -219,7 +219,7 @@ OSStatus GeneratePreviewForURL(void* /*thisInterface*/, QLPreviewRequestRef prev
FileTreeNode root{}; FileTreeNode root{};
for (auto const& [path, size] : metainfo.files().sortedByPath()) for (auto const& [path, size] : metainfo.files().sorted_by_path())
{ {
FileTreeNode* curNode = &root; FileTreeNode* curNode = &root;
size_t level = 0; size_t level = 0;

View File

@@ -84,7 +84,7 @@ protected:
EXPECT_EQ(builder.total_size(), metainfo.total_size()); EXPECT_EQ(builder.total_size(), metainfo.total_size());
for (size_t i = 0, n = std::min(builder.file_count(), metainfo.file_count()); i < n; ++i) for (size_t i = 0, n = std::min(builder.file_count(), metainfo.file_count()); i < n; ++i)
{ {
EXPECT_EQ(builder.file_size(i), metainfo.files().fileSize(i)); EXPECT_EQ(builder.file_size(i), metainfo.files().file_size(i));
EXPECT_EQ(builder.path(i), metainfo.files().path(i)); EXPECT_EQ(builder.path(i), metainfo.files().path(i));
} }
EXPECT_EQ(builder.name(), metainfo.name()); EXPECT_EQ(builder.name(), metainfo.name());

View File

@@ -176,7 +176,7 @@ protected:
{ {
auto paths = std::set<std::string>{}; auto paths = std::set<std::string>{};
for (tr_file_index_t i = 0, n = files.fileCount(); i < n; ++i) for (tr_file_index_t i = 0, n = files.file_count(); i < n; ++i)
{ {
auto walk = tr_pathbuf{ parent, '/', files.path(i) }; auto walk = tr_pathbuf{ parent, '/', files.path(i) };
createFileWithContents(walk, std::data(Content), std::size(Content)); createFileWithContents(walk, std::data(Content), std::size(Content));
@@ -332,7 +332,7 @@ TEST_F(RemoveTest, PreservesDirectoryHierarchyIfPossible)
// after remove, the subtree should be: // after remove, the subtree should be:
expected_tree = { parent, recycle_bin.c_str() }; expected_tree = { parent, recycle_bin.c_str() };
for (tr_file_index_t i = 0, n = files.fileCount(); i < n; ++i) for (tr_file_index_t i = 0, n = files.file_count(); i < n; ++i)
{ {
expected_tree.emplace(tr_pathbuf{ recycle_bin, '/', files.path(i) }); expected_tree.emplace(tr_pathbuf{ recycle_bin, '/', files.path(i) });
} }

View File

@@ -32,13 +32,13 @@ TEST_F(TorrentFilesTest, add)
auto constexpr Size = size_t{ 1024 }; auto constexpr Size = size_t{ 1024 };
auto files = tr_torrent_files{}; auto files = tr_torrent_files{};
EXPECT_EQ(size_t{ 0U }, files.fileCount()); EXPECT_EQ(size_t{ 0U }, files.file_count());
EXPECT_TRUE(std::empty(files)); EXPECT_TRUE(std::empty(files));
auto const file_index = files.add(Path, Size); auto const file_index = files.add(Path, Size);
EXPECT_EQ(tr_file_index_t{ 0U }, file_index); EXPECT_EQ(tr_file_index_t{ 0U }, file_index);
EXPECT_EQ(size_t{ 1U }, files.fileCount()); EXPECT_EQ(size_t{ 1U }, files.file_count());
EXPECT_EQ(Size, files.fileSize(file_index)); EXPECT_EQ(Size, files.file_size(file_index));
EXPECT_EQ(Path, files.path(file_index)); EXPECT_EQ(Path, files.path(file_index));
EXPECT_FALSE(std::empty(files)); EXPECT_FALSE(std::empty(files));
} }
@@ -52,11 +52,11 @@ TEST_F(TorrentFilesTest, setPath)
auto files = tr_torrent_files{}; auto files = tr_torrent_files{};
auto const file_index = files.add(Path1, Size); auto const file_index = files.add(Path1, Size);
EXPECT_EQ(Path1, files.path(file_index)); EXPECT_EQ(Path1, files.path(file_index));
EXPECT_EQ(Size, files.fileSize(file_index)); EXPECT_EQ(Size, files.file_size(file_index));
files.setPath(file_index, Path2); files.set_path(file_index, Path2);
EXPECT_EQ(Path2, files.path(file_index)); EXPECT_EQ(Path2, files.path(file_index));
EXPECT_EQ(Size, files.fileSize(file_index)); EXPECT_EQ(Size, files.file_size(file_index));
} }
TEST_F(TorrentFilesTest, clear) TEST_F(TorrentFilesTest, clear)
@@ -67,13 +67,13 @@ TEST_F(TorrentFilesTest, clear)
auto files = tr_torrent_files{}; auto files = tr_torrent_files{};
files.add(Path1, Size); files.add(Path1, Size);
EXPECT_EQ(size_t{ 1U }, files.fileCount()); EXPECT_EQ(size_t{ 1U }, files.file_count());
files.add(Path2, Size); files.add(Path2, Size);
EXPECT_EQ(size_t{ 2U }, files.fileCount()); EXPECT_EQ(size_t{ 2U }, files.file_count());
files.clear(); files.clear();
EXPECT_TRUE(std::empty(files)); EXPECT_TRUE(std::empty(files));
EXPECT_EQ(size_t{ 0U }, files.fileCount()); EXPECT_EQ(size_t{ 0U }, files.file_count());
} }
TEST_F(TorrentFilesTest, find) TEST_F(TorrentFilesTest, find)
@@ -135,10 +135,10 @@ TEST_F(TorrentFilesTest, hasAnyLocalData)
auto const search_path_2 = tr_pathbuf{ "/tmp"sv }; auto const search_path_2 = tr_pathbuf{ "/tmp"sv };
auto search_path = std::vector<std::string_view>{ search_path_1.sv(), search_path_2.sv() }; auto search_path = std::vector<std::string_view>{ search_path_1.sv(), search_path_2.sv() };
EXPECT_TRUE(files.hasAnyLocalData(std::data(search_path), 2U)); EXPECT_TRUE(files.has_any_local_data(std::data(search_path), 2U));
EXPECT_TRUE(files.hasAnyLocalData(std::data(search_path), 1U)); EXPECT_TRUE(files.has_any_local_data(std::data(search_path), 1U));
EXPECT_FALSE(files.hasAnyLocalData(std::data(search_path) + 1, 1U)); EXPECT_FALSE(files.has_any_local_data(std::data(search_path) + 1, 1U));
EXPECT_FALSE(files.hasAnyLocalData(std::data(search_path), 0U)); EXPECT_FALSE(files.has_any_local_data(std::data(search_path), 0U));
} }
TEST_F(TorrentFilesTest, isSubpathPortable) TEST_F(TorrentFilesTest, isSubpathPortable)
@@ -179,6 +179,6 @@ TEST_F(TorrentFilesTest, isSubpathPortable)
for (auto const& [subpath, expected] : Tests) for (auto const& [subpath, expected] : Tests)
{ {
EXPECT_EQ(expected, tr_torrent_files::isSubpathPortable(subpath)) << " subpath " << subpath; EXPECT_EQ(expected, tr_torrent_files::is_subpath_sanitized(subpath)) << " subpath " << subpath;
} }
} }

View File

@@ -217,11 +217,11 @@ int tr_main(int argc, char* argv[])
for (tr_file_index_t i = 0; i < n_files; ++i) for (tr_file_index_t i = 0; i < n_files; ++i)
{ {
auto const& path = builder.path(i); auto const& path = builder.path(i);
if (!tr_torrent_files::isSubpathPortable(path)) if (!tr_torrent_files::is_subpath_sanitized(path, false))
{ {
fmt::print(stderr, "WARNING\n"); fmt::print(stderr, "WARNING\n");
fmt::print(stderr, "filename \"{:s}\" may not be portable on all systems.\n", path); fmt::print(stderr, "filename \"{:s}\" may not be portable on all systems.\n", path);
fmt::print(stderr, "consider \"{:s}\" instead.\n", tr_torrent_files::makeSubpathPortable(path)); fmt::print(stderr, "consider \"{:s}\" instead.\n", tr_torrent_files::sanitize_subpath(path, false));
} }
} }