make file attributes (in file_storage) type safe

This commit is contained in:
arvidn 2017-08-03 02:31:03 -07:00 committed by Arvid Norberg
parent 35232a5a5f
commit a8a5986046
8 changed files with 200 additions and 190 deletions

View File

@ -16,6 +16,7 @@
#include "libtorrent/peer_info.hpp"
#include "libtorrent/alert_types.hpp" // for picker_flags_t
#include "libtorrent/session_types.hpp" // for save_state_flags_t
#include "libtorrent/file_storage.hpp" // for file_flags_t
#include "libtorrent/alert.hpp"
#include <vector>
@ -312,6 +313,7 @@ void bind_converters()
to_python_converter<lt::save_state_flags_t, from_bitfield_flag<lt::save_state_flags_t>>();
to_python_converter<lt::session_flags_t, from_bitfield_flag<lt::session_flags_t>>();
to_python_converter<lt::remove_flags_t, from_bitfield_flag<lt::remove_flags_t>>();
to_python_converter<lt::file_flags_t, from_bitfield_flag<lt::file_flags_t>>();
// work-around types
to_python_converter<lt::aux::noexcept_movable<lt::address>, address_to_tuple<
@ -369,4 +371,5 @@ void bind_converters()
to_bitfield_flag<lt::save_state_flags_t>();
to_bitfield_flag<lt::session_flags_t>();
to_bitfield_flag<lt::remove_flags_t>();
to_bitfield_flag<lt::file_flags_t>();
}

View File

@ -102,7 +102,7 @@ namespace
{ return FileIter(self, self.end_file()); }
void add_file_wstring(file_storage& fs, std::wstring const& file, std::int64_t size
, int flags, std::time_t md, std::string link)
, file_flags_t const flags, std::time_t md, std::string link)
{
fs.add_file(file, size, flags, md, link);
}
@ -115,7 +115,7 @@ namespace
}
void add_file(file_storage& fs, std::string const& file, std::int64_t size
, int flags, std::time_t md, std::string link)
, file_flags_t const flags, std::time_t md, std::string link)
{
fs.add_file(file, size, flags, md, link);
}
@ -124,6 +124,8 @@ namespace
{
ct.add_tracker(url, tier);
}
struct dummy13 {};
}
void bind_create_torrent()
@ -145,14 +147,15 @@ void bind_create_torrent()
std::string (file_storage::*file_storage_file_path)(file_index_t, std::string const&) const = &file_storage::file_path;
std::int64_t (file_storage::*file_storage_file_size)(file_index_t) const = &file_storage::file_size;
std::int64_t (file_storage::*file_storage_file_offset)(file_index_t) const = &file_storage::file_offset;
std::uint32_t (file_storage::*file_storage_file_flags)(file_index_t) const = &file_storage::file_flags;
file_flags_t (file_storage::*file_storage_file_flags)(file_index_t) const = &file_storage::file_flags;
#ifndef TORRENT_NO_DEPRECATE
file_entry (file_storage::*at)(int) const = &file_storage::at;
#endif
// TODO: 3 move this to its own file
class_<file_storage>("file_storage")
{
scope s = class_<file_storage>("file_storage")
.def("is_valid", &file_storage::is_valid)
.def("add_file", add_file, (arg("path"), arg("size"), arg("flags") = 0, arg("mtime") = 0, arg("linkpath") = ""))
.def("num_files", &file_storage::num_files)
@ -185,12 +188,19 @@ void bind_create_torrent()
.def("name", &file_storage::name, return_value_policy<copy_const_reference>())
;
enum_<file_storage::file_flags_t>("file_flags_t")
.value("flag_pad_file", file_storage::flag_pad_file)
.value("flag_hidden", file_storage::flag_hidden)
.value("flag_executable", file_storage::flag_executable)
.value("flag_symlink", file_storage::flag_symlink)
;
s.attr("flag_pad_file") = file_storage::flag_pad_file;
s.attr("flag_hidden") = file_storage::flag_hidden;
s.attr("flag_executable") = file_storage::flag_executable;
s.attr("flag_symlink") = file_storage::flag_symlink;
}
{
scope s = class_<dummy13>("file_flags_t");
s.attr("flag_pad_file") = file_storage::flag_pad_file;
s.attr("flag_hidden") = file_storage::flag_hidden;
s.attr("flag_executable") = file_storage::flag_executable;
s.attr("flag_symlink") = file_storage::flag_symlink;
}
class_<create_torrent>("create_torrent", no_init)
.def(init<file_storage&>())

View File

@ -190,7 +190,7 @@ int main(int argc, char* argv[])
{
piece_index_t const first = st.map_file(i, 0, 0).piece;
piece_index_t const last = st.map_file(i, (std::max)(std::int64_t(st.file_size(i))-1, std::int64_t(0)), 0).piece;
int const flags = st.file_flags(i);
auto const flags = st.file_flags(i);
std::stringstream file_hash;
if (!st.hash(i).is_all_zeros())
file_hash << st.hash(i);

View File

@ -46,6 +46,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/string_view.hpp"
#include "libtorrent/aux_/vector.hpp"
#include "libtorrent/aux_/noexcept_movable.hpp"
#include "libtorrent/flags.hpp"
namespace libtorrent {
@ -192,6 +193,10 @@ namespace libtorrent {
std::int64_t size;
};
// hidden
struct file_flags_tag;
using file_flags_t = flags::bitfield_flag<std::uint8_t, file_flags_tag>;
// The ``file_storage`` class represents a file list and the piece
// size. Everything necessary to interpret a regular bittorrent storage
// file structure.
@ -214,25 +219,12 @@ namespace libtorrent {
// not.
bool is_valid() const { return m_piece_length > 0; }
// file attribute flags
enum flags_t
{
// the file is a pad file. It's required to contain zeros
// at it will not be saved to disk. Its purpose is to make
// the following file start on a piece boundary.
pad_file = 1,
// this file has the hidden attribute set. This is primarily
// a windows attribute
attribute_hidden = 2,
// this file has the executable attribute set.
attribute_executable = 4,
// this file is a symbolic link. It should have a link
// target string associated with it.
attribute_symlink = 8
};
#ifndef TORRENT_NO_DEPRECATE
static constexpr file_flags_t TORRENT_DEPRECATED_MEMBER pad_file = 0_bit;
static constexpr file_flags_t TORRENT_DEPRECATED_MEMBER attribute_hidden = 1_bit;
static constexpr file_flags_t TORRENT_DEPRECATED_MEMBER attribute_executable = 2_bit;
static constexpr file_flags_t TORRENT_DEPRECATED_MEMBER attribute_symlink = 3_bit;
#endif
// allocates space for ``num_files`` in the internal file list. This can
// be used to avoid reallocating the internal file list when the number
@ -278,9 +270,10 @@ namespace libtorrent {
// can be changed by calling ``set_name``.
void add_file_borrow(char const* filename, int filename_len
, std::string const& path, std::int64_t file_size
, std::uint32_t file_flags = 0, char const* filehash = 0
, file_flags_t file_flags = {}, char const* filehash = 0
, std::int64_t mtime = 0, string_view symlink_path = string_view());
void add_file(std::string const& path, std::int64_t file_size, std::uint32_t file_flags = 0
void add_file(std::string const& path, std::int64_t file_size
, file_flags_t file_flags = {}
, std::time_t mtime = 0, string_view symlink_path = string_view());
// renames the file at ``index`` to ``new_filename``. Keep in mind
@ -295,7 +288,8 @@ namespace libtorrent {
// instead, use the wchar -> utf8 conversion functions
// and pass in utf8 strings
TORRENT_DEPRECATED
void add_file(std::wstring const& p, std::int64_t size, std::uint32_t flags = 0
void add_file(std::wstring const& p, std::int64_t size
, file_flags_t flags = {}
, std::time_t mtime = 0, string_view s_p = "");
TORRENT_DEPRECATED
void rename_file(file_index_t index, std::wstring const& new_filename);
@ -477,35 +471,27 @@ namespace libtorrent {
// the set.
void all_path_hashes(std::unordered_set<std::uint32_t>& table) const;
// flags indicating various attributes for files in
// a file_storage.
enum file_flags_t : std::uint32_t
{
// this file is a pad file. The creator of the
// torrent promises the file is entirely filled with
// zeros and does not need to be downloaded. The
// purpose is just to align the next file to either
// a block or piece boundary.
flag_pad_file = 1,
// the file is a pad file. It's required to contain zeros
// at it will not be saved to disk. Its purpose is to make
// the following file start on a piece boundary.
static constexpr file_flags_t flag_pad_file = 0_bit;
// this file is hidden (sets the hidden attribute
// on windows)
flag_hidden = 2,
// this file has the hidden attribute set. This is primarily
// a windows attribute
static constexpr file_flags_t flag_hidden = 1_bit;
// this file is executable (sets the executable bit
// on posix like systems)
flag_executable = 4,
// this file has the executable attribute set.
static constexpr file_flags_t flag_executable = 2_bit;
// this file is a symlink. The symlink target is
// specified in a separate field
flag_symlink = 8
};
// this file is a symbolic link. It should have a link
// target string associated with it.
static constexpr file_flags_t flag_symlink = 3_bit;
std::vector<std::string> const& paths() const { return m_paths; }
// returns a bitmask of flags from file_flags_t that apply
// to file at ``index``.
std::uint32_t file_flags(file_index_t index) const;
file_flags_t file_flags(file_index_t index) const;
// returns true if the file at the specified index has been renamed to
// have an absolute path, i.e. is not anchored in the save path of the

View File

@ -50,148 +50,147 @@ POSSIBILITY OF SUCH DAMAGE.
using namespace std::placeholders;
namespace libtorrent {
namespace {
namespace {
bool default_pred(std::string const&) { return true; }
inline bool default_pred(std::string const&) { return true; }
bool ignore_subdir(std::string const& leaf)
{ return leaf == ".." || leaf == "."; }
inline bool ignore_subdir(std::string const& leaf)
{ return leaf == ".." || leaf == "."; }
std::uint32_t get_file_attributes(std::string const& p)
{
file_flags_t get_file_attributes(std::string const& p)
{
#ifdef TORRENT_WINDOWS
WIN32_FILE_ATTRIBUTE_DATA attr;
std::wstring path = convert_to_wstring(p);
GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr);
if (attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES) return 0;
if (attr.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return file_storage::attribute_hidden;
return 0;
WIN32_FILE_ATTRIBUTE_DATA attr;
std::wstring path = convert_to_wstring(p);
GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr);
if (attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES) return {};
if (attr.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return file_storage::flag_hidden;
return {};
#else
struct ::stat s;
if (::lstat(convert_to_native(p).c_str(), &s) < 0) return 0;
std::uint32_t file_attr = 0;
if (s.st_mode & S_IXUSR)
file_attr += file_storage::attribute_executable;
if (S_ISLNK(s.st_mode))
file_attr += file_storage::attribute_symlink;
return file_attr;
struct ::stat s;
if (::lstat(convert_to_native(p).c_str(), &s) < 0) return {};
file_flags_t file_attr = {};
if (s.st_mode & S_IXUSR)
file_attr |= file_storage::flag_executable;
if (S_ISLNK(s.st_mode))
file_attr |= file_storage::flag_symlink;
return file_attr;
#endif
}
}
#ifndef TORRENT_WINDOWS
std::string get_symlink_path_impl(char const* path)
{
constexpr int MAX_SYMLINK_PATH = 200;
std::string get_symlink_path_impl(char const* path)
{
constexpr int MAX_SYMLINK_PATH = 200;
char buf[MAX_SYMLINK_PATH];
std::string f = convert_to_native(path);
int char_read = int(readlink(f.c_str(), buf, MAX_SYMLINK_PATH));
if (char_read < 0) return "";
if (char_read < MAX_SYMLINK_PATH) buf[char_read] = 0;
else buf[0] = 0;
return convert_from_native(buf);
}
char buf[MAX_SYMLINK_PATH];
std::string f = convert_to_native(path);
int char_read = int(readlink(f.c_str(), buf, MAX_SYMLINK_PATH));
if (char_read < 0) return "";
if (char_read < MAX_SYMLINK_PATH) buf[char_read] = 0;
else buf[0] = 0;
return convert_from_native(buf);
}
#endif
std::string get_symlink_path(std::string const& p)
{
std::string get_symlink_path(std::string const& p)
{
#if defined TORRENT_WINDOWS
TORRENT_UNUSED(p);
return "";
TORRENT_UNUSED(p);
return "";
#else
return get_symlink_path_impl(p.c_str());
return get_symlink_path_impl(p.c_str());
#endif
}
}
void add_files_impl(file_storage& fs, std::string const& p
, std::string const& l, std::function<bool(std::string)> pred
, std::uint32_t const flags)
{
std::string f = combine_path(p, l);
if (!pred(f)) return;
error_code ec;
file_status s;
stat_file(f, &s, ec, (flags & create_torrent::symlinks) ? dont_follow_links : 0);
if (ec) return;
void add_files_impl(file_storage& fs, std::string const& p
, std::string const& l, std::function<bool(std::string)> pred
, std::uint32_t const flags)
{
std::string f = combine_path(p, l);
if (!pred(f)) return;
error_code ec;
file_status s;
stat_file(f, &s, ec, (flags & create_torrent::symlinks) ? dont_follow_links : 0);
if (ec) return;
// recurse into directories
bool recurse = (s.mode & file_status::directory) != 0;
// recurse into directories
bool recurse = (s.mode & file_status::directory) != 0;
// if the file is not a link or we're following links, and it's a directory
// only then should we recurse
// if the file is not a link or we're following links, and it's a directory
// only then should we recurse
#ifndef TORRENT_WINDOWS
if ((s.mode & file_status::link) && (flags & create_torrent::symlinks))
recurse = false;
if ((s.mode & file_status::link) && (flags & create_torrent::symlinks))
recurse = false;
#endif
if (recurse)
if (recurse)
{
for (directory i(f, ec); !i.done(); i.next(ec))
{
for (directory i(f, ec); !i.done(); i.next(ec))
{
std::string leaf = i.file();
if (ignore_subdir(leaf)) continue;
add_files_impl(fs, p, combine_path(l, leaf), pred, flags);
}
std::string leaf = i.file();
if (ignore_subdir(leaf)) continue;
add_files_impl(fs, p, combine_path(l, leaf), pred, flags);
}
}
else
{
// #error use the fields from s
file_flags_t const file_flags = get_file_attributes(f);
// mask all bits to check if the file is a symlink
if ((file_flags & file_storage::flag_symlink)
&& (flags & create_torrent::symlinks))
{
std::string sym_path = get_symlink_path(f);
fs.add_file(l, 0, file_flags, std::time_t(s.mtime), sym_path);
}
else
{
// #error use the fields from s
std::uint32_t file_flags = get_file_attributes(f);
// mask all bits to check if the file is a symlink
if ((file_flags & file_storage::attribute_symlink)
&& (flags & create_torrent::symlinks))
{
std::string sym_path = get_symlink_path(f);
fs.add_file(l, 0, file_flags, std::time_t(s.mtime), sym_path);
}
else
{
fs.add_file(l, s.file_size, file_flags, std::time_t(s.mtime));
}
fs.add_file(l, s.file_size, file_flags, std::time_t(s.mtime));
}
}
}
struct hash_state
{
create_torrent& ct;
storage_holder storage;
disk_io_thread& iothread;
piece_index_t piece_counter;
piece_index_t completed_piece;
std::function<void(piece_index_t)> const& f;
error_code& ec;
};
struct hash_state
{
create_torrent& ct;
storage_holder storage;
disk_io_thread& iothread;
piece_index_t piece_counter;
piece_index_t completed_piece;
std::function<void(piece_index_t)> const& f;
error_code& ec;
};
void on_hash(piece_index_t const piece, sha1_hash const& piece_hash
, storage_error const& error, hash_state* st)
void on_hash(piece_index_t const piece, sha1_hash const& piece_hash
, storage_error const& error, hash_state* st)
{
if (error)
{
if (error)
{
// on error
st->ec = error.ec;
st->iothread.abort(true);
return;
}
st->ct.set_hash(piece, piece_hash);
st->f(st->completed_piece);
++st->completed_piece;
if (st->piece_counter < st->ct.files().end_piece())
{
st->iothread.async_hash(st->storage, st->piece_counter
, disk_interface::sequential_access
, std::bind(&on_hash, _1, _2, _3, st));
++st->piece_counter;
}
else
{
st->iothread.abort(true);
}
st->iothread.submit_jobs();
// on error
st->ec = error.ec;
st->iothread.abort(true);
return;
}
st->ct.set_hash(piece, piece_hash);
st->f(st->completed_piece);
++st->completed_piece;
if (st->piece_counter < st->ct.files().end_piece())
{
st->iothread.async_hash(st->storage, st->piece_counter
, disk_interface::sequential_access
, std::bind(&on_hash, _1, _2, _3, st));
++st->piece_counter;
}
else
{
st->iothread.abort(true);
}
st->iothread.submit_jobs();
}
} // anonymous namespace
} // anonymous namespace
#ifndef TORRENT_NO_DEPRECATE
@ -526,7 +525,7 @@ namespace libtorrent {
file_index_t const first(0);
if (m_include_mtime) info["mtime"] = m_files.mtime(first);
info["length"] = m_files.file_size(first);
std::uint32_t const flags = m_files.file_flags(first);
file_flags_t const flags = m_files.file_flags(first);
if (flags & (file_storage::flag_pad_file
| file_storage::flag_hidden
| file_storage::flag_executable
@ -577,8 +576,8 @@ namespace libtorrent {
path_e.list().push_back(entry(e));
}
std::uint32_t const flags = m_files.file_flags(i);
if (flags != 0)
file_flags_t const flags = m_files.file_flags(i);
if (flags)
{
std::string& attr = file_e["attr"].string();
if (flags & file_storage::flag_pad_file) attr += 'p';

View File

@ -56,6 +56,18 @@ using namespace std::placeholders;
namespace libtorrent {
constexpr file_flags_t file_storage::flag_pad_file;
constexpr file_flags_t file_storage::flag_hidden;
constexpr file_flags_t file_storage::flag_executable;
constexpr file_flags_t file_storage::flag_symlink;
#ifndef TORRENT_NO_DEPRECATE
constexpr file_flags_t file_storage::pad_file;
constexpr file_flags_t file_storage::attribute_hidden;
constexpr file_flags_t file_storage::attribute_executable;
constexpr file_flags_t file_storage::attribute_symlink;
#endif
file_storage::file_storage()
: m_piece_length(0)
, m_num_pieces(0)
@ -337,7 +349,7 @@ namespace {
void file_storage::add_file(file_entry const& fe, char const* filehash)
{
std::uint32_t flags = 0;
file_flags_t flags = {};
if (fe.pad_file) flags |= file_storage::flag_pad_file;
if (fe.hidden_attribute) flags |= file_storage::flag_hidden;
if (fe.executable_attribute) flags |= file_storage::flag_executable;
@ -359,7 +371,7 @@ namespace {
}
void file_storage::add_file(std::wstring const& file, std::int64_t file_size
, std::uint32_t file_flags, std::time_t mtime, string_view symlink_path)
, file_flags_t const file_flags, std::time_t mtime, string_view symlink_path)
{
add_file(wchar_utf8(file), file_size, file_flags, mtime, symlink_path);
}
@ -532,7 +544,7 @@ namespace {
}
void file_storage::add_file(std::string const& path, std::int64_t file_size
, std::uint32_t file_flags, std::time_t mtime, string_view symlink_path)
, file_flags_t const file_flags, std::time_t mtime, string_view symlink_path)
{
add_file_borrow(nullptr, 0, path, file_size, file_flags, nullptr, mtime
, symlink_path);
@ -540,7 +552,7 @@ namespace {
void file_storage::add_file_borrow(char const* filename, int const filename_len
, std::string const& path, std::int64_t const file_size
, std::uint32_t const file_flags, char const* filehash
, file_flags_t const file_flags, char const* filehash
, std::int64_t const mtime, string_view symlink_path)
{
TORRENT_ASSERT_PRECOND(file_size >= 0);
@ -576,10 +588,10 @@ namespace {
e.size = aux::numeric_cast<std::uint64_t>(file_size);
e.offset = aux::numeric_cast<std::uint64_t>(m_total_size);
e.pad_file = (file_flags & file_storage::flag_pad_file) != 0;
e.hidden_attribute = (file_flags & file_storage::flag_hidden) != 0;
e.executable_attribute = (file_flags & file_storage::flag_executable) != 0;
e.symlink_attribute = (file_flags & file_storage::flag_symlink) != 0;
e.pad_file = bool(file_flags & file_storage::flag_pad_file);
e.hidden_attribute = bool(file_flags & file_storage::flag_hidden);
e.executable_attribute = bool(file_flags & file_storage::flag_executable);
e.symlink_attribute = bool(file_flags & file_storage::flag_symlink);
if (filehash)
{
@ -805,14 +817,14 @@ namespace {
return m_files[index].offset;
}
std::uint32_t file_storage::file_flags(file_index_t const index) const
file_flags_t file_storage::file_flags(file_index_t const index) const
{
TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
internal_file_entry const& fe = m_files[index];
return (fe.pad_file ? flag_pad_file : 0u)
| (fe.hidden_attribute ? flag_hidden : 0u)
| (fe.executable_attribute ? flag_executable : 0u)
| (fe.symlink_attribute ? flag_symlink : 0u);
return (fe.pad_file ? file_storage::flag_pad_file : file_flags_t{})
| (fe.hidden_attribute ? file_storage::flag_hidden : file_flags_t{})
| (fe.executable_attribute ? file_storage::flag_executable : file_flags_t{})
| (fe.symlink_attribute ? file_storage::flag_symlink : file_flags_t{});
}
bool file_storage::file_absolute_path(file_index_t const index) const

View File

@ -317,11 +317,11 @@ namespace libtorrent {
if (path.empty()) path = "_";
}
namespace {
namespace {
std::uint32_t get_file_attributes(bdecode_node const& dict)
file_flags_t get_file_attributes(bdecode_node const& dict)
{
std::uint32_t file_flags = 0;
file_flags_t file_flags = {};
bdecode_node const attr = dict.dict_find_string("attr");
if (attr)
{
@ -370,7 +370,7 @@ namespace libtorrent {
{
if (dict.type() != bdecode_node::dict_t) return false;
std::uint32_t file_flags = get_file_attributes(dict);
file_flags_t file_flags = get_file_attributes(dict);
// symlinks have an implied "size" of zero. i.e. they use up 0 bytes of
// the torrent payload space
@ -445,7 +445,7 @@ namespace libtorrent {
// bitcomet pad file
if (path.find("_____padding_file_") != std::string::npos)
file_flags = file_storage::flag_pad_file;
file_flags |= file_storage::flag_pad_file;
bdecode_node const fh = dict.dict_find_string("sha1");
char const* filehash = nullptr;
@ -566,7 +566,7 @@ namespace libtorrent {
return 0;
}
} // anonymous namespace
} // anonymous namespace
web_seed_entry::web_seed_entry(std::string const& url_, type_t type_
, std::string const& auth_

View File

@ -683,8 +683,8 @@ TORRENT_TEST(parse_torrents)
else if (std::string(test_torrents[i].file) == "pad_file.torrent")
{
TEST_EQUAL(ti->num_files(), 2);
TEST_EQUAL(ti->files().file_flags(file_index_t{0}) & file_storage::flag_pad_file, false);
TEST_EQUAL(ti->files().file_flags(file_index_t{1}) & file_storage::flag_pad_file, true);
TEST_EQUAL(bool(ti->files().file_flags(file_index_t{0}) & file_storage::flag_pad_file), false);
TEST_EQUAL(bool(ti->files().file_flags(file_index_t{1}) & file_storage::flag_pad_file), true);
}
else if (std::string(test_torrents[i].file) == "creation_date.torrent")
{
@ -776,7 +776,7 @@ TORRENT_TEST(parse_torrents)
{
piece_index_t const first = ti->map_file(i, 0, 0).piece;
piece_index_t const last = ti->map_file(i, std::max(fs.file_size(i)-1, std::int64_t(0)), 0).piece;
int const flags = fs.file_flags(i);
file_flags_t const flags = fs.file_flags(i);
sha1_hash const ih = fs.hash(i);
std::printf(" %11" PRId64 " %c%c%c%c [ %4d, %4d ] %7u %s %s %s%s\n"
, fs.file_size(i)