premiere-libtorrent/src/storage.cpp

817 lines
23 KiB
C++
Raw Normal View History

/*
Copyright (c) 2003-2016, Arvid Norberg, Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/config.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/aux_/storage_utils.hpp"
#include <ctime>
#include <algorithm>
#include <set>
2004-12-21 13:30:09 +01:00
#include <functional>
#include <cstdio>
#include "libtorrent/aux_/disable_warnings_push.hpp"
#if defined(__APPLE__)
// for getattrlist()
#include <sys/attr.h>
#include <unistd.h>
// for statfs()
#include <sys/param.h>
#include <sys/mount.h>
#endif
#if defined(__linux__)
#include <sys/statfs.h>
#endif
#if defined(__FreeBSD__)
// for statfs()
#include <sys/param.h>
#include <sys/mount.h>
#endif
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/aux_/path.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/disk_buffer_holder.hpp"
2014-07-06 21:18:00 +02:00
#include "libtorrent/stat_cache.hpp"
#include "libtorrent/hex.hpp" // to_hex
// for convert_to_wstring and convert_to_native
#include "libtorrent/aux_/escape_string.hpp"
namespace libtorrent {
void clear_bufs(span<iovec_t const> bufs)
{
for (auto buf : bufs)
std::memset(buf.data(), 0, buf.size());
}
default_storage::default_storage(storage_params const& params
, file_pool& pool)
: storage_interface(*params.files)
, m_pool(pool)
2014-07-06 21:18:00 +02:00
, m_allocate_files(params.mode == storage_mode_allocate)
{
if (params.mapped_files) m_mapped_files.reset(new file_storage(*params.mapped_files));
if (params.priorities) m_file_priority = *params.priorities;
TORRENT_ASSERT(files().num_files() > 0);
2014-07-06 21:18:00 +02:00
m_save_path = complete(params.path);
m_part_file_name = "." + (params.info
? aux::to_hex(params.info->info_hash())
2014-07-06 21:18:00 +02:00
: params.files->name()) + ".parts";
}
default_storage::~default_storage()
{
error_code ec;
if (m_part_file) m_part_file->flush_metadata(ec);
// this may be called from a different
// thread than the disk thread
m_pool.release(storage_index());
2014-07-06 21:18:00 +02:00
}
void default_storage::need_partfile()
2007-06-10 22:46:09 +02:00
{
2014-07-06 21:18:00 +02:00
if (m_part_file) return;
m_part_file.reset(new part_file(
m_save_path, m_part_file_name
, files().num_pieces(), files().piece_length()));
2014-07-06 21:18:00 +02:00
}
void default_storage::set_file_priority(
aux::vector<std::uint8_t, file_index_t> const& prio
, storage_error& ec)
2014-07-06 21:18:00 +02:00
{
// extend our file priorities in case it's truncated
// the default assumed priority is 4 (the default)
2014-07-06 21:18:00 +02:00
if (prio.size() > m_file_priority.size())
m_file_priority.resize(prio.size(), default_piece_priority);
2014-07-06 21:18:00 +02:00
file_storage const& fs = files();
for (file_index_t i(0); i < prio.end_index(); ++i)
{
int const old_prio = m_file_priority[i];
2014-07-06 21:18:00 +02:00
int new_prio = prio[i];
if (old_prio == 0 && new_prio != 0)
{
2014-07-06 21:18:00 +02:00
// move stuff out of the part file
file_handle f = open_file(i, open_mode_t::read_write, ec);
if (ec) return;
2014-07-06 21:18:00 +02:00
need_partfile();
m_part_file->export_file([&f, &ec](std::int64_t file_offset, span<char> buf)
{
iovec_t const v = {buf.data(), buf.size()};
std::int64_t const ret = f->writev(file_offset, v, ec.ec);
2017-04-23 03:33:38 +02:00
TORRENT_UNUSED(ret);
TORRENT_ASSERT(ec || ret == std::int64_t(v.size()));
}, fs.file_offset(i), fs.file_size(i), ec.ec);
2014-07-06 21:18:00 +02:00
if (ec)
{
ec.file(i);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::partfile_write;
return;
}
}
2014-07-06 21:18:00 +02:00
else if (old_prio != 0 && new_prio == 0)
{
2014-07-06 21:18:00 +02:00
// move stuff into the part file
// this is not implemented yet.
// pretend that we didn't set the priority to 0.
std::string fp = fs.file_path(i, m_save_path);
if (exists(fp))
new_prio = 1;
/*
file_handle f = open_file(i, open_mode_t::read_only, ec);
2014-07-06 21:18:00 +02:00
if (ec.ec != boost::system::errc::no_such_file_or_directory)
{
if (ec) return;
2014-07-06 21:18:00 +02:00
need_partfile();
2014-07-06 21:18:00 +02:00
m_part_file->import_file(*f, fs.file_offset(i), fs.file_size(i), ec.ec);
if (ec)
{
ec.file(i);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::partfile_read;
return;
}
2014-07-06 21:18:00 +02:00
// remove the file
std::string p = fs.file_path(i, m_save_path);
delete_one_file(p, ec.ec);
if (ec)
{
ec.file(i);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::remove;
}
}
2014-07-06 21:18:00 +02:00
*/
}
2014-07-06 21:18:00 +02:00
ec.ec.clear();
m_file_priority[i] = std::uint8_t(new_prio);
2014-07-06 21:18:00 +02:00
}
if (m_part_file) m_part_file->flush_metadata(ec.ec);
if (ec)
{
ec.file(file_index_t(-1));
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::partfile_write;
}
}
2014-07-06 21:18:00 +02:00
void default_storage::initialize(storage_error& ec)
{
m_stat_cache.reserve(files().num_files());
#ifdef TORRENT_WINDOWS
// don't do full file allocations on network drives
2014-06-09 06:08:41 +02:00
#if TORRENT_USE_WSTRING
std::wstring file_name = convert_to_wstring(m_save_path);
2016-10-27 05:30:58 +02:00
int const drive_type = GetDriveTypeW(file_name.c_str());
2014-06-09 06:08:41 +02:00
#else
int const drive_type = GetDriveTypeA(m_save_path.c_str());
2014-06-09 06:08:41 +02:00
#endif
if (drive_type == DRIVE_REMOTE)
m_allocate_files = false;
#endif
{
2017-05-08 13:48:42 +02:00
std::unique_lock<std::mutex> l(m_file_created_mutex);
m_file_created.resize(files().num_files(), false);
}
2013-12-30 03:50:29 +01:00
// first, create all missing directories
std::string last_path;
file_storage const& fs = files();
for (file_index_t file_index(0); file_index < fs.end_file(); ++file_index)
{
// ignore files that have priority 0
if (m_file_priority.end_index() > file_index
2014-07-06 21:18:00 +02:00
&& m_file_priority[file_index] == 0)
{
continue;
}
// ignore pad files
if (files().pad_file_at(file_index)) continue;
error_code err;
std::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)
{
ec.file(file_index);
ec.operation = storage_error::stat;
ec.ec = err;
break;
}
// if the file already exists, but is larger than what
2013-12-30 03:50:29 +01:00
// it's supposed to be, truncate it
// if the file is empty, just create it either way.
if ((!err && size > files().file_size(file_index))
2014-07-06 21:18:00 +02:00
|| files().file_size(file_index) == 0)
{
std::string file_path = files().file_path(file_index, m_save_path);
std::string dir = parent_path(file_path);
if (dir != last_path)
{
last_path = dir;
2014-07-06 21:18:00 +02:00
create_directories(last_path, ec.ec);
if (ec.ec)
{
ec.file(file_index);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::mkdir;
break;
}
}
2014-07-06 21:18:00 +02:00
ec.ec.clear();
file_handle f = open_file(file_index, open_mode_t::read_write
| open_mode_t::random_access, ec);
if (ec)
{
ec.file(file_index);
ec.operation = storage_error::fallocate;
return;
}
size = files().file_size(file_index);
f->set_size(size, ec.ec);
2014-07-06 21:18:00 +02:00
if (ec)
{
ec.file(file_index);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::fallocate;
break;
}
}
2014-07-06 21:18:00 +02:00
ec.ec.clear();
}
// close files that were opened in write mode
m_pool.release(storage_index());
}
2014-07-06 21:18:00 +02:00
bool default_storage::has_any_file(storage_error& ec)
{
m_stat_cache.reserve(files().num_files());
2014-07-06 21:18:00 +02:00
file_storage const& fs = files();
for (file_index_t i(0); i < fs.end_file(); ++i)
{
std::int64_t const sz = m_stat_cache.get_filesize(
i, files(), m_save_path, ec.ec);
2014-07-06 21:18:00 +02:00
if (sz < 0)
{
if (ec && ec.ec != boost::system::errc::no_such_file_or_directory)
2014-07-06 21:18:00 +02:00
{
ec.file(i);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::stat;
m_stat_cache.clear();
return false;
}
// some files not existing is expected and not an error
ec.ec.clear();
2014-07-06 21:18:00 +02:00
}
if (sz > 0) return true;
}
2014-07-06 21:18:00 +02:00
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
2014-07-06 21:18:00 +02:00
if (ec && ec.ec == boost::system::errc::no_such_file_or_directory)
ec.ec.clear();
2014-07-06 21:18:00 +02:00
if (ec)
{
ec.file(file_index_t(-1));
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::stat;
return false;
}
return false;
}
void default_storage::rename_file(file_index_t const index, std::string const& new_filename
, storage_error& ec)
{
if (index < file_index_t(0) || index >= files().end_file()) return;
std::string old_name = files().file_path(index, m_save_path);
m_pool.release(storage_index(), index);
// if the old file doesn't exist, just succeed and change the filename
// that will be created. This shortcut is important because the
// destination directory may not exist yet, which would cause a failure
// even though we're not moving a file (yet). It's better for it to
// fail later when we try to write to the file the first time, because
// the user then will have had a chance to make the destination directory
// valid.
if (exists(old_name, ec.ec))
{
std::string new_path;
if (is_complete(new_filename)) new_path = new_filename;
else new_path = combine_path(m_save_path, new_filename);
std::string new_dir = parent_path(new_path);
// create any missing directories that the new filename
// lands in
create_directories(new_dir, ec.ec);
if (ec.ec)
{
ec.file(index);
ec.operation = storage_error::rename;
return;
}
rename(old_name, new_path, ec.ec);
2014-07-06 21:18:00 +02:00
// if old_name doesn't exist, that's not an error
// here. Once we start writing to the file, it will
// be written to the new filename
if (ec.ec == boost::system::errc::no_such_file_or_directory)
ec.ec.clear();
2013-02-09 22:50:22 +01:00
if (ec)
{
ec.file(index);
ec.operation = storage_error::rename;
return;
}
2013-02-09 22:50:22 +01:00
}
else if (ec.ec)
{
// if exists fails, report that error
ec.file(index);
2014-07-06 21:18:00 +02:00
ec.operation = storage_error::rename;
return;
}
// if old path doesn't exist, just rename the file
// in our file_storage, so that when it is created
// it will get the new name
if (!m_mapped_files)
{ m_mapped_files.reset(new file_storage(files())); }
m_mapped_files->rename_file(index, new_filename);
}
2015-04-19 08:28:21 +02:00
void default_storage::release_files(storage_error&)
2004-12-21 13:30:09 +01:00
{
if (m_part_file)
{
error_code ignore;
m_part_file->flush_metadata(ignore);
m_part_file.reset();
}
2014-07-06 21:18:00 +02:00
// make sure we don't have the files open
m_pool.release(storage_index());
2014-07-06 21:18:00 +02:00
2017-01-28 20:36:23 +01:00
// make sure we can pick up new files added to the download directory when
// we start the torrent again
m_stat_cache.clear();
2003-12-07 17:26:16 +01:00
}
void default_storage::delete_files(int const options, storage_error& ec)
{
2014-07-06 21:18:00 +02:00
#if TORRENT_USE_ASSERTS
// this is a fence job, we expect no other
// threads to hold any references to any files
// in this file storage. Assert that that's the
2014-07-06 21:18:00 +02:00
// case
if (!m_pool.assert_idle_files(storage_index()))
2014-07-06 21:18:00 +02:00
{
TORRENT_ASSERT_FAIL();
2014-07-06 21:18:00 +02:00
}
#endif
// make sure we don't have the files open
m_pool.release(storage_index());
// if there's a part file open, make sure to destruct it to have it
// release the underlying part file. Otherwise we may not be able to
// delete it
if (m_part_file) m_part_file.reset();
aux::delete_files(files(), m_save_path, m_part_file_name, options, ec);
}
bool default_storage::verify_resume_data(add_torrent_params const& rd
, aux::vector<std::string, file_index_t> const& links
, storage_error& ec)
{
return aux::verify_resume_data(rd, links, files()
, m_file_priority, m_stat_cache, m_save_path, ec);
}
status_t default_storage::move_storage(std::string const& sp
, move_flags_t const flags, storage_error& ec)
2004-07-18 02:39:58 +02:00
{
m_pool.release(storage_index());
status_t ret;
std::tie(ret, m_save_path) = aux::move_storage(files(), m_save_path, sp
, m_part_file.get(), flags, ec);
2017-01-28 20:36:23 +01:00
// clear the stat cache in case the new location has new files
m_stat_cache.clear();
return ret;
}
int default_storage::readv(span<iovec_t const> bufs
, piece_index_t const piece, int const offset
, open_mode_t const flags, storage_error& error)
{
#ifdef TORRENT_SIMULATE_SLOW_READ
std::this_thread::sleep_for(seconds(1));
#endif
return readwritev(files(), bufs, piece, offset, error
, [this, flags](file_index_t const file_index
, std::int64_t const file_offset
, span<iovec_t const> vec, storage_error& ec)
{
if (files().pad_file_at(file_index))
{
// reading from a pad file yields zeroes
clear_bufs(vec);
return bufs_size(vec);
}
if (file_index < m_file_priority.end_index()
&& m_file_priority[file_index] == 0)
{
need_partfile();
error_code e;
peer_request map = files().map_file(file_index
, file_offset, 0);
int const ret = m_part_file->readv(vec
, map.piece, map.start, e);
if (e)
{
ec.ec = e;
ec.file(file_index);
ec.operation = storage_error::partfile_read;
return -1;
}
return ret;
}
file_handle handle = open_file(file_index
, open_mode_t::read_only | flags, ec);
if (ec) return -1;
error_code e;
int const ret = int(handle->readv(file_offset
, vec, e, flags));
// set this unconditionally in case the upper layer would like to treat
// short reads as errors
ec.operation = storage_error::read;
// we either get an error or 0 or more bytes read
TORRENT_ASSERT(e || ret >= 0);
TORRENT_ASSERT(ret <= bufs_size(vec));
if (e)
{
ec.ec = e;
ec.file(file_index);
return -1;
}
return ret;
});
2014-07-06 21:18:00 +02:00
}
int default_storage::writev(span<iovec_t const> bufs
, piece_index_t const piece, int const offset
, open_mode_t const flags, storage_error& error)
2014-07-06 21:18:00 +02:00
{
return readwritev(files(), bufs, piece, offset, error
, [this, flags](file_index_t const file_index
, std::int64_t const file_offset
, span<iovec_t const> vec, storage_error& ec)
{
if (files().pad_file_at(file_index))
{
// writing to a pad-file is a no-op
return bufs_size(vec);
}
if (file_index < m_file_priority.end_index()
&& m_file_priority[file_index] == 0)
{
need_partfile();
error_code e;
peer_request map = files().map_file(file_index
, file_offset, 0);
int const ret = m_part_file->writev(vec
, map.piece, map.start, e);
if (e)
{
ec.ec = e;
ec.file(file_index);
ec.operation = storage_error::partfile_write;
return -1;
}
return ret;
}
// invalidate our stat cache for this file, since
// we're writing to it
m_stat_cache.set_dirty(file_index);
file_handle handle = open_file(file_index
, open_mode_t::read_write, ec);
if (ec) return -1;
error_code e;
int const ret = int(handle->writev(file_offset
, vec, e, flags));
// set this unconditionally in case the upper layer would like to treat
// short reads as errors
ec.operation = storage_error::write;
// we either get an error or 0 or more bytes read
TORRENT_ASSERT(e || ret >= 0);
TORRENT_ASSERT(ret <= bufs_size(vec));
if (e)
{
ec.ec = e;
ec.file(file_index);
return -1;
}
return ret;
});
}
file_handle default_storage::open_file(file_index_t const file
, open_mode_t mode, storage_error& ec) const
{
file_handle h = open_file_impl(file, mode, ec.ec);
if (((mode & open_mode_t::rw_mask) != open_mode_t::read_only)
&& ec.ec == boost::system::errc::no_such_file_or_directory)
{
// this means the directory the file is in doesn't exist.
// so create it
ec.ec.clear();
std::string path = files().file_path(file, m_save_path);
create_directories(parent_path(path), ec.ec);
if (ec.ec)
{
ec.file(file);
ec.operation = storage_error::mkdir;
return file_handle();
}
// if the directory creation failed, don't try to open the file again
// but actually just fail
h = open_file_impl(file, mode, ec.ec);
}
if (ec.ec)
{
ec.file(file);
ec.operation = storage_error::open;
return file_handle();
}
TORRENT_ASSERT(h);
if (m_allocate_files && (mode & open_mode_t::rw_mask) != open_mode_t::read_only)
{
2017-05-08 13:48:42 +02:00
std::unique_lock<std::mutex> l(m_file_created_mutex);
if (m_file_created.size() != files().num_files())
m_file_created.resize(files().num_files(), false);
TORRENT_ASSERT(int(m_file_created.size()) == files().num_files());
TORRENT_ASSERT(file < m_file_created.end_index());
// if this is the first time we open this file for writing,
// and we have m_allocate_files enabled, set the final size of
// the file right away, to allocate it on the filesystem.
if (m_file_created[file] == false)
{
m_file_created.set_bit(file);
l.unlock();
error_code e;
std::int64_t const size = files().file_size(file);
h->set_size(size, e);
if (e)
{
ec.ec = e;
ec.file(file);
ec.operation = storage_error::fallocate;
return h;
}
m_stat_cache.set_dirty(file);
}
}
return h;
}
file_handle default_storage::open_file_impl(file_index_t file, open_mode_t mode
2014-07-06 21:18:00 +02:00
, error_code& ec) const
{
bool const lock_files = m_settings ? settings().get_bool(settings_pack::lock_files) : false;
if (lock_files) mode |= open_mode_t::lock_file;
if (!m_allocate_files) mode |= open_mode_t::sparse;
2011-02-22 03:53:26 +01:00
2014-07-06 21:18:00 +02:00
// files with priority 0 should always be sparse
if (m_file_priority.end_index() > file && m_file_priority[file] == 0)
mode |= open_mode_t::sparse;
if (m_settings && settings().get_bool(settings_pack::no_atime_storage)) mode |= open_mode_t::no_atime;
2011-02-22 03:53:26 +01:00
2014-07-06 21:18:00 +02:00
// if we have a cache already, don't store the data twice by leaving it in the OS cache as well
if (m_settings
&& settings().get_int(settings_pack::disk_io_write_mode)
== settings_pack::disable_os_cache)
2011-02-22 03:53:26 +01:00
{
mode |= open_mode_t::no_cache;
2011-02-22 03:53:26 +01:00
}
file_handle ret = m_pool.open_file(storage_index(), m_save_path, file
, files(), mode, ec);
if (ec && test(mode & open_mode_t::lock_file))
{
// we failed to open the file and we're trying to lock it. It's
// possible we're failing because we have another handle to this
// file in use (but waiting to be closed). Just retry to open it
// without locking.
mode &= ~open_mode_t::lock_file;
ret = m_pool.open_file(storage_index(), m_save_path, file, files()
, mode, ec);
}
return ret;
2003-12-07 02:26:57 +01:00
}
2014-07-06 21:18:00 +02:00
bool default_storage::tick()
2003-12-07 02:26:57 +01:00
{
2014-07-06 21:18:00 +02:00
error_code ec;
if (m_part_file) m_part_file->flush_metadata(ec);
2014-07-06 21:18:00 +02:00
return false;
}
storage_interface* default_storage_constructor(storage_params const& params
, file_pool& pool)
{
return new default_storage(params, pool);
}
// -- disabled_storage --------------------------------------------------
namespace {
// this storage implementation does not write anything to disk
// and it pretends to read, and just leaves garbage in the buffers
// this is useful when simulating many clients on the same machine
// or when running stress tests and want to take the cost of the
// disk I/O out of the picture. This cannot be used for any kind
// of normal bittorrent operation, since it will just send garbage
// to peers and throw away all the data it downloads. It would end
// up being banned immediately
class disabled_storage final : public storage_interface
{
public:
explicit disabled_storage(file_storage const& fs) : storage_interface(fs) {}
2016-07-10 02:10:38 +02:00
bool has_any_file(storage_error&) override { return false; }
void set_file_priority(aux::vector<std::uint8_t, file_index_t> const&
, storage_error&) override {}
void rename_file(file_index_t, std::string const&, storage_error&) override {}
2016-07-10 02:10:38 +02:00
void release_files(storage_error&) override {}
void delete_files(int, storage_error&) override {}
void initialize(storage_error&) override {}
status_t move_storage(std::string const&, move_flags_t, storage_error&) override { return status_t::no_error; }
int readv(span<iovec_t const> bufs
, piece_index_t, int, open_mode_t, storage_error&) override
{
return bufs_size(bufs);
}
int writev(span<iovec_t const> bufs
, piece_index_t, int, open_mode_t, storage_error&) override
{
return bufs_size(bufs);
}
2016-07-10 02:10:38 +02:00
bool verify_resume_data(add_torrent_params const&
, aux::vector<std::string, file_index_t> const&
, storage_error&) override { return false; }
};
}
storage_interface* disabled_storage_constructor(storage_params const& params, file_pool&)
{
return new disabled_storage(*params.files);
}
2003-12-09 09:49:49 +01:00
2014-07-06 21:18:00 +02:00
// -- zero_storage ------------------------------------------------------
namespace {
// this storage implementation always reads zeroes, and always discards
// anything written to it
struct zero_storage final : storage_interface
{
explicit zero_storage(file_storage const& fs) : storage_interface(fs) {}
2016-07-10 02:10:38 +02:00
void initialize(storage_error&) override {}
int readv(span<iovec_t const> bufs
, piece_index_t, int, open_mode_t, storage_error&) override
{
int ret = 0;
for (auto const& b : bufs)
{
std::memset(b.data(), 0, b.size());
ret += int(b.size());
}
return 0;
}
int writev(span<iovec_t const> bufs
, piece_index_t, int, open_mode_t, storage_error&) override
{
int ret = 0;
for (auto const& b : bufs)
ret += int(b.size());
return 0;
}
2016-07-10 02:10:38 +02:00
bool has_any_file(storage_error&) override { return false; }
void set_file_priority(aux::vector<std::uint8_t, file_index_t> const& /* prio */
, storage_error&) override {}
status_t move_storage(std::string const& /* save_path */
, move_flags_t, storage_error&) override { return status_t::no_error; }
2016-07-10 02:10:38 +02:00
bool verify_resume_data(add_torrent_params const& /* rd */
, aux::vector<std::string, file_index_t> const& /* links */
, storage_error&) override
{ return false; }
2016-07-10 02:10:38 +02:00
void release_files(storage_error&) override {}
void rename_file(file_index_t
, std::string const& /* new_filename */, storage_error&) override {}
2016-07-10 02:10:38 +02:00
void delete_files(int, storage_error&) override {}
};
}
storage_interface* zero_storage_constructor(storage_params const& params, file_pool&)
{
return new zero_storage(*params.files);
}
2003-12-07 02:26:57 +01:00
} // namespace libtorrent