Merge pull request #402 from arvidn/resume-data-no-timestamps

Resume data no timestamps
This commit is contained in:
Arvid Norberg 2016-02-07 01:47:53 -05:00
commit ee94ac6ef7
25 changed files with 240 additions and 681 deletions

View File

@ -1,3 +1,4 @@
* resume data no longer has timestamps of files
1.1.0 release

View File

@ -110,7 +110,7 @@ namespace libtorrent {
#endif
bool pending() const;
void get_all(std::vector<alert*>& alerts, int& num_resume);
void get_all(std::vector<alert*>& alerts);
template <class T>
bool should_post() const
@ -147,8 +147,6 @@ namespace libtorrent {
void set_dispatch_function(boost::function<void(std::auto_ptr<alert>)> const&);
#endif
int num_queued_resume() const;
#ifndef TORRENT_DISABLE_EXTENSIONS
void add_extension(boost::shared_ptr<plugin> ext);
#endif
@ -179,9 +177,6 @@ namespace libtorrent {
// posted to the queue
boost::function<void()> m_notify;
// the number of resume data alerts in the alert queue
int m_num_queued_resume;
// this is either 0 or 1, it indicates which m_alerts and m_allocations
// the alert_manager is allowed to use right now. This is swapped when
// the client calls get_all(), at which point all of the alert objects

View File

@ -781,16 +781,6 @@ namespace libtorrent
std::map<std::string, boost::shared_ptr<torrent> > m_uuids;
// when saving resume data for many torrents, torrents are
// queued up in this list in order to not have too many of them
// outstanding at any given time, since the resume data may use
// a lot of memory.
std::list<boost::shared_ptr<torrent> > m_save_resume_queue;
// the number of save resume data disk jobs that are currently
// outstanding
int m_num_save_resume;
// peer connections are put here when disconnected to avoid
// race conditions with the disk thread. It's important that
// peer connections are destructed from the network thread,
@ -1162,12 +1152,6 @@ namespace libtorrent
std::list<boost::shared_ptr<tracker_logger> > m_tracker_loggers;
#endif
// TODO: 2 the throttling of saving resume data could probably be
// factored out into a separate class
virtual void queue_async_resume_data(boost::shared_ptr<torrent> const& t) TORRENT_OVERRIDE;
virtual void done_async_resume() TORRENT_OVERRIDE;
void async_resume_dispatched();
// state for keeping track of external IPs
external_ip m_external_ip;

View File

@ -166,8 +166,6 @@ namespace libtorrent { namespace aux
virtual bool has_connection(peer_connection* p) const = 0;
virtual void insert_peer(boost::shared_ptr<peer_connection> const& c) = 0;
virtual void queue_async_resume_data(boost::shared_ptr<torrent> const& t) = 0;
virtual void done_async_resume() = 0;
virtual void evict_torrent(torrent* t) = 0;
virtual void remove_torrent(torrent_handle const& h, int options = 0) = 0;

View File

@ -86,8 +86,6 @@ namespace libtorrent
, boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_delete_files(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_set_file_priority(piece_manager* storage
, std::vector<boost::uint8_t> const& prio
, boost::function<void(disk_io_job const*)> const& handler) = 0;

View File

@ -89,7 +89,6 @@ namespace libtorrent
, release_files
, delete_files
, check_fastresume
, save_resume_data
, rename_file
, stop_torrent
, cache_piece
@ -151,8 +150,6 @@ namespace libtorrent
// for other jobs, it may point to other job-specific types
// for move_storage and rename_file this is a string allocated
// with malloc()
// an entry* for save_resume_data
// for aiocb_complete this points to the aiocb that completed
// for get_cache_info this points to a cache_status object which
// is filled in
union

View File

@ -317,8 +317,6 @@ namespace libtorrent
, bdecode_node const* resume_data
, std::vector<std::string>& links
, boost::function<void(disk_io_job const*)> const& handler);
void async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler);
void async_rename_file(piece_manager* storage, int index, std::string const& name
, boost::function<void(disk_io_job const*)> const& handler);
void async_stop_torrent(piece_manager* storage
@ -414,7 +412,6 @@ namespace libtorrent
int do_release_files(disk_io_job* j, jobqueue_t& completed_jobs);
int do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs);
int do_check_fastresume(disk_io_job* j, jobqueue_t& completed_jobs);
int do_save_resume_data(disk_io_job* j, jobqueue_t& completed_jobs);
int do_rename_file(disk_io_job* j, jobqueue_t& completed_jobs);
int do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs);
int do_read_and_hash(disk_io_job* j, jobqueue_t& completed_jobs);

View File

@ -437,6 +437,7 @@ namespace libtorrent
enable_outgoing_tcp,
enable_incoming_tcp,
#ifndef TORRENT_NO_DEPRECATE
// ``ignore_resume_timestamps`` determines if the storage, when
// loading resume data files, should verify that the file modification
// time with the timestamps in the resume data. This defaults to
@ -447,6 +448,10 @@ namespace libtorrent
// redownload potentially missed pieces than to go through the whole
// storage to look for them.
ignore_resume_timestamps,
#else
// hidden
deprecated8,
#endif
// ``no_recheck_incomplete_resume`` determines if the storage should
// check the whole files when resume data is incomplete or missing or

View File

@ -35,13 +35,15 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <time.h>
#include <vector>
#include <string>
#include <boost/cstdint.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/file_storage.hpp"
namespace libtorrent
{
@ -50,36 +52,52 @@ namespace libtorrent
stat_cache();
~stat_cache();
void init(int num_files);
enum
{
cache_error = -1,
not_in_cache = -2,
no_exist = -3
};
void reserve(int num_files);
// returns the size of the file or one
// of the enums, noent or not_in_cache
boost::int64_t get_filesize(int i) const;
time_t get_filetime(int i) const;
// returns the size of the file unless an error occurs, in which case ec
// is set to indicate the error
boost::int64_t get_filesize(int i, file_storage const& fs
, std::string const& save_path, error_code& ec);
void set_cache(int i, boost::int64_t size, time_t time);
void set_noexist(int i);
void set_error(int i);
void set_dirty(int i);
void clear();
// internal
enum
{
not_in_cache = -1,
file_error = -2 // (first index in m_errors)
};
// internal
void set_cache(int i, boost::int64_t size);
void set_error(int i, error_code const& ec);
private:
// returns the index to the specified error. Either an existing one or a
// newly added entry
int add_error(error_code const& ec);
struct stat_cache_t
{
stat_cache_t(boost::int64_t s, time_t t = 0): file_size(s), file_time(t) {}
stat_cache_t(boost::int64_t s): file_size(s) {}
// the size of the file. Negative values have special meaning. -1 means
// not-in-cache (i.e. there's no data for this file in the cache).
// lower values (larger negative values) indicate that an error
// occurred while stat()ing the file. The positive value is an index
// into m_errors, that recorded the actual error.
boost::int64_t file_size;
time_t file_time;
};
// one entry per file
std::vector<stat_cache_t> m_stat_cache;
// These are the errors that have happened when stating files. Each entry
// that had an error, refers to an index into this vector.
std::vector<error_code> m_errors;
};
}

View File

@ -113,7 +113,6 @@ POSSIBILITY OF SUCH DAMAGE.
// virtual bool verify_resume_data(bdecode_node const& rd
// , std::vector<std::string> const* links
// , storage_error& error) { return false; }
// virtual bool write_resume_data(entry& rd) const { return false; }
// virtual boost::int64_t physical_offset(int piece, int offset)
// { return piece * m_files.piece_length() + offset; };
// virtual sha1_hash hash_for_slot(int piece, partial_hash& ph, int piece_size)
@ -314,16 +313,6 @@ namespace libtorrent
, std::vector<std::string> const* links
, storage_error& ec) = 0;
// This function should fill in resume data, the current state of the
// storage, in ``rd``. The default storage adds file timestamps and
// sizes.
//
// Returning ``true`` indicates an error occurred.
//
// If an error occurs, ``storage_error`` should be set to reflect it.
//
virtual void write_resume_data(entry& rd, storage_error& ec) const = 0;
// This function should release all the file handles that it keeps open
// to files belonging to this storage. The default implementation just
// calls file_pool::release_files().
@ -433,7 +422,6 @@ namespace libtorrent
virtual bool verify_resume_data(bdecode_node const& rd
, std::vector<std::string> const* links
, storage_error& error) TORRENT_OVERRIDE;
virtual void write_resume_data(entry& rd, storage_error& ec) const TORRENT_OVERRIDE;
virtual bool tick() TORRENT_OVERRIDE;
int readv(file::iovec_t const* bufs, int num_bufs
@ -518,7 +506,6 @@ namespace libtorrent
virtual bool verify_resume_data(bdecode_node const&
, std::vector<std::string> const*
, storage_error&) TORRENT_OVERRIDE { return false; }
virtual void write_resume_data(entry&, storage_error&) const TORRENT_OVERRIDE {}
};
// this storage implementation always reads zeroes, and always discards
@ -541,7 +528,6 @@ namespace libtorrent
, std::vector<std::string> const* /* links */
, storage_error&) TORRENT_OVERRIDE
{ return false; }
virtual void write_resume_data(entry&, storage_error&) const TORRENT_OVERRIDE {}
virtual void release_files(storage_error&) TORRENT_OVERRIDE {}
virtual void rename_file(int /* index */
, std::string const& /* new_filenamem */, storage_error&) TORRENT_OVERRIDE {}
@ -661,8 +647,6 @@ namespace libtorrent
storage_interface* get_storage_impl() { return m_storage.get(); }
void write_resume_data(entry& rd, storage_error& ec) const;
#ifdef TORRENT_DEBUG
void assert_torrent_refcount() const;
#endif

View File

@ -480,7 +480,6 @@ namespace libtorrent
bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; }
void force_recheck();
void save_resume_data(int flags);
bool do_async_save_resume_data();
bool need_save_resume_data() const
{
@ -1138,7 +1137,6 @@ namespace libtorrent
void on_files_deleted(disk_io_job const* j);
void on_torrent_paused(disk_io_job const* j);
void on_storage_moved(disk_io_job const* j);
void on_save_resume_data(disk_io_job const* j);
void on_file_renamed(disk_io_job const* j);
void on_cache_flushed(disk_io_job const* j);

View File

@ -219,6 +219,7 @@ namespace libtorrent
void rename_file(int index, std::string const& new_filename)
{
TORRENT_ASSERT(is_loaded());
if (m_files.file_name(index) == new_filename) return;
copy_on_write();
m_files.rename_file(index, new_filename);
}

View File

@ -44,18 +44,11 @@ namespace libtorrent
alert_manager::alert_manager(int queue_limit, boost::uint32_t alert_mask)
: m_alert_mask(alert_mask)
, m_queue_size_limit(queue_limit)
, m_num_queued_resume(0)
, m_generation(0)
{}
alert_manager::~alert_manager() {}
int alert_manager::num_queued_resume() const
{
mutex::scoped_lock lock(m_mutex);
return m_num_queued_resume;
}
alert* alert_manager::wait_for_alert(time_duration max_wait)
{
mutex::scoped_lock lock(m_mutex);
@ -73,10 +66,6 @@ namespace libtorrent
void alert_manager::maybe_notify(alert* a, mutex::scoped_lock& lock)
{
if (a->type() == save_resume_data_failed_alert::alert_type
|| a->type() == save_resume_data_alert::alert_type)
++m_num_queued_resume;
if (m_alerts[m_generation].size() == 1)
{
lock.unlock();
@ -102,6 +91,8 @@ namespace libtorrent
{
(*i)->on_alert(a);
}
#else
TORRENT_UNUSED(a);
#endif
}
@ -168,17 +159,14 @@ namespace libtorrent
}
#endif
void alert_manager::get_all(std::vector<alert*>& alerts, int& num_resume)
void alert_manager::get_all(std::vector<alert*>& alerts)
{
mutex::scoped_lock lock(m_mutex);
TORRENT_ASSERT(m_num_queued_resume <= m_alerts[m_generation].size());
alerts.clear();
if (m_alerts[m_generation].empty()) return;
m_alerts[m_generation].get_pointers(alerts);
num_resume = m_num_queued_resume;
m_num_queued_resume = 0;
// swap buffers
m_generation = (m_generation + 1) & 1;

View File

@ -214,7 +214,6 @@ const char* const job_action_name[] =
"release_files",
"delete_files",
"check_fastresume",
"save_resume_data",
"rename_file",
"stop_torrent",
"cache_piece",

View File

@ -52,7 +52,6 @@ namespace libtorrent
TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block >= 0);
TORRENT_ASSERT(m_ref.storage == 0 || m_ref.piece < static_cast<piece_manager*>(m_ref.storage)->files()->num_pieces());
TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block <= static_cast<piece_manager*>(m_ref.storage)->files()->piece_length() / 0x4000);
TORRENT_ASSERT(j.action != disk_io_job::save_resume_data);
TORRENT_ASSERT(j.action != disk_io_job::rename_file);
TORRENT_ASSERT(j.action != disk_io_job::move_storage);
}
@ -69,7 +68,6 @@ namespace libtorrent
TORRENT_ASSERT(m_ref.block >= 0);
TORRENT_ASSERT(m_ref.piece < static_cast<piece_manager*>(m_ref.storage)->files()->num_pieces());
TORRENT_ASSERT(m_ref.block <= static_cast<piece_manager*>(m_ref.storage)->files()->piece_length() / 0x4000);
TORRENT_ASSERT(j.action != disk_io_job::save_resume_data);
TORRENT_ASSERT(j.action != disk_io_job::rename_file);
TORRENT_ASSERT(j.action != disk_io_job::move_storage);
}

View File

@ -62,8 +62,6 @@ namespace libtorrent
{
if (action == rename_file || action == move_storage)
free(buffer.string);
else if (action == save_resume_data)
delete static_cast<entry*>(buffer.resume_data);
}
bool disk_io_job::completed(cached_piece_entry const* pe, int block_size)

View File

@ -1038,7 +1038,6 @@ namespace libtorrent
&disk_io_thread::do_release_files,
&disk_io_thread::do_delete_files,
&disk_io_thread::do_check_fastresume,
&disk_io_thread::do_save_resume_data,
&disk_io_thread::do_rename_file,
&disk_io_thread::do_stop_torrent,
&disk_io_thread::do_cache_piece,
@ -1904,23 +1903,6 @@ namespace libtorrent
add_fence_job(storage, j);
}
void disk_io_thread::async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler)
{
#ifdef TORRENT_DEBUG
// the caller must increment the torrent refcount before
// issuing an async disk request
storage->assert_torrent_refcount();
#endif
disk_io_job* j = allocate_job(disk_io_job::save_resume_data);
j->storage = storage->shared_from_this();
j->buffer.resume_data = NULL;
j->callback = handler;
add_fence_job(storage, j);
}
void disk_io_thread::async_rename_file(piece_manager* storage, int index, std::string const& name
, boost::function<void(disk_io_job const*)> const& handler)
{
@ -2601,22 +2583,6 @@ namespace libtorrent
return j->storage->check_fastresume(*rd, links.get(), j->error);
}
int disk_io_thread::do_save_resume_data(disk_io_job* j, jobqueue_t& completed_jobs)
{
// if this assert fails, something's wrong with the fence logic
TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1);
mutex::scoped_lock l(m_cache_mutex);
flush_cache(j->storage.get(), flush_write_cache, completed_jobs, l);
l.unlock();
entry* resume_data = new entry(entry::dictionary_t);
j->storage->get_storage_impl()->write_resume_data(*resume_data, j->error);
TORRENT_ASSERT(j->buffer.resume_data == 0);
j->buffer.resume_data = resume_data;
return j->error ? -1 : 0;
}
int disk_io_thread::do_rename_file(disk_io_job* j, jobqueue_t& /* completed_jobs */ )
{
// if this assert fails, something's wrong with the fence logic

View File

@ -379,7 +379,6 @@ namespace aux {
, *this
#endif
)
, m_num_save_resume(0)
, m_work(io_service::work(m_io_service))
, m_max_queue_pos(-1)
, m_key(0)
@ -642,58 +641,6 @@ namespace aux {
m_host_resolver.async_resolve(host, flags, h);
}
void session_impl::queue_async_resume_data(boost::shared_ptr<torrent> const& t)
{
INVARIANT_CHECK;
int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit);
if (m_num_save_resume + m_alerts.num_queued_resume() >= loaded_limit
&& m_user_load_torrent
&& loaded_limit > 0)
{
TORRENT_ASSERT(t);
// do loaded torrents first, otherwise they'll just be
// evicted and have to be loaded again
if (t->is_loaded())
m_save_resume_queue.push_front(t);
else
m_save_resume_queue.push_back(t);
return;
}
if (t->do_async_save_resume_data())
++m_num_save_resume;
}
// this is called whenever a save_resume_data comes back
// from the disk thread
void session_impl::done_async_resume()
{
TORRENT_ASSERT(m_num_save_resume > 0);
--m_num_save_resume;
}
// this is called when one or all save resume alerts are
// popped off the alert queue
void session_impl::async_resume_dispatched()
{
INVARIANT_CHECK;
int num_queued_resume = m_alerts.num_queued_resume();
int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit);
while (!m_save_resume_queue.empty()
&& (m_num_save_resume + num_queued_resume < loaded_limit
|| loaded_limit == 0))
{
boost::shared_ptr<torrent> t = m_save_resume_queue.front();
m_save_resume_queue.erase(m_save_resume_queue.begin());
if (t->do_async_save_resume_data())
++m_num_save_resume;
}
}
void session_impl::save_state(entry* eptr, boost::uint32_t flags) const
{
TORRENT_ASSERT(is_single_thread());
@ -6452,15 +6399,7 @@ retry:
void session_impl::pop_alerts(std::vector<alert*>* alerts)
{
int num_resume = 0;
m_alerts.get_all(*alerts, num_resume);
if (num_resume > 0)
{
// we can only issue more resume data jobs from
// the network thread
m_io_service.post(boost::bind(&session_impl::async_resume_dispatched
, this));
}
m_alerts.get_all(*alerts);
}
#ifndef TORRENT_NO_DEPRECATE
@ -6892,11 +6831,6 @@ retry:
{
TORRENT_ASSERT(is_single_thread());
int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit);
TORRENT_ASSERT(m_num_save_resume <= loaded_limit);
// if (m_num_save_resume < loaded_limit)
// TORRENT_ASSERT(m_save_resume_queue.empty());
TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size());
if (m_settings.get_int(settings_pack::unchoke_slots_limit) < 0

View File

@ -32,19 +32,34 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/stat_cache.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/file.hpp"
#include <string>
namespace libtorrent
{
class file_storage;
stat_cache::stat_cache() {}
stat_cache::~stat_cache() {}
void stat_cache::set_cache(int i, boost::int64_t size, time_t time)
void stat_cache::set_cache(int i, boost::int64_t size)
{
TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache);
m_stat_cache[i].file_size = size;
m_stat_cache[i].file_time = time;
}
void stat_cache::set_error(int i, error_code const& ec)
{
TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache);
int error_index = add_error(ec);
m_stat_cache[i].file_size = file_error - error_index;
}
void stat_cache::set_dirty(int i)
@ -54,36 +69,38 @@ namespace libtorrent
m_stat_cache[i].file_size = not_in_cache;
}
void stat_cache::set_noexist(int i)
boost::int64_t stat_cache::get_filesize(int i, file_storage const& fs
, std::string const& save_path, error_code& ec)
{
TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache);
m_stat_cache[i].file_size = no_exist;
TORRENT_ASSERT(i < int(fs.num_files()));
if (i >= int(m_stat_cache.size())) m_stat_cache.resize(i + 1, not_in_cache);
boost::int64_t sz = m_stat_cache[i].file_size;
if (sz < not_in_cache)
{
ec = m_errors[-sz + file_error];
return file_error;
}
else if (sz == not_in_cache)
{
// query the filesystem
file_status s;
std::string file_path = fs.file_path(i, save_path);
stat_file(file_path, &s, ec);
if (ec)
{
set_error(i, ec);
sz = file_error;
}
else
{
set_cache(i, s.file_size);
sz = s.file_size;
}
}
return sz;
}
void stat_cache::set_error(int i)
{
TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache);
m_stat_cache[i].file_size = cache_error;
}
boost::int64_t stat_cache::get_filesize(int i) const
{
if (i >= int(m_stat_cache.size())) return not_in_cache;
return m_stat_cache[i].file_size;
}
time_t stat_cache::get_filetime(int i) const
{
if (i >= int(m_stat_cache.size())) return not_in_cache;
if (m_stat_cache[i].file_size < 0) return m_stat_cache[i].file_size;
return m_stat_cache[i].file_time;
}
void stat_cache::init(int num_files)
void stat_cache::reserve(int num_files)
{
m_stat_cache.resize(num_files, not_in_cache);
}
@ -91,7 +108,15 @@ namespace libtorrent
void stat_cache::clear()
{
std::vector<stat_cache_t>().swap(m_stat_cache);
std::vector<error_code>().swap(m_errors);
}
int stat_cache::add_error(error_code const& ec)
{
std::vector<error_code>::iterator i = std::find(m_errors.begin(), m_errors.end(), ec);
if (i != m_errors.end()) return i - m_errors.begin();
m_errors.push_back(ec);
return m_errors.size() - 1;
}
}

View File

@ -509,7 +509,7 @@ namespace libtorrent
void default_storage::initialize(storage_error& ec)
{
m_stat_cache.init(files().num_files());
m_stat_cache.reserve(files().num_files());
#ifdef TORRENT_WINDOWS
// don't do full file allocations on network drives
@ -540,25 +540,22 @@ namespace libtorrent
// ignore pad files
if (files().pad_file_at(file_index)) continue;
if (m_stat_cache.get_filesize(file_index) == stat_cache::not_in_cache)
error_code err;
boost::int64_t size = m_stat_cache.get_filesize(file_index, files()
, m_save_path, err);
if (err && err != boost::system::errc::no_such_file_or_directory)
{
file_status s;
std::string file_path = files().file_path(file_index, m_save_path);
stat_file(file_path, &s, ec.ec);
if (ec && ec.ec != boost::system::errc::no_such_file_or_directory)
{
m_stat_cache.set_error(file_index);
ec.file = file_index;
ec.operation = storage_error::stat;
break;
}
m_stat_cache.set_cache(file_index, s.file_size, s.mtime);
ec.file = file_index;
ec.operation = storage_error::stat;
ec.ec = err;
break;
}
// if the file already exists, but is larger than what
// it's supposed to be, truncate it
// if the file is empty, just create it either way.
if ((!ec && m_stat_cache.get_filesize(file_index) > files().file_size(file_index))
if ((!err && size > files().file_size(file_index))
|| files().file_size(file_index) == 0)
{
std::string file_path = files().file_path(file_index, m_save_path);
@ -579,9 +576,14 @@ namespace libtorrent
ec.ec.clear();
file_handle f = open_file(file_index, file::read_write
| file::random_access, ec);
if (ec) return;
if (ec)
{
ec.file = file_index;
ec.operation = storage_error::fallocate;
return;
}
boost::int64_t size = files().file_size(file_index);
size = files().file_size(file_index);
f->set_size(size, ec.ec);
if (ec)
{
@ -589,8 +591,6 @@ namespace libtorrent
ec.operation = storage_error::fallocate;
break;
}
size_t mtime = m_stat_cache.get_filetime(file_index);
m_stat_cache.set_cache(file_index, size, mtime);
}
ec.ec.clear();
}
@ -609,48 +609,37 @@ namespace libtorrent
bool default_storage::has_any_file(storage_error& ec)
{
m_stat_cache.init(files().num_files());
m_stat_cache.reserve(files().num_files());
std::string file_path;
for (int i = 0; i < files().num_files(); ++i)
{
file_status s;
boost::int64_t cache_status = m_stat_cache.get_filesize(i);
if (cache_status < 0 && cache_status != stat_cache::no_exist)
boost::int64_t sz = m_stat_cache.get_filesize(
i, files(), m_save_path, ec.ec);
if (sz < 0)
{
file_path = files().file_path(i, m_save_path);
stat_file(file_path, &s, ec.ec);
boost::int64_t r = s.file_size;
if (ec.ec || !(s.mode & file_status::regular_file)) r = -1;
if (ec && ec.ec == boost::system::errc::no_such_file_or_directory)
{
ec.ec.clear();
r = -3;
}
m_stat_cache.set_cache(i, r, s.mtime);
if (ec)
if (ec && ec.ec != boost::system::errc::no_such_file_or_directory)
{
ec.file = i;
ec.operation = storage_error::stat;
m_stat_cache.clear();
return false;
}
// some files not existing is expected and not an error
ec.ec.clear();
}
// if we didn't find the file, check the next one
if (m_stat_cache.get_filesize(i) == stat_cache::no_exist) continue;
if (m_stat_cache.get_filesize(i) > 0)
return true;
if (sz > 0) return true;
}
file_status s;
stat_file(combine_path(m_save_path, m_part_file_name), &s, ec.ec);
if (!ec) return true;
// the part file not existing is expected
if (ec && ec.ec == boost::system::errc::no_such_file_or_directory)
ec.ec.clear();
if (ec)
{
ec.file = -1;
@ -831,244 +820,12 @@ namespace libtorrent
#endif
}
void default_storage::write_resume_data(entry& rd, storage_error& ec) const
{
TORRENT_ASSERT(rd.type() == entry::dictionary_t);
entry::list_type& fl = rd["file sizes"].list();
if (m_part_file)
{
error_code ignore;
const_cast<part_file&>(*m_part_file).flush_metadata(ignore);
}
file_storage const& fs = files();
for (int i = 0; i < fs.num_files(); ++i)
{
boost::int64_t file_size = 0;
time_t file_time = 0;
boost::int64_t cache_state = m_stat_cache.get_filesize(i);
if (cache_state != stat_cache::not_in_cache)
{
if (cache_state >= 0)
{
file_size = cache_state;
file_time = m_stat_cache.get_filetime(i);
}
}
else
{
file_status s;
error_code error;
stat_file(fs.file_path(i, m_save_path), &s, error);
if (!error)
{
file_size = s.file_size;
file_time = s.mtime;
}
else if (error == error_code(boost::system::errc::no_such_file_or_directory
, generic_category()))
{
m_stat_cache.set_noexist(i);
}
else
{
ec.ec = error;
ec.file = i;
ec.operation = storage_error::stat;
m_stat_cache.set_error(i);
}
}
fl.push_back(entry(entry::list_t));
entry::list_type& p = fl.back().list();
p.push_back(entry(file_size));
p.push_back(entry(file_time));
}
}
bool default_storage::verify_resume_data(bdecode_node const& rd
, std::vector<std::string> const* links
, storage_error& ec)
{
// TODO: make this more generic to not just work if files have been
// renamed, but also if they have been merged into a single file for instance
// maybe use the same format as .torrent files and reuse some code from torrent_info
bdecode_node mapped_files = rd.dict_find_list("mapped_files");
if (mapped_files && mapped_files.list_size() == m_files.num_files())
{
m_mapped_files.reset(new file_storage(m_files));
for (int i = 0; i < m_files.num_files(); ++i)
{
std::string new_filename = mapped_files.list_string_value_at(i);
if (new_filename.empty()) continue;
m_mapped_files->rename_file(i, new_filename);
}
}
bdecode_node file_priority = rd.dict_find_list("file_priority");
if (file_priority && file_priority.list_size()
== files().num_files())
{
m_file_priority.resize(file_priority.list_size());
for (int i = 0; i < file_priority.list_size(); ++i)
m_file_priority[i] = boost::uint8_t(file_priority.list_int_value_at(i, 1));
}
bdecode_node file_sizes_ent = rd.dict_find_list("file sizes");
if (file_sizes_ent == 0)
{
ec.ec = errors::missing_file_sizes;
ec.file = -1;
ec.operation = storage_error::check_resume;
return false;
}
if (file_sizes_ent.list_size() == 0)
{
ec.ec = errors::no_files_in_resume_data;
return false;
}
file_storage const& fs = files();
if (file_sizes_ent.list_size() != fs.num_files())
{
ec.ec = errors::mismatching_number_of_files;
ec.file = -1;
ec.operation = storage_error::check_resume;
return false;
}
bool seed = false;
bdecode_node slots = rd.dict_find_list("slots");
if (slots)
{
if (int(slots.list_size()) == m_files.num_pieces())
{
seed = true;
for (int i = 0; i < slots.list_size(); ++i)
{
if (slots.list_int_value_at(i, -1) >= 0) continue;
seed = false;
break;
}
}
}
else if (bdecode_node pieces = rd.dict_find_string("pieces"))
{
if (int(pieces.string_length()) == m_files.num_pieces())
{
seed = true;
char const* p = pieces.string_ptr();
for (int i = 0; i < pieces.string_length(); ++i)
{
if ((p[i] & 1) == 1) continue;
seed = false;
break;
}
}
}
else
{
ec.ec = errors::missing_pieces;
ec.file = -1;
ec.operation = storage_error::check_resume;
return false;
}
for (int i = 0; i < file_sizes_ent.list_size(); ++i)
{
if (fs.pad_file_at(i)) continue;
bdecode_node e = file_sizes_ent.list_at(i);
if (e.type() != bdecode_node::list_t
|| e.list_size() < 2
|| e.list_at(0).type() != bdecode_node::int_t
|| e.list_at(1).type() != bdecode_node::int_t)
{
ec.ec = errors::missing_file_sizes;
ec.file = i;
ec.operation = storage_error::check_resume;
return false;
}
boost::int64_t expected_size = e.list_int_value_at(0);
time_t expected_time = e.list_int_value_at(1);
// if we're a seed, the expected size should match
// the actual full size according to the torrent
if (seed && expected_size < fs.file_size(i))
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::check_resume;
return false;
}
boost::int64_t file_size = m_stat_cache.get_filesize(i);
time_t file_time;
if (file_size >= 0)
{
file_time = m_stat_cache.get_filetime(i);
}
else
{
file_status s;
error_code error;
std::string file_path = fs.file_path(i, m_save_path);
stat_file(file_path, &s, error);
if (error)
{
if (error != boost::system::errc::no_such_file_or_directory)
{
m_stat_cache.set_error(i);
ec.ec = error;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
m_stat_cache.set_noexist(i);
if (expected_size != 0)
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::none;
return false;
}
file_size = 0;
file_time = 0;
}
else
{
file_size = s.file_size;
file_time = s.mtime;
}
}
if (expected_size > file_size)
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::none;
return false;
}
if (settings().get_bool(settings_pack::ignore_resume_timestamps)) continue;
// allow some slack, because of FAT volumes
if (expected_time != 0 &&
(file_time > expected_time + 5 * 60 || file_time < expected_time - 5))
{
ec.ec = errors::mismatching_file_timestamp;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
}
// TODO: 2 we probably need to do this unconditionally in this function.
// Even if the resume data file appears stale, we need to create these
// hard links, right?
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
if (links)
{
@ -1104,6 +861,55 @@ namespace libtorrent
}
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
if (rd && rd.type() == bdecode_node::dict_t)
{
bdecode_node pieces = rd.dict_find_string("pieces");
if (pieces && pieces.type() == bdecode_node::string_t
&& int(pieces.string_length()) == fs.num_pieces())
{
char const* pieces_str = pieces.string_ptr();
// parse have bitmask. Verify that the files we expect to have
// actually do exist
for (int i = 0; i < fs.num_pieces(); ++i)
{
if ((pieces_str[i] & 1) == 0) continue;
std::vector<file_slice> f = fs.map_block(i, 0, 1);
TORRENT_ASSERT(!f.empty());
const int file_index = f[0].file_index;
error_code error;
boost::int64_t size = m_stat_cache.get_filesize(f[0].file_index
, fs, m_save_path, error);
if (size < 0)
{
if (error != boost::system::errc::no_such_file_or_directory)
{
ec.ec = error;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
else
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
}
// OK, this file existed, good. Now, skip all remaining pieces in
// this file. We're just sanity-checking whether the files exist
// or not.
peer_request pr = fs.map_file(file_index, 0
, fs.file_size(file_index) + 1);
i = (std::max)(i + 1, pr.piece);
}
}
}
return true;
}
@ -1597,12 +1403,6 @@ namespace libtorrent
}
#endif
// used in torrent_handle.cpp
void piece_manager::write_resume_data(entry& rd, storage_error& ec) const
{
m_storage->write_resume_data(rd, ec);
}
int piece_manager::check_no_fastresume(storage_error& ec)
{
bool has_files = false;
@ -1631,13 +1431,14 @@ namespace libtorrent
int piece_manager::check_init_storage(storage_error& ec)
{
storage_error se;
// initialize may clear the error we pass in and it's important to
// preserve the error code in ec, even when initialize() is successful
m_storage->initialize(se);
if (se)
{
ec = se;
return fatal_disk_error;
}
return no_error;
}

View File

@ -5036,35 +5036,6 @@ namespace libtorrent
}
}
void torrent::on_save_resume_data(disk_io_job const* j)
{
TORRENT_ASSERT(is_single_thread());
torrent_ref_holder h(this, "save_resume");
dec_refcount("save_resume");
m_ses.done_async_resume();
if (!j->buffer.resume_data)
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle(), j->error.ec);
return;
}
if (!need_loaded())
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, m_error);
return;
}
m_need_save_resume_data = false;
m_last_saved_resume = m_ses.session_time();
write_resume_data(*j->buffer.resume_data);
alerts().emplace_alert<save_resume_data_alert>(
boost::shared_ptr<entry>(j->buffer.resume_data), get_handle());
const_cast<disk_io_job*>(j)->buffer.resume_data = 0;
state_updated();
}
void torrent::on_file_renamed(disk_io_job const* j)
{
TORRENT_ASSERT(is_single_thread());
@ -5521,10 +5492,6 @@ namespace libtorrent
// this call is only valid on torrents with metadata
if (!valid_metadata() || is_seed()) return;
// the vector need to have exactly one element for every file
// in the torrent
TORRENT_ASSERT(int(files.size()) == m_torrent_file->num_files());
int limit = int(files.size());
if (valid_metadata() && limit > m_torrent_file->num_files())
limit = m_torrent_file->num_files();
@ -5545,7 +5512,7 @@ namespace libtorrent
m_file_priority[i] = 0;
}
// storage may be NULL during shutdown
// storage may be NULL during construction and shutdown
if (m_torrent_file->num_pieces() > 0 && m_storage)
{
inc_refcount("file_priority");
@ -6916,9 +6883,6 @@ namespace libtorrent
m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, me);
}
// TODO: make this more generic to not just work if files have been
// renamed, but also if they have been merged into a single file for instance
// maybe use the same format as .torrent files and reuse some code from torrent_info
// The mapped_files needs to be read both in the network thread
// and in the disk thread, since they both have their own mapped files structures
// which are kept in sync
@ -6947,28 +6911,15 @@ namespace libtorrent
{
const int num_files = (std::min)(file_priority.list_size()
, m_torrent_file->num_files());
m_file_priority.resize(num_files, 4);
std::vector<int> file_prio(num_files);
for (int i = 0; i < num_files; ++i)
{
m_file_priority[i] = file_priority.list_int_value_at(i, 1);
file_prio[i] = file_priority.list_int_value_at(i, 1);
// this is suspicious, leave seed mode
if (m_file_priority[i] == 0) m_seed_mode = false;
}
// unallocated slots are assumed to be priority 1, so cut off any
// trailing ones
int end_range = num_files - 1;
for (; end_range >= 0; --end_range) if (m_file_priority[end_range] != 1) break;
m_file_priority.resize(end_range + 1, 4);
// initialize pad files to priority 0
file_storage const& fs = m_torrent_file->files();
for (int i = 0; i < (std::min)(fs.num_files(), end_range + 1); ++i)
{
if (!fs.pad_file_at(i)) continue;
m_file_priority[i] = 0;
if (file_prio[i] == 0) m_seed_mode = false;
}
update_piece_priorities();
prioritize_files(file_prio);
}
}
@ -7275,9 +7226,6 @@ namespace libtorrent
}
// write renamed files
// TODO: 0 make this more generic to not just work if files have been
// renamed, but also if they have been merged into a single file for instance.
// using file_base
if (&m_torrent_file->files() != &m_torrent_file->orig_files()
&& m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files())
{
@ -8766,7 +8714,7 @@ namespace libtorrent
TORRENT_ASSERT(index >= 0);
TORRENT_ASSERT(index < m_torrent_file->num_files());
// stoage may be NULL during shutdown
// storage may be NULL during shutdown
if (!m_storage.get())
{
if (alerts().should_post<file_rename_failed_alert>())
@ -9558,58 +9506,30 @@ namespace libtorrent
m_save_resume_flags = boost::uint8_t(flags);
state_updated();
if (m_state == torrent_status::checking_files
|| m_state == torrent_status::checking_resume_data)
{
if (!need_loaded())
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, m_error);
return;
}
// storage may be NULL during shutdown
if (!m_storage)
{
TORRENT_ASSERT(m_abort);
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, boost::asio::error::operation_aborted);
return;
}
boost::shared_ptr<entry> rd(new entry);
write_resume_data(*rd);
alerts().emplace_alert<save_resume_data_alert>(rd, get_handle());
return;
}
// TODO: 3 this really needs to be moved to do_async_save_resume_data.
// flags need to be passed on
if ((flags & torrent_handle::flush_disk_cache) && m_storage.get())
m_ses.disk_thread().async_release_files(m_storage.get());
m_ses.queue_async_resume_data(shared_from_this());
}
bool torrent::do_async_save_resume_data()
{
if (!need_loaded())
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle(), m_error);
return false;
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, m_error);
return;
}
/*
// storage may be NULL during shutdown
if (!m_storage)
{
TORRENT_ASSERT(m_abort);
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, boost::asio::error::operation_aborted);
return false;
return;
}
inc_refcount("save_resume");
m_ses.disk_thread().async_save_resume_data(m_storage.get()
, boost::bind(&torrent::on_save_resume_data, shared_from_this(), _1));
return true;
*/
if ((flags & torrent_handle::flush_disk_cache) && m_storage.get())
m_ses.disk_thread().async_release_files(m_storage.get());
state_updated();
boost::shared_ptr<entry> rd(new entry);
write_resume_data(*rd);
alerts().emplace_alert<save_resume_data_alert>(rd, get_handle());
}
bool torrent::should_check_files() const

View File

@ -734,19 +734,6 @@ namespace libtorrent
{
entry ret(entry::dictionary_t);
TORRENT_SYNC_CALL1(write_resume_data, boost::ref(ret));
t = m_torrent.lock();
if (t)
{
bool done = false;
session_impl& ses = static_cast<session_impl&>(t->session());
storage_error ec;
ses.get_io_service().dispatch(boost::bind(&aux::fun_wrap, boost::ref(done), boost::ref(ses.cond)
, boost::ref(ses.mut), boost::function<void(void)>(boost::bind(
&piece_manager::write_resume_data, &t->storage(), boost::ref(ret), boost::ref(ec)))));
t.reset();
aux::torrent_wait(done, ses);
}
return ret;
}

View File

@ -59,8 +59,7 @@ TORRENT_TEST(limit)
TEST_EQUAL(mgr.pending(), true);
std::vector<alert*> alerts;
int num_resume;
mgr.get_all(alerts, num_resume);
mgr.get_all(alerts);
// even though we posted 600, the limit was 500
TEST_EQUAL(alerts.size(), 500);
@ -75,7 +74,7 @@ TORRENT_TEST(limit)
TEST_EQUAL(mgr.pending(), true);
mgr.get_all(alerts, num_resume);
mgr.get_all(alerts);
// even though we posted 600, the limit was 200
TEST_EQUAL(alerts.size(), 200);
@ -96,8 +95,7 @@ TORRENT_TEST(priority_limit)
mgr.emplace_alert<file_rename_failed_alert>(torrent_handle(), i, error_code());
std::vector<alert*> alerts;
int num_resume;
mgr.get_all(alerts, num_resume);
mgr.get_all(alerts);
// even though we posted 400, the limit was 100 for half of them and
// 200 for the other half, meaning we should have 200 alerts now
@ -173,8 +171,7 @@ TORRENT_TEST(notify_function)
// however, if we pop all the alerts and post new ones, there will be
// and edge triggering the notify call
std::vector<alert*> alerts;
int num_resume;
mgr.get_all(alerts, num_resume);
mgr.get_all(alerts);
TEST_EQUAL(mgr.pending(), false);
@ -258,8 +255,7 @@ TORRENT_TEST(wait_for_alert)
TEST_CHECK(a->type() == torrent_added_alert::alert_type);
std::vector<alert*> alerts;
int num_resume = 0;
mgr.get_all(alerts, num_resume);
mgr.get_all(alerts);
start = clock_type::now();
thread posting_thread(boost::bind(&post_torrent_added, &mgr));
@ -274,40 +270,6 @@ TORRENT_TEST(wait_for_alert)
posting_thread.join();
}
TORRENT_TEST(queued_resume)
{
alert_manager mgr(100, 0xffffffff);
TEST_EQUAL(mgr.num_queued_resume(), 0);
for (int i = 0; i < 17; ++i)
mgr.emplace_alert<torrent_added_alert>(torrent_handle());
TEST_EQUAL(mgr.num_queued_resume(), 0);
std::vector<alert*> alerts;
int num_resume = 0;
mgr.get_all(alerts, num_resume);
TEST_EQUAL(num_resume, 0);
TEST_EQUAL(alerts.size(), 17);
TEST_EQUAL(mgr.num_queued_resume(), 0);
error_code ec(boost::system::errc::no_such_file_or_directory
, generic_category());
for (int i = 0; i < 2; ++i)
mgr.emplace_alert<save_resume_data_failed_alert>(torrent_handle(), ec);
TEST_EQUAL(mgr.num_queued_resume(), 2);
mgr.get_all(alerts, num_resume);
TEST_EQUAL(num_resume, 2);
TEST_EQUAL(alerts.size(), 2);
TEST_EQUAL(mgr.num_queued_resume(), 0);
}
TORRENT_TEST(alert_mask)
{
alert_manager mgr(100, 0xffffffff);

View File

@ -42,39 +42,46 @@ TORRENT_TEST(stat_cache)
stat_cache sc;
sc.init(10);
for (int i = 0; i < 10; ++i)
file_storage fs;
for (int i = 0; i < 20; ++i)
{
TEST_CHECK(sc.get_filesize(i) == stat_cache::not_in_cache);
TEST_CHECK(sc.get_filetime(i) == stat_cache::not_in_cache);
char buf[50];
snprintf(buf, sizeof(buf), "test_torrent/test-%d", i);
fs.add_file(buf, (i + 1) * 10);
}
// out of bound accesses count as not-in-cache
TEST_CHECK(sc.get_filesize(10) == stat_cache::not_in_cache);
TEST_CHECK(sc.get_filesize(11) == stat_cache::not_in_cache);
std::string save_path = ".";
sc.set_error(3);
TEST_CHECK(sc.get_filesize(3) == stat_cache::cache_error);
sc.reserve(10);
sc.set_noexist(3);
TEST_CHECK(sc.get_filesize(3) == stat_cache::no_exist);
sc.set_error(3, error_code(boost::system::errc::permission_denied, generic_category()));
ec.clear();
TEST_EQUAL(sc.get_filesize(3, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::permission_denied, generic_category()));
sc.set_cache(3, 101, 5555);
TEST_CHECK(sc.get_filesize(3) == 101);
TEST_CHECK(sc.get_filetime(3) == 5555);
sc.set_error(3, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
ec.clear();
TEST_EQUAL(sc.get_filesize(3, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
sc.set_error(11);
TEST_CHECK(sc.get_filesize(10) == stat_cache::not_in_cache);
TEST_CHECK(sc.get_filesize(11) == stat_cache::cache_error);
ec.clear();
sc.set_cache(3, 101);
TEST_EQUAL(sc.get_filesize(3, fs, save_path, ec), 101);
TEST_CHECK(!ec);
sc.set_noexist(13);
TEST_CHECK(sc.get_filesize(12) == stat_cache::not_in_cache);
TEST_CHECK(sc.get_filesize(13) == stat_cache::no_exist);
sc.set_error(11, error_code(boost::system::errc::broken_pipe, generic_category()));
ec.clear();
TEST_EQUAL(sc.get_filesize(11, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::broken_pipe, generic_category()));
sc.set_cache(15, 1000, 3000);
TEST_CHECK(sc.get_filesize(14) == stat_cache::not_in_cache);
TEST_CHECK(sc.get_filesize(15) == 1000);
TEST_CHECK(sc.get_filetime(15) == 3000);
ec.clear();
sc.set_error(13, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
TEST_EQUAL(sc.get_filesize(13, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
ec.clear();
sc.set_cache(15, 1000);
TEST_CHECK(sc.get_filesize(15, fs, save_path, ec) == 1000);
TEST_CHECK(!ec);
}

View File

@ -664,7 +664,7 @@ TORRENT_TEST(fastresume)
{
print_alerts(ses, "ses");
s = h.status();
if (s.progress == 1.0f)
if (s.progress == 1.0f)
{
std::cout << "progress: 1.0f" << std::endl;
break;
@ -692,7 +692,6 @@ TORRENT_TEST(fastresume)
return;
std::cerr << resume.to_string() << "\n";
TEST_CHECK(resume.dict().find("file sizes") != resume.dict().end());
// make sure the fast resume check fails! since we removed the file
{
@ -795,7 +794,6 @@ TORRENT_TEST(rename_file_fastresume)
TEST_CHECK(!exists(combine_path(test_path, "tmp2/temporary")));
TEST_CHECK(exists(combine_path(test_path, "tmp2/testing_renamed_files")));
TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end());
TEST_CHECK(resume.dict().find("file sizes") != resume.dict().end());
std::cerr << resume.to_string() << "\n";