2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2003-2016, Arvid Norberg, Daniel Wallin
|
2003-10-23 01:00:57 +02:00
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2015-06-06 08:10:53 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
|
|
|
#include "libtorrent/error_code.hpp"
|
|
|
|
|
2015-04-21 03:16:28 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
2015-04-18 04:33:39 +02:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <ctime>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
2004-12-21 13:30:09 +01:00
|
|
|
#include <functional>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-11-26 15:11:25 +01:00
|
|
|
#include <boost/ref.hpp>
|
2004-12-21 13:30:09 +01:00
|
|
|
#include <boost/bind.hpp>
|
2005-03-05 00:45:16 +01:00
|
|
|
#include <boost/version.hpp>
|
2009-01-03 09:11:31 +01:00
|
|
|
#include <boost/scoped_array.hpp>
|
2015-06-06 08:10:53 +02:00
|
|
|
#include <boost/system/error_code.hpp>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#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>
|
2004-01-25 19:18:36 +01:00
|
|
|
#endif
|
|
|
|
|
2015-04-21 03:16:28 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
2015-04-18 04:33:39 +02:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/storage.hpp"
|
|
|
|
#include "libtorrent/torrent.hpp"
|
|
|
|
#include "libtorrent/hasher.hpp"
|
2003-10-30 00:28:09 +01:00
|
|
|
#include "libtorrent/session.hpp"
|
2003-12-07 06:53:04 +01:00
|
|
|
#include "libtorrent/peer_id.hpp"
|
2004-01-16 00:57:11 +01:00
|
|
|
#include "libtorrent/file.hpp"
|
2004-01-25 19:18:36 +01:00
|
|
|
#include "libtorrent/invariant_check.hpp"
|
2006-11-14 14:36:10 +01:00
|
|
|
#include "libtorrent/file_pool.hpp"
|
2006-10-11 16:02:21 +02:00
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
2008-04-10 12:03:23 +02:00
|
|
|
#include "libtorrent/disk_buffer_holder.hpp"
|
2009-01-03 09:11:31 +01:00
|
|
|
#include "libtorrent/alloca.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/stat_cache.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2009-04-04 11:52:25 +02:00
|
|
|
#include <cstdio>
|
|
|
|
|
2008-09-17 01:32:27 +02:00
|
|
|
//#define TORRENT_PARTIAL_HASH_LOG
|
|
|
|
|
2009-03-01 01:02:33 +01:00
|
|
|
// for convert_to_wstring and convert_to_native
|
2015-03-15 00:10:20 +01:00
|
|
|
#include "libtorrent/aux_/escape_string.hpp"
|
2005-08-16 20:39:38 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#define DEBUG_STORAGE 0
|
|
|
|
#define DEBUG_DELETE_FILES 0
|
|
|
|
|
2015-09-02 07:30:40 +02:00
|
|
|
#if __cplusplus >= 201103L || defined __clang__
|
2015-08-02 21:55:05 +02:00
|
|
|
|
|
|
|
#if DEBUG_STORAGE
|
|
|
|
#define DLOG(...) fprintf(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define DLOG(...) do {} while (false)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if DEBUG_DELETE_FILES
|
|
|
|
#define DFLOG(...) fprintf(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define DFLOG(...) do {} while (false)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2015-04-27 04:21:12 +02:00
|
|
|
#if DEBUG_STORAGE
|
|
|
|
#define DLOG fprintf
|
|
|
|
#else
|
|
|
|
#define DLOG TORRENT_WHILE_0 fprintf
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if DEBUG_DELETE_FILES
|
|
|
|
#define DFLOG fprintf
|
|
|
|
#else
|
|
|
|
#define DFLOG TORRENT_WHILE_0 fprintf
|
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-08-02 21:55:05 +02:00
|
|
|
#endif // cplusplus
|
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
2009-01-03 09:11:31 +01:00
|
|
|
int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target)
|
|
|
|
{
|
|
|
|
int size = 0;
|
|
|
|
int ret = 1;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
*target = *bufs;
|
2016-04-25 23:22:09 +02:00
|
|
|
size += int(bufs->iov_len);
|
2009-01-03 09:11:31 +01:00
|
|
|
if (size >= bytes)
|
|
|
|
{
|
|
|
|
target->iov_len -= size - bytes;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
++bufs;
|
|
|
|
++target;
|
|
|
|
++ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void advance_bufs(file::iovec_t*& bufs, int bytes)
|
|
|
|
{
|
|
|
|
int size = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
2016-04-25 23:22:09 +02:00
|
|
|
size += int(bufs->iov_len);
|
2009-01-13 09:58:35 +01:00
|
|
|
if (size >= bytes)
|
2009-01-03 09:11:31 +01:00
|
|
|
{
|
2015-08-16 18:17:23 +02:00
|
|
|
bufs->iov_base = reinterpret_cast<char*>(bufs->iov_base)
|
|
|
|
+ bufs->iov_len - (size - bytes);
|
2009-01-03 09:11:31 +01:00
|
|
|
bufs->iov_len = size - bytes;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
++bufs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
void clear_bufs(file::iovec_t const* bufs, int num_bufs)
|
2009-01-03 09:11:31 +01:00
|
|
|
{
|
2009-01-11 03:02:34 +01:00
|
|
|
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
|
2009-01-03 09:11:31 +01:00
|
|
|
std::memset(i->iov_base, 0, i->iov_len);
|
|
|
|
}
|
|
|
|
|
2015-04-19 08:28:21 +02:00
|
|
|
namespace {
|
|
|
|
|
2009-01-12 19:08:18 +01:00
|
|
|
int count_bufs(file::iovec_t const* bufs, int bytes)
|
|
|
|
{
|
|
|
|
int size = 0;
|
|
|
|
int count = 1;
|
|
|
|
if (bytes == 0) return 0;
|
|
|
|
for (file::iovec_t const* i = bufs;; ++i, ++count)
|
|
|
|
{
|
2016-04-25 23:22:09 +02:00
|
|
|
size += int(i->iov_len);
|
2009-01-12 19:08:18 +01:00
|
|
|
if (size >= bytes) return count;
|
|
|
|
}
|
|
|
|
}
|
2015-08-03 06:25:14 +02:00
|
|
|
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
static boost::atomic<int> event_id;
|
|
|
|
static mutex disk_access_mutex;
|
|
|
|
|
|
|
|
// this is opened and closed by the disk_io_thread class
|
|
|
|
FILE* g_access_log = NULL;
|
|
|
|
|
|
|
|
enum access_log_flags_t
|
|
|
|
{
|
|
|
|
op_read = 0,
|
|
|
|
op_write = 1,
|
|
|
|
op_start = 0,
|
|
|
|
op_end = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
void write_access_log(boost::uint64_t offset, boost::uint32_t fileid, int flags, time_point timestamp)
|
|
|
|
{
|
|
|
|
if (g_access_log == NULL) return;
|
|
|
|
|
|
|
|
// the event format in the log is:
|
|
|
|
// uint64_t timestamp (microseconds)
|
|
|
|
// uint64_t file offset
|
|
|
|
// uint32_t file-id
|
|
|
|
// uint8_t event (0: start read, 1: start write, 2: complete read, 4: complete write)
|
|
|
|
char event[29];
|
|
|
|
char* ptr = event;
|
|
|
|
detail::write_uint64(timestamp.time_since_epoch().count(), ptr);
|
|
|
|
detail::write_uint64(offset, ptr);
|
2015-09-02 07:30:40 +02:00
|
|
|
detail::write_uint64(static_cast<boost::uint64_t>(event_id++), ptr);
|
2015-08-03 06:25:14 +02:00
|
|
|
detail::write_uint32(fileid, ptr);
|
|
|
|
detail::write_uint8(flags, ptr);
|
|
|
|
|
|
|
|
mutex::scoped_lock l(disk_access_mutex);
|
|
|
|
int ret = fwrite(event, 1, sizeof(event), g_access_log);
|
|
|
|
l.unlock();
|
|
|
|
if (ret != sizeof(event))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR writing to disk access log: (%d) %s\n"
|
|
|
|
, errno, strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2015-11-16 04:36:55 +01:00
|
|
|
|
2015-04-19 08:28:21 +02:00
|
|
|
} // anonymous namespace
|
2009-01-12 19:08:18 +01:00
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
struct write_fileop : fileop
|
|
|
|
{
|
|
|
|
write_fileop(default_storage& st, int flags)
|
|
|
|
: m_storage(st)
|
|
|
|
, m_flags(flags)
|
|
|
|
{}
|
|
|
|
|
2016-03-20 16:38:55 +01:00
|
|
|
int file_op(int const file_index
|
|
|
|
, boost::int64_t const file_offset
|
|
|
|
, int const size
|
2015-11-16 04:36:55 +01:00
|
|
|
, file::iovec_t const* bufs, storage_error& ec)
|
2016-04-30 17:05:54 +02:00
|
|
|
override final
|
2015-11-16 04:36:55 +01:00
|
|
|
{
|
|
|
|
if (m_storage.files().pad_file_at(file_index))
|
|
|
|
{
|
|
|
|
// writing to a pad-file is a no-op
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int num_bufs = count_bufs(bufs, size);
|
|
|
|
|
|
|
|
if (file_index < int(m_storage.m_file_priority.size())
|
|
|
|
&& m_storage.m_file_priority[file_index] == 0)
|
|
|
|
{
|
|
|
|
m_storage.need_partfile();
|
|
|
|
|
|
|
|
error_code e;
|
|
|
|
peer_request map = m_storage.files().map_file(file_index
|
|
|
|
, file_offset, 0);
|
|
|
|
int ret = m_storage.m_part_file->writev(bufs, num_bufs
|
|
|
|
, 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_storage.m_stat_cache.set_dirty(file_index);
|
|
|
|
|
|
|
|
file_handle handle = m_storage.open_file(file_index
|
|
|
|
, file::read_write, ec);
|
|
|
|
if (ec) return -1;
|
|
|
|
|
|
|
|
// please ignore the adjusted_offset. It's just file_offset.
|
|
|
|
boost::int64_t adjusted_offset =
|
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
m_storage.files().file_base_deprecated(file_index) +
|
|
|
|
#endif
|
|
|
|
file_offset;
|
|
|
|
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
write_access_log(adjusted_offset, handle->file_id(), op_start | op_write, clock_type::now());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
error_code e;
|
|
|
|
int ret = handle->writev(adjusted_offset
|
|
|
|
, bufs, num_bufs, e, m_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);
|
|
|
|
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
write_access_log(adjusted_offset + ret , handle->file_id(), op_end | op_write, clock_type::now());
|
|
|
|
#endif
|
|
|
|
TORRENT_ASSERT(ret <= bufs_size(bufs, num_bufs));
|
|
|
|
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
ec.ec = e;
|
|
|
|
ec.file = file_index;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
default_storage& m_storage;
|
|
|
|
int m_flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct read_fileop : fileop
|
|
|
|
{
|
2016-03-20 16:38:55 +01:00
|
|
|
read_fileop(default_storage& st, int const flags)
|
2015-11-16 04:36:55 +01:00
|
|
|
: m_storage(st)
|
|
|
|
, m_flags(flags)
|
|
|
|
{}
|
|
|
|
|
2016-03-20 16:38:55 +01:00
|
|
|
int file_op(int const file_index
|
|
|
|
, boost::int64_t const file_offset
|
|
|
|
, int const size
|
2015-11-16 04:36:55 +01:00
|
|
|
, file::iovec_t const* bufs, storage_error& ec)
|
2016-04-30 17:05:54 +02:00
|
|
|
override final
|
2015-11-16 04:36:55 +01:00
|
|
|
{
|
|
|
|
int num_bufs = count_bufs(bufs, size);
|
|
|
|
|
|
|
|
if (m_storage.files().pad_file_at(file_index))
|
|
|
|
{
|
|
|
|
// reading from a pad file yields zeroes
|
|
|
|
clear_bufs(bufs, num_bufs);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_index < int(m_storage.m_file_priority.size())
|
|
|
|
&& m_storage.m_file_priority[file_index] == 0)
|
|
|
|
{
|
|
|
|
m_storage.need_partfile();
|
|
|
|
|
|
|
|
error_code e;
|
|
|
|
peer_request map = m_storage.files().map_file(file_index
|
|
|
|
, file_offset, 0);
|
|
|
|
int ret = m_storage.m_part_file->readv(bufs, num_bufs
|
|
|
|
, 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 = m_storage.open_file(file_index
|
|
|
|
, file::read_only | m_flags, ec);
|
|
|
|
if (ec) return -1;
|
|
|
|
|
|
|
|
// please ignore the adjusted_offset. It's just file_offset.
|
|
|
|
boost::int64_t adjusted_offset =
|
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
m_storage.files().file_base_deprecated(file_index) +
|
|
|
|
#endif
|
|
|
|
file_offset;
|
|
|
|
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
write_access_log(adjusted_offset, handle->file_id(), op_start | op_read, clock_type::now());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
error_code e;
|
|
|
|
int ret = handle->readv(adjusted_offset
|
|
|
|
, bufs, num_bufs, e, m_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);
|
|
|
|
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
write_access_log(adjusted_offset + ret , handle->file_id(), op_end | op_read, clock_type::now());
|
|
|
|
#endif
|
|
|
|
TORRENT_ASSERT(ret <= bufs_size(bufs, num_bufs));
|
|
|
|
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
ec.ec = e;
|
|
|
|
ec.file = file_index;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
default_storage& m_storage;
|
2016-03-20 16:38:55 +01:00
|
|
|
int const m_flags;
|
2015-11-16 04:36:55 +01:00
|
|
|
};
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
default_storage::default_storage(storage_params const& params)
|
|
|
|
: m_files(*params.files)
|
|
|
|
, m_pool(*params.pool)
|
|
|
|
, 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(m_files.num_files() > 0);
|
|
|
|
m_save_path = complete(params.path);
|
|
|
|
m_part_file_name = "." + (params.info
|
|
|
|
? to_hex(params.info->info_hash().to_string())
|
|
|
|
: 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(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
, m_files.num_pieces(), m_files.piece_length()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void default_storage::set_file_priority(std::vector<boost::uint8_t> const& prio, storage_error& ec)
|
|
|
|
{
|
|
|
|
// extend our file priorities in case it's truncated
|
|
|
|
// the default assumed priority is 1
|
|
|
|
if (prio.size() > m_file_priority.size())
|
|
|
|
m_file_priority.resize(prio.size(), 1);
|
|
|
|
|
|
|
|
file_storage const& fs = files();
|
|
|
|
for (int i = 0; i < int(prio.size()); ++i)
|
2007-09-19 21:55:11 +02:00
|
|
|
{
|
2016-03-03 00:38:13 +01:00
|
|
|
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)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// move stuff out of the part file
|
2015-01-03 17:57:21 +01:00
|
|
|
file_handle f = open_file(i, file::read_write, ec);
|
|
|
|
if (ec) return;
|
2009-05-03 21:09:06 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
need_partfile();
|
|
|
|
|
|
|
|
m_part_file->export_file(*f, fs.file_offset(i), fs.file_size(i), ec.ec);
|
|
|
|
if (ec)
|
2009-05-03 21:09:06 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = i;
|
|
|
|
ec.operation = storage_error::partfile_write;
|
|
|
|
return;
|
2009-05-03 21:09:06 +02:00
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else if (old_prio != 0 && new_prio == 0)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
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;
|
|
|
|
/*
|
2015-01-03 17:57:21 +01:00
|
|
|
file_handle f = open_file(i, file::read_only, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ec.ec != boost::system::errc::no_such_file_or_directory)
|
2009-05-03 21:09:06 +02:00
|
|
|
{
|
2015-01-03 17:57:21 +01:00
|
|
|
if (ec) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
need_partfile();
|
2009-05-21 18:15:05 +02:00
|
|
|
|
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)
|
2009-05-21 18:15:05 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = i;
|
|
|
|
ec.operation = storage_error::partfile_read;
|
|
|
|
return;
|
2009-05-21 18:15:05 +02:00
|
|
|
}
|
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)
|
2009-05-21 18:15:05 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = i;
|
|
|
|
ec.operation = storage_error::remove;
|
2009-05-21 18:15:05 +02:00
|
|
|
}
|
2009-05-03 21:09:06 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
*/
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.ec.clear();
|
|
|
|
m_file_priority[i] = new_prio;
|
|
|
|
}
|
|
|
|
if (m_part_file) m_part_file->flush_metadata(ec.ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
ec.file = -1;
|
|
|
|
ec.operation = storage_error::partfile_write;
|
2007-09-19 21:55:11 +02:00
|
|
|
}
|
2014-02-28 11:19:29 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void default_storage::initialize(storage_error& ec)
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2015-12-11 07:08:57 +01:00
|
|
|
m_stat_cache.reserve(files().num_files());
|
2014-06-07 04:57:24 +02:00
|
|
|
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
// don't do full file allocations on network drives
|
2014-06-09 06:08:41 +02:00
|
|
|
#if TORRENT_USE_WSTRING
|
2016-04-17 22:56:07 +02:00
|
|
|
std::wstring file_name = convert_to_wstring(m_save_path);
|
|
|
|
int drive_type = GetDriveTypeW(file_name.c_str());
|
2014-06-09 06:08:41 +02:00
|
|
|
#else
|
|
|
|
int drive_type = GetDriveTypeA(m_save_path.c_str());
|
|
|
|
#endif
|
|
|
|
|
2014-06-07 04:57:24 +02:00
|
|
|
if (drive_type == DRIVE_REMOTE)
|
|
|
|
m_allocate_files = false;
|
|
|
|
#endif
|
|
|
|
|
2013-12-30 03:50:29 +01:00
|
|
|
m_file_created.resize(files().num_files(), false);
|
|
|
|
|
2007-04-19 05:06:15 +02:00
|
|
|
// first, create all missing directories
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string last_path;
|
2013-08-12 09:30:57 +02:00
|
|
|
for (int file_index = 0; file_index < files().num_files(); ++file_index)
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2008-10-30 07:09:23 +01:00
|
|
|
// ignore files that have priority 0
|
|
|
|
if (int(m_file_priority.size()) > file_index
|
2014-07-06 21:18:00 +02:00
|
|
|
&& m_file_priority[file_index] == 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2008-10-30 07:09:23 +01:00
|
|
|
|
|
|
|
// ignore pad files
|
2013-08-12 09:30:57 +02:00
|
|
|
if (files().pad_file_at(file_index)) continue;
|
2007-04-19 05:06:15 +02:00
|
|
|
|
2015-12-11 07:08:57 +01:00
|
|
|
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)
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2015-12-11 07:08:57 +01:00
|
|
|
ec.file = file_index;
|
|
|
|
ec.operation = storage_error::stat;
|
|
|
|
ec.ec = err;
|
|
|
|
break;
|
2009-10-26 02:29:39 +01:00
|
|
|
}
|
|
|
|
|
2011-01-19 08:39:55 +01:00
|
|
|
// 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
|
2011-01-19 08:39:55 +01:00
|
|
|
// if the file is empty, just create it either way.
|
2015-12-11 07:08:57 +01:00
|
|
|
if ((!err && size > files().file_size(file_index))
|
2014-07-06 21:18:00 +02:00
|
|
|
|| files().file_size(file_index) == 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2015-02-18 04:32:49 +01:00
|
|
|
std::string file_path = files().file_path(file_index, m_save_path);
|
2011-08-01 02:22:54 +02:00
|
|
|
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)
|
2011-08-01 02:22:54 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = file_index;
|
|
|
|
ec.operation = storage_error::mkdir;
|
2011-08-01 02:22:54 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.ec.clear();
|
2015-01-03 17:57:21 +01:00
|
|
|
file_handle f = open_file(file_index, file::read_write
|
|
|
|
| file::random_access, ec);
|
2015-12-11 07:08:57 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
ec.file = file_index;
|
|
|
|
ec.operation = storage_error::fallocate;
|
|
|
|
return;
|
|
|
|
}
|
2015-01-03 17:57:21 +01:00
|
|
|
|
2015-12-11 07:08:57 +01:00
|
|
|
size = files().file_size(file_index);
|
2015-02-18 04:32:49 +01:00
|
|
|
f->set_size(size, ec.ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ec)
|
2008-07-18 01:41:46 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = file_index;
|
|
|
|
ec.operation = storage_error::fallocate;
|
|
|
|
break;
|
2008-07-18 01:41:46 +02:00
|
|
|
}
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.ec.clear();
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2007-10-07 03:29:38 +02:00
|
|
|
// close files that were opened in write mode
|
2008-05-28 10:44:40 +02:00
|
|
|
m_pool.release(this);
|
2013-01-06 05:53:26 +01:00
|
|
|
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
print_open_files("release files", m_files.name().c_str());
|
|
|
|
#endif
|
2008-05-28 10:44:40 +02:00
|
|
|
}
|
|
|
|
|
2013-02-16 09:26:55 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-07-06 21:18:00 +02:00
|
|
|
void default_storage::finalize_file(int, storage_error&) {}
|
2013-02-16 09:26:55 +01:00
|
|
|
#endif
|
2010-01-09 19:40:05 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool default_storage::has_any_file(storage_error& ec)
|
2009-04-10 09:22:27 +02:00
|
|
|
{
|
2015-12-11 07:08:57 +01:00
|
|
|
m_stat_cache.reserve(files().num_files());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
std::string file_path;
|
2013-08-12 09:30:57 +02:00
|
|
|
for (int i = 0; i < files().num_files(); ++i)
|
2009-04-10 09:22:27 +02:00
|
|
|
{
|
2015-12-11 07:08:57 +01:00
|
|
|
boost::int64_t sz = m_stat_cache.get_filesize(
|
|
|
|
i, files(), m_save_path, ec.ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-12-11 07:08:57 +01: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;
|
|
|
|
ec.operation = storage_error::stat;
|
|
|
|
m_stat_cache.clear();
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-11 07:08:57 +01:00
|
|
|
// some files not existing is expected and not an error
|
|
|
|
ec.ec.clear();
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2015-12-11 07:08:57 +01:00
|
|
|
if (sz > 0) return true;
|
2009-04-10 09:22:27 +02:00
|
|
|
}
|
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;
|
|
|
|
|
2015-12-11 07:08:57 +01:00
|
|
|
// 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();
|
2015-12-11 07:08:57 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
ec.file = -1;
|
|
|
|
ec.operation = storage_error::stat;
|
|
|
|
return false;
|
|
|
|
}
|
2009-04-10 09:22:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-26 22:25:37 +01:00
|
|
|
void default_storage::rename_file(int index, std::string const& new_filename
|
|
|
|
, storage_error& ec)
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (index < 0 || index >= files().num_files()) return;
|
2013-06-10 00:30:02 +02:00
|
|
|
std::string old_name = files().file_path(index, m_save_path);
|
2010-11-25 00:49:22 +01:00
|
|
|
m_pool.release(this, index);
|
2008-05-28 10:44:40 +02:00
|
|
|
|
2014-12-26 22:25:37 +01:00
|
|
|
// 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.
|
2015-01-13 19:28:06 +01:00
|
|
|
if (exists(old_name, ec.ec))
|
2014-12-26 22:25:37 +01:00
|
|
|
{
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2015-01-13 19:28:06 +01:00
|
|
|
print_open_files("release files", m_files.name().c_str());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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)
|
2014-12-26 22:25:37 +01:00
|
|
|
{
|
2015-01-13 19:28:06 +01:00
|
|
|
ec.file = index;
|
|
|
|
ec.operation = storage_error::rename;
|
2014-12-26 22:25:37 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-13 19:28:06 +01:00
|
|
|
rename(old_name, new_path, ec.ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-01-13 19:28:06 +01: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
|
|
|
|
2015-01-13 19:28:06 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
ec.file = index;
|
|
|
|
ec.operation = storage_error::rename;
|
|
|
|
return;
|
|
|
|
}
|
2013-02-09 22:50:22 +01:00
|
|
|
}
|
2015-01-13 19:28:06 +01:00
|
|
|
else if (ec.ec)
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
2015-01-13 19:28:06 +01:00
|
|
|
// if exists fails, report that error
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = index;
|
|
|
|
ec.operation = storage_error::rename;
|
|
|
|
return;
|
2008-05-28 10:44:40 +02:00
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
|
|
|
|
// 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(m_files)); }
|
|
|
|
m_mapped_files->rename_file(index, new_filename);
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
|
|
|
|
2015-04-19 08:28:21 +02:00
|
|
|
void default_storage::release_files(storage_error&)
|
2004-12-21 13:30:09 +01:00
|
|
|
{
|
2015-10-24 21:35:42 +02: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
|
2008-05-28 10:44:40 +02:00
|
|
|
m_pool.release(this);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
print_open_files("release files", m_files.name().c_str());
|
|
|
|
#endif
|
2003-12-07 17:26:16 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void default_storage::delete_one_file(std::string const& p, error_code& ec)
|
2009-04-12 20:04:20 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
remove(p, ec);
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2015-09-06 22:47:10 +02:00
|
|
|
DFLOG(stderr, "[%p] delete_one_file: %s [%s]\n", static_cast<void*>(this)
|
|
|
|
, p.c_str(), ec.message().c_str());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (ec == boost::system::errc::no_such_file_or_directory)
|
|
|
|
ec.clear();
|
2009-04-12 20:04:20 +02:00
|
|
|
}
|
|
|
|
|
2016-03-13 08:50:37 +01:00
|
|
|
void default_storage::delete_files(int const options, storage_error& ec)
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
2016-03-13 08:50:37 +01:00
|
|
|
DFLOG(stderr, "[%p] delete_files [%x]\n", static_cast<void*>(this)
|
|
|
|
, options);
|
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
|
2015-06-06 08:10:53 +02:00
|
|
|
// in this file storage. Assert that that's the
|
2014-07-06 21:18:00 +02:00
|
|
|
// case
|
|
|
|
if (!m_pool.assert_idle_files(this))
|
|
|
|
{
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
print_open_files("delete-files idle assert failed", m_files.name().c_str());
|
|
|
|
#endif
|
|
|
|
TORRENT_ASSERT(false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-10-13 05:33:33 +02:00
|
|
|
// make sure we don't have the files open
|
2008-05-28 10:44:40 +02:00
|
|
|
m_pool.release(this);
|
2007-10-13 05:33:33 +02:00
|
|
|
|
2016-03-13 08:50:37 +01:00
|
|
|
// 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();
|
|
|
|
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
print_open_files("release files", m_files.name().c_str());
|
|
|
|
#endif
|
|
|
|
|
2016-03-13 08:50:37 +01:00
|
|
|
if (options == session::delete_files)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2016-03-13 08:50:37 +01:00
|
|
|
m_pool.mark_deleted(m_files);
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2016-03-13 08:50:37 +01:00
|
|
|
// delete the files from disk
|
|
|
|
std::set<std::string> directories;
|
|
|
|
typedef std::set<std::string>::iterator iter_t;
|
|
|
|
for (int i = 0; i < files().num_files(); ++i)
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
2016-03-13 08:50:37 +01:00
|
|
|
std::string fp = files().file_path(i);
|
|
|
|
bool complete = is_complete(fp);
|
|
|
|
std::string p = complete ? fp : combine_path(m_save_path, fp);
|
|
|
|
if (!complete)
|
2013-06-10 00:30:02 +02:00
|
|
|
{
|
2016-03-13 08:50:37 +01:00
|
|
|
std::string bp = parent_path(fp);
|
|
|
|
std::pair<iter_t, bool> ret;
|
|
|
|
ret.second = true;
|
|
|
|
while (ret.second && !bp.empty())
|
|
|
|
{
|
|
|
|
ret = directories.insert(combine_path(m_save_path, bp));
|
|
|
|
bp = parent_path(bp);
|
|
|
|
}
|
2013-06-10 00:30:02 +02:00
|
|
|
}
|
2016-03-13 08:50:37 +01:00
|
|
|
delete_one_file(p, ec.ec);
|
|
|
|
if (ec) { ec.file = i; ec.operation = storage_error::remove; }
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
|
|
|
|
2016-03-13 08:50:37 +01:00
|
|
|
// remove the directories. Reverse order to delete
|
|
|
|
// subdirectories first
|
2007-11-16 20:04:43 +01:00
|
|
|
|
2016-03-13 08:50:37 +01:00
|
|
|
for (std::set<std::string>::reverse_iterator i = directories.rbegin()
|
|
|
|
, end(directories.rend()); i != end; ++i)
|
|
|
|
{
|
|
|
|
error_code error;
|
|
|
|
delete_one_file(*i, error);
|
|
|
|
if (error && !ec) { ec.file = -1; ec.ec = error; ec.operation = storage_error::remove; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options == session::delete_files
|
|
|
|
|| options == session::delete_partfile)
|
2007-11-16 20:04:43 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
error_code error;
|
2016-03-13 08:50:37 +01:00
|
|
|
remove(combine_path(m_save_path, m_part_file_name), error);
|
|
|
|
DFLOG(stderr, "[%p] delete partfile %s/%s [%s]\n", static_cast<void*>(this)
|
|
|
|
, m_save_path.c_str(), m_part_file_name.c_str(), error.message().c_str());
|
|
|
|
if (error && error != boost::system::errc::no_such_file_or_directory)
|
|
|
|
{
|
|
|
|
ec.file = -1;
|
|
|
|
ec.ec = error;
|
|
|
|
ec.operation = storage_error::remove;
|
|
|
|
}
|
2007-11-16 20:04:43 +01:00
|
|
|
}
|
|
|
|
|
2015-09-06 22:47:10 +02:00
|
|
|
DFLOG(stderr, "[%p] delete_files result: %s\n", static_cast<void*>(this)
|
|
|
|
, ec.ec.message().c_str());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
print_open_files("delete-files done", m_files.name().c_str());
|
|
|
|
#endif
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
|
|
|
|
2016-02-15 00:17:32 +01:00
|
|
|
bool default_storage::verify_resume_data(add_torrent_params const& rd
|
2015-03-21 01:12:40 +01:00
|
|
|
, std::vector<std::string> const* links
|
|
|
|
, storage_error& ec)
|
2007-03-28 01:49:05 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
file_storage const& fs = files();
|
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
if (links)
|
|
|
|
{
|
|
|
|
// if this is a mutable torrent, and we need to pick up some files
|
|
|
|
// from other torrents, do that now. Note that there is an inherent
|
|
|
|
// race condition here. We checked if the files existed on a different
|
|
|
|
// thread a while ago. These files may no longer exist or may have been
|
|
|
|
// moved. If so, we just fail. The user is responsible to not touch
|
|
|
|
// other torrents until a new mutable torrent has been completely
|
|
|
|
// added.
|
|
|
|
int idx = 0;
|
|
|
|
for (std::vector<std::string>::const_iterator i = links->begin();
|
|
|
|
i != links->end(); ++i, ++idx)
|
|
|
|
{
|
|
|
|
if (i->empty()) continue;
|
|
|
|
|
|
|
|
error_code err;
|
|
|
|
std::string file_path = fs.file_path(idx, m_save_path);
|
|
|
|
hard_link(*i, file_path, err);
|
|
|
|
|
|
|
|
// if the file already exists, that's not an error
|
|
|
|
// TODO: 2 is this risky? The upper layer will assume we have the
|
|
|
|
// whole file. Perhaps we should verify that at least the size
|
|
|
|
// of the file is correct
|
|
|
|
if (!err || err == boost::system::errc::file_exists)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ec.ec = err;
|
|
|
|
ec.file = idx;
|
|
|
|
ec.operation = storage_error::hard_link;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
|
2016-02-15 00:17:32 +01:00
|
|
|
bool const seed = rd.have_pieces.all_set();
|
2015-12-15 06:03:43 +01:00
|
|
|
|
2016-02-15 00:17:32 +01:00
|
|
|
// parse have bitmask. Verify that the files we expect to have
|
|
|
|
// actually do exist
|
|
|
|
for (int i = 0; i < rd.have_pieces.size(); ++i)
|
|
|
|
{
|
|
|
|
if (rd.have_pieces.get_bit(i) == false) continue;
|
2015-12-06 18:45:20 +01:00
|
|
|
|
2016-02-15 00:17:32 +01:00
|
|
|
std::vector<file_slice> f = fs.map_block(i, 0, 1);
|
|
|
|
TORRENT_ASSERT(!f.empty());
|
2015-12-06 18:45:20 +01:00
|
|
|
|
2016-02-16 07:43:06 +01:00
|
|
|
int const file_index = f[0].file_index;
|
2016-02-15 00:17:32 +01:00
|
|
|
error_code error;
|
|
|
|
boost::int64_t const size = m_stat_cache.get_filesize(f[0].file_index
|
|
|
|
, fs, m_save_path, error);
|
2015-12-15 06:03:43 +01:00
|
|
|
|
2016-02-15 00:17:32 +01:00
|
|
|
if (size < 0)
|
|
|
|
{
|
|
|
|
if (error != boost::system::errc::no_such_file_or_directory)
|
|
|
|
{
|
|
|
|
ec.ec = error;
|
2016-03-03 00:38:13 +01:00
|
|
|
ec.file = file_index;
|
2016-02-15 00:17:32 +01:00
|
|
|
ec.operation = storage_error::stat;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ec.ec = errors::mismatching_file_size;
|
2016-03-03 00:38:13 +01:00
|
|
|
ec.file = file_index;
|
2016-02-15 00:17:32 +01:00
|
|
|
ec.operation = storage_error::stat;
|
|
|
|
return false;
|
2015-12-06 18:45:20 +01:00
|
|
|
}
|
|
|
|
}
|
2016-02-15 00:17:32 +01:00
|
|
|
|
2016-02-16 07:43:06 +01:00
|
|
|
if (seed && size != fs.file_size(file_index))
|
2015-12-15 06:03:43 +01:00
|
|
|
{
|
2016-02-15 00:17:32 +01:00
|
|
|
// the resume data indicates we're a seed, but this file has
|
|
|
|
// the wrong size. Reject the resume data
|
|
|
|
ec.ec = errors::mismatching_file_size;
|
2016-03-03 00:38:13 +01:00
|
|
|
ec.file = file_index;
|
2015-12-15 06:03:43 +01:00
|
|
|
ec.operation = storage_error::check_resume;
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-06 18:45:20 +01:00
|
|
|
|
2016-02-15 00:17:32 +01:00
|
|
|
// OK, this file existed, good. Now, skip all remaining pieces in
|
|
|
|
// this file. We're just sanity-checking whether the files exist
|
|
|
|
// or not.
|
2016-03-03 00:38:13 +01:00
|
|
|
peer_request const pr = fs.map_file(file_index, 0
|
2016-02-15 00:17:32 +01:00
|
|
|
, fs.file_size(file_index) + 1);
|
|
|
|
i = (std::max)(i + 1, pr.piece);
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
return true;
|
2007-03-28 01:49:05 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int default_storage::move_storage(std::string const& sp, int flags, storage_error& ec)
|
2004-07-18 02:39:58 +02:00
|
|
|
{
|
2013-05-09 04:50:16 +02:00
|
|
|
int ret = piece_manager::no_error;
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string save_path = complete(sp);
|
2004-07-18 02:39:58 +02:00
|
|
|
|
2013-05-09 04:50:16 +02:00
|
|
|
// check to see if any of the files exist
|
2014-07-06 21:18:00 +02:00
|
|
|
error_code e;
|
2009-03-17 10:31:30 +01:00
|
|
|
file_storage const& f = files();
|
|
|
|
|
2013-05-09 04:50:16 +02:00
|
|
|
file_status s;
|
|
|
|
if (flags == fail_if_exist)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
stat_file(save_path, &s, e);
|
|
|
|
if (e != boost::system::errc::no_such_file_or_directory)
|
2013-05-09 04:50:16 +02:00
|
|
|
{
|
|
|
|
// the directory exists, check all the files
|
2013-08-12 09:30:57 +02:00
|
|
|
for (int i = 0; i < f.num_files(); ++i)
|
2013-05-09 04:50:16 +02:00
|
|
|
{
|
2013-06-10 00:30:02 +02:00
|
|
|
// files moved out to absolute paths are ignored
|
2013-08-12 09:30:57 +02:00
|
|
|
if (is_complete(f.file_path(i))) continue;
|
2013-06-10 00:30:02 +02:00
|
|
|
|
2013-08-12 09:30:57 +02:00
|
|
|
std::string new_path = f.file_path(i, save_path);
|
2014-07-06 21:18:00 +02:00
|
|
|
stat_file(new_path, &s, e);
|
|
|
|
if (e != boost::system::errc::no_such_file_or_directory)
|
|
|
|
{
|
|
|
|
ec.ec = e;
|
|
|
|
ec.file = i;
|
|
|
|
ec.operation = storage_error::stat;
|
2013-05-09 04:50:16 +02:00
|
|
|
return piece_manager::file_exist;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2013-05-09 04:50:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-10 04:18:14 +02:00
|
|
|
// collect all directories in to_move. This is because we
|
|
|
|
// try to move entire directories by default (instead of
|
|
|
|
// files independently).
|
2014-07-06 21:18:00 +02:00
|
|
|
std::map<std::string, int> to_move;
|
2013-08-12 09:30:57 +02:00
|
|
|
for (int i = 0; i < f.num_files(); ++i)
|
2013-05-10 04:18:14 +02:00
|
|
|
{
|
2013-06-10 00:30:02 +02:00
|
|
|
// files moved out to absolute paths are not moved
|
2013-08-12 09:30:57 +02:00
|
|
|
if (is_complete(f.file_path(i))) continue;
|
2013-06-10 00:30:02 +02:00
|
|
|
|
2013-08-12 09:30:57 +02:00
|
|
|
std::string split = split_path(f.file_path(i));
|
2014-07-06 21:18:00 +02:00
|
|
|
to_move.insert(to_move.begin(), std::make_pair(split, i));
|
2013-05-10 04:18:14 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
e.clear();
|
|
|
|
stat_file(save_path, &s, e);
|
|
|
|
if (e == boost::system::errc::no_such_file_or_directory)
|
2013-05-09 04:50:16 +02:00
|
|
|
{
|
2016-04-23 16:02:52 +02:00
|
|
|
create_directories(save_path, e);
|
|
|
|
if (e)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-04-23 16:02:52 +02:00
|
|
|
ec.ec = e;
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = -1;
|
|
|
|
ec.operation = storage_error::mkdir;
|
2015-06-06 08:10:53 +02:00
|
|
|
return piece_manager::fatal_disk_error;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2013-05-09 04:50:16 +02:00
|
|
|
}
|
2016-04-23 16:02:52 +02:00
|
|
|
else if (e)
|
2013-05-09 04:50:16 +02:00
|
|
|
{
|
2016-04-23 16:02:52 +02:00
|
|
|
ec.ec = e;
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = -1;
|
|
|
|
ec.operation = storage_error::mkdir;
|
2015-06-06 08:10:53 +02:00
|
|
|
return piece_manager::fatal_disk_error;
|
2013-05-09 04:50:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_pool.release(this);
|
|
|
|
|
2015-08-02 05:57:11 +02:00
|
|
|
#if defined TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
print_open_files("release files", m_files.name().c_str());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (std::map<std::string, int>::const_iterator i = to_move.begin()
|
2009-03-17 10:31:30 +01:00
|
|
|
, end(to_move.end()); i != end; ++i)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
std::string old_path = combine_path(m_save_path, i->first);
|
|
|
|
std::string new_path = combine_path(save_path, i->first);
|
2004-07-18 02:39:58 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
e.clear();
|
|
|
|
rename(old_path, new_path, e);
|
|
|
|
// if the source file doesn't exist. That's not a problem
|
2016-03-16 01:41:12 +01:00
|
|
|
// we just ignore that file
|
2014-07-06 21:18:00 +02:00
|
|
|
if (e == boost::system::errc::no_such_file_or_directory)
|
|
|
|
e.clear();
|
|
|
|
|
2016-03-16 01:41:12 +01:00
|
|
|
// on OSX, the error when trying to rename a file across different
|
|
|
|
// volumes is EXDEV, which will make it fall back to copying.
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (e)
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (flags == dont_replace && e == boost::system::errc::file_exists)
|
2009-03-17 10:31:30 +01:00
|
|
|
{
|
2013-05-09 04:50:16 +02:00
|
|
|
if (ret == piece_manager::no_error) ret = piece_manager::need_full_check;
|
|
|
|
continue;
|
2009-03-17 10:31:30 +01:00
|
|
|
}
|
2013-05-09 04:50:16 +02:00
|
|
|
|
2016-03-16 01:41:12 +01:00
|
|
|
if (e == boost::system::errc::invalid_argument
|
|
|
|
|| e == boost::system::errc::permission_denied)
|
|
|
|
{
|
|
|
|
ec.ec = e;
|
|
|
|
ec.file = i->second;
|
|
|
|
ec.operation = storage_error::rename;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (e != boost::system::errc::no_such_file_or_directory)
|
2009-03-31 10:05:46 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
e.clear();
|
|
|
|
recursive_copy(old_path, new_path, ec.ec);
|
|
|
|
if (ec.ec == boost::system::errc::no_such_file_or_directory)
|
2013-10-28 06:07:55 +01:00
|
|
|
{
|
|
|
|
// it's a bit weird that rename() would not return
|
|
|
|
// ENOENT, but the file still wouldn't exist. But,
|
|
|
|
// in case it does, we're done.
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.ec.clear();
|
2013-10-28 06:07:55 +01:00
|
|
|
break;
|
|
|
|
}
|
2013-05-09 04:50:16 +02:00
|
|
|
if (ec)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.file = i->second;
|
|
|
|
ec.operation = storage_error::copy;
|
2013-05-09 04:50:16 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// ignore errors when removing
|
2015-08-14 05:52:25 +02:00
|
|
|
error_code ignore;
|
|
|
|
remove_all(old_path, ignore);
|
2013-05-09 04:50:16 +02:00
|
|
|
}
|
|
|
|
break;
|
2009-03-31 10:05:46 +02:00
|
|
|
}
|
2008-06-05 18:56:39 +02:00
|
|
|
}
|
2009-03-17 10:31:30 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!ec)
|
2011-07-03 17:57:41 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_part_file)
|
|
|
|
{
|
|
|
|
// TODO: if everything moves OK, except for the partfile
|
|
|
|
// we currently won't update the save path, which breaks things.
|
|
|
|
// it would probably make more sense to give up on the partfile
|
|
|
|
m_part_file->move_partfile(save_path, ec.ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
ec.file = -1;
|
|
|
|
ec.operation = storage_error::partfile_move;
|
2015-06-06 08:10:53 +02:00
|
|
|
return piece_manager::fatal_disk_error;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
2009-09-05 09:21:10 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_save_path = save_path;
|
2009-09-05 09:21:10 +02:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int default_storage::readv(file::iovec_t const* bufs, int num_bufs
|
2015-11-19 07:38:04 +01:00
|
|
|
, int piece, int offset, int flags, storage_error& ec)
|
2009-01-17 09:35:48 +01:00
|
|
|
{
|
2015-11-16 04:36:55 +01:00
|
|
|
read_fileop op(*this, flags);
|
|
|
|
|
2009-09-05 09:21:10 +02:00
|
|
|
#ifdef TORRENT_SIMULATE_SLOW_READ
|
|
|
|
boost::thread::sleep(boost::get_system_time()
|
|
|
|
+ boost::posix_time::milliseconds(1000));
|
|
|
|
#endif
|
2015-11-16 04:36:55 +01:00
|
|
|
return readwritev(files(), bufs, piece, offset, num_bufs, op, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int default_storage::writev(file::iovec_t const* bufs, int num_bufs
|
2015-11-19 07:38:04 +01:00
|
|
|
, int piece, int offset, int flags, storage_error& ec)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2015-11-16 04:36:55 +01:00
|
|
|
write_fileop op(*this, flags);
|
|
|
|
return readwritev(files(), bufs, piece, offset, num_bufs, op, ec);
|
2009-01-17 09:35:48 +01:00
|
|
|
}
|
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
// much of what needs to be done when reading and writing is buffer
|
|
|
|
// management and piece to file mapping. Most of that is the same for reading
|
|
|
|
// and writing. This function is a template, and the fileop decides what to
|
|
|
|
// do with the file and the buffers.
|
|
|
|
int readwritev(file_storage const& files, file::iovec_t const* const bufs
|
|
|
|
, const int piece, const int offset, const int num_bufs, fileop& op
|
|
|
|
, storage_error& ec)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-01-03 09:11:31 +01:00
|
|
|
TORRENT_ASSERT(bufs != 0);
|
2015-11-19 07:38:04 +01:00
|
|
|
TORRENT_ASSERT(piece >= 0);
|
2015-11-16 04:36:55 +01:00
|
|
|
TORRENT_ASSERT(piece < files.num_pieces());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(offset >= 0);
|
2009-01-03 09:11:31 +01:00
|
|
|
TORRENT_ASSERT(num_bufs > 0);
|
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
const int size = bufs_size(bufs, num_bufs);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(size > 0);
|
2015-11-16 04:36:55 +01:00
|
|
|
TORRENT_ASSERT(files.is_loaded());
|
2003-12-07 17:26:16 +01:00
|
|
|
|
2003-12-07 02:26:57 +01:00
|
|
|
// find the file iterator and file offset
|
2015-11-16 04:36:55 +01:00
|
|
|
boost::uint64_t torrent_offset = piece * boost::uint64_t(files.piece_length()) + offset;
|
|
|
|
int file_index = files.file_index_at_offset(torrent_offset);
|
|
|
|
TORRENT_ASSERT(torrent_offset >= files.file_offset(file_index));
|
|
|
|
TORRENT_ASSERT(torrent_offset < files.file_offset(file_index) + files.file_size(file_index));
|
|
|
|
boost::int64_t file_offset = torrent_offset - files.file_offset(file_index);
|
2015-11-14 06:21:03 +01:00
|
|
|
|
|
|
|
// the number of bytes left before this read or write operation is
|
|
|
|
// completely satisfied.
|
2009-01-17 09:35:48 +01:00
|
|
|
int bytes_left = size;
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
TORRENT_ASSERT(bytes_left >= 0);
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
// copy the iovec array so we can use it to keep track of our current
|
|
|
|
// location by updating the head base pointer and size. (see
|
|
|
|
// advance_bufs())
|
2009-01-11 03:02:34 +01:00
|
|
|
file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
|
|
|
copy_bufs(bufs, size, current_buf);
|
2009-01-12 19:08:18 +01:00
|
|
|
TORRENT_ASSERT(count_bufs(current_buf, size) == num_bufs);
|
2015-11-14 06:21:03 +01:00
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
file::iovec_t* tmp_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
// the number of bytes left to read in the current file (specified by
|
|
|
|
// file_index). This is the minimum of (file_size - file_offset) and
|
|
|
|
// bytes_left.
|
2009-01-17 09:35:48 +01:00
|
|
|
int file_bytes_left;
|
2008-10-03 07:49:41 +02:00
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
while (bytes_left > 0)
|
|
|
|
{
|
2009-01-17 09:35:48 +01:00
|
|
|
file_bytes_left = bytes_left;
|
2015-11-16 04:36:55 +01:00
|
|
|
if (file_offset + file_bytes_left > files.file_size(file_index))
|
|
|
|
file_bytes_left = (std::max)(static_cast<int>(files.file_size(file_index) - file_offset), 0);
|
2008-10-03 07:49:41 +02:00
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
// there are no bytes left in this file, move to the next one
|
|
|
|
// this loop skips over empty files
|
|
|
|
while (file_bytes_left == 0)
|
|
|
|
{
|
|
|
|
++file_index;
|
|
|
|
file_offset = 0;
|
2015-11-16 04:36:55 +01:00
|
|
|
TORRENT_ASSERT(file_index < files.num_files());
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
// this should not happen. bytes_left should be clamped by the total
|
|
|
|
// size of the torrent, so we should never run off the end of it
|
2015-11-16 04:36:55 +01:00
|
|
|
if (file_index >= files.num_files()) return size;
|
2015-11-14 06:21:03 +01:00
|
|
|
|
|
|
|
file_bytes_left = bytes_left;
|
2015-11-16 04:36:55 +01:00
|
|
|
if (file_offset + file_bytes_left > files.file_size(file_index))
|
|
|
|
file_bytes_left = (std::max)(static_cast<int>(files.file_size(file_index) - file_offset), 0);
|
2008-10-03 07:49:41 +02:00
|
|
|
}
|
2007-05-04 05:28:42 +02:00
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
// make a copy of the iovec array that _just_ covers the next
|
|
|
|
// file_bytes_left bytes, i.e. just this one operation
|
|
|
|
copy_bufs(current_buf, file_bytes_left, tmp_buf);
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
int bytes_transferred = op.file_op(file_index, file_offset,
|
|
|
|
file_bytes_left, tmp_buf, ec);
|
|
|
|
if (ec) return -1;
|
2009-01-14 04:05:35 +01:00
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
// advance our position in the iovec array and the file offset.
|
|
|
|
advance_bufs(current_buf, bytes_transferred);
|
2015-11-14 06:21:03 +01:00
|
|
|
bytes_left -= bytes_transferred;
|
|
|
|
file_offset += bytes_transferred;
|
|
|
|
|
2015-11-16 04:36:55 +01:00
|
|
|
TORRENT_ASSERT(count_bufs(current_buf, bytes_left) <= num_bufs);
|
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
// if the file operation returned 0, we've hit end-of-file. We're done
|
|
|
|
if (bytes_transferred == 0)
|
2014-10-13 01:49:51 +02:00
|
|
|
{
|
2015-11-14 06:21:03 +01:00
|
|
|
if (file_bytes_left > 0 )
|
|
|
|
{
|
|
|
|
// fill in this information in case the caller wants to treat
|
2015-11-16 04:36:55 +01:00
|
|
|
// a short-read as an error
|
2015-11-14 06:21:03 +01:00
|
|
|
ec.file = file_index;
|
|
|
|
}
|
|
|
|
return size - bytes_left;
|
2014-10-13 01:49:51 +02:00
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
2009-01-17 09:35:48 +01:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
file_handle default_storage::open_file(int file, int mode
|
2015-01-03 17:57:21 +01:00
|
|
|
, storage_error& ec) const
|
|
|
|
{
|
|
|
|
file_handle h = open_file_impl(file, mode, ec.ec);
|
|
|
|
if (((mode & file::rw_mask) != file::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);
|
2015-11-16 04:36:55 +01:00
|
|
|
|
|
|
|
if (m_allocate_files && (mode & file::rw_mask) != file::read_only)
|
|
|
|
{
|
|
|
|
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.size());
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
error_code e;
|
|
|
|
h->set_size(files().file_size(file), e);
|
|
|
|
m_file_created.set_bit(file);
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
ec.ec = e;
|
|
|
|
ec.file = file;
|
|
|
|
ec.operation = storage_error::fallocate;
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-03 17:57:21 +01:00
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
file_handle default_storage::open_file_impl(int file, int mode
|
2014-07-06 21:18:00 +02:00
|
|
|
, error_code& ec) const
|
2009-01-17 09:35:48 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
bool lock_files = m_settings ? settings().get_bool(settings_pack::lock_files) : false;
|
|
|
|
if (lock_files) mode |= file::lock_file;
|
2015-03-09 07:09:50 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_allocate_files) mode |= file::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 (int(m_file_priority.size()) > file && m_file_priority[file] == 0)
|
|
|
|
mode |= file::sparse;
|
2011-11-02 05:45:22 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_settings && settings().get_bool(settings_pack::no_atime_storage)) mode |= file::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
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
mode |= file::no_cache;
|
2011-02-22 03:53:26 +01:00
|
|
|
}
|
|
|
|
|
2015-03-09 07:09:50 +01:00
|
|
|
file_handle ret = m_pool.open_file(const_cast<default_storage*>(this)
|
|
|
|
, m_save_path, file, files(), mode, ec);
|
|
|
|
if (ec && (mode & file::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 &= ~file::lock_file;
|
|
|
|
ret = m_pool.open_file(const_cast<default_storage*>(this)
|
|
|
|
, 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);
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
return false;
|
2009-01-03 09:11:31 +01:00
|
|
|
}
|
|
|
|
|
2015-08-03 06:25:14 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
bool default_storage::disk_write_access_log() {
|
|
|
|
return g_access_log != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void default_storage::disk_write_access_log(bool enable) {
|
|
|
|
if (enable)
|
|
|
|
{
|
2015-08-03 06:38:00 +02:00
|
|
|
if (g_access_log == NULL)
|
|
|
|
{
|
|
|
|
g_access_log = fopen("file_access.log", "a+");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (g_access_log != NULL)
|
|
|
|
{
|
|
|
|
FILE* f = g_access_log;
|
|
|
|
g_access_log = NULL;
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
}
|
2015-08-03 06:25:14 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
storage_interface* default_storage_constructor(storage_params const& params)
|
2009-01-03 09:11:31 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
return new default_storage(params);
|
2009-01-03 09:11:31 +01:00
|
|
|
}
|
|
|
|
|
2016-01-10 17:50:56 +01:00
|
|
|
int disabled_storage::readv(file::iovec_t const* bufs
|
|
|
|
, int num_bufs, int, int, int, storage_error&)
|
2010-01-09 19:40:05 +01:00
|
|
|
{
|
2016-01-10 17:50:56 +01:00
|
|
|
return bufs_size(bufs, num_bufs);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2010-01-09 19:40:05 +01:00
|
|
|
|
2016-01-10 17:50:56 +01:00
|
|
|
int disabled_storage::writev(file::iovec_t const* bufs
|
|
|
|
, int num_bufs, int, int, int, storage_error&)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-01-10 17:50:56 +01:00
|
|
|
return bufs_size(bufs, num_bufs);
|
2010-01-09 19:40:05 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
storage_interface* disabled_storage_constructor(storage_params const& params)
|
2007-03-16 06:29:23 +01:00
|
|
|
{
|
2015-08-14 05:52:25 +02:00
|
|
|
TORRENT_UNUSED(params);
|
2015-08-02 21:55:05 +02:00
|
|
|
return new disabled_storage;
|
2007-03-16 06:29:23 +01:00
|
|
|
}
|
2003-12-09 09:49:49 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// -- zero_storage ------------------------------------------------------
|
|
|
|
|
|
|
|
int zero_storage::readv(file::iovec_t const* bufs, int num_bufs
|
2015-04-20 06:52:49 +02:00
|
|
|
, int /* piece */, int /* offset */, int /* flags */, storage_error&)
|
2009-08-02 08:40:45 +02:00
|
|
|
{
|
2011-04-17 00:58:11 +02:00
|
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
2009-08-02 08:40:45 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
memset(bufs[i].iov_base, 0, bufs[i].iov_len);
|
2016-04-25 23:22:09 +02:00
|
|
|
ret += int(bufs[i].iov_len);
|
2011-04-17 00:58:11 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
return 0;
|
2011-04-17 00:58:11 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int zero_storage::writev(file::iovec_t const* bufs, int num_bufs
|
2015-04-26 02:51:44 +02:00
|
|
|
, int /* piece */, int /* offset */, int /* flags */, storage_error&)
|
2011-04-17 00:58:11 +02:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
2016-04-25 23:22:09 +02:00
|
|
|
ret += int(bufs[i].iov_len);
|
2014-07-06 21:18:00 +02:00
|
|
|
return 0;
|
2011-04-17 00:58:11 +02:00
|
|
|
}
|
2009-08-02 08:40:45 +02:00
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
storage_interface* zero_storage_constructor(storage_params const&)
|
2009-08-02 08:40:45 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
return new zero_storage;
|
2009-08-02 08:40:45 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void storage_piece_set::add_piece(cached_piece_entry* p)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(p->in_storage == false);
|
|
|
|
TORRENT_ASSERT(p->storage.get() == this);
|
|
|
|
TORRENT_ASSERT(m_cached_pieces.count(p) == 0);
|
|
|
|
m_cached_pieces.insert(p);
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
p->in_storage = true;
|
|
|
|
#endif
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
|
2015-08-02 05:57:11 +02:00
|
|
|
bool storage_piece_set::has_piece(cached_piece_entry const* p) const
|
2004-01-04 13:54:38 +01:00
|
|
|
{
|
2015-08-02 05:57:11 +02:00
|
|
|
return m_cached_pieces.count(const_cast<cached_piece_entry*>(p)) > 0;
|
2004-01-04 13:54:38 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void storage_piece_set::remove_piece(cached_piece_entry* p)
|
2014-02-28 11:19:29 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(p->in_storage == true);
|
|
|
|
TORRENT_ASSERT(m_cached_pieces.count(p) == 1);
|
|
|
|
m_cached_pieces.erase(p);
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
p->in_storage = false;
|
|
|
|
#endif
|
2014-02-28 11:19:29 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// -- piece_manager -----------------------------------------------------
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
piece_manager::piece_manager(
|
|
|
|
storage_interface* storage_impl
|
|
|
|
, boost::shared_ptr<void> const& torrent
|
|
|
|
, file_storage* files)
|
|
|
|
: m_files(*files)
|
|
|
|
, m_storage(storage_impl)
|
|
|
|
, m_torrent(torrent)
|
2008-07-18 17:31:22 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
piece_manager::~piece_manager()
|
|
|
|
{}
|
2004-12-21 13:30:09 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_DEBUG
|
|
|
|
void piece_manager::assert_torrent_refcount() const
|
2008-11-17 02:19:46 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_torrent) return;
|
|
|
|
// sorry about this layer violation, but it's
|
|
|
|
// quite convenient to make sure the torrent won't
|
|
|
|
// get unloaded under our feet later
|
|
|
|
TORRENT_ASSERT(static_cast<torrent*>(m_torrent.get())->refcount() > 0);
|
2008-11-17 02:19:46 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2008-11-17 02:19:46 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int piece_manager::check_no_fastresume(storage_error& ec)
|
2004-12-21 13:30:09 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
bool has_files = false;
|
|
|
|
if (!m_storage->settings().get_bool(settings_pack::no_recheck_incomplete_resume))
|
|
|
|
{
|
|
|
|
storage_error se;
|
|
|
|
has_files = m_storage->has_any_file(se);
|
2007-06-10 22:46:09 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (se)
|
|
|
|
{
|
|
|
|
ec = se;
|
2015-06-06 08:10:53 +02:00
|
|
|
return fatal_disk_error;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (has_files)
|
|
|
|
{
|
|
|
|
// always initialize the storage
|
|
|
|
int ret = check_init_storage(ec);
|
|
|
|
return ret != no_error ? ret : need_full_check;
|
|
|
|
}
|
|
|
|
}
|
2008-05-28 10:44:40 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
return check_init_storage(ec);
|
|
|
|
}
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int piece_manager::check_init_storage(storage_error& ec)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
storage_error se;
|
2015-12-06 18:45:20 +01:00
|
|
|
// initialize may clear the error we pass in and it's important to
|
|
|
|
// preserve the error code in ec, even when initialize() is successful
|
2014-07-06 21:18:00 +02:00
|
|
|
m_storage->initialize(se);
|
|
|
|
if (se)
|
|
|
|
{
|
|
|
|
ec = se;
|
2015-06-06 08:10:53 +02:00
|
|
|
return fatal_disk_error;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
return no_error;
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
|
|
|
|
2005-10-13 09:59:05 +02:00
|
|
|
// check if the fastresume data is up to date
|
2015-06-06 08:10:53 +02:00
|
|
|
// if it is, use it and return true. If it
|
2005-10-13 09:59:05 +02:00
|
|
|
// isn't return false and the full check
|
2015-03-21 01:12:40 +01:00
|
|
|
// will be run. If the links pointer is non-null, it has the same number
|
|
|
|
// of elements as there are files. Each element is either empty or contains
|
|
|
|
// the absolute path to a file identical to the corresponding file in this
|
|
|
|
// torrent. The storage must create hard links (or copy) those files. If
|
|
|
|
// any file does not exist or is inaccessible, the disk job must fail.
|
2008-03-08 07:06:31 +01:00
|
|
|
int piece_manager::check_fastresume(
|
2016-02-15 00:17:32 +01:00
|
|
|
add_torrent_params const& rd
|
2015-03-21 01:12:40 +01:00
|
|
|
, std::vector<std::string> const* links
|
|
|
|
, storage_error& ec)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_files.piece_length() > 0);
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
// if we don't have any resume data, return
|
2016-02-15 00:17:32 +01:00
|
|
|
if (rd.have_pieces.empty()) return check_no_fastresume(ec);
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
if (!m_storage->verify_resume_data(rd, links, ec))
|
2014-07-06 21:18:00 +02:00
|
|
|
return check_no_fastresume(ec);
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
return check_init_storage(ec);
|
2005-10-13 09:59:05 +02:00
|
|
|
}
|
2004-01-25 22:51:30 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// ====== disk_job_fence implementation ========
|
2007-05-31 00:23:17 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
disk_job_fence::disk_job_fence()
|
|
|
|
: m_has_fence(0)
|
|
|
|
, m_outstanding_jobs(0)
|
|
|
|
{}
|
2007-05-31 00:23:17 +02:00
|
|
|
|
2015-08-19 15:22:00 +02:00
|
|
|
int disk_job_fence::job_complete(disk_io_job* j, tailqueue<disk_io_job>& jobs)
|
2005-10-13 09:59:05 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
2008-12-13 04:32:57 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(j->flags & disk_io_job::in_progress);
|
|
|
|
j->flags &= ~disk_io_job::in_progress;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(m_outstanding_jobs > 0);
|
|
|
|
--m_outstanding_jobs;
|
|
|
|
if (j->flags & disk_io_job::fence)
|
2005-10-13 09:59:05 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// a fence job just completed. Make sure the fence logic
|
|
|
|
// works by asserting m_outstanding_jobs is in fact 0 now
|
|
|
|
TORRENT_ASSERT(m_outstanding_jobs == 0);
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// the fence can now be lowered
|
|
|
|
--m_has_fence;
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// now we need to post all jobs that have been queued up
|
|
|
|
// while this fence was up. However, if there's another fence
|
|
|
|
// in the queue, stop there and raise the fence again
|
|
|
|
int ret = 0;
|
|
|
|
while (m_blocked_jobs.size())
|
|
|
|
{
|
2015-08-11 02:03:24 +02:00
|
|
|
disk_io_job *bj = static_cast<disk_io_job*>(m_blocked_jobs.pop_front());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (bj->flags & disk_io_job::fence)
|
2007-10-08 22:01:36 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// we encountered another fence. We cannot post anymore
|
|
|
|
// jobs from the blocked jobs queue. We have to go back
|
|
|
|
// into a raised fence mode and wait for all current jobs
|
|
|
|
// to complete. The exception is that if there are no jobs
|
|
|
|
// executing currently, we should add the fence job.
|
|
|
|
if (m_outstanding_jobs == 0 && jobs.empty())
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((bj->flags & disk_io_job::in_progress) == 0);
|
|
|
|
bj->flags |= disk_io_job::in_progress;
|
|
|
|
++m_outstanding_jobs;
|
|
|
|
++ret;
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(bj->blocked);
|
|
|
|
bj->blocked = false;
|
|
|
|
#endif
|
|
|
|
jobs.push_back(bj);
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// put the fence job back in the blocked queue
|
|
|
|
m_blocked_jobs.push_front(bj);
|
|
|
|
}
|
|
|
|
return ret;
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((bj->flags & disk_io_job::in_progress) == 0);
|
|
|
|
bj->flags |= disk_io_job::in_progress;
|
2004-03-24 23:50:07 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
++m_outstanding_jobs;
|
|
|
|
++ret;
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(bj->blocked);
|
|
|
|
bj->blocked = false;
|
|
|
|
#endif
|
|
|
|
jobs.push_back(bj);
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
return ret;
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2008-02-14 04:48:20 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// there are still outstanding jobs, even if we have a
|
|
|
|
// fence, it's not time to lower it yet
|
|
|
|
// also, if we don't have a fence, we're done
|
|
|
|
if (m_outstanding_jobs > 0 || m_has_fence == 0) return 0;
|
2005-05-13 02:39:39 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// there's a fence raised, and no outstanding operations.
|
|
|
|
// it means we can execute the fence job right now.
|
|
|
|
TORRENT_ASSERT(m_blocked_jobs.size() > 0);
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// this is the fence job
|
2015-08-11 02:03:24 +02:00
|
|
|
disk_io_job *bj = static_cast<disk_io_job*>(m_blocked_jobs.pop_front());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(bj->flags & disk_io_job::fence);
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((bj->flags & disk_io_job::in_progress) == 0);
|
|
|
|
bj->flags |= disk_io_job::in_progress;
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
++m_outstanding_jobs;
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(bj->blocked);
|
|
|
|
bj->blocked = false;
|
|
|
|
#endif
|
|
|
|
// prioritize fence jobs since they're blocking other jobs
|
|
|
|
jobs.push_front(bj);
|
|
|
|
return 1;
|
2009-02-14 05:31:08 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool disk_job_fence::is_blocked(disk_io_job* j)
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
|
|
|
DLOG(stderr, "[%p] is_blocked: fence: %d num_outstanding: %d\n"
|
2015-09-06 22:47:10 +02:00
|
|
|
, static_cast<void*>(this), m_has_fence, int(m_outstanding_jobs));
|
2008-02-14 04:48:20 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// if this is the job that raised the fence, don't block it
|
|
|
|
// ignore fence can only ignore one fence. If there are several,
|
|
|
|
// this job still needs to get queued up
|
|
|
|
if (m_has_fence == 0)
|
2009-05-21 18:15:05 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((j->flags & disk_io_job::in_progress) == 0);
|
|
|
|
j->flags |= disk_io_job::in_progress;
|
|
|
|
++m_outstanding_jobs;
|
|
|
|
return false;
|
2009-05-21 18:15:05 +02:00
|
|
|
}
|
2015-06-06 08:10:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_blocked_jobs.push_back(j);
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(j->blocked == false);
|
|
|
|
j->blocked = true;
|
2008-10-07 11:24:30 +02:00
|
|
|
#endif
|
2008-02-14 04:48:20 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
return true;
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool disk_job_fence::has_fence() const
|
2007-10-08 22:01:36 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
2016-04-17 22:56:07 +02:00
|
|
|
return m_has_fence != 0;
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int disk_job_fence::num_blocked() const
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
|
|
|
return m_blocked_jobs.size();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-09 09:55:03 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// j is the fence job. It must have exclusive access to the storage
|
|
|
|
// fj is the flush job. If the job j is queued, we need to issue
|
|
|
|
// this job
|
2014-08-01 09:32:54 +02:00
|
|
|
int disk_job_fence::raise_fence(disk_io_job* j, disk_io_job* fj
|
2014-10-25 22:07:50 +02:00
|
|
|
, counters& cnt)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((j->flags & disk_io_job::fence) == 0);
|
|
|
|
j->flags |= disk_io_job::fence;
|
2003-12-10 01:24:16 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
DLOG(stderr, "[%p] raise_fence: fence: %d num_outstanding: %d\n"
|
2015-09-06 22:47:10 +02:00
|
|
|
, static_cast<void*>(this), m_has_fence, int(m_outstanding_jobs));
|
2003-12-08 09:55:24 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_has_fence == 0 && m_outstanding_jobs == 0)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
++m_has_fence;
|
2015-09-06 22:47:10 +02:00
|
|
|
DLOG(stderr, "[%p] raise_fence: need posting\n"
|
|
|
|
, static_cast<void*>(this));
|
2003-12-09 09:49:49 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// the job j is expected to be put on the job queue
|
|
|
|
// after this, without being passed through is_blocked()
|
|
|
|
// that's why we're accounting for it here
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// fj is expected to be discarded by the caller
|
|
|
|
j->flags |= disk_io_job::in_progress;
|
|
|
|
++m_outstanding_jobs;
|
|
|
|
return fence_post_fence;
|
2004-01-24 20:19:17 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
++m_has_fence;
|
|
|
|
if (m_has_fence > 1)
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(fj->blocked == false);
|
|
|
|
fj->blocked = true;
|
|
|
|
#endif
|
|
|
|
m_blocked_jobs.push_back(fj);
|
2014-10-25 22:07:50 +02:00
|
|
|
cnt.inc_stats_counter(counters::blocked_disk_jobs);
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// in this case, fj is expected to be put on the job queue
|
|
|
|
fj->flags |= disk_io_job::in_progress;
|
|
|
|
++m_outstanding_jobs;
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(j->blocked == false);
|
|
|
|
j->blocked = true;
|
2004-01-24 18:14:03 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
m_blocked_jobs.push_back(j);
|
2014-10-25 22:07:50 +02:00
|
|
|
cnt.inc_stats_counter(counters::blocked_disk_jobs);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
return m_has_fence > 1 ? fence_post_none : fence_post_flush;
|
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
} // namespace libtorrent
|
2003-12-01 23:09:58 +01:00
|
|
|
|