fixed issue with creating torrents with a single file in a directory and some security issues with paths in torrents

This commit is contained in:
Arvid Norberg
2005-05-11 23:03:12 +00:00
parent d38e66a223
commit 7814b03370
9 changed files with 119 additions and 95 deletions

View File

@@ -655,6 +655,9 @@ in the torrent, you can use ``begin_files()``, ``end_files()``,
``rbegin_files()`` and ``rend_files()``. These will give you standard vector ``rbegin_files()`` and ``rend_files()``. These will give you standard vector
iterators with the type ``file_entry``. iterators with the type ``file_entry``.
The ``path`` is the full (relative) path of each file. i.e. if it is a multi-file
torrent, all the files starts with a directory with the same name as ``torrent_info::name()``.
:: ::
struct file_entry struct file_entry

View File

@@ -81,8 +81,7 @@ int main(int argc, char* argv[])
std::cout << "info hash: " << t.info_hash() << "\n"; std::cout << "info hash: " << t.info_hash() << "\n";
std::cout << "files:\n"; std::cout << "files:\n";
for (torrent_info::file_iterator i = t.begin_files(); for (torrent_info::file_iterator i = t.begin_files();
i != t.end_files(); i != t.end_files(); ++i)
++i)
{ {
std::cout << " " << std::setw(11) << i->size std::cout << " " << std::setw(11) << i->size
<< " " << i->path.string() << "\n"; << " " << i->path.string() << "\n";

View File

@@ -62,6 +62,7 @@ void add_files(
} }
else else
{ {
std::cerr << "adding \"" << l.string() << "\"\n";
file fi(f, file::in); file fi(f, file::in);
fi.seek(0, file::end); fi.seek(0, file::end);
libtorrent::size_type size = fi.tell(); libtorrent::size_type size = fi.tell();

View File

@@ -181,6 +181,13 @@ namespace libtorrent
// an optional string naming the software used // an optional string naming the software used
// to create the torrent file // to create the torrent file
std::string m_created_by; std::string m_created_by;
// this is used when creating a torrent. If there's
// only one file there are cases where it's impossible
// to know if it should be written as a multifile torrent
// or not. e.g. test/test there's one file and one directory
// and they have the same name.
bool m_multifile;
}; };
} }

View File

@@ -386,6 +386,7 @@ namespace libtorrent
assert(peer_count < 2048); assert(peer_count < 2048);
m_piece_map[i].peer_count++; m_piece_map[i].peer_count++;
assert(m_piece_map[i].peer_count != 0);
// if we have the piece, we don't have to move // if we have the piece, we don't have to move
// any entries in the piece_info vector // any entries in the piece_info vector

View File

@@ -47,6 +47,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"
#include "libtorrent/peer_connection.hpp" #include "libtorrent/peer_connection.hpp"
#include "libtorrent/alert_types.hpp" #include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
#if defined(_MSC_VER) && _MSC_VER < 1300 #if defined(_MSC_VER) && _MSC_VER < 1300
# define for if (false) {} else for # define for if (false) {} else for
@@ -339,6 +340,8 @@ namespace libtorrent
// choked. // choked.
policy::peer* policy::find_choke_candidate() policy::peer* policy::find_choke_candidate()
{ {
INVARIANT_CHECK;
peer* worst_peer = 0; peer* worst_peer = 0;
size_type min_weight = std::numeric_limits<int>::min(); size_type min_weight = std::numeric_limits<int>::min();
@@ -381,6 +384,8 @@ namespace libtorrent
policy::peer* policy::find_unchoke_candidate() policy::peer* policy::find_unchoke_candidate()
{ {
INVARIANT_CHECK;
// if all of our peers are unchoked, there's // if all of our peers are unchoked, there's
// no left to unchoke // no left to unchoke
if (m_num_unchoked == m_torrent->num_peers()) if (m_num_unchoked == m_torrent->num_peers())
@@ -463,8 +468,7 @@ namespace libtorrent
policy::peer* candidate =0; policy::peer* candidate =0;
for (std::vector<peer>::iterator i = m_peers.begin(); for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end(); i != m_peers.end(); ++i)
++i)
{ {
if(i->connection) continue; if(i->connection) continue;
if(i->banned) continue; if(i->banned) continue;
@@ -488,6 +492,8 @@ namespace libtorrent
policy::peer* policy::find_seed_choke_candidate() policy::peer* policy::find_seed_choke_candidate()
{ {
INVARIANT_CHECK;
// first choice candidate. // first choice candidate.
// it is a candidate we owe nothing to and which has been unchoked // it is a candidate we owe nothing to and which has been unchoked
// the longest. // the longest.
@@ -506,8 +512,7 @@ namespace libtorrent
size_type lowest_share_diff = 0; // not valid when secondCandidate==0 size_type lowest_share_diff = 0; // not valid when secondCandidate==0
for (std::vector<peer>::iterator i = m_peers.begin(); for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end(); i != m_peers.end(); ++i)
++i)
{ {
peer_connection* c = i->connection; peer_connection* c = i->connection;
// ignore peers that are choked or // ignore peers that are choked or
@@ -520,13 +525,13 @@ namespace libtorrent
// select as second candidate the one that we owe the least // select as second candidate the one that we owe the least
// to // to
if(!second_candidate || share_diff <= lowest_share_diff) if (!second_candidate || share_diff <= lowest_share_diff)
{ {
lowest_share_diff = share_diff; lowest_share_diff = share_diff;
second_candidate = &(*i); second_candidate = &(*i);
} }
// select as first candidate the one that we don't owe anything to // select as first candidate the one that we don't owe anything to
// and has been waiting for an unchoke the longest // and has been waiting for an unchoke the longest
if (share_diff > 0) continue; if (share_diff > 0) continue;
if (!candidate || last_unchoke > i->last_optimistically_unchoked) if (!candidate || last_unchoke > i->last_optimistically_unchoked)
@@ -543,13 +548,14 @@ namespace libtorrent
policy::peer* policy::find_seed_unchoke_candidate() policy::peer* policy::find_seed_unchoke_candidate()
{ {
INVARIANT_CHECK;
peer* candidate = 0; peer* candidate = 0;
boost::posix_time::ptime last_unchoke boost::posix_time::ptime last_unchoke
= second_clock::universal_time(); = second_clock::universal_time();
for (std::vector<peer>::iterator i = m_peers.begin(); for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end(); i != m_peers.end(); ++i)
++i)
{ {
peer_connection* c = i->connection; peer_connection* c = i->connection;
if (c == 0) continue; if (c == 0) continue;
@@ -568,6 +574,7 @@ namespace libtorrent
peer* p = find_seed_unchoke_candidate(); peer* p = find_seed_unchoke_candidate();
if (p != 0) if (p != 0)
{ {
assert(p->connection->is_choked());
p->connection->send_unchoke(); p->connection->send_unchoke();
p->last_optimistically_unchoked p->last_optimistically_unchoked
= second_clock::universal_time(); = second_clock::universal_time();
@@ -578,6 +585,8 @@ namespace libtorrent
void policy::pulse() void policy::pulse()
{ {
INVARIANT_CHECK;
if (m_torrent->is_paused()) return; if (m_torrent->is_paused()) return;
using namespace boost::posix_time; using namespace boost::posix_time;
@@ -697,6 +706,7 @@ namespace libtorrent
peer* p = find_seed_choke_candidate(); peer* p = find_seed_choke_candidate();
assert(p != 0); assert(p != 0);
assert(!p->connection->is_choked());
p->connection->send_choke(); p->connection->send_choke();
--m_num_unchoked; --m_num_unchoked;
} }
@@ -749,6 +759,7 @@ namespace libtorrent
peer* p = find_choke_candidate(); peer* p = find_choke_candidate();
if (!p) break; if (!p) break;
assert(p); assert(p);
assert(!p->connection->is_choked());
p->connection->send_choke(); p->connection->send_choke();
--m_num_unchoked; --m_num_unchoked;
} }
@@ -759,6 +770,7 @@ namespace libtorrent
peer* p = find_choke_candidate(); peer* p = find_choke_candidate();
if (p) if (p)
{ {
assert(!p->connection->is_choked());
p->connection->send_choke(); p->connection->send_choke();
--m_num_unchoked; --m_num_unchoked;
unchoke_one_peer(); unchoke_one_peer();
@@ -770,9 +782,6 @@ namespace libtorrent
while (m_num_unchoked < m_torrent->m_uploads_quota.given while (m_num_unchoked < m_torrent->m_uploads_quota.given
&& unchoke_one_peer()); && unchoke_one_peer());
} }
#ifndef NDEBUG
check_invariant();
#endif
} }
void policy::ban_peer(const peer_connection& c) void policy::ban_peer(const peer_connection& c)
@@ -1016,6 +1025,7 @@ namespace libtorrent
assert(p->connection); assert(p->connection);
assert(!p->connection->is_disconnecting()); assert(!p->connection->is_disconnecting());
assert(p->connection->is_choked());
p->connection->send_unchoke(); p->connection->send_unchoke();
p->last_optimistically_unchoked = second_clock::universal_time(); p->last_optimistically_unchoked = second_clock::universal_time();
++m_num_unchoked; ++m_num_unchoked;
@@ -1068,6 +1078,10 @@ namespace libtorrent
// this is called whenever a peer connection is closed // this is called whenever a peer connection is closed
void policy::connection_closed(const peer_connection& c) void policy::connection_closed(const peer_connection& c)
{ {
INVARIANT_CHECK;
bool unchoked = false;
std::vector<peer>::iterator i = std::find_if( std::vector<peer>::iterator i = std::find_if(
m_peers.begin() m_peers.begin()
, m_peers.end() , m_peers.end()
@@ -1080,11 +1094,7 @@ namespace libtorrent
i->connected = second_clock::universal_time(); i->connected = second_clock::universal_time();
if (!i->connection->is_choked() && !m_torrent->is_aborted()) if (!i->connection->is_choked() && !m_torrent->is_aborted())
{ {
// if the peer that is diconnecting is unchoked unchoked = true;
// then unchoke another peer in order to maintain
// the total number of unchoked peers
--m_num_unchoked;
unchoke_one_peer();
} }
if (c.failed()) if (c.failed())
@@ -1105,6 +1115,15 @@ namespace libtorrent
i->prev_amount_download += c.statistics().total_payload_download(); i->prev_amount_download += c.statistics().total_payload_download();
i->prev_amount_upload += c.statistics().total_payload_upload(); i->prev_amount_upload += c.statistics().total_payload_upload();
i->connection = 0; i->connection = 0;
if (unchoked)
{
// if the peer that is diconnecting is unchoked
// then unchoke another peer in order to maintain
// the total number of unchoked peers
--m_num_unchoked;
unchoke_one_peer();
}
} }
void policy::peer_is_interesting(peer_connection& c) void policy::peer_is_interesting(peer_connection& c)
@@ -1129,13 +1148,13 @@ namespace libtorrent
assert(m_torrent->m_uploads_quota.given >= 2); assert(m_torrent->m_uploads_quota.given >= 2);
int actual_unchoked = 0; int actual_unchoked = 0;
for (std::vector<peer>::const_iterator i = m_peers.begin(); for (std::vector<peer>::const_iterator i = m_peers.begin();
i != m_peers.end(); i != m_peers.end(); ++i)
++i)
{ {
if (!i->connection) continue; if (!i->connection) continue;
if (!i->connection->is_choked()) actual_unchoked++; if (!i->connection->is_choked()) actual_unchoked++;
} }
assert(actual_unchoked <= m_torrent->m_uploads_quota.given); // assert(actual_unchoked <= m_torrent->m_uploads_quota.given);
assert(actual_unchoked == m_num_unchoked);
} }
#endif #endif

View File

@@ -343,10 +343,6 @@ namespace libtorrent { namespace detail
void session_impl::operator()() void session_impl::operator()()
{ {
eh_initializer(); eh_initializer();
#ifndef NDEBUG
try
{
#endif
if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0) if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0)
{ {
@@ -364,6 +360,8 @@ namespace libtorrent { namespace detail
#endif #endif
for(;;) for(;;)
{ {
try
{
#ifndef NDEBUG #ifndef NDEBUG
{ {
@@ -747,6 +745,23 @@ namespace libtorrent { namespace detail
} }
m_tracker_manager.tick(); m_tracker_manager.tick();
}
catch (std::bad_cast& e)
{
std::cerr << e.what() << "\n";
assert(false);
}
catch (std::exception& e)
{
std::cerr << e.what() << "\n";
assert(false);
}
catch (...)
{
std::cerr << "error!\n";
assert(false);
}
} }
while (!m_tracker_manager.send_finished()) while (!m_tracker_manager.send_finished())
@@ -758,24 +773,6 @@ namespace libtorrent { namespace detail
boost::thread::sleep(t); boost::thread::sleep(t);
} }
#ifndef NDEBUG
}
catch (std::bad_cast& e)
{
std::cerr << e.what() << "\n";
assert(false);
}
catch (std::exception& e)
{
std::cerr << e.what() << "\n";
assert(false);
}
catch (...)
{
std::cerr << "error!\n";
assert(false);
}
#endif
} }
@@ -915,6 +912,7 @@ namespace libtorrent
= m_checker_impl.m_torrents.begin() = m_checker_impl.m_torrents.begin()
, end(m_checker_impl.m_torrents.end()); i != end; ++i) , end(m_checker_impl.m_torrents.end()); i != end; ++i)
{ {
if (i->abort) continue;
ret.push_back(torrent_handle(&m_impl, &m_checker_impl ret.push_back(torrent_handle(&m_impl, &m_checker_impl
, i->info_hash)); , i->info_hash));
} }

View File

@@ -98,17 +98,6 @@ namespace
log.flush(); log.flush();
} }
path get_filename(
libtorrent::torrent_info const& t
, path const& p)
{
assert(t.num_files() > 0);
if (t.num_files() == 1)
return p;
else
return t.name() / p;
}
struct file_entry struct file_entry
{ {
file_entry(boost::shared_ptr<file> const& f_) file_entry(boost::shared_ptr<file> const& f_)
@@ -183,7 +172,7 @@ namespace libtorrent
std::time_t time = 0; std::time_t time = 0;
try try
{ {
path f = p / get_filename(t, i->path); path f = p / i->path;
size = file_size(f); size = file_size(f);
time = last_write_time(f); time = last_write_time(f);
} }
@@ -211,7 +200,7 @@ namespace libtorrent
std::time_t time = 0; std::time_t time = 0;
try try
{ {
path f = p / get_filename(t, i->path); path f = p / i->path;
size = file_size(f); size = file_size(f);
time = last_write_time(f); time = last_write_time(f);
} }
@@ -399,7 +388,7 @@ namespace libtorrent
} }
boost::shared_ptr<file> in(m_pimpl->files.open_file( boost::shared_ptr<file> in(m_pimpl->files.open_file(
m_pimpl->save_path / get_filename(m_pimpl->info, file_iter->path) m_pimpl->save_path / file_iter->path
, file::in)); , file::in));
assert(file_offset < file_iter->size); assert(file_offset < file_iter->size);
@@ -449,7 +438,7 @@ namespace libtorrent
if (left_to_read > 0) if (left_to_read > 0)
{ {
++file_iter; ++file_iter;
path path = m_pimpl->save_path / get_filename(m_pimpl->info, file_iter->path); path path = m_pimpl->save_path / file_iter->path;
file_offset = 0; file_offset = 0;
in = m_pimpl->files.open_file(path, file::in); in = m_pimpl->files.open_file(path, file::in);
@@ -491,7 +480,7 @@ namespace libtorrent
assert(file_iter != m_pimpl->info.end_files()); assert(file_iter != m_pimpl->info.end_files());
} }
path p(m_pimpl->save_path / get_filename(m_pimpl->info, file_iter->path)); path p(m_pimpl->save_path / file_iter->path);
boost::shared_ptr<file> out = m_pimpl->files.open_file(p, file::out | file::in); boost::shared_ptr<file> out = m_pimpl->files.open_file(p, file::out | file::in);
assert(file_offset < file_iter->size); assert(file_offset < file_iter->size);
@@ -547,7 +536,7 @@ namespace libtorrent
++file_iter; ++file_iter;
assert(file_iter != m_pimpl->info.end_files()); assert(file_iter != m_pimpl->info.end_files());
path p = m_pimpl->save_path / get_filename(m_pimpl->info, file_iter->path); path p = m_pimpl->save_path / file_iter->path;
file_offset = 0; file_offset = 0;
out = m_pimpl->files.open_file(p, file::out | file::in); out = m_pimpl->files.open_file(p, file::out | file::in);
out->seek(0); out->seek(0);
@@ -1080,7 +1069,7 @@ namespace libtorrent
for (torrent_info::file_iterator file_iter = m_info.begin_files(), for (torrent_info::file_iterator file_iter = m_info.begin_files(),
end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter) end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter)
{ {
path dir = m_save_path / get_filename(m_info, file_iter->path); path dir = m_save_path / file_iter->path;
if (!exists(dir.branch_path())) if (!exists(dir.branch_path()))
create_directories(dir.branch_path()); create_directories(dir.branch_path());
} }

View File

@@ -62,27 +62,30 @@ using namespace boost::filesystem;
namespace namespace
{ {
void extract_single_file(const entry& dict, file_entry& target) void extract_single_file(const entry& dict, file_entry& target
, std::string const& root_dir)
{ {
target.size = dict["length"].integer(); target.size = dict["length"].integer();
target.path = root_dir;
const entry::list_type& list = dict["path"].list(); const entry::list_type& list = dict["path"].list();
for (entry::list_type::const_iterator i = list.begin(); for (entry::list_type::const_iterator i = list.begin();
i != list.end(); i != list.end(); ++i)
++i)
{ {
target.path /= i->string(); if (i->string() != "..")
target.path /= i->string();
} }
if (target.path.is_complete()) throw std::runtime_error("torrent contains " if (target.path.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '" "a file with an absolute path: '"
+ target.path.native_file_string() + "'"); + target.path.native_file_string() + "'");
} }
void extract_files(const entry::list_type& list, std::vector<file_entry>& target) void extract_files(const entry::list_type& list, std::vector<file_entry>& target
, std::string const& root_dir)
{ {
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i) for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
{ {
target.push_back(file_entry()); target.push_back(file_entry());
extract_single_file(*i, target.back()); extract_single_file(*i, target.back(), root_dir);
} }
} }
@@ -105,6 +108,7 @@ namespace libtorrent
// standard constructor that parses a torrent file // standard constructor that parses a torrent file
torrent_info::torrent_info(const entry& torrent_file) torrent_info::torrent_info(const entry& torrent_file)
: m_creation_date(date(not_a_date_time)) : m_creation_date(date(not_a_date_time))
, m_multifile(false)
{ {
try try
{ {
@@ -125,6 +129,7 @@ namespace libtorrent
, m_info_hash(info_hash) , m_info_hash(info_hash)
, m_name() , m_name()
, m_creation_date(second_clock::universal_time()) , m_creation_date(second_clock::universal_time())
, m_multifile(false)
{ {
} }
@@ -134,6 +139,7 @@ namespace libtorrent
, m_info_hash(0) , m_info_hash(0)
, m_name() , m_name()
, m_creation_date(second_clock::universal_time()) , m_creation_date(second_clock::universal_time())
, m_multifile(false)
{ {
} }
@@ -181,7 +187,12 @@ namespace libtorrent
// extract file name (or the directory name if it's a multifile libtorrent) // extract file name (or the directory name if it's a multifile libtorrent)
m_name = info["name"].string(); m_name = info["name"].string();
path tmp = m_name;
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '" + m_name + "'");
if (tmp.has_branch_path()) throw std::runtime_error(
"torrent contains name with directories: '" + m_name + "'");
// extract file list // extract file list
entry const* i = info.find_key("files"); entry const* i = info.find_key("files");
if (i == 0) if (i == 0)
@@ -190,14 +201,13 @@ namespace libtorrent
// field. // field.
file_entry e; file_entry e;
e.path = m_name; e.path = m_name;
if (e.path.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '" + e.path.native_file_string() + "'");
e.size = info["length"].integer(); e.size = info["length"].integer();
m_files.push_back(e); m_files.push_back(e);
} }
else else
{ {
extract_files(i->list(), m_files); extract_files(i->list(), m_files, m_name);
m_multifile = true;
} }
// calculate total size of all pieces // calculate total size of all pieces
@@ -321,22 +331,24 @@ namespace libtorrent
{ {
assert(file.begin() != file.end()); assert(file.begin() != file.end());
if (m_files.empty()) if (!file.has_branch_path())
{ {
// you have already added at least one file with a
// path to the file (branch_path), which means that
// all the other files need to be in the same top
// directory as the first file.
assert(m_files.empty());
assert(!m_multifile);
m_name = file.string(); m_name = file.string();
} }
else else
{ {
#ifndef NDEBUG #ifndef NDEBUG
if (m_files.size() == 1) if (!m_files.empty())
assert(*m_files.front().path.begin() == *file.begin());
else
assert(m_name == *file.begin()); assert(m_name == *file.begin());
#endif #endif
m_multifile = true;
m_name = *file.begin(); m_name = *file.begin();
if (m_files.size() == 1)
remove_dir(m_files.front().path);
remove_dir(file);
} }
file_entry e; file_entry e;
@@ -352,8 +364,7 @@ namespace libtorrent
m_piece_hash.resize(num_pieces); m_piece_hash.resize(num_pieces);
for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces; for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces;
i != m_piece_hash.end(); i != m_piece_hash.end(); ++i)
++i)
{ {
i->clear(); i->clear();
} }
@@ -374,28 +385,23 @@ namespace libtorrent
{ {
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
// you have to add files to the torrent first
assert(!m_files.empty());
entry info(entry::dictionary_t); entry info(entry::dictionary_t);
assert(!m_files.empty()); info["name"] = m_name;
if (!m_multifile)
if (m_files.size() == 1)
{ {
info["name"] = m_name;
info["length"] = m_files.front().size; info["length"] = m_files.front().size;
} }
else else
{
info["name"] = m_name;
}
if (m_files.size() > 1)
{ {
entry& files = info["files"]; entry& files = info["files"];
files = entry(entry::list_t); files = entry(entry::list_t);
for (std::vector<file_entry>::const_iterator i = m_files.begin(); for (std::vector<file_entry>::const_iterator i = m_files.begin();
i != m_files.end(); i != m_files.end(); ++i)
++i)
{ {
files.list().push_back(entry(entry::dictionary_t)); files.list().push_back(entry(entry::dictionary_t));
entry& file_e = files.list().back(); entry& file_e = files.list().back();
@@ -403,11 +409,12 @@ namespace libtorrent
entry& path_e = file_e["path"]; entry& path_e = file_e["path"];
path_e = entry(entry::list_t); path_e = entry(entry::list_t);
fs::path file_path(i->path); fs::path const& file_path(i->path);
assert(file_path.has_branch_path());
assert(*file_path.begin() == m_name);
for (fs::path::iterator j = file_path.begin(); for (fs::path::iterator j = boost::next(file_path.begin());
j != file_path.end(); j != file_path.end(); ++j)
++j)
{ {
path_e.list().push_back(*j); path_e.list().push_back(*j);
} }