Fix storage initialization (#2944)

If `default_storage::has_any_file` fails during fastresume data check, `piece_manager::check_no_fastresume` will skip storage initialization. In such case, any storage operation that require part file will cause an application crash.
This commit is contained in:
d-komarov 2018-04-21 19:35:55 +03:00 committed by Arvid Norberg
parent e1f99fb309
commit 44479bcca3
3 changed files with 30 additions and 16 deletions

View File

@ -352,10 +352,14 @@ TORRENT_EXPORT void assert_fail(char const* expr, int line
// if production asserts are defined, don't abort, just print the error // if production asserts are defined, don't abort, just print the error
#ifndef TORRENT_PRODUCTION_ASSERTS #ifndef TORRENT_PRODUCTION_ASSERTS
#ifdef _MSC_VER
__debugbreak();
#else
// send SIGINT to the current process // send SIGINT to the current process
// to break into the debugger // to break into the debugger
raise(SIGABRT); raise(SIGABRT);
abort(); abort();
#endif
#endif #endif
} }

View File

@ -1792,19 +1792,10 @@ namespace libtorrent
int piece_manager::check_no_fastresume(storage_error& ec) int piece_manager::check_no_fastresume(storage_error& ec)
{ {
bool has_files = false;
if (!m_storage->settings().get_bool(settings_pack::no_recheck_incomplete_resume)) if (!m_storage->settings().get_bool(settings_pack::no_recheck_incomplete_resume))
{ {
storage_error se; storage_error se;
has_files = m_storage->has_any_file(se); if (m_storage->has_any_file(se))
if (se)
{
ec = se;
return fatal_disk_error;
}
if (has_files)
{ {
// always initialize the storage // always initialize the storage
int ret = check_init_storage(ec); int ret = check_init_storage(ec);

View File

@ -415,6 +415,18 @@ void test_rename(std::string const& test_path)
TEST_EQUAL(s->files().file_path(0), "new_filename"); TEST_EQUAL(s->files().file_path(0), "new_filename");
} }
struct test_error_storage : default_storage
{
test_error_storage(storage_params const& params) : default_storage(params) {};
bool has_any_file(storage_error& ec) TORRENT_FINAL
{
ec.ec = make_error_code(boost::system::errc::permission_denied);
ec.operation = storage_error::stat;
return false;
}
};
void test_check_files(std::string const& test_path void test_check_files(std::string const& test_path
, libtorrent::storage_mode_t storage_mode , libtorrent::storage_mode_t storage_mode
, bool unbuffered) , bool unbuffered)
@ -458,22 +470,21 @@ void test_check_files(std::string const& test_path
bencode(std::back_inserter(buf), t.generate()); bencode(std::back_inserter(buf), t.generate());
info = boost::make_shared<torrent_info>(&buf[0], buf.size(), boost::ref(ec), 0); info = boost::make_shared<torrent_info>(&buf[0], buf.size(), boost::ref(ec), 0);
aux::session_settings set;
file_pool fp; file_pool fp;
boost::asio::io_service ios; boost::asio::io_service ios;
counters cnt; counters cnt;
disk_io_thread io(ios, cnt, NULL); disk_io_thread io(ios, cnt, NULL);
io.set_num_threads(1); io.set_num_threads(1);
disk_buffer_pool dp(16 * 1024, ios, boost::bind(&nop)); std::vector<uint8_t> prio(info->num_files(), 0);
storage_params p; storage_params p;
p.files = &fs; p.files = &fs;
p.path = test_path; p.path = test_path;
p.pool = &fp; p.pool = &fp;
p.mode = storage_mode; p.mode = storage_mode;
p.priorities = &prio;
boost::shared_ptr<void> dummy; boost::shared_ptr<void> dummy;
boost::shared_ptr<piece_manager> pm = boost::make_shared<piece_manager>(new default_storage(p), dummy, &fs); boost::shared_ptr<piece_manager> pm = boost::make_shared<piece_manager>(new test_error_storage(p), dummy, &fs);
libtorrent::mutex lock;
bool done = false; bool done = false;
bdecode_node frd; bdecode_node frd;
@ -481,10 +492,18 @@ void test_check_files(std::string const& test_path
io.async_check_fastresume(pm.get(), &frd, links io.async_check_fastresume(pm.get(), &frd, links
, boost::bind(&on_check_resume_data, _1, &done)); , boost::bind(&on_check_resume_data, _1, &done));
io.submit_jobs(); io.submit_jobs();
ios.reset();
run_until(ios, done); run_until(ios, done);
io.set_num_threads(0); for (int i = 0; i < info->num_pieces(); ++i)
{
done = false;
io.async_hash(pm.get(), i, disk_io_job::sequential_access | disk_io_job::volatile_read
, boost::bind(&on_check_resume_data, _1, &done), reinterpret_cast<void*>(1));
io.submit_jobs();
run_until(ios, done);
}
io.abort(true);
} }
// TODO: 2 split this test up into smaller parts // TODO: 2 split this test up into smaller parts