mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 (128–255),
|
// characters and characters in the extended character set (128–255),
|
||||||
// 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('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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) });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user