2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
2003-11-29 02:54:41 +01:00
|
|
|
Copyright (c) 2003, 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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/pch.hpp"
|
|
|
|
|
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
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
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>
|
2008-10-01 01:37:42 +02:00
|
|
|
#if BOOST_VERSION >= 103500
|
|
|
|
#include <boost/system/system_error.hpp>
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
2008-09-27 21:49:31 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
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"
|
2009-12-09 02:55:30 +01:00
|
|
|
#include "libtorrent/allocator.hpp" // page_size
|
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-04-04 11:52:25 +02:00
|
|
|
#if TORRENT_USE_IOSTREAM
|
2005-09-01 23:04:21 +02:00
|
|
|
#include <ios>
|
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
|
|
|
#endif
|
|
|
|
|
2007-04-17 23:54:40 +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
|
|
|
|
|
2007-04-18 02:36:09 +02:00
|
|
|
#if defined(__linux__)
|
|
|
|
#include <sys/statfs.h>
|
|
|
|
#endif
|
|
|
|
|
2007-09-10 15:46:08 +02:00
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
// for statfs()
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#endif
|
|
|
|
|
2009-03-01 01:02:33 +01:00
|
|
|
// for convert_to_wstring and convert_to_native
|
|
|
|
#include "libtorrent/escape_string.hpp"
|
2005-08-16 20:39:38 +02:00
|
|
|
|
2009-04-04 11:52:25 +02:00
|
|
|
#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG && TORRENT_USE_IOSTREAM
|
2004-01-25 19:18:36 +01:00
|
|
|
namespace
|
|
|
|
{
|
2004-12-21 13:30:09 +01:00
|
|
|
using namespace libtorrent;
|
2003-12-07 15:12:14 +01:00
|
|
|
|
2009-06-28 02:36:41 +02:00
|
|
|
void print_to_log(std::string const& s)
|
2003-12-07 15:12:14 +01:00
|
|
|
{
|
|
|
|
static std::ofstream log("log.txt");
|
|
|
|
log << s;
|
2003-12-10 01:24:16 +01:00
|
|
|
log.flush();
|
2003-12-07 15:12:14 +01:00
|
|
|
}
|
|
|
|
}
|
2007-04-17 23:54:40 +02:00
|
|
|
#endif
|
2003-12-07 15:12:14 +01:00
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
|
|
|
|
void recursive_copy(std::string const& old_path, std::string const& new_path, error_code& ec)
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2008-07-18 01:41:46 +02:00
|
|
|
TORRENT_ASSERT(!ec);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (is_directory(old_path, ec))
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
create_directory(new_path, ec);
|
|
|
|
if (ec) return;
|
|
|
|
for (directory i(old_path, ec); !i.done(); i.next(ec))
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string f = i.file();
|
|
|
|
recursive_copy(f, combine_path(new_path, f), ec);
|
2008-07-18 01:41:46 +02:00
|
|
|
if (ec) return;
|
2008-06-05 18:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
else if (!ec)
|
2009-04-12 20:04:20 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
copy_file(old_path, new_path, ec);
|
2009-04-12 20:04:20 +02:00
|
|
|
}
|
2008-06-05 18:56:39 +02:00
|
|
|
}
|
2004-01-17 21:04:19 +01:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
void recursive_remove(std::string const& old_path)
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
error_code ec;
|
|
|
|
if (is_directory(old_path, ec))
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
for (directory i(old_path, ec); !i.done(); i.next(ec))
|
|
|
|
recursive_remove(combine_path(old_path, i.file()));
|
|
|
|
remove(old_path, ec);
|
2008-06-05 18:56:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
remove(old_path, ec);
|
2008-06-05 18:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2005-03-05 00:45:16 +01:00
|
|
|
std::vector<std::pair<size_type, std::time_t> > get_filesizes(
|
2009-10-26 02:29:39 +01:00
|
|
|
file_storage const& storage, std::string const& p)
|
2004-01-17 21:04:19 +01:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string save_path = complete(p);
|
2005-03-05 00:45:16 +01:00
|
|
|
std::vector<std::pair<size_type, std::time_t> > sizes;
|
2009-10-26 02:29:39 +01:00
|
|
|
for (file_storage::iterator i = storage.begin()
|
|
|
|
, end(storage.end()); i != end; ++i)
|
2004-01-17 21:04:19 +01:00
|
|
|
{
|
2005-03-05 00:45:16 +01:00
|
|
|
size_type size = 0;
|
|
|
|
std::time_t time = 0;
|
2009-10-26 02:29:39 +01:00
|
|
|
|
|
|
|
if (!i->pad_file)
|
2008-04-07 03:45:33 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
file_status s;
|
|
|
|
error_code ec;
|
2010-11-25 00:49:22 +01:00
|
|
|
stat_file(combine_path(save_path, storage.file_path(*i)), &s, ec);
|
2009-10-26 02:29:39 +01:00
|
|
|
|
|
|
|
if (!ec)
|
|
|
|
{
|
|
|
|
size = s.file_size;
|
|
|
|
time = s.mtime;
|
|
|
|
}
|
2004-01-18 11:22:18 +01:00
|
|
|
}
|
2005-03-05 00:45:16 +01:00
|
|
|
sizes.push_back(std::make_pair(size, time));
|
2004-01-17 21:04:19 +01:00
|
|
|
}
|
|
|
|
return sizes;
|
|
|
|
}
|
|
|
|
|
2007-05-24 20:53:55 +02:00
|
|
|
// matches the sizes and timestamps of the files passed in
|
|
|
|
// in non-compact mode, actual file sizes and timestamps
|
|
|
|
// are allowed to be bigger and more recent than the fast
|
|
|
|
// resume data. This is because full allocation will not move
|
|
|
|
// pieces, so any older version of the resume data will
|
|
|
|
// still be a correct subset of the actual data on disk.
|
2010-04-11 23:02:43 +02:00
|
|
|
enum flags_t
|
|
|
|
{
|
|
|
|
compact_mode = 1,
|
|
|
|
ignore_timestamps = 2
|
|
|
|
};
|
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
bool match_filesizes(
|
2008-05-28 10:44:40 +02:00
|
|
|
file_storage const& fs
|
2009-10-26 02:29:39 +01:00
|
|
|
, std::string p
|
2005-06-16 17:41:04 +02:00
|
|
|
, std::vector<std::pair<size_type, std::time_t> > const& sizes
|
2010-04-11 23:02:43 +02:00
|
|
|
, int flags
|
2009-06-28 02:36:41 +02:00
|
|
|
, error_code& error)
|
2004-01-17 21:04:19 +01:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
if ((int)sizes.size() != fs.num_files())
|
2005-06-16 17:41:04 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::mismatching_number_of_files;
|
2005-06-16 17:41:04 +02:00
|
|
|
return false;
|
|
|
|
}
|
2004-10-10 02:42:48 +02:00
|
|
|
p = complete(p);
|
2004-01-17 21:04:19 +01:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
std::vector<std::pair<size_type, std::time_t> >::const_iterator size_iter
|
2005-03-05 00:45:16 +01:00
|
|
|
= sizes.begin();
|
2008-05-28 10:44:40 +02:00
|
|
|
for (file_storage::iterator i = fs.begin()
|
2009-10-26 02:29:39 +01:00
|
|
|
, end(fs.end());i != end; ++i, ++size_iter)
|
2004-01-17 21:04:19 +01:00
|
|
|
{
|
2005-03-05 00:45:16 +01:00
|
|
|
size_type size = 0;
|
|
|
|
std::time_t time = 0;
|
2008-11-17 00:33:59 +01:00
|
|
|
if (i->pad_file) continue;
|
2008-04-07 03:45:33 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
file_status s;
|
|
|
|
error_code ec;
|
2010-11-25 00:49:22 +01:00
|
|
|
stat_file(combine_path(p, fs.file_path(*i)), &s, ec);
|
2009-10-26 02:29:39 +01:00
|
|
|
|
|
|
|
if (!ec)
|
2008-04-07 03:45:33 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
size = s.file_size;
|
|
|
|
time = s.mtime;
|
2004-01-18 11:22:18 +01:00
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2010-04-11 23:02:43 +02:00
|
|
|
if (((flags & compact_mode) && size != size_iter->first)
|
|
|
|
|| (!(flags & compact_mode) && size < size_iter->first))
|
2005-06-16 17:41:04 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::mismatching_file_size;
|
2005-03-05 00:45:16 +01:00
|
|
|
return false;
|
2005-06-16 17:41:04 +02:00
|
|
|
}
|
2010-04-11 23:02:43 +02:00
|
|
|
|
|
|
|
if (flags & ignore_timestamps) continue;
|
|
|
|
|
2008-10-15 22:19:03 +02:00
|
|
|
// allow one second 'slack', because of FAT volumes
|
|
|
|
// in sparse mode, allow the files to be more recent
|
|
|
|
// than the resume data, but only by 5 minutes
|
2010-04-11 23:02:43 +02:00
|
|
|
if (((flags & compact_mode) && (time > size_iter->second + 1 || time < size_iter->second - 1)) ||
|
|
|
|
(!(flags & compact_mode) && (time > size_iter->second + 5 * 60 || time < size_iter->second - 1)))
|
2005-06-16 17:41:04 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::mismatching_file_timestamp;
|
2005-06-16 17:41:04 +02:00
|
|
|
return false;
|
|
|
|
}
|
2004-01-17 21:04:19 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2009-01-03 09:11:31 +01:00
|
|
|
// for backwards compatibility, let the default readv and
|
|
|
|
// writev implementations be implemented in terms of the
|
|
|
|
// old read and write
|
2009-01-11 03:02:34 +01:00
|
|
|
int storage_interface::readv(file::iovec_t const* bufs
|
2009-01-03 09:11:31 +01:00
|
|
|
, int slot, int offset, int num_bufs)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
|
|
|
|
{
|
2009-09-05 09:21:10 +02:00
|
|
|
int r = read((char*)i->iov_base, slot, offset, i->iov_len);
|
2009-01-03 09:11:31 +01:00
|
|
|
offset += i->iov_len;
|
|
|
|
if (r == -1) return -1;
|
|
|
|
ret += r;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
int storage_interface::writev(file::iovec_t const* bufs, int slot
|
2009-01-03 09:11:31 +01:00
|
|
|
, int offset, int num_bufs)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
|
|
|
|
{
|
2009-09-05 09:21:10 +02:00
|
|
|
int r = write((char const*)i->iov_base, slot, offset, i->iov_len);
|
2009-01-03 09:11:31 +01:00
|
|
|
offset += i->iov_len;
|
|
|
|
if (r == -1) return -1;
|
|
|
|
ret += r;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target)
|
|
|
|
{
|
|
|
|
int size = 0;
|
|
|
|
int ret = 1;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
*target = *bufs;
|
|
|
|
size += bufs->iov_len;
|
|
|
|
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 (;;)
|
|
|
|
{
|
|
|
|
size += bufs->iov_len;
|
2009-01-13 09:58:35 +01:00
|
|
|
if (size >= bytes)
|
2009-01-03 09:11:31 +01:00
|
|
|
{
|
|
|
|
((char*&)bufs->iov_base) += bufs->iov_len - (size - bytes);
|
|
|
|
bufs->iov_len = size - bytes;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
++bufs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
int bufs_size(file::iovec_t const* bufs, int num_bufs)
|
2009-01-03 09:11:31 +01:00
|
|
|
{
|
|
|
|
int size = 0;
|
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
|
|
|
size += i->iov_len;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2009-01-11 03:02:34 +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);
|
|
|
|
}
|
|
|
|
|
2011-05-08 11:04:59 +02:00
|
|
|
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
|
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)
|
|
|
|
{
|
|
|
|
size += i->iov_len;
|
|
|
|
TORRENT_ASSERT(size <= bytes);
|
|
|
|
if (size >= bytes) return count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-05-21 18:15:05 +02:00
|
|
|
int piece_manager::hash_for_slot(int slot, partial_hash& ph, int piece_size
|
|
|
|
, int small_piece_size, sha1_hash* small_hash)
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
2010-09-25 23:39:27 +02:00
|
|
|
TORRENT_ASSERT_VAL(!error(), error());
|
2009-05-21 18:15:05 +02:00
|
|
|
int num_read = 0;
|
2007-06-10 22:46:09 +02:00
|
|
|
int slot_size = piece_size - ph.offset;
|
2007-09-20 03:03:31 +02:00
|
|
|
if (slot_size > 0)
|
2007-09-19 21:55:11 +02:00
|
|
|
{
|
2009-01-11 11:32:57 +01:00
|
|
|
int block_size = 16 * 1024;
|
2009-05-21 18:15:05 +02:00
|
|
|
if (m_storage->disk_pool()) block_size = m_storage->disk_pool()->block_size();
|
2009-01-11 03:02:34 +01:00
|
|
|
int size = slot_size;
|
2009-01-11 11:32:57 +01:00
|
|
|
int num_blocks = (size + block_size - 1) / block_size;
|
2009-05-03 21:09:06 +02:00
|
|
|
|
|
|
|
// when we optimize for speed we allocate all the buffers we
|
|
|
|
// need for the rest of the piece, and read it all in one call
|
|
|
|
// and then hash it. When optimizing for memory usage, we read
|
|
|
|
// one block at a time and hash it. This ends up only using a
|
|
|
|
// single buffer
|
2009-05-21 18:15:05 +02:00
|
|
|
if (m_storage->settings().optimize_hashing_for_speed)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2009-05-03 21:09:06 +02:00
|
|
|
file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks);
|
|
|
|
for (int i = 0; i < num_blocks; ++i)
|
|
|
|
{
|
2009-05-21 18:15:05 +02:00
|
|
|
bufs[i].iov_base = m_storage->disk_pool()->allocate_buffer("hash temp");
|
2009-05-03 21:09:06 +02:00
|
|
|
bufs[i].iov_len = (std::min)(block_size, size);
|
|
|
|
size -= bufs[i].iov_len;
|
|
|
|
}
|
2009-05-21 18:15:05 +02:00
|
|
|
num_read = m_storage->readv(bufs, slot, ph.offset, num_blocks);
|
2011-01-20 07:17:27 +01:00
|
|
|
// TODO: if the read fails, set error and exit immediately
|
2009-05-03 21:09:06 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < num_blocks; ++i)
|
|
|
|
{
|
2009-09-05 16:20:00 +02:00
|
|
|
if (small_hash && small_piece_size <= block_size)
|
2009-05-21 18:15:05 +02:00
|
|
|
{
|
|
|
|
ph.h.update((char const*)bufs[i].iov_base, small_piece_size);
|
|
|
|
*small_hash = hasher(ph.h).final();
|
|
|
|
small_hash = 0; // avoid this case again
|
2011-02-15 11:05:25 +01:00
|
|
|
if (int(bufs[i].iov_len) > small_piece_size)
|
2009-09-05 18:32:54 +02:00
|
|
|
ph.h.update((char const*)bufs[i].iov_base + small_piece_size
|
|
|
|
, bufs[i].iov_len - small_piece_size);
|
2009-05-21 18:15:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ph.h.update((char const*)bufs[i].iov_base, bufs[i].iov_len);
|
|
|
|
small_piece_size -= bufs[i].iov_len;
|
|
|
|
}
|
2009-06-10 10:30:55 +02:00
|
|
|
ph.offset += bufs[i].iov_len;
|
2009-05-21 18:15:05 +02:00
|
|
|
m_storage->disk_pool()->free_buffer((char*)bufs[i].iov_base);
|
2009-05-03 21:09:06 +02:00
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
2009-05-03 21:09:06 +02:00
|
|
|
else
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2009-05-03 21:09:06 +02:00
|
|
|
file::iovec_t buf;
|
2009-05-21 18:15:05 +02:00
|
|
|
disk_buffer_holder holder(*m_storage->disk_pool()
|
|
|
|
, m_storage->disk_pool()->allocate_buffer("hash temp"));
|
2009-05-03 21:09:06 +02:00
|
|
|
buf.iov_base = holder.get();
|
|
|
|
for (int i = 0; i < num_blocks; ++i)
|
|
|
|
{
|
|
|
|
buf.iov_len = (std::min)(block_size, size);
|
2009-05-21 18:15:05 +02:00
|
|
|
int ret = m_storage->readv(&buf, slot, ph.offset, 1);
|
|
|
|
if (ret > 0) num_read += ret;
|
2011-01-20 07:17:27 +01:00
|
|
|
// TODO: if the read fails, set error and exit immediately
|
2009-05-21 18:15:05 +02:00
|
|
|
|
2009-09-05 18:32:54 +02:00
|
|
|
if (small_hash && small_piece_size <= block_size)
|
2009-05-21 18:15:05 +02:00
|
|
|
{
|
2009-06-15 00:50:35 +02:00
|
|
|
if (small_piece_size > 0) ph.h.update((char const*)buf.iov_base, small_piece_size);
|
2009-05-21 18:15:05 +02:00
|
|
|
*small_hash = hasher(ph.h).final();
|
|
|
|
small_hash = 0; // avoid this case again
|
2011-02-15 11:05:25 +01:00
|
|
|
if (int(buf.iov_len) > small_piece_size)
|
2009-09-05 18:32:54 +02:00
|
|
|
ph.h.update((char const*)buf.iov_base + small_piece_size
|
|
|
|
, buf.iov_len - small_piece_size);
|
2009-05-21 18:15:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ph.h.update((char const*)buf.iov_base, buf.iov_len);
|
|
|
|
small_piece_size -= buf.iov_len;
|
|
|
|
}
|
|
|
|
|
2009-05-03 21:09:06 +02:00
|
|
|
ph.offset += buf.iov_len;
|
|
|
|
size -= buf.iov_len;
|
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
2009-05-21 18:15:05 +02:00
|
|
|
if (error()) return 0;
|
2007-09-19 21:55:11 +02:00
|
|
|
}
|
2009-05-21 18:15:05 +02:00
|
|
|
return num_read;
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
default_storage::default_storage(file_storage const& fs, file_storage const* mapped, std::string const& path
|
|
|
|
, file_pool& fp, std::vector<boost::uint8_t> const& file_prio)
|
|
|
|
: m_files(fs)
|
|
|
|
, m_file_priority(file_prio)
|
|
|
|
, m_pool(fp)
|
|
|
|
, m_page_size(page_size())
|
|
|
|
, m_allocate_files(false)
|
|
|
|
{
|
|
|
|
if (mapped) m_mapped_files.reset(new file_storage(*mapped));
|
|
|
|
|
|
|
|
TORRENT_ASSERT(m_files.begin() != m_files.end());
|
|
|
|
m_save_path = complete(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
default_storage::~default_storage() { m_pool.release(this); }
|
|
|
|
|
|
|
|
bool default_storage::initialize(bool allocate_files)
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2009-02-14 04:56:07 +01:00
|
|
|
m_allocate_files = allocate_files;
|
2008-07-18 01:41:46 +02:00
|
|
|
error_code ec;
|
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;
|
2008-05-28 10:44:40 +02:00
|
|
|
for (file_storage::iterator file_iter = files().begin(),
|
|
|
|
end_iter = files().end(); file_iter != end_iter; ++file_iter)
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2010-11-25 00:49:22 +01:00
|
|
|
int file_index = files().file_index(*file_iter);
|
2008-10-03 07:49:41 +02:00
|
|
|
|
2008-10-30 07:09:23 +01:00
|
|
|
// ignore files that have priority 0
|
|
|
|
if (int(m_file_priority.size()) > file_index
|
|
|
|
&& m_file_priority[file_index] == 0) continue;
|
|
|
|
|
|
|
|
// ignore pad files
|
|
|
|
if (file_iter->pad_file) continue;
|
2007-04-19 05:06:15 +02:00
|
|
|
|
2011-08-01 02:22:54 +02:00
|
|
|
std::string file_path = combine_path(m_save_path, files().file_path(*file_iter));
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
file_status s;
|
|
|
|
stat_file(file_path, &s, ec);
|
2011-01-19 10:19:26 +01:00
|
|
|
if (ec && ec != boost::system::errc::no_such_file_or_directory
|
|
|
|
&& ec != boost::system::errc::not_a_directory)
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
set_error(file_path, ec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ec is either ENOENT or the file existed and s is valid
|
2011-01-19 08:39:55 +01:00
|
|
|
// allocate file only if it is not exist and (allocate_files == true)
|
|
|
|
// if the file already exists, but is larger than what
|
|
|
|
// it's supposed to be, also truncate it
|
|
|
|
// if the file is empty, just create it either way.
|
2011-02-12 23:48:21 +01:00
|
|
|
if ((ec && allocate_files) || (!ec && s.file_size > file_iter->size) || file_iter->size == 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2011-08-01 02:22:54 +02:00
|
|
|
std::string dir = parent_path(file_path);
|
|
|
|
|
|
|
|
if (dir != last_path)
|
|
|
|
{
|
|
|
|
last_path = dir;
|
|
|
|
|
|
|
|
create_directories(last_path, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
set_error(dir, ec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
ec.clear();
|
2011-08-01 02:22:54 +02:00
|
|
|
|
2010-11-29 06:44:29 +01:00
|
|
|
boost::intrusive_ptr<file> f = open_file(file_iter, file::read_write, ec);
|
2010-03-06 21:09:18 +01:00
|
|
|
if (ec) set_error(file_path, ec);
|
2008-07-18 01:41:46 +02:00
|
|
|
else if (f)
|
|
|
|
{
|
|
|
|
f->set_size(file_iter->size, ec);
|
2010-03-06 21:09:18 +01:00
|
|
|
if (ec) set_error(file_path, ec);
|
2008-07-18 01:41:46 +02:00
|
|
|
}
|
2011-02-12 23:11:00 +01:00
|
|
|
if (ec) break;
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
2011-02-13 02:16:06 +01:00
|
|
|
ec.clear();
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2008-07-20 18:00:08 +02:00
|
|
|
std::vector<boost::uint8_t>().swap(m_file_priority);
|
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);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
void default_storage::finalize_file(int index)
|
2010-01-09 19:40:05 +01:00
|
|
|
{
|
2011-01-19 09:42:17 +01:00
|
|
|
TORRENT_ASSERT(index >= 0 && index < files().num_files());
|
|
|
|
if (index < 0 || index >= files().num_files()) return;
|
2010-01-09 19:40:05 +01:00
|
|
|
|
|
|
|
error_code ec;
|
2010-11-29 06:44:29 +01:00
|
|
|
boost::intrusive_ptr<file> f = open_file(files().begin() + index, file::read_write, ec);
|
2010-01-09 19:40:05 +01:00
|
|
|
if (ec || !f) return;
|
|
|
|
|
|
|
|
f->finalize();
|
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::has_any_file()
|
2009-04-10 09:22:27 +02:00
|
|
|
{
|
|
|
|
file_storage::iterator i = files().begin();
|
|
|
|
file_storage::iterator end = files().end();
|
|
|
|
|
|
|
|
for (; i != end; ++i)
|
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
error_code ec;
|
|
|
|
file_status s;
|
2010-11-25 00:49:22 +01:00
|
|
|
stat_file(combine_path(m_save_path, files().file_path(*i)), &s, ec);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (ec) continue;
|
|
|
|
if (s.mode & file_status::regular_file && i->size > 0)
|
2009-04-10 09:22:27 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::rename_file(int index, std::string const& new_filename)
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
2011-01-19 09:42:17 +01:00
|
|
|
if (index < 0 || index >= files().num_files()) return true;
|
2010-11-25 00:49:22 +01:00
|
|
|
std::string old_name = combine_path(m_save_path, files().file_path(files().at(index)));
|
|
|
|
m_pool.release(this, index);
|
2008-05-28 10:44:40 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
error_code ec;
|
|
|
|
rename(old_name, combine_path(m_save_path, new_filename), ec);
|
|
|
|
|
|
|
|
if (ec && ec != boost::system::errc::no_such_file_or_directory)
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
set_error(old_name, ec);
|
2008-05-28 10:44:40 +02:00
|
|
|
return true;
|
|
|
|
}
|
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);
|
2008-02-14 04:48:20 +01:00
|
|
|
return false;
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::release_files()
|
2004-12-21 13:30:09 +01:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
m_pool.release(this);
|
2008-02-14 04:48:20 +01:00
|
|
|
return false;
|
2003-12-07 17:26:16 +01:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
void default_storage::delete_one_file(std::string const& p)
|
2009-04-12 20:04:20 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
error_code ec;
|
|
|
|
remove(p, ec);
|
|
|
|
|
|
|
|
if (ec && ec != boost::system::errc::no_such_file_or_directory)
|
|
|
|
set_error(p, ec);
|
2009-04-12 20:04:20 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::delete_files()
|
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
|
|
|
|
|
|
|
// delete the files from disk
|
|
|
|
std::set<std::string> directories;
|
|
|
|
typedef std::set<std::string>::iterator iter_t;
|
2008-05-28 10:44:40 +02:00
|
|
|
for (file_storage::iterator i = files().begin()
|
|
|
|
, end(files().end()); i != end; ++i)
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
2010-11-25 00:49:22 +01:00
|
|
|
std::string fp = files().file_path(*i);
|
|
|
|
std::string p = combine_path(m_save_path, fp);
|
|
|
|
std::string bp = parent_path(fp);
|
2007-10-21 02:19:37 +02:00
|
|
|
std::pair<iter_t, bool> ret;
|
|
|
|
ret.second = true;
|
2007-10-13 05:33:33 +02:00
|
|
|
while (ret.second && !bp.empty())
|
|
|
|
{
|
2009-11-28 09:58:07 +01:00
|
|
|
ret = directories.insert(combine_path(m_save_path, bp));
|
2009-10-26 02:29:39 +01:00
|
|
|
bp = parent_path(bp);
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
2009-04-12 20:04:20 +02:00
|
|
|
delete_one_file(p);
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove the directories. Reverse order to delete
|
|
|
|
// subdirectories first
|
2007-11-16 20:04:43 +01:00
|
|
|
|
|
|
|
for (std::set<std::string>::reverse_iterator i = directories.rbegin()
|
2007-11-16 20:38:58 +01:00
|
|
|
, end(directories.rend()); i != end; ++i)
|
2007-11-16 20:04:43 +01:00
|
|
|
{
|
2009-04-12 20:04:20 +02:00
|
|
|
delete_one_file(*i);
|
2007-11-16 20:04:43 +01:00
|
|
|
}
|
|
|
|
|
2009-04-12 20:04:20 +02:00
|
|
|
if (error()) return true;
|
2008-07-18 01:41:46 +02:00
|
|
|
return false;
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::write_resume_data(entry& rd) const
|
2007-05-24 20:53:55 +02:00
|
|
|
{
|
2008-04-13 20:54:36 +02:00
|
|
|
TORRENT_ASSERT(rd.type() == entry::dictionary_t);
|
|
|
|
|
2007-05-24 20:53:55 +02:00
|
|
|
std::vector<std::pair<size_type, std::time_t> > file_sizes
|
2008-05-28 10:44:40 +02:00
|
|
|
= get_filesizes(files(), m_save_path);
|
2007-05-24 20:53:55 +02:00
|
|
|
|
|
|
|
entry::list_type& fl = rd["file sizes"].list();
|
|
|
|
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
|
|
|
|
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
entry::list_type p;
|
|
|
|
p.push_back(entry(i->first));
|
|
|
|
p.push_back(entry(i->second));
|
|
|
|
fl.push_back(entry(p));
|
|
|
|
}
|
2008-07-19 13:12:40 +02:00
|
|
|
|
2008-02-14 04:48:20 +01:00
|
|
|
return false;
|
2007-05-24 20:53:55 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
int default_storage::sparse_end(int slot) const
|
2009-02-17 01:11:38 +01:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(slot >= 0);
|
|
|
|
TORRENT_ASSERT(slot < m_files.num_pieces());
|
|
|
|
|
|
|
|
size_type file_offset = (size_type)slot * m_files.piece_length();
|
2010-11-29 06:44:29 +01:00
|
|
|
file_storage::iterator file_iter;
|
2009-02-17 01:11:38 +01:00
|
|
|
|
|
|
|
for (file_iter = files().begin();;)
|
|
|
|
{
|
|
|
|
if (file_offset < file_iter->size)
|
|
|
|
break;
|
|
|
|
|
|
|
|
file_offset -= file_iter->size;
|
|
|
|
++file_iter;
|
|
|
|
TORRENT_ASSERT(file_iter != files().end());
|
|
|
|
}
|
|
|
|
|
|
|
|
error_code ec;
|
2010-11-29 06:44:29 +01:00
|
|
|
boost::intrusive_ptr<file> file_handle = open_file(file_iter, file::read_only, ec);
|
2009-02-17 01:11:38 +01:00
|
|
|
if (!file_handle || ec) return slot;
|
|
|
|
|
|
|
|
size_type data_start = file_handle->sparse_end(file_offset);
|
2011-02-21 06:24:41 +01:00
|
|
|
return int((data_start + m_files.piece_length() - 1) / m_files.piece_length());
|
2009-02-17 01:11:38 +01:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::verify_resume_data(lazy_entry const& rd, error_code& error)
|
2007-03-28 01:49:05 +02:00
|
|
|
{
|
2011-01-31 01:47:09 +01:00
|
|
|
// TODO: make this more generic to not just work if files have been
|
|
|
|
// renamed, but also if they have been merged into a single file for instance
|
|
|
|
// maybe use the same format as .torrent files and reuse some code from torrent_info
|
2009-06-22 02:52:57 +02:00
|
|
|
lazy_entry const* mapped_files = rd.dict_find_list("mapped_files");
|
|
|
|
if (mapped_files && mapped_files->list_size() == m_files.num_files())
|
|
|
|
{
|
|
|
|
m_mapped_files.reset(new file_storage(m_files));
|
|
|
|
for (int i = 0; i < m_files.num_files(); ++i)
|
|
|
|
{
|
|
|
|
std::string new_filename = mapped_files->list_string_value_at(i);
|
|
|
|
if (new_filename.empty()) continue;
|
|
|
|
m_mapped_files->rename_file(i, new_filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-20 18:00:08 +02:00
|
|
|
lazy_entry const* file_priority = rd.dict_find_list("file_priority");
|
|
|
|
if (file_priority && file_priority->list_size()
|
|
|
|
== files().num_files())
|
|
|
|
{
|
|
|
|
m_file_priority.resize(file_priority->list_size());
|
|
|
|
for (int i = 0; i < file_priority->list_size(); ++i)
|
2011-02-21 06:24:41 +01:00
|
|
|
m_file_priority[i] = boost::uint8_t(file_priority->list_int_value_at(i, 1));
|
2008-07-20 18:00:08 +02:00
|
|
|
}
|
|
|
|
|
2007-03-28 01:49:05 +02:00
|
|
|
std::vector<std::pair<size_type, std::time_t> > file_sizes;
|
2008-07-01 01:14:31 +02:00
|
|
|
lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes");
|
|
|
|
if (file_sizes_ent == 0)
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::missing_file_sizes;
|
2008-02-14 04:48:20 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
for (int i = 0; i < file_sizes_ent->list_size(); ++i)
|
2007-03-28 01:49:05 +02:00
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
lazy_entry const* e = file_sizes_ent->list_at(i);
|
|
|
|
if (e->type() != lazy_entry::list_t
|
|
|
|
|| e->list_size() != 2
|
|
|
|
|| e->list_at(0)->type() != lazy_entry::int_t
|
|
|
|
|| e->list_at(1)->type() != lazy_entry::int_t)
|
|
|
|
continue;
|
2007-03-28 01:49:05 +02:00
|
|
|
file_sizes.push_back(std::pair<size_type, std::time_t>(
|
2008-07-01 01:14:31 +02:00
|
|
|
e->list_int_value_at(0), std::time_t(e->list_int_value_at(1))));
|
2007-03-28 01:49:05 +02:00
|
|
|
}
|
|
|
|
|
2007-05-24 20:53:55 +02:00
|
|
|
if (file_sizes.empty())
|
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::no_files_in_resume_data;
|
2007-05-24 20:53:55 +02:00
|
|
|
return false;
|
|
|
|
}
|
2008-02-14 04:48:20 +01:00
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
bool seed = false;
|
|
|
|
|
2008-07-18 15:51:26 +02:00
|
|
|
lazy_entry const* slots = rd.dict_find_list("slots");
|
|
|
|
if (slots)
|
2008-07-01 01:14:31 +02:00
|
|
|
{
|
2008-07-18 15:51:26 +02:00
|
|
|
if (int(slots->list_size()) == m_files.num_pieces())
|
2008-07-01 01:14:31 +02:00
|
|
|
{
|
2008-07-18 15:51:26 +02:00
|
|
|
seed = true;
|
|
|
|
for (int i = 0; i < slots->list_size(); ++i)
|
|
|
|
{
|
2008-09-20 08:02:40 +02:00
|
|
|
if (slots->list_int_value_at(i, -1) >= 0) continue;
|
2008-07-18 15:51:26 +02:00
|
|
|
seed = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (lazy_entry const* pieces = rd.dict_find_string("pieces"))
|
|
|
|
{
|
|
|
|
if (int(pieces->string_length()) == m_files.num_pieces())
|
|
|
|
{
|
|
|
|
seed = true;
|
|
|
|
char const* p = pieces->string_ptr();
|
|
|
|
for (int i = 0; i < pieces->string_length(); ++i)
|
|
|
|
{
|
|
|
|
if ((p[i] & 1) == 1) continue;
|
|
|
|
seed = false;
|
|
|
|
break;
|
|
|
|
}
|
2008-07-01 01:14:31 +02:00
|
|
|
}
|
|
|
|
}
|
2008-07-18 15:51:26 +02:00
|
|
|
else
|
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::missing_pieces;
|
2008-07-18 15:51:26 +02:00
|
|
|
return false;
|
|
|
|
}
|
2007-03-28 01:49:05 +02:00
|
|
|
|
2007-04-17 23:54:40 +02:00
|
|
|
bool full_allocation_mode = false;
|
2008-07-18 15:51:26 +02:00
|
|
|
if (rd.dict_find_string_value("allocation") != "compact")
|
2008-07-01 01:14:31 +02:00
|
|
|
full_allocation_mode = true;
|
2007-04-17 23:54:40 +02:00
|
|
|
|
2007-03-28 01:49:05 +02:00
|
|
|
if (seed)
|
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
if (files().num_files() != (int)file_sizes.size())
|
2007-03-28 01:49:05 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::mismatching_number_of_files;
|
2007-03-28 01:49:05 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<size_type, std::time_t> >::iterator
|
|
|
|
fs = file_sizes.begin();
|
|
|
|
// the resume data says we have the entire torrent
|
|
|
|
// make sure the file sizes are the right ones
|
2008-05-28 10:44:40 +02:00
|
|
|
for (file_storage::iterator i = files().begin()
|
|
|
|
, end(files().end()); i != end; ++i, ++fs)
|
2007-03-28 01:49:05 +02:00
|
|
|
{
|
2009-04-09 19:36:05 +02:00
|
|
|
if (!i->pad_file && i->size != fs->first)
|
2007-03-28 01:49:05 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::mismatching_file_size;
|
2007-03-28 01:49:05 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-04-11 23:02:43 +02:00
|
|
|
int flags = (full_allocation_mode ? 0 : compact_mode)
|
|
|
|
| (settings().ignore_resume_timestamps ? ignore_timestamps : 0);
|
|
|
|
|
|
|
|
return match_filesizes(files(), m_save_path, file_sizes, flags, error);
|
2009-03-01 01:02:33 +01:00
|
|
|
|
2007-03-28 01:49:05 +02:00
|
|
|
}
|
|
|
|
|
2004-07-18 02:39:58 +02:00
|
|
|
// returns true on success
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::move_storage(std::string const& sp)
|
2004-07-18 02:39:58 +02:00
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string save_path = complete(sp);
|
2004-07-18 02:39:58 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
error_code ec;
|
|
|
|
file_status s;
|
|
|
|
stat_file(save_path, &s, ec);
|
|
|
|
if (ec == boost::system::errc::no_such_file_or_directory)
|
|
|
|
create_directories(save_path, ec);
|
|
|
|
else if (ec)
|
2009-03-01 01:02:33 +01:00
|
|
|
return false;
|
2004-07-18 13:40:22 +02:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
m_pool.release(this);
|
2004-12-21 13:30:09 +01:00
|
|
|
|
2009-03-17 10:31:30 +01:00
|
|
|
bool ret = true;
|
|
|
|
std::set<std::string> to_move;
|
|
|
|
file_storage const& f = files();
|
|
|
|
|
|
|
|
for (file_storage::iterator i = f.begin()
|
|
|
|
, end(f.end()); i != end; ++i)
|
|
|
|
{
|
2010-11-25 00:49:22 +01:00
|
|
|
std::string split = split_path(f.file_path(*i));
|
2009-10-26 02:29:39 +01:00
|
|
|
to_move.insert(to_move.begin(), split);
|
2009-03-17 10:31:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (std::set<std::string>::const_iterator i = to_move.begin()
|
|
|
|
, end(to_move.end()); i != end; ++i)
|
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string old_path = combine_path(m_save_path, *i);
|
|
|
|
std::string new_path = combine_path(save_path, *i);
|
2004-07-18 02:39:58 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
rename(old_path, new_path, ec);
|
|
|
|
if (ec && ec != boost::system::errc::no_such_file_or_directory)
|
2008-06-05 18:56:39 +02:00
|
|
|
{
|
2009-03-17 10:31:30 +01:00
|
|
|
error_code ec;
|
|
|
|
recursive_copy(old_path, new_path, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
set_error(old_path, ec);
|
2009-03-17 10:31:30 +01:00
|
|
|
ret = false;
|
|
|
|
}
|
2009-03-31 10:05:46 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
recursive_remove(old_path);
|
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
break;
|
2008-06-05 18:56:39 +02:00
|
|
|
}
|
2009-03-17 10:31:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret) m_save_path = save_path;
|
|
|
|
|
|
|
|
return ret;
|
2004-07-18 02:39:58 +02:00
|
|
|
}
|
|
|
|
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2007-03-16 06:29:23 +01:00
|
|
|
/*
|
2011-04-17 00:58:11 +02:00
|
|
|
void default_storage::shuffle()
|
2004-01-27 00:38:08 +01:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
int num_pieces = files().num_pieces();
|
2004-01-27 00:38:08 +01:00
|
|
|
|
|
|
|
std::vector<int> pieces(num_pieces);
|
|
|
|
for (std::vector<int>::iterator i = pieces.begin();
|
2007-03-16 06:29:23 +01:00
|
|
|
i != pieces.end(); ++i)
|
2004-01-27 00:38:08 +01:00
|
|
|
{
|
2004-01-28 12:37:46 +01:00
|
|
|
*i = static_cast<int>(i - pieces.begin());
|
2004-01-27 00:38:08 +01:00
|
|
|
}
|
2004-01-27 18:48:58 +01:00
|
|
|
std::srand((unsigned int)std::time(0));
|
2004-01-27 00:38:08 +01:00
|
|
|
std::vector<int> targets(pieces);
|
|
|
|
std::random_shuffle(pieces.begin(), pieces.end());
|
|
|
|
std::random_shuffle(targets.begin(), targets.end());
|
|
|
|
|
2005-08-16 21:06:25 +02:00
|
|
|
for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
|
2004-01-27 00:38:08 +01:00
|
|
|
{
|
|
|
|
const int slot_index = targets[i];
|
|
|
|
const int piece_index = pieces[i];
|
2008-05-28 10:44:40 +02:00
|
|
|
const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
|
2004-01-27 00:38:08 +01:00
|
|
|
std::vector<char> buf(slot_size);
|
|
|
|
read(&buf[0], piece_index, 0, slot_size);
|
|
|
|
write(&buf[0], slot_index, 0, slot_size);
|
|
|
|
}
|
|
|
|
}
|
2007-03-16 06:29:23 +01:00
|
|
|
*/
|
2004-01-27 00:38:08 +01:00
|
|
|
#endif
|
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
#define TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size) \
|
2009-01-21 08:31:49 +01:00
|
|
|
int num_blocks = (piece_size + disk_pool()->block_size() - 1) / disk_pool()->block_size(); \
|
2009-01-11 03:02:34 +01:00
|
|
|
file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks); \
|
|
|
|
for (int i = 0, size = piece_size; i < num_blocks; ++i) \
|
|
|
|
{ \
|
2009-01-23 10:13:31 +01:00
|
|
|
bufs[i].iov_base = disk_pool()->allocate_buffer("move temp"); \
|
2009-01-21 08:31:49 +01:00
|
|
|
bufs[i].iov_len = (std::min)(disk_pool()->block_size(), size); \
|
2009-01-11 03:02:34 +01:00
|
|
|
size -= bufs[i].iov_len; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TORRENT_FREE_BLOCKS(bufs, num_blocks) \
|
|
|
|
for (int i = 0; i < num_blocks; ++i) \
|
2009-01-21 08:31:49 +01:00
|
|
|
disk_pool()->free_buffer((char*)bufs[i].iov_base);
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
#define TORRENT_SET_SIZE(bufs, size, num_bufs) \
|
2009-01-21 08:31:49 +01:00
|
|
|
for (num_bufs = 0; size > 0; size -= disk_pool()->block_size(), ++num_bufs) \
|
|
|
|
bufs[num_bufs].iov_len = (std::min)(disk_pool()->block_size(), size)
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::move_slot(int src_slot, int dst_slot)
|
2007-04-30 03:06:29 +02:00
|
|
|
{
|
2009-01-11 03:02:34 +01:00
|
|
|
bool r = true;
|
2008-05-28 10:44:40 +02:00
|
|
|
int piece_size = m_files.piece_size(dst_slot);
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size);
|
|
|
|
|
|
|
|
readv(bufs, src_slot, 0, num_blocks); if (error()) goto ret;
|
|
|
|
writev(bufs, dst_slot, 0, num_blocks); if (error()) goto ret;
|
|
|
|
|
|
|
|
r = false;
|
|
|
|
ret:
|
|
|
|
TORRENT_FREE_BLOCKS(bufs, num_blocks)
|
|
|
|
return r;
|
2007-04-30 03:06:29 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::swap_slots(int slot1, int slot2)
|
2007-05-16 03:16:08 +02:00
|
|
|
{
|
2009-01-11 03:02:34 +01:00
|
|
|
bool r = true;
|
|
|
|
|
2007-05-16 03:16:08 +02:00
|
|
|
// the size of the target slot is the size of the piece
|
2008-05-28 10:44:40 +02:00
|
|
|
int piece1_size = m_files.piece_size(slot2);
|
|
|
|
int piece2_size = m_files.piece_size(slot1);
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece1_size);
|
|
|
|
TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece2_size);
|
|
|
|
|
|
|
|
readv(bufs1, slot1, 0, num_blocks1); if (error()) goto ret;
|
|
|
|
readv(bufs2, slot2, 0, num_blocks2); if (error()) goto ret;
|
|
|
|
writev(bufs1, slot2, 0, num_blocks1); if (error()) goto ret;
|
|
|
|
writev(bufs2, slot1, 0, num_blocks2); if (error()) goto ret;
|
|
|
|
|
|
|
|
r = false;
|
|
|
|
ret:
|
|
|
|
TORRENT_FREE_BLOCKS(bufs1, num_blocks1)
|
|
|
|
TORRENT_FREE_BLOCKS(bufs2, num_blocks2)
|
|
|
|
return r;
|
2007-05-16 03:16:08 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
bool default_storage::swap_slots3(int slot1, int slot2, int slot3)
|
2007-05-16 03:16:08 +02:00
|
|
|
{
|
2009-01-11 03:02:34 +01:00
|
|
|
bool r = true;
|
|
|
|
|
2007-05-16 03:16:08 +02:00
|
|
|
// the size of the target slot is the size of the piece
|
2008-05-28 10:44:40 +02:00
|
|
|
int piece_size = m_files.piece_length();
|
|
|
|
int piece1_size = m_files.piece_size(slot2);
|
|
|
|
int piece2_size = m_files.piece_size(slot3);
|
|
|
|
int piece3_size = m_files.piece_size(slot1);
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece_size);
|
|
|
|
TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece_size);
|
|
|
|
|
|
|
|
int tmp1 = 0;
|
|
|
|
int tmp2 = 0;
|
|
|
|
TORRENT_SET_SIZE(bufs1, piece1_size, tmp1);
|
|
|
|
readv(bufs1, slot1, 0, tmp1); if (error()) goto ret;
|
|
|
|
TORRENT_SET_SIZE(bufs2, piece2_size, tmp2);
|
|
|
|
readv(bufs2, slot2, 0, tmp2); if (error()) goto ret;
|
|
|
|
writev(bufs1, slot2, 0, tmp1); if (error()) goto ret;
|
|
|
|
TORRENT_SET_SIZE(bufs1, piece3_size, tmp1);
|
|
|
|
readv(bufs1, slot3, 0, tmp1); if (error()) goto ret;
|
|
|
|
writev(bufs2, slot3, 0, tmp2); if (error()) goto ret;
|
|
|
|
writev(bufs1, slot1, 0, tmp1); if (error()) goto ret;
|
|
|
|
ret:
|
|
|
|
TORRENT_FREE_BLOCKS(bufs1, num_blocks1)
|
|
|
|
TORRENT_FREE_BLOCKS(bufs2, num_blocks2)
|
|
|
|
return r;
|
2007-04-30 03:06:29 +02:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
int default_storage::writev(file::iovec_t const* bufs, int slot, int offset
|
2009-01-03 09:11:31 +01:00
|
|
|
, int num_bufs)
|
2009-01-17 09:35:48 +01:00
|
|
|
{
|
2009-05-23 05:03:52 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
2009-06-15 00:20:23 +02:00
|
|
|
disk_buffer_pool* pool = disk_pool();
|
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " write "
|
2009-09-05 09:21:10 +02:00
|
|
|
<< physical_offset(slot, offset) << std::endl;
|
2009-06-15 00:20:23 +02:00
|
|
|
}
|
2009-05-23 05:03:52 +02:00
|
|
|
#endif
|
2011-04-17 00:58:11 +02:00
|
|
|
fileop op = { &file::writev, &default_storage::write_unaligned
|
2009-01-21 08:31:49 +01:00
|
|
|
, m_settings ? settings().disk_io_write_mode : 0, file::read_write };
|
2009-05-23 05:03:52 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
int ret = readwritev(bufs, slot, offset, num_bufs, op);
|
2009-06-15 00:20:23 +02:00
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " write_end "
|
2009-09-05 09:21:10 +02:00
|
|
|
<< (physical_offset(slot, offset) + ret) << std::endl;
|
2009-06-15 00:20:23 +02:00
|
|
|
}
|
2009-05-23 05:03:52 +02:00
|
|
|
return ret;
|
|
|
|
#else
|
2010-01-17 08:39:07 +01:00
|
|
|
return readwritev(bufs, slot, offset, num_bufs, op);
|
2009-05-23 05:03:52 +02:00
|
|
|
#endif
|
2009-01-17 09:35:48 +01:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
size_type default_storage::physical_offset(int slot, int offset)
|
2009-09-05 09:21:10 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(slot >= 0);
|
|
|
|
TORRENT_ASSERT(slot < m_files.num_pieces());
|
|
|
|
TORRENT_ASSERT(offset >= 0);
|
|
|
|
|
|
|
|
// find the file and file
|
|
|
|
size_type tor_off = size_type(slot)
|
|
|
|
* files().piece_length() + offset;
|
|
|
|
file_storage::iterator file_iter = files().file_at_offset(tor_off);
|
2011-07-03 17:57:41 +02:00
|
|
|
while (file_iter->pad_file)
|
|
|
|
{
|
|
|
|
++file_iter;
|
2011-07-03 23:09:42 +02:00
|
|
|
if (file_iter == files().end())
|
2011-07-03 17:57:41 +02:00
|
|
|
return size_type(slot) * files().piece_length() + offset;
|
2011-07-12 10:53:20 +02:00
|
|
|
// update offset as well, since we're moving it up ahead
|
|
|
|
tor_off = file_iter->offset;
|
2011-07-03 17:57:41 +02:00
|
|
|
}
|
2010-11-14 09:17:27 +01:00
|
|
|
TORRENT_ASSERT(!file_iter->pad_file);
|
2009-09-05 09:21:10 +02:00
|
|
|
|
|
|
|
size_type file_offset = tor_off - file_iter->offset;
|
|
|
|
TORRENT_ASSERT(file_offset >= 0);
|
|
|
|
|
|
|
|
// open the file read only to avoid re-opening
|
|
|
|
// it in case it's already opened in read-only mode
|
2010-01-09 19:40:05 +01:00
|
|
|
error_code ec;
|
2010-11-29 06:44:29 +01:00
|
|
|
boost::intrusive_ptr<file> f = open_file(file_iter, file::read_only, ec);
|
2009-09-05 09:21:10 +02:00
|
|
|
|
|
|
|
size_type ret = 0;
|
|
|
|
if (f && !ec) ret = f->phys_offset(file_offset);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
// this means we don't support true physical offset
|
|
|
|
// just make something up
|
|
|
|
return size_type(slot) * files().piece_length() + offset;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-04-26 09:03:05 +02:00
|
|
|
void default_storage::hint_read(int slot, int offset, int size)
|
|
|
|
{
|
|
|
|
size_type start = slot * (size_type)m_files.piece_length() + offset;
|
|
|
|
TORRENT_ASSERT(start + size <= m_files.total_size());
|
|
|
|
|
|
|
|
size_type file_offset = start;
|
|
|
|
file_storage::iterator file_iter;
|
|
|
|
|
|
|
|
// TODO: use binary search!
|
|
|
|
for (file_iter = files().begin();;)
|
|
|
|
{
|
|
|
|
if (file_offset < file_iter->size)
|
|
|
|
break;
|
|
|
|
|
|
|
|
file_offset -= file_iter->size;
|
|
|
|
++file_iter;
|
|
|
|
TORRENT_ASSERT(file_iter != files().end());
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::intrusive_ptr<file> file_handle;
|
|
|
|
int bytes_left = size;
|
|
|
|
int slot_size = static_cast<int>(m_files.piece_size(slot));
|
|
|
|
|
|
|
|
if (offset + bytes_left > slot_size)
|
|
|
|
bytes_left = slot_size - offset;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(bytes_left >= 0);
|
|
|
|
|
|
|
|
int file_bytes_left;
|
|
|
|
for (;bytes_left > 0; ++file_iter, bytes_left -= file_bytes_left)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(file_iter != files().end());
|
|
|
|
|
|
|
|
file_bytes_left = bytes_left;
|
|
|
|
if (file_offset + file_bytes_left > file_iter->size)
|
|
|
|
file_bytes_left = (std::max)(static_cast<int>(file_iter->size - file_offset), 0);
|
|
|
|
|
|
|
|
if (file_bytes_left == 0) continue;
|
|
|
|
|
|
|
|
if (file_iter->pad_file) continue;
|
|
|
|
|
|
|
|
error_code ec;
|
|
|
|
file_handle = open_file(file_iter, file::read_only, ec);
|
|
|
|
|
|
|
|
// failing to hint that we want to read is not a big deal
|
|
|
|
// just swollow the error and keep going
|
|
|
|
if (!file_handle || ec) continue;
|
|
|
|
|
|
|
|
file_handle->hint_read(file_offset, file_bytes_left);
|
|
|
|
file_offset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
int default_storage::readv(file::iovec_t const* bufs, int slot, int offset
|
2009-01-17 09:35:48 +01:00
|
|
|
, int num_bufs)
|
|
|
|
{
|
2009-05-23 05:03:52 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
2009-06-15 00:20:23 +02:00
|
|
|
disk_buffer_pool* pool = disk_pool();
|
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " read "
|
2009-09-05 09:21:10 +02:00
|
|
|
<< physical_offset(slot, offset) << std::endl;
|
2009-06-15 00:20:23 +02:00
|
|
|
}
|
2009-05-23 05:03:52 +02:00
|
|
|
#endif
|
2011-04-17 00:58:11 +02:00
|
|
|
fileop op = { &file::readv, &default_storage::read_unaligned
|
2009-01-21 08:31:49 +01:00
|
|
|
, m_settings ? settings().disk_io_read_mode : 0, file::read_only };
|
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
|
2009-05-23 05:03:52 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
int ret = readwritev(bufs, slot, offset, num_bufs, op);
|
2009-06-15 00:20:23 +02:00
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " read_end "
|
2009-09-05 09:21:10 +02:00
|
|
|
<< (physical_offset(slot, offset) + ret) << std::endl;
|
2009-06-15 00:20:23 +02:00
|
|
|
}
|
2009-05-23 05:03:52 +02:00
|
|
|
return ret;
|
|
|
|
#else
|
2009-01-17 09:35:48 +01:00
|
|
|
return readwritev(bufs, slot, offset, num_bufs, op);
|
2009-05-23 05:03:52 +02:00
|
|
|
#endif
|
2009-01-17 09:35:48 +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.
|
2011-04-17 00:58:11 +02:00
|
|
|
int default_storage::readwritev(file::iovec_t const* bufs, int slot, int offset
|
2009-01-17 09:35:48 +01:00
|
|
|
, int num_bufs, fileop const& op)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-01-03 09:11:31 +01:00
|
|
|
TORRENT_ASSERT(bufs != 0);
|
2009-01-17 09:35:48 +01:00
|
|
|
TORRENT_ASSERT(slot >= 0);
|
|
|
|
TORRENT_ASSERT(slot < m_files.num_pieces());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(offset >= 0);
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(offset < m_files.piece_size(slot));
|
2009-01-03 09:11:31 +01:00
|
|
|
TORRENT_ASSERT(num_bufs > 0);
|
|
|
|
|
|
|
|
int size = bufs_size(bufs, num_bufs);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(size > 0);
|
2003-12-07 17:26:16 +01:00
|
|
|
|
2011-05-08 11:04:59 +02:00
|
|
|
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
|
2006-04-25 23:04:48 +02:00
|
|
|
std::vector<file_slice> slices
|
2008-05-28 10:44:40 +02:00
|
|
|
= files().map_block(slot, offset, size);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(!slices.empty());
|
2006-04-25 23:04:48 +02:00
|
|
|
#endif
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
size_type start = slot * (size_type)m_files.piece_length() + offset;
|
|
|
|
TORRENT_ASSERT(start + size <= m_files.total_size());
|
2003-12-07 02:26:57 +01:00
|
|
|
|
|
|
|
// find the file iterator and file offset
|
|
|
|
size_type file_offset = start;
|
2010-11-29 06:44:29 +01:00
|
|
|
file_storage::iterator file_iter;
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2011-04-26 09:03:05 +02:00
|
|
|
// TODO: use binary search!
|
2008-05-28 10:44:40 +02:00
|
|
|
for (file_iter = files().begin();;)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
|
|
|
if (file_offset < file_iter->size)
|
|
|
|
break;
|
|
|
|
|
|
|
|
file_offset -= file_iter->size;
|
|
|
|
++file_iter;
|
2008-10-03 07:49:41 +02:00
|
|
|
TORRENT_ASSERT(file_iter != files().end());
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
2009-01-17 09:35:48 +01:00
|
|
|
|
2007-05-04 05:28:42 +02:00
|
|
|
int buf_pos = 0;
|
2008-07-18 01:41:46 +02:00
|
|
|
error_code ec;
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2010-08-23 10:48:02 +02:00
|
|
|
boost::intrusive_ptr<file> file_handle;
|
2009-01-17 09:35:48 +01:00
|
|
|
int bytes_left = size;
|
2008-05-28 10:44:40 +02:00
|
|
|
int slot_size = static_cast<int>(m_files.piece_size(slot));
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
if (offset + bytes_left > slot_size)
|
|
|
|
bytes_left = slot_size - offset;
|
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
|
|
|
|
2011-05-08 11:04:59 +02:00
|
|
|
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
|
2007-05-09 22:16:18 +02:00
|
|
|
int counter = 0;
|
|
|
|
#endif
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
|
|
|
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);
|
2009-01-17 09:35:48 +01:00
|
|
|
int file_bytes_left;
|
|
|
|
for (;bytes_left > 0; ++file_iter, bytes_left -= file_bytes_left
|
|
|
|
, buf_pos += file_bytes_left)
|
2007-05-09 22:16:18 +02:00
|
|
|
{
|
2008-10-03 07:49:41 +02:00
|
|
|
TORRENT_ASSERT(file_iter != files().end());
|
|
|
|
TORRENT_ASSERT(buf_pos >= 0);
|
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
file_bytes_left = bytes_left;
|
|
|
|
if (file_offset + file_bytes_left > file_iter->size)
|
|
|
|
file_bytes_left = (std::max)(static_cast<int>(file_iter->size - file_offset), 0);
|
2008-10-03 07:49:41 +02:00
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
if (file_bytes_left == 0) continue;
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2011-05-08 11:04:59 +02:00
|
|
|
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
|
2008-10-03 07:49:41 +02:00
|
|
|
TORRENT_ASSERT(int(slices.size()) > counter);
|
|
|
|
size_type slice_size = slices[counter].size;
|
2009-01-17 09:35:48 +01:00
|
|
|
TORRENT_ASSERT(slice_size == file_bytes_left);
|
2010-11-29 06:44:29 +01:00
|
|
|
TORRENT_ASSERT((files().begin() + slices[counter].file_index)
|
|
|
|
== file_iter);
|
2008-10-03 07:49:41 +02:00
|
|
|
++counter;
|
2007-05-09 22:16:18 +02:00
|
|
|
#endif
|
2007-05-04 05:28:42 +02:00
|
|
|
|
2008-10-03 07:49:41 +02:00
|
|
|
if (file_iter->pad_file)
|
|
|
|
{
|
2009-01-17 09:35:48 +01:00
|
|
|
if (op.mode == file::read_only)
|
|
|
|
{
|
|
|
|
int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs);
|
|
|
|
TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs);
|
|
|
|
TORRENT_ASSERT(num_tmp_bufs <= num_bufs);
|
|
|
|
clear_bufs(tmp_bufs, num_tmp_bufs);
|
|
|
|
}
|
|
|
|
advance_bufs(current_buf, file_bytes_left);
|
|
|
|
TORRENT_ASSERT(count_bufs(current_buf, bytes_left - file_bytes_left) <= num_bufs);
|
2010-12-13 17:47:12 +01:00
|
|
|
file_offset = 0;
|
2008-10-03 07:49:41 +02:00
|
|
|
continue;
|
|
|
|
}
|
2007-05-04 05:28:42 +02:00
|
|
|
|
2008-10-03 07:49:41 +02:00
|
|
|
error_code ec;
|
2010-11-29 06:44:29 +01:00
|
|
|
file_handle = open_file(file_iter, op.mode, ec);
|
2011-02-12 23:00:08 +01:00
|
|
|
if ((op.mode == file::read_write) && ec == boost::system::errc::no_such_file_or_directory)
|
|
|
|
{
|
|
|
|
// this means the directory the file is in doesn't exist.
|
|
|
|
// so create it
|
|
|
|
ec.clear();
|
|
|
|
std::string path = combine_path(m_save_path, files().file_path(*file_iter));
|
|
|
|
create_directories(parent_path(path), ec);
|
|
|
|
// if the directory creation failed, don't try to open the file again
|
|
|
|
// but actually just fail
|
|
|
|
if (!ec) file_handle = open_file(file_iter, op.mode, ec);
|
|
|
|
}
|
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
if (!file_handle || ec)
|
2008-10-03 07:49:41 +02:00
|
|
|
{
|
2010-11-25 00:49:22 +01:00
|
|
|
std::string path = combine_path(m_save_path, files().file_path(*file_iter));
|
2009-01-21 08:31:49 +01:00
|
|
|
TORRENT_ASSERT(ec);
|
2008-10-03 07:49:41 +02:00
|
|
|
set_error(path, ec);
|
|
|
|
return -1;
|
2006-08-10 21:18:11 +02:00
|
|
|
}
|
2008-10-03 07:49:41 +02:00
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs);
|
|
|
|
TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs);
|
2009-01-12 19:08:18 +01:00
|
|
|
TORRENT_ASSERT(num_tmp_bufs <= num_bufs);
|
2009-01-17 09:35:48 +01:00
|
|
|
int bytes_transferred = 0;
|
|
|
|
// if the file is opened in no_buffer mode, and the
|
|
|
|
// read is unaligned, we need to fall back on a slow
|
|
|
|
// special read that reads aligned buffers and copies
|
|
|
|
// it into the one supplied
|
2010-11-25 00:49:22 +01:00
|
|
|
size_type adjusted_offset = files().file_base(*file_iter) + file_offset;
|
2009-01-17 09:35:48 +01:00
|
|
|
if ((file_handle->open_mode() & file::no_buffer)
|
2010-11-25 00:49:22 +01:00
|
|
|
&& ((adjusted_offset & (file_handle->pos_alignment()-1)) != 0
|
2009-02-22 21:18:42 +01:00
|
|
|
|| (uintptr_t(tmp_bufs->iov_base) & (file_handle->buf_alignment()-1)) != 0))
|
2009-01-17 09:35:48 +01:00
|
|
|
{
|
2011-02-21 06:24:41 +01:00
|
|
|
bytes_transferred = (int)(this->*op.unaligned_op)(file_handle, adjusted_offset
|
2010-11-25 00:49:22 +01:00
|
|
|
, tmp_bufs, num_tmp_bufs, ec);
|
2011-06-07 10:18:51 +02:00
|
|
|
if (op.mode == file::read_write
|
|
|
|
&& adjusted_offset + bytes_transferred == file_iter->size
|
|
|
|
&& file_handle->pos_alignment() > 0)
|
|
|
|
{
|
|
|
|
// we were writing, and we just wrote the last block of the file
|
|
|
|
// we likely wrote a bit too much, since we're restricted to
|
|
|
|
// a specific alignment for writes. Make sure to truncate the size
|
|
|
|
|
|
|
|
// TODO: what if file_base is used to merge several virtual files
|
|
|
|
// into a single physical file?
|
|
|
|
file_handle->set_size(file_iter->size, ec);
|
|
|
|
}
|
2009-01-17 09:35:48 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-11-25 00:49:22 +01:00
|
|
|
bytes_transferred = (int)((*file_handle).*op.regular_op)(adjusted_offset
|
|
|
|
, tmp_bufs, num_tmp_bufs, ec);
|
2009-01-17 09:35:48 +01:00
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
file_offset = 0;
|
2008-10-03 07:49:41 +02:00
|
|
|
|
2009-01-14 04:05:35 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
2010-11-25 00:49:22 +01:00
|
|
|
set_error(combine_path(m_save_path, files().file_path(*file_iter)), ec);
|
2009-01-14 04:05:35 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
if (file_bytes_left != bytes_transferred)
|
2011-04-28 06:31:45 +02:00
|
|
|
return bytes_transferred;
|
2009-05-21 18:15:05 +02:00
|
|
|
|
2009-01-17 09:35:48 +01:00
|
|
|
advance_bufs(current_buf, bytes_transferred);
|
|
|
|
TORRENT_ASSERT(count_bufs(current_buf, bytes_left - file_bytes_left) <= num_bufs);
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
2009-01-17 09:35:48 +01:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// these functions are inefficient, but should be fairly uncommon. The read
|
|
|
|
// case happens if unaligned files are opened in no_buffer mode or if clients
|
|
|
|
// makes unaligned requests (and the disk cache is disabled or fully utilized
|
|
|
|
// for write cache).
|
|
|
|
|
|
|
|
// they read an unaligned buffer from a file that requires aligned access
|
2009-01-21 08:31:49 +01:00
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
size_type default_storage::read_unaligned(boost::intrusive_ptr<file> const& file_handle
|
2009-01-17 09:35:48 +01:00
|
|
|
, size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec)
|
|
|
|
{
|
2009-01-21 08:31:49 +01:00
|
|
|
const int pos_align = file_handle->pos_alignment()-1;
|
|
|
|
const int size_align = file_handle->size_alignment()-1;
|
|
|
|
|
|
|
|
const int size = bufs_size(bufs, num_bufs);
|
|
|
|
const int start_adjust = file_offset & pos_align;
|
2011-02-21 06:24:41 +01:00
|
|
|
TORRENT_ASSERT(start_adjust == (file_offset % file_handle->pos_alignment()));
|
|
|
|
const size_type aligned_start = file_offset - start_adjust;
|
2009-01-21 08:31:49 +01:00
|
|
|
const int aligned_size = ((size+start_adjust) & size_align)
|
|
|
|
? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust;
|
|
|
|
TORRENT_ASSERT((aligned_size & size_align) == 0);
|
|
|
|
|
2011-02-22 03:53:26 +01:00
|
|
|
// allocate a temporary, aligned, buffer
|
|
|
|
aligned_holder aligned_buf(aligned_size);
|
|
|
|
file::iovec_t b = {aligned_buf.get(), aligned_size};
|
2009-01-21 08:31:49 +01:00
|
|
|
size_type ret = file_handle->readv(aligned_start, &b, 1, ec);
|
2011-02-22 03:53:26 +01:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(ec);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-06-07 10:18:51 +02:00
|
|
|
if (ret - start_adjust < size) return (std::max)(ret - start_adjust, size_type(0));
|
2011-02-22 03:53:26 +01:00
|
|
|
|
|
|
|
char* read_buf = aligned_buf.get() + start_adjust;
|
2009-01-21 08:31:49 +01:00
|
|
|
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i)
|
|
|
|
{
|
|
|
|
memcpy(i->iov_base, read_buf, i->iov_len);
|
|
|
|
read_buf += i->iov_len;
|
|
|
|
}
|
2011-02-22 03:53:26 +01:00
|
|
|
|
2009-01-21 08:31:49 +01:00
|
|
|
return size;
|
2009-01-17 09:35:48 +01:00
|
|
|
}
|
|
|
|
|
2011-02-22 03:53:26 +01:00
|
|
|
// this is the really expensive one. To write unaligned, we need to read
|
|
|
|
// an aligned block, overlay the unaligned buffer, and then write it back
|
2011-04-17 00:58:11 +02:00
|
|
|
size_type default_storage::write_unaligned(boost::intrusive_ptr<file> const& file_handle
|
2009-01-17 09:35:48 +01:00
|
|
|
, size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec)
|
|
|
|
{
|
2011-02-22 03:53:26 +01:00
|
|
|
const int pos_align = file_handle->pos_alignment()-1;
|
|
|
|
const int size_align = file_handle->size_alignment()-1;
|
|
|
|
|
|
|
|
const int size = bufs_size(bufs, num_bufs);
|
|
|
|
const int start_adjust = file_offset & pos_align;
|
|
|
|
TORRENT_ASSERT(start_adjust == (file_offset % file_handle->pos_alignment()));
|
|
|
|
const size_type aligned_start = file_offset - start_adjust;
|
|
|
|
const int aligned_size = ((size+start_adjust) & size_align)
|
|
|
|
? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust;
|
|
|
|
TORRENT_ASSERT((aligned_size & size_align) == 0);
|
|
|
|
|
|
|
|
// allocate a temporary, aligned, buffer
|
|
|
|
aligned_holder aligned_buf(aligned_size);
|
|
|
|
file::iovec_t b = {aligned_buf.get(), aligned_size};
|
|
|
|
size_type ret = file_handle->readv(aligned_start, &b, 1, ec);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(ec);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, we read the portion of the file. Now, overlay the buffer we're writing
|
|
|
|
|
|
|
|
char* write_buf = aligned_buf.get() + start_adjust;
|
|
|
|
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i)
|
|
|
|
{
|
|
|
|
memcpy(write_buf, i->iov_base, i->iov_len);
|
|
|
|
write_buf += i->iov_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the buffer back to disk
|
|
|
|
ret = file_handle->writev(aligned_start, &b, 1, ec);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(ec);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-06-07 10:18:51 +02:00
|
|
|
if (ret - start_adjust < size) return (std::max)(ret - start_adjust, size_type(0));
|
2011-02-22 03:53:26 +01:00
|
|
|
return size;
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
int default_storage::write(
|
2004-01-25 19:18:36 +01:00
|
|
|
const char* buf
|
|
|
|
, int slot
|
|
|
|
, int offset
|
|
|
|
, int size)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-08-30 09:38:52 +02:00
|
|
|
file::iovec_t b = { (file::iovec_base_t)buf, size };
|
2009-01-03 09:11:31 +01:00
|
|
|
return writev(&b, slot, offset, 1);
|
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
int default_storage::read(
|
2009-01-03 09:11:31 +01:00
|
|
|
char* buf
|
|
|
|
, int slot
|
|
|
|
, int offset
|
|
|
|
, int size)
|
|
|
|
{
|
2009-08-30 09:38:52 +02:00
|
|
|
file::iovec_t b = { (file::iovec_base_t)buf, size };
|
2009-01-03 09:11:31 +01:00
|
|
|
return readv(&b, slot, offset, 1);
|
|
|
|
}
|
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
boost::intrusive_ptr<file> default_storage::open_file(file_storage::iterator fe, int mode
|
2010-11-29 06:44:29 +01:00
|
|
|
, error_code& ec) const
|
2010-01-09 19:40:05 +01:00
|
|
|
{
|
|
|
|
int cache_setting = m_settings ? settings().disk_io_write_mode : 0;
|
|
|
|
if (cache_setting == session_settings::disable_os_cache
|
|
|
|
|| (cache_setting == session_settings::disable_os_cache_for_aligned_files
|
2010-11-29 06:44:29 +01:00
|
|
|
&& ((fe->offset + files().file_base(*fe)) & (m_page_size-1)) == 0))
|
2010-01-09 19:40:05 +01:00
|
|
|
mode |= file::no_buffer;
|
2011-06-09 08:08:24 +02:00
|
|
|
bool lock_files = m_settings ? settings().lock_files : false;
|
|
|
|
if (lock_files) mode |= file::lock_file;
|
2010-01-09 19:40:05 +01:00
|
|
|
if (!m_allocate_files) mode |= file::sparse;
|
2010-02-02 20:44:52 +01:00
|
|
|
if (m_settings && settings().no_atime_storage) mode |= file::no_atime;
|
2010-01-09 19:40:05 +01:00
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
return m_pool.open_file(const_cast<default_storage*>(this), m_save_path, fe, files(), mode, ec);
|
2010-01-09 19:40:05 +01:00
|
|
|
}
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
storage_interface* default_storage_constructor(file_storage const& fs
|
2010-07-15 03:14:36 +02:00
|
|
|
, file_storage const* mapped, std::string const& path, file_pool& fp
|
|
|
|
, std::vector<boost::uint8_t> const& file_prio)
|
2007-03-16 06:29:23 +01:00
|
|
|
{
|
2011-04-17 00:58:11 +02:00
|
|
|
return new default_storage(fs, mapped, path, fp, file_prio);
|
2007-03-16 06:29:23 +01:00
|
|
|
}
|
2003-12-09 09:49:49 +01:00
|
|
|
|
2011-04-17 00:58:11 +02:00
|
|
|
int disabled_storage::readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs)
|
2009-08-02 08:40:45 +02:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
2011-04-17 00:58:11 +02:00
|
|
|
disk_buffer_pool* pool = disk_pool();
|
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " read "
|
|
|
|
<< physical_offset(slot, offset) << std::endl;
|
|
|
|
}
|
2009-08-02 08:40:45 +02:00
|
|
|
#endif
|
2011-04-17 00:58:11 +02:00
|
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
|
|
|
ret += bufs[i].iov_len;
|
2009-08-02 08:40:45 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
2011-04-17 00:58:11 +02:00
|
|
|
if (pool)
|
2009-08-02 08:40:45 +02:00
|
|
|
{
|
2011-04-17 00:58:11 +02:00
|
|
|
pool->m_disk_access_log << log_time() << " read_end "
|
|
|
|
<< (physical_offset(slot, offset) + ret) << std::endl;
|
|
|
|
}
|
2009-08-02 08:40:45 +02:00
|
|
|
#endif
|
2011-04-17 00:58:11 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int disabled_storage::writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs)
|
|
|
|
{
|
2009-08-02 08:40:45 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
2011-04-17 00:58:11 +02:00
|
|
|
disk_buffer_pool* pool = disk_pool();
|
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " write "
|
|
|
|
<< physical_offset(slot, offset) << std::endl;
|
|
|
|
}
|
2009-08-02 08:40:45 +02:00
|
|
|
#endif
|
2011-04-17 00:58:11 +02:00
|
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
|
|
|
ret += bufs[i].iov_len;
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
if (pool)
|
|
|
|
{
|
|
|
|
pool->m_disk_access_log << log_time() << " write_end "
|
|
|
|
<< (physical_offset(slot, offset) + ret) << std::endl;
|
2009-08-02 08:40:45 +02:00
|
|
|
}
|
2011-04-17 00:58:11 +02:00
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
}
|
2009-08-02 08:40:45 +02:00
|
|
|
|
|
|
|
storage_interface* disabled_storage_constructor(file_storage const& fs
|
2010-07-15 03:14:36 +02:00
|
|
|
, file_storage const* mapped, std::string const& path, file_pool& fp
|
|
|
|
, std::vector<boost::uint8_t> const&)
|
2009-08-02 08:40:45 +02:00
|
|
|
{
|
|
|
|
return new disabled_storage(fs.piece_length());
|
|
|
|
}
|
|
|
|
|
2003-12-09 09:49:49 +01:00
|
|
|
// -- piece_manager -----------------------------------------------------
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
piece_manager::piece_manager(
|
|
|
|
boost::shared_ptr<void> const& torrent
|
2008-05-28 10:44:40 +02:00
|
|
|
, boost::intrusive_ptr<torrent_info const> info
|
2009-10-26 02:29:39 +01:00
|
|
|
, std::string const& save_path
|
2007-03-16 06:29:23 +01:00
|
|
|
, file_pool& fp
|
2007-06-10 22:46:09 +02:00
|
|
|
, disk_io_thread& io
|
2008-03-08 07:06:31 +01:00
|
|
|
, storage_constructor_type sc
|
2010-07-15 03:14:36 +02:00
|
|
|
, storage_mode_t sm
|
|
|
|
, std::vector<boost::uint8_t> const& file_prio)
|
2008-05-28 10:44:40 +02:00
|
|
|
: m_info(info)
|
|
|
|
, m_files(m_info->files())
|
2009-06-24 11:09:35 +02:00
|
|
|
, m_storage(sc(m_info->orig_files(), &m_info->files() != &m_info->orig_files()
|
2010-07-15 03:14:36 +02:00
|
|
|
? &m_info->files() : 0, save_path, fp, file_prio))
|
2008-03-08 07:06:31 +01:00
|
|
|
, m_storage_mode(sm)
|
2004-10-18 12:46:55 +02:00
|
|
|
, m_save_path(complete(save_path))
|
2007-10-30 07:50:08 +01:00
|
|
|
, m_state(state_none)
|
2007-10-08 22:01:36 +02:00
|
|
|
, m_current_slot(0)
|
|
|
|
, m_out_of_place(false)
|
|
|
|
, m_scratch_piece(-1)
|
2009-05-31 21:44:56 +02:00
|
|
|
, m_last_piece(-1)
|
2007-09-01 05:00:31 +02:00
|
|
|
, m_storage_constructor(sc)
|
2007-06-10 22:46:09 +02:00
|
|
|
, m_io_thread(io)
|
|
|
|
, m_torrent(torrent)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-01-21 08:31:49 +01:00
|
|
|
m_storage->m_disk_pool = &m_io_thread;
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
|
2010-01-09 19:40:05 +01:00
|
|
|
void piece_manager::finalize_file(int index)
|
|
|
|
{ m_storage->finalize_file(index); }
|
|
|
|
|
2004-01-04 13:54:38 +01:00
|
|
|
piece_manager::~piece_manager()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-01-09 19:40:05 +01:00
|
|
|
void piece_manager::async_finalize_file(int file)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::finalize_file;
|
|
|
|
j.piece = file;
|
|
|
|
boost::function<void(int, disk_io_job const&)> empty;
|
|
|
|
m_io_thread.add_job(j, empty);
|
|
|
|
}
|
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
void piece_manager::async_save_resume_data(
|
|
|
|
boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::save_resume_data;
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2008-07-18 17:31:22 +02:00
|
|
|
void piece_manager::async_clear_read_cache(
|
|
|
|
boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::clear_read_cache;
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2007-06-11 23:24:14 +02:00
|
|
|
void piece_manager::async_release_files(
|
|
|
|
boost::function<void(int, disk_io_job const&)> const& handler)
|
2004-12-21 13:30:09 +01:00
|
|
|
{
|
2007-06-10 22:46:09 +02:00
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::release_files;
|
2007-06-11 23:24:14 +02:00
|
|
|
m_io_thread.add_job(j, handler);
|
2004-12-21 13:30:09 +01:00
|
|
|
}
|
|
|
|
|
2008-11-17 02:19:46 +01:00
|
|
|
void piece_manager::abort_disk_io()
|
|
|
|
{
|
|
|
|
m_io_thread.stop(this);
|
|
|
|
}
|
|
|
|
|
2007-10-13 05:33:33 +02:00
|
|
|
void piece_manager::async_delete_files(
|
|
|
|
boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::delete_files;
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
void piece_manager::async_move_storage(std::string const& p
|
2007-06-11 23:24:14 +02:00
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler)
|
2004-12-21 13:30:09 +01:00
|
|
|
{
|
2007-06-10 22:46:09 +02:00
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::move_storage;
|
2009-10-26 02:29:39 +01:00
|
|
|
j.str = p;
|
2007-06-11 23:24:14 +02:00
|
|
|
m_io_thread.add_job(j, handler);
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
void piece_manager::async_check_fastresume(lazy_entry const* resume_data
|
2008-03-08 07:06:31 +01:00
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(resume_data != 0);
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::check_fastresume;
|
|
|
|
j.buffer = (char*)resume_data;
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
void piece_manager::async_rename_file(int index, std::string const& name
|
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.piece = index;
|
|
|
|
j.str = name;
|
|
|
|
j.action = disk_io_job::rename_file;
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
void piece_manager::async_check_files(
|
|
|
|
boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::check_files;
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2009-02-03 08:46:24 +01:00
|
|
|
void piece_manager::async_read_and_hash(
|
|
|
|
peer_request const& r
|
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler
|
2010-01-31 20:14:00 +01:00
|
|
|
, int cache_expiry)
|
2009-02-03 08:46:24 +01:00
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::read_and_hash;
|
|
|
|
j.piece = r.piece;
|
|
|
|
j.offset = r.start;
|
|
|
|
j.buffer_size = r.length;
|
|
|
|
j.buffer = 0;
|
2010-01-31 20:14:00 +01:00
|
|
|
j.cache_min_time = cache_expiry;
|
2009-02-03 08:46:24 +01:00
|
|
|
TORRENT_ASSERT(r.length <= 16 * 1024);
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
#ifdef TORRENT_DEBUG
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
2009-02-03 08:46:24 +01:00
|
|
|
// if this assert is hit, it suggests
|
|
|
|
// that check_files was not successful
|
|
|
|
TORRENT_ASSERT(slot_for(r.piece) >= 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:45:42 +01:00
|
|
|
void piece_manager::async_cache(int piece
|
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler
|
2010-01-31 20:14:00 +01:00
|
|
|
, int cache_expiry)
|
2010-01-15 17:45:42 +01:00
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::cache_piece;
|
|
|
|
j.piece = piece;
|
|
|
|
j.offset = 0;
|
|
|
|
j.buffer_size = 0;
|
|
|
|
j.buffer = 0;
|
2010-01-31 20:14:00 +01:00
|
|
|
j.cache_min_time = cache_expiry;
|
2010-01-15 17:45:42 +01:00
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
void piece_manager::async_read(
|
|
|
|
peer_request const& r
|
2007-09-11 19:45:20 +02:00
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler
|
2010-01-31 20:14:00 +01:00
|
|
|
, int cache_line_size
|
|
|
|
, int cache_expiry)
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::read;
|
|
|
|
j.piece = r.piece;
|
|
|
|
j.offset = r.start;
|
|
|
|
j.buffer_size = r.length;
|
2008-04-10 11:11:54 +02:00
|
|
|
j.buffer = 0;
|
2010-01-31 20:14:00 +01:00
|
|
|
j.max_cache_line = cache_line_size;
|
|
|
|
j.cache_min_time = cache_expiry;
|
|
|
|
|
2007-09-11 19:45:20 +02:00
|
|
|
// if a buffer is not specified, only one block can be read
|
|
|
|
// since that is the size of the pool allocator's buffers
|
2008-04-10 11:11:54 +02:00
|
|
|
TORRENT_ASSERT(r.length <= 16 * 1024);
|
2007-06-10 22:46:09 +02:00
|
|
|
m_io_thread.add_job(j, handler);
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
2008-05-28 10:44:40 +02:00
|
|
|
// if this assert is hit, it suggests
|
|
|
|
// that check_files was not successful
|
2008-01-07 05:47:20 +01:00
|
|
|
TORRENT_ASSERT(slot_for(r.piece) >= 0);
|
|
|
|
#endif
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
|
2011-03-16 08:45:51 +01:00
|
|
|
int piece_manager::async_write(
|
2007-06-10 22:46:09 +02:00
|
|
|
peer_request const& r
|
2008-04-10 12:03:23 +02:00
|
|
|
, disk_buffer_holder& buffer
|
2007-06-10 22:46:09 +02:00
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(r.length <= 16 * 1024);
|
2008-04-10 12:03:23 +02:00
|
|
|
// the buffer needs to be allocated through the io_thread
|
2008-05-05 08:25:22 +02:00
|
|
|
TORRENT_ASSERT(m_io_thread.is_disk_buffer(buffer.get()));
|
2007-06-10 22:46:09 +02:00
|
|
|
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::write;
|
|
|
|
j.piece = r.piece;
|
|
|
|
j.offset = r.start;
|
|
|
|
j.buffer_size = r.length;
|
2008-05-05 08:25:22 +02:00
|
|
|
j.buffer = buffer.get();
|
2011-03-16 08:45:51 +01:00
|
|
|
int queue_size = m_io_thread.add_job(j, handler);
|
2008-04-10 12:03:23 +02:00
|
|
|
buffer.release();
|
2011-03-16 08:45:51 +01:00
|
|
|
|
|
|
|
return queue_size;
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void piece_manager::async_hash(int piece
|
|
|
|
, boost::function<void(int, disk_io_job const&)> const& handler)
|
|
|
|
{
|
|
|
|
disk_io_job j;
|
|
|
|
j.storage = this;
|
|
|
|
j.action = disk_io_job::hash;
|
|
|
|
j.piece = piece;
|
|
|
|
|
|
|
|
m_io_thread.add_job(j, handler);
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string piece_manager::save_path() const
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock l(m_mutex);
|
2007-06-10 22:46:09 +02:00
|
|
|
return m_save_path;
|
|
|
|
}
|
|
|
|
|
2011-03-20 02:19:14 +01:00
|
|
|
sha1_hash piece_manager::hash_for_piece_impl(int piece, int* readback)
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
2010-10-18 09:38:14 +02:00
|
|
|
TORRENT_ASSERT(!m_storage->error());
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
partial_hash ph;
|
|
|
|
|
|
|
|
std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece);
|
|
|
|
if (i != m_piece_hasher.end())
|
|
|
|
{
|
|
|
|
ph = i->second;
|
|
|
|
m_piece_hasher.erase(i);
|
|
|
|
}
|
2007-04-30 03:06:29 +02:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
int slot = slot_for(piece);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(slot != has_no_slot);
|
2011-03-20 02:19:14 +01:00
|
|
|
int read = hash_for_slot(slot, ph, m_files.piece_size(piece));
|
|
|
|
if (readback) *readback = read;
|
2009-05-21 18:15:05 +02:00
|
|
|
if (m_storage->error()) return sha1_hash(0);
|
|
|
|
return ph.h.final();
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
int piece_manager::move_storage_impl(std::string const& save_path)
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
|
|
|
if (m_storage->move_storage(save_path))
|
|
|
|
{
|
2009-10-26 02:29:39 +01:00
|
|
|
m_save_path = complete(save_path);
|
2008-06-15 22:52:46 +02:00
|
|
|
return 0;
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
2008-06-15 22:52:46 +02:00
|
|
|
return -1;
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
2007-10-13 05:33:33 +02:00
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
void piece_manager::write_resume_data(entry& rd) const
|
2003-12-25 13:11:31 +01:00
|
|
|
{
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock lock(m_mutex);
|
2004-01-25 22:37:19 +01:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
m_storage->write_resume_data(rd);
|
|
|
|
|
2007-10-09 02:25:01 +02:00
|
|
|
if (m_storage_mode == storage_mode_compact)
|
2003-12-27 02:34:50 +01:00
|
|
|
{
|
2008-04-13 20:54:36 +02:00
|
|
|
entry::list_type& slots = rd["slots"].list();
|
2008-03-08 07:06:31 +01:00
|
|
|
slots.clear();
|
2007-10-09 02:25:01 +02:00
|
|
|
std::vector<int>::const_reverse_iterator last;
|
|
|
|
for (last = m_slot_to_piece.rbegin();
|
|
|
|
last != m_slot_to_piece.rend(); ++last)
|
|
|
|
{
|
2008-03-08 07:06:31 +01:00
|
|
|
if (*last != unallocated) break;
|
2007-10-09 02:25:01 +02:00
|
|
|
}
|
2003-12-27 02:34:50 +01:00
|
|
|
|
2007-10-09 02:25:01 +02:00
|
|
|
for (std::vector<int>::const_iterator i =
|
|
|
|
m_slot_to_piece.begin();
|
|
|
|
i != last.base(); ++i)
|
|
|
|
{
|
2008-03-08 07:06:31 +01:00
|
|
|
slots.push_back((*i >= 0) ? *i : unassigned);
|
2007-10-09 02:25:01 +02:00
|
|
|
}
|
|
|
|
}
|
2008-06-23 20:31:52 +02:00
|
|
|
|
|
|
|
rd["allocation"] = m_storage_mode == storage_mode_sparse?"sparse"
|
|
|
|
:m_storage_mode == storage_mode_allocate?"full":"compact";
|
2003-12-25 13:11:31 +01:00
|
|
|
}
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
void piece_manager::mark_failed(int piece_index)
|
2004-01-12 04:05:10 +01:00
|
|
|
{
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock lock(m_mutex);
|
|
|
|
|
2004-01-25 22:37:19 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
if (m_storage_mode != storage_mode_compact) return;
|
2004-01-13 04:08:59 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
|
2004-01-13 04:08:59 +01:00
|
|
|
int slot_index = m_piece_to_slot[piece_index];
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(slot_index >= 0);
|
2004-01-12 04:05:10 +01:00
|
|
|
|
2004-01-16 17:19:27 +01:00
|
|
|
m_slot_to_piece[slot_index] = unassigned;
|
|
|
|
m_piece_to_slot[piece_index] = has_no_slot;
|
2004-01-13 04:08:59 +01:00
|
|
|
m_free_slots.push_back(slot_index);
|
2004-01-12 04:05:10 +01:00
|
|
|
}
|
|
|
|
|
2011-04-26 09:03:05 +02:00
|
|
|
void piece_manager::hint_read_impl(int piece_index, int offset, int size)
|
|
|
|
{
|
|
|
|
m_last_piece = piece_index;
|
|
|
|
int slot = slot_for(piece_index);
|
2011-04-26 10:08:54 +02:00
|
|
|
if (slot <= 0) return;
|
2011-04-26 09:03:05 +02:00
|
|
|
m_storage->hint_read(slot, offset, size);
|
|
|
|
}
|
|
|
|
|
2008-04-05 23:18:27 +02:00
|
|
|
int piece_manager::read_impl(
|
2009-01-03 09:11:31 +01:00
|
|
|
file::iovec_t* bufs
|
2007-03-16 06:29:23 +01:00
|
|
|
, int piece_index
|
|
|
|
, int offset
|
2009-01-03 09:11:31 +01:00
|
|
|
, int num_bufs)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-01-03 09:11:31 +01:00
|
|
|
TORRENT_ASSERT(bufs);
|
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);
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece_index;
|
2007-10-08 22:01:36 +02:00
|
|
|
int slot = slot_for(piece_index);
|
2009-01-03 09:11:31 +01:00
|
|
|
return m_storage->readv(bufs, slot, offset, num_bufs);
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
|
2008-04-05 23:18:27 +02:00
|
|
|
int piece_manager::write_impl(
|
2009-01-03 09:11:31 +01:00
|
|
|
file::iovec_t* bufs
|
2003-12-09 09:49:49 +01:00
|
|
|
, int piece_index
|
2004-01-25 19:18:36 +01:00
|
|
|
, int offset
|
2009-01-03 09:11:31 +01:00
|
|
|
, int num_bufs)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-01-03 09:11:31 +01:00
|
|
|
TORRENT_ASSERT(bufs);
|
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);
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces());
|
2007-06-10 22:46:09 +02:00
|
|
|
|
2009-01-03 09:11:31 +01:00
|
|
|
int size = bufs_size(bufs, num_bufs);
|
|
|
|
|
|
|
|
file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
|
|
|
std::copy(bufs, bufs + num_bufs, iov);
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece_index;
|
2008-04-13 00:08:07 +02:00
|
|
|
int slot = allocate_slot_for_piece(piece_index);
|
2009-01-03 09:11:31 +01:00
|
|
|
int ret = m_storage->writev(bufs, slot, offset, num_bufs);
|
2008-04-13 00:08:07 +02:00
|
|
|
// only save the partial hash if the write succeeds
|
|
|
|
if (ret != size) return ret;
|
|
|
|
|
2011-05-05 06:02:10 +02:00
|
|
|
if (m_storage->settings().disable_hash_checks) return ret;
|
|
|
|
|
2009-04-04 11:52:25 +02:00
|
|
|
#if defined TORRENT_PARTIAL_HASH_LOG && TORRENT_USE_IOSTREAM
|
2008-09-17 01:32:27 +02:00
|
|
|
std::ofstream out("partial_hash.log", std::ios::app);
|
|
|
|
#endif
|
2008-04-13 00:08:07 +02:00
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
if (offset == 0)
|
|
|
|
{
|
|
|
|
partial_hash& ph = m_piece_hasher[piece_index];
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(ph.offset == 0);
|
2007-06-10 22:46:09 +02:00
|
|
|
ph.offset = size;
|
2009-01-03 09:11:31 +01:00
|
|
|
|
|
|
|
for (file::iovec_t* i = iov, *end(iov + num_bufs); i < end; ++i)
|
|
|
|
ph.h.update((char const*)i->iov_base, i->iov_len);
|
|
|
|
|
2009-04-04 11:52:25 +02:00
|
|
|
#if defined TORRENT_PARTIAL_HASH_LOG && TORRENT_USE_IOSTREAM
|
2008-04-13 00:08:07 +02:00
|
|
|
out << time_now_string() << " NEW ["
|
|
|
|
" s: " << this
|
|
|
|
<< " p: " << piece_index
|
|
|
|
<< " off: " << offset
|
|
|
|
<< " size: " << size
|
|
|
|
<< " entries: " << m_piece_hasher.size()
|
|
|
|
<< " ]" << std::endl;
|
2008-09-17 01:32:27 +02:00
|
|
|
#endif
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece_index);
|
|
|
|
if (i != m_piece_hasher.end())
|
|
|
|
{
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(i->second.offset > 0);
|
2008-02-18 04:07:14 +01:00
|
|
|
int hash_offset = i->second.offset;
|
|
|
|
TORRENT_ASSERT(offset >= hash_offset);
|
|
|
|
#endif
|
2007-06-10 22:46:09 +02:00
|
|
|
if (offset == i->second.offset)
|
|
|
|
{
|
2008-09-17 01:32:27 +02:00
|
|
|
#ifdef TORRENT_PARTIAL_HASH_LOG
|
2008-04-13 00:08:07 +02:00
|
|
|
out << time_now_string() << " UPDATING ["
|
|
|
|
" s: " << this
|
|
|
|
<< " p: " << piece_index
|
|
|
|
<< " off: " << offset
|
|
|
|
<< " size: " << size
|
|
|
|
<< " entries: " << m_piece_hasher.size()
|
|
|
|
<< " ]" << std::endl;
|
2008-09-17 01:32:27 +02:00
|
|
|
#endif
|
2009-01-03 09:11:31 +01:00
|
|
|
for (file::iovec_t* b = iov, *end(iov + num_bufs); b < end; ++b)
|
|
|
|
{
|
|
|
|
i->second.h.update((char const*)b->iov_base, b->iov_len);
|
|
|
|
i->second.offset += b->iov_len;
|
|
|
|
}
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
2008-09-17 01:32:27 +02:00
|
|
|
#ifdef TORRENT_PARTIAL_HASH_LOG
|
|
|
|
else
|
2008-04-13 00:08:07 +02:00
|
|
|
{
|
|
|
|
out << time_now_string() << " SKIPPING (out of order) ["
|
|
|
|
" s: " << this
|
|
|
|
<< " p: " << piece_index
|
|
|
|
<< " off: " << offset
|
|
|
|
<< " size: " << size
|
|
|
|
<< " entries: " << m_piece_hasher.size()
|
|
|
|
<< " ]" << std::endl;
|
|
|
|
}
|
2008-09-17 01:32:27 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef TORRENT_PARTIAL_HASH_LOG
|
|
|
|
else
|
2008-04-13 00:08:07 +02:00
|
|
|
{
|
|
|
|
out << time_now_string() << " SKIPPING (no entry) ["
|
|
|
|
" s: " << this
|
|
|
|
<< " p: " << piece_index
|
|
|
|
<< " off: " << offset
|
|
|
|
<< " size: " << size
|
|
|
|
<< " entries: " << m_piece_hasher.size()
|
|
|
|
<< " ]" << std::endl;
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
2008-09-17 01:32:27 +02:00
|
|
|
#endif
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
|
2008-04-13 00:08:07 +02:00
|
|
|
return ret;
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
|
2009-09-05 09:21:10 +02:00
|
|
|
size_type piece_manager::physical_offset(
|
|
|
|
int piece_index
|
|
|
|
, int offset)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(offset >= 0);
|
|
|
|
TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces());
|
|
|
|
|
|
|
|
int slot = slot_for(piece_index);
|
|
|
|
// we may not have a slot for this piece yet.
|
|
|
|
// assume there is no re-mapping of slots
|
|
|
|
if (slot < 0) slot = piece_index;
|
|
|
|
return m_storage->physical_offset(slot, offset);
|
|
|
|
}
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
int piece_manager::identify_data(
|
2009-05-21 18:15:05 +02:00
|
|
|
sha1_hash const& large_hash
|
|
|
|
, sha1_hash const& small_hash
|
2008-03-08 07:06:31 +01:00
|
|
|
, int current_slot)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2006-11-15 22:39:58 +01:00
|
|
|
// INVARIANT_CHECK;
|
2004-01-25 19:18:36 +01:00
|
|
|
typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
|
|
|
|
map_iter begin1;
|
|
|
|
map_iter end1;
|
|
|
|
map_iter begin2;
|
|
|
|
map_iter end2;
|
|
|
|
|
|
|
|
// makes the lookups for the small digest and the large digest
|
2008-03-08 07:06:31 +01:00
|
|
|
boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash);
|
|
|
|
boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash);
|
2004-01-25 19:18:36 +01:00
|
|
|
|
|
|
|
// copy all potential piece indices into this vector
|
|
|
|
std::vector<int> matching_pieces;
|
|
|
|
for (map_iter i = begin1; i != end1; ++i)
|
|
|
|
matching_pieces.push_back(i->second);
|
|
|
|
for (map_iter i = begin2; i != end2; ++i)
|
|
|
|
matching_pieces.push_back(i->second);
|
|
|
|
|
|
|
|
// no piece matched the data in the slot
|
|
|
|
if (matching_pieces.empty())
|
2004-01-27 18:48:58 +01:00
|
|
|
return unassigned;
|
2004-01-25 19:18:36 +01:00
|
|
|
|
|
|
|
// ------------------------------------------
|
|
|
|
// CHECK IF THE PIECE IS IN ITS CORRECT PLACE
|
|
|
|
// ------------------------------------------
|
|
|
|
|
|
|
|
if (std::find(
|
|
|
|
matching_pieces.begin()
|
|
|
|
, matching_pieces.end()
|
|
|
|
, current_slot) != matching_pieces.end())
|
|
|
|
{
|
2007-06-10 22:46:09 +02:00
|
|
|
// the current slot is among the matching pieces, so
|
|
|
|
// we will assume that the piece is in the right place
|
2004-01-25 19:18:36 +01:00
|
|
|
const int piece_index = current_slot;
|
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
int other_slot = m_piece_to_slot[piece_index];
|
|
|
|
if (other_slot >= 0)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
|
|
|
// we have already found a piece with
|
|
|
|
// this index.
|
|
|
|
|
|
|
|
// take one of the other matching pieces
|
|
|
|
// that hasn't already been assigned
|
|
|
|
int other_piece = -1;
|
|
|
|
for (std::vector<int>::iterator i = matching_pieces.begin();
|
2005-11-01 19:30:39 +01:00
|
|
|
i != matching_pieces.end(); ++i)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2008-03-08 07:06:31 +01:00
|
|
|
if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue;
|
2004-01-25 19:18:36 +01:00
|
|
|
other_piece = *i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (other_piece >= 0)
|
|
|
|
{
|
2004-01-27 00:38:08 +01:00
|
|
|
// replace the old slot with 'other_piece'
|
2004-01-25 19:18:36 +01:00
|
|
|
m_slot_to_piece[other_slot] = other_piece;
|
|
|
|
m_piece_to_slot[other_piece] = other_slot;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-01-27 00:38:08 +01:00
|
|
|
// this index is the only piece with this
|
|
|
|
// hash. The previous slot we found with
|
2006-04-25 23:04:48 +02:00
|
|
|
// this hash must be the same piece. Mark
|
2004-01-27 00:38:08 +01:00
|
|
|
// that piece as unassigned, since this slot
|
|
|
|
// is the correct place for the piece.
|
2004-01-25 19:18:36 +01:00
|
|
|
m_slot_to_piece[other_slot] = unassigned;
|
2007-10-08 22:01:36 +02:00
|
|
|
if (m_storage_mode == storage_mode_compact)
|
|
|
|
m_free_slots.push_back(other_slot);
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot);
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0);
|
2004-01-26 02:08:59 +01:00
|
|
|
m_piece_to_slot[piece_index] = has_no_slot;
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot);
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
return piece_index;
|
|
|
|
}
|
|
|
|
|
2004-01-26 02:08:59 +01:00
|
|
|
// find a matching piece that hasn't
|
|
|
|
// already been assigned
|
2004-01-27 18:48:58 +01:00
|
|
|
int free_piece = unassigned;
|
2004-01-26 02:08:59 +01:00
|
|
|
for (std::vector<int>::iterator i = matching_pieces.begin();
|
2005-05-13 02:39:39 +02:00
|
|
|
i != matching_pieces.end(); ++i)
|
2004-01-26 02:08:59 +01:00
|
|
|
{
|
2008-03-08 07:06:31 +01:00
|
|
|
if (m_piece_to_slot[*i] >= 0) continue;
|
2004-01-26 02:08:59 +01:00
|
|
|
free_piece = *i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-01-26 02:45:30 +01:00
|
|
|
if (free_piece >= 0)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot);
|
2004-01-26 02:45:30 +01:00
|
|
|
return free_piece;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(free_piece == unassigned);
|
2004-01-27 18:48:58 +01:00
|
|
|
return unassigned;
|
2004-01-26 02:45:30 +01:00
|
|
|
}
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
|
|
|
|
2009-06-28 02:36:41 +02:00
|
|
|
int piece_manager::check_no_fastresume(error_code& error)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2011-02-06 01:50:12 +01:00
|
|
|
bool has_files = false;
|
|
|
|
if (!m_storage->settings().no_recheck_incomplete_resume)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2011-02-09 09:20:51 +01:00
|
|
|
has_files = m_storage->has_any_file();
|
2011-02-06 01:50:12 +01:00
|
|
|
if (m_storage->error())
|
|
|
|
return fatal_disk_error;
|
|
|
|
|
|
|
|
if (has_files)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2011-02-06 01:50:12 +01:00
|
|
|
m_state = state_full_check;
|
|
|
|
m_piece_to_slot.clear();
|
|
|
|
m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
|
|
|
|
m_slot_to_piece.clear();
|
|
|
|
m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
|
|
|
|
if (m_storage_mode == storage_mode_compact)
|
|
|
|
{
|
|
|
|
m_unallocated_slots.clear();
|
|
|
|
m_free_slots.clear();
|
|
|
|
}
|
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
|
|
|
return need_full_check;
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
|
|
|
}
|
2009-04-10 09:22:27 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
if (m_storage_mode == storage_mode_compact)
|
|
|
|
{
|
|
|
|
// in compact mode without checking, we need to
|
|
|
|
// populate the unallocated list
|
|
|
|
TORRENT_ASSERT(m_unallocated_slots.empty());
|
2008-05-28 10:44:40 +02:00
|
|
|
for (int i = 0, end(m_files.num_pieces()); i < end; ++i)
|
2008-03-08 07:06:31 +01:00
|
|
|
m_unallocated_slots.push_back(i);
|
2008-03-14 11:17:27 +01:00
|
|
|
m_piece_to_slot.clear();
|
2008-05-28 10:44:40 +02:00
|
|
|
m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
|
2008-03-14 11:17:27 +01:00
|
|
|
m_slot_to_piece.clear();
|
2008-05-28 10:44:40 +02:00
|
|
|
m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return check_init_storage(error);
|
|
|
|
}
|
|
|
|
|
2009-06-28 02:36:41 +02:00
|
|
|
int piece_manager::check_init_storage(error_code& error)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
|
|
|
if (m_storage->initialize(m_storage_mode == storage_mode_allocate))
|
|
|
|
{
|
2009-06-28 02:36:41 +02:00
|
|
|
error = m_storage->error();
|
|
|
|
TORRENT_ASSERT(error);
|
2008-03-08 07:06:31 +01:00
|
|
|
return fatal_disk_error;
|
|
|
|
}
|
|
|
|
m_state = state_finished;
|
2009-01-11 03:02:34 +01:00
|
|
|
m_scratch_buffer.reset();
|
|
|
|
m_scratch_buffer2.reset();
|
2008-03-08 07:06:31 +01:00
|
|
|
if (m_storage_mode != storage_mode_compact)
|
|
|
|
{
|
|
|
|
// if no piece is out of place
|
|
|
|
// since we're in full allocation mode, we can
|
|
|
|
// forget the piece allocation tables
|
|
|
|
std::vector<int>().swap(m_piece_to_slot);
|
|
|
|
std::vector<int>().swap(m_slot_to_piece);
|
|
|
|
std::vector<int>().swap(m_free_slots);
|
|
|
|
std::vector<int>().swap(m_unallocated_slots);
|
|
|
|
}
|
|
|
|
return no_error;
|
|
|
|
}
|
|
|
|
|
2005-10-13 09:59:05 +02:00
|
|
|
// check if the fastresume data is up to date
|
|
|
|
// if it is, use it and return true. If it
|
|
|
|
// isn't return false and the full check
|
|
|
|
// will be run
|
2008-03-08 07:06:31 +01:00
|
|
|
int piece_manager::check_fastresume(
|
2009-06-28 02:36:41 +02:00
|
|
|
lazy_entry const& rd, error_code& error)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock lock(m_mutex);
|
2004-01-25 19:18:36 +01:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_files.piece_length() > 0);
|
2009-05-23 17:58:32 +02:00
|
|
|
|
|
|
|
m_current_slot = 0;
|
2007-06-10 22:46:09 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
// if we don't have any resume data, return
|
2008-07-01 01:14:31 +02:00
|
|
|
if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error);
|
2005-05-13 02:39:39 +02:00
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
if (rd.type() != lazy_entry::dict_t)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::not_a_dictionary;
|
2008-03-08 07:06:31 +01:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
int block_size = (std::min)(16 * 1024, m_files.piece_length());
|
2011-02-21 06:24:41 +01:00
|
|
|
int blocks_per_piece = int(rd.dict_find_int_value("blocks per piece", -1));
|
2008-07-01 01:14:31 +02:00
|
|
|
if (blocks_per_piece != -1
|
|
|
|
&& blocks_per_piece != m_files.piece_length() / block_size)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::invalid_blocks_per_piece;
|
2008-03-08 07:06:31 +01:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
storage_mode_t storage_mode = storage_mode_compact;
|
2008-07-01 01:14:31 +02:00
|
|
|
if (rd.dict_find_string_value("allocation") != "compact")
|
2008-03-08 07:06:31 +01:00
|
|
|
storage_mode = storage_mode_sparse;
|
|
|
|
|
2008-07-18 15:51:26 +02:00
|
|
|
if (!m_storage->verify_resume_data(rd, error))
|
|
|
|
return check_no_fastresume(error);
|
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
// assume no piece is out of place (i.e. in a slot
|
|
|
|
// other than the one it should be in)
|
|
|
|
bool out_of_place = false;
|
2008-04-13 20:54:36 +02:00
|
|
|
|
|
|
|
// if we don't have a piece map, we need the slots
|
|
|
|
// if we're in compact mode, we also need the slots map
|
2008-07-01 01:14:31 +02:00
|
|
|
if (storage_mode == storage_mode_compact || rd.dict_find("pieces") == 0)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2008-04-13 20:54:36 +02:00
|
|
|
// read slots map
|
2008-07-01 01:14:31 +02:00
|
|
|
lazy_entry const* slots = rd.dict_find_list("slots");
|
|
|
|
if (slots == 0)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::missing_slots;
|
2008-04-13 20:54:36 +02:00
|
|
|
return check_no_fastresume(error);
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
if ((int)slots->list_size() > m_files.num_pieces())
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::too_many_slots;
|
2008-04-13 20:54:36 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
2008-06-23 01:41:03 +02:00
|
|
|
if (m_storage_mode == storage_mode_compact)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
int num_pieces = int(m_files.num_pieces());
|
2008-04-13 20:54:36 +02:00
|
|
|
m_slot_to_piece.resize(num_pieces, unallocated);
|
|
|
|
m_piece_to_slot.resize(num_pieces, has_no_slot);
|
2008-07-01 01:14:31 +02:00
|
|
|
for (int i = 0; i < slots->list_size(); ++i)
|
2007-10-08 22:01:36 +02:00
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
lazy_entry const* e = slots->list_at(i);
|
|
|
|
if (e->type() != lazy_entry::int_t)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::invalid_slot_list;
|
2008-04-13 20:54:36 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
int index = int(e->int_value());
|
2008-04-13 20:54:36 +02:00
|
|
|
if (index >= num_pieces || index < -2)
|
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::invalid_piece_index;
|
2008-04-13 20:54:36 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
if (index >= 0)
|
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
m_slot_to_piece[i] = index;
|
|
|
|
m_piece_to_slot[index] = i;
|
|
|
|
if (i != index) out_of_place = true;
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
|
|
|
else if (index == unassigned)
|
|
|
|
{
|
|
|
|
if (m_storage_mode == storage_mode_compact)
|
2008-07-01 01:14:31 +02:00
|
|
|
m_free_slots.push_back(i);
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(index == unallocated);
|
|
|
|
if (m_storage_mode == storage_mode_compact)
|
2008-07-01 01:14:31 +02:00
|
|
|
m_unallocated_slots.push_back(i);
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
for (int i = 0; i < slots->list_size(); ++i)
|
2007-10-08 22:01:36 +02:00
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
lazy_entry const* e = slots->list_at(i);
|
|
|
|
if (e->type() != lazy_entry::int_t)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::invalid_slot_list;
|
2008-04-13 20:54:36 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
int index = int(e->int_value());
|
|
|
|
if (index != i && index >= 0)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::invalid_piece_index;
|
2008-04-13 20:54:36 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2005-05-13 02:39:39 +02:00
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
// This will corrupt the storage
|
|
|
|
// use while debugging to find
|
|
|
|
// states that cannot be scanned
|
|
|
|
// by check_pieces.
|
|
|
|
// m_storage->shuffle();
|
2004-01-27 00:39:24 +01:00
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
if (m_storage_mode == storage_mode_compact)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2008-04-13 20:54:36 +02:00
|
|
|
if (m_unallocated_slots.empty()) switch_to_full_mode();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_free_slots.empty());
|
|
|
|
TORRENT_ASSERT(m_unallocated_slots.empty());
|
|
|
|
|
|
|
|
if (out_of_place)
|
|
|
|
{
|
|
|
|
// in this case we're in full allocation mode, but
|
|
|
|
// we're resuming a compact allocated storage
|
|
|
|
m_state = state_expand_pieces;
|
|
|
|
m_current_slot = 0;
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::pieces_need_reorder;
|
2008-07-18 15:51:26 +02:00
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
2008-04-13 20:54:36 +02:00
|
|
|
return need_full_check;
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
2008-06-23 01:41:03 +02:00
|
|
|
else if (m_storage_mode == storage_mode_compact)
|
|
|
|
{
|
|
|
|
// read piece map
|
2008-07-01 01:14:31 +02:00
|
|
|
lazy_entry const* pieces = rd.dict_find("pieces");
|
|
|
|
if (pieces == 0 || pieces->type() != lazy_entry::string_t)
|
2008-06-23 01:41:03 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::missing_pieces;
|
2008-06-23 01:41:03 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
2008-07-01 01:14:31 +02:00
|
|
|
if ((int)pieces->string_length() != m_files.num_pieces())
|
2008-06-23 01:41:03 +02:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
error = errors::too_many_slots;
|
2008-06-23 01:41:03 +02:00
|
|
|
return check_no_fastresume(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int num_pieces = int(m_files.num_pieces());
|
|
|
|
m_slot_to_piece.resize(num_pieces, unallocated);
|
|
|
|
m_piece_to_slot.resize(num_pieces, has_no_slot);
|
2008-07-01 01:14:31 +02:00
|
|
|
char const* have_pieces = pieces->string_ptr();
|
2008-06-23 01:41:03 +02:00
|
|
|
for (int i = 0; i < num_pieces; ++i)
|
|
|
|
{
|
|
|
|
if (have_pieces[i] & 1)
|
|
|
|
{
|
|
|
|
m_slot_to_piece[i] = i;
|
|
|
|
m_piece_to_slot[i] = i;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_free_slots.push_back(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_unallocated_slots.empty()) switch_to_full_mode();
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
|
|
|
return check_init_storage(error);
|
2005-10-13 09:59:05 +02:00
|
|
|
}
|
2004-01-25 22:51:30 +01:00
|
|
|
|
2007-05-31 00:23:17 +02:00
|
|
|
/*
|
2007-06-10 22:46:09 +02:00
|
|
|
state chart:
|
2007-05-31 00:23:17 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
check_fastresume() ----------+
|
|
|
|
|
|
|
|
|
| | |
|
|
|
|
| v v
|
2007-10-08 22:01:36 +02:00
|
|
|
| +------------+ +---------------+
|
|
|
|
| | full_check |-->| expand_pieses |
|
|
|
|
| +------------+ +---------------+
|
|
|
|
| | |
|
|
|
|
| v |
|
|
|
|
| +--------------+ |
|
2008-03-08 07:06:31 +01:00
|
|
|
+->| finished | <------+
|
2007-06-01 01:35:48 +02:00
|
|
|
+--------------+
|
2007-05-31 00:23:17 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2005-10-13 09:59:05 +02:00
|
|
|
// performs the full check and full allocation
|
|
|
|
// (if necessary). returns true if finished and
|
|
|
|
// false if it should be called again
|
|
|
|
// the second return value is the progress the
|
|
|
|
// file check is at. 0 is nothing done, and 1
|
|
|
|
// is finished
|
2009-06-28 02:36:41 +02:00
|
|
|
int piece_manager::check_files(int& current_slot, int& have_piece, error_code& error)
|
2005-10-13 09:59:05 +02:00
|
|
|
{
|
2008-12-13 04:32:57 +01:00
|
|
|
if (m_state == state_none) return check_no_fastresume(error);
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
current_slot = m_current_slot;
|
|
|
|
have_piece = -1;
|
2007-10-08 22:01:36 +02:00
|
|
|
if (m_state == state_expand_pieces)
|
2005-10-13 09:59:05 +02:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
if (m_scratch_piece >= 0)
|
2005-10-13 09:59:05 +02:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
int piece = m_scratch_piece;
|
|
|
|
int other_piece = m_slot_to_piece[piece];
|
|
|
|
m_scratch_piece = -1;
|
|
|
|
|
|
|
|
if (other_piece >= 0)
|
|
|
|
{
|
2011-02-22 03:53:26 +01:00
|
|
|
if (!m_scratch_buffer2.get())
|
|
|
|
m_scratch_buffer2.reset(page_aligned_allocator::malloc(m_files.piece_length()));
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
int piece_size = m_files.piece_size(other_piece);
|
2011-02-22 03:53:26 +01:00
|
|
|
file::iovec_t b = {m_scratch_buffer2.get(), piece_size};
|
|
|
|
if (m_storage->readv(&b, piece, 0, 1) != piece_size)
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
2009-06-28 02:36:41 +02:00
|
|
|
error = m_storage->error();
|
|
|
|
TORRENT_ASSERT(error);
|
2008-03-08 07:06:31 +01:00
|
|
|
return fatal_disk_error;
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
m_scratch_piece = other_piece;
|
|
|
|
m_piece_to_slot[other_piece] = unassigned;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the slot where this piece belongs is
|
|
|
|
// free. Just move the piece there.
|
2008-05-28 10:44:40 +02:00
|
|
|
int piece_size = m_files.piece_size(piece);
|
2011-02-22 03:53:26 +01:00
|
|
|
file::iovec_t b = {m_scratch_buffer.get(), piece_size};
|
|
|
|
if (m_storage->writev(&b, piece, 0, 1) != piece_size)
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
2009-06-28 02:36:41 +02:00
|
|
|
error = m_storage->error();
|
|
|
|
TORRENT_ASSERT(error);
|
2008-03-08 07:06:31 +01:00
|
|
|
return fatal_disk_error;
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
m_piece_to_slot[piece] = piece;
|
|
|
|
m_slot_to_piece[piece] = piece;
|
|
|
|
|
2011-02-22 03:53:26 +01:00
|
|
|
if (other_piece >= 0) m_scratch_buffer.swap(m_scratch_buffer2);
|
2007-04-17 23:54:40 +02:00
|
|
|
|
2008-07-18 15:51:26 +02:00
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
2008-03-08 07:06:31 +01:00
|
|
|
return need_full_check;
|
2007-05-31 00:23:17 +02:00
|
|
|
}
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
while (m_current_slot < m_files.num_pieces()
|
2007-10-08 22:01:36 +02:00
|
|
|
&& (m_slot_to_piece[m_current_slot] == m_current_slot
|
|
|
|
|| m_slot_to_piece[m_current_slot] < 0))
|
2007-04-18 21:12:30 +02:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
++m_current_slot;
|
2007-04-18 21:12:30 +02:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
if (m_current_slot == m_files.num_pieces())
|
2007-04-18 21:12:30 +02:00
|
|
|
{
|
2008-03-08 07:06:31 +01:00
|
|
|
return check_init_storage(error);
|
2007-04-18 21:12:30 +02:00
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_current_slot < m_files.num_pieces());
|
2008-03-16 13:42:59 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
int piece = m_slot_to_piece[m_current_slot];
|
|
|
|
TORRENT_ASSERT(piece >= 0);
|
|
|
|
int other_piece = m_slot_to_piece[piece];
|
|
|
|
if (other_piece >= 0)
|
2007-05-31 00:23:17 +02:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
// there is another piece in the slot
|
|
|
|
// where this one goes. Store it in the scratch
|
|
|
|
// buffer until next iteration.
|
2011-02-22 03:53:26 +01:00
|
|
|
if (!m_scratch_buffer.get())
|
|
|
|
m_scratch_buffer.reset(page_aligned_allocator::malloc(m_files.piece_length()));
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
int piece_size = m_files.piece_size(other_piece);
|
2011-02-22 03:53:26 +01:00
|
|
|
file::iovec_t b = {m_scratch_buffer.get(), piece_size};
|
2011-02-24 19:01:32 +01:00
|
|
|
if (m_storage->readv(&b, piece, 0, 1) != piece_size)
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
2009-06-28 02:36:41 +02:00
|
|
|
error = m_storage->error();
|
|
|
|
TORRENT_ASSERT(error);
|
2008-03-08 07:06:31 +01:00
|
|
|
return fatal_disk_error;
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
m_scratch_piece = other_piece;
|
|
|
|
m_piece_to_slot[other_piece] = unassigned;
|
2007-05-31 00:23:17 +02:00
|
|
|
}
|
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
// the slot where this piece belongs is
|
|
|
|
// free. Just move the piece there.
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece;
|
2007-10-08 22:01:36 +02:00
|
|
|
m_storage->move_slot(m_current_slot, piece);
|
2009-05-31 21:44:56 +02:00
|
|
|
if (m_storage->error()) return -1;
|
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
m_piece_to_slot[piece] = piece;
|
|
|
|
m_slot_to_piece[m_current_slot] = unassigned;
|
|
|
|
m_slot_to_piece[piece] = piece;
|
|
|
|
|
2008-07-18 15:51:26 +02:00
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
2008-03-08 07:06:31 +01:00
|
|
|
return need_full_check;
|
2005-08-16 22:42:10 +02:00
|
|
|
}
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_state == state_full_check);
|
2010-08-23 07:51:12 +02:00
|
|
|
if (m_state == state_finished) return 0;
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2008-07-18 01:41:46 +02:00
|
|
|
int skip = check_one_piece(have_piece);
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2008-07-18 01:41:46 +02:00
|
|
|
if (skip == -1)
|
|
|
|
{
|
2009-06-28 02:36:41 +02:00
|
|
|
error = m_storage->error();
|
|
|
|
TORRENT_ASSERT(error);
|
2008-07-18 01:41:46 +02:00
|
|
|
return fatal_disk_error;
|
|
|
|
}
|
|
|
|
|
2009-02-17 01:11:38 +01:00
|
|
|
if (skip > 0)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2008-02-14 04:48:20 +01:00
|
|
|
clear_error();
|
|
|
|
// skip means that the piece we checked failed to be read from disk
|
2009-02-14 05:31:08 +01:00
|
|
|
// completely. This may be caused by the file not being there, or the
|
|
|
|
// piece overlapping with a sparse region. We should skip 'skip' number
|
|
|
|
// of pieces
|
2004-03-24 23:50:07 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
if (m_storage_mode == storage_mode_compact)
|
2004-01-25 19:18:36 +01:00
|
|
|
{
|
2009-02-17 01:11:38 +01:00
|
|
|
for (int i = m_current_slot; i < m_current_slot + skip - 1; ++i)
|
2007-10-08 22:01:36 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
|
|
|
|
m_unallocated_slots.push_back(i);
|
|
|
|
}
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2009-02-14 05:31:08 +01:00
|
|
|
// current slot will increase by one below
|
|
|
|
m_current_slot += skip - 1;
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2008-02-14 04:48:20 +01:00
|
|
|
|
2005-10-13 09:59:05 +02:00
|
|
|
++m_current_slot;
|
2008-03-08 07:06:31 +01:00
|
|
|
current_slot = m_current_slot;
|
2005-05-13 02:39:39 +02:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
if (m_current_slot >= m_files.num_pieces())
|
2005-10-13 09:59:05 +02:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_current_slot == m_files.num_pieces());
|
2005-10-13 09:59:05 +02:00
|
|
|
|
|
|
|
// clear the memory we've been using
|
|
|
|
std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
|
2007-10-08 22:01:36 +02:00
|
|
|
|
|
|
|
if (m_storage_mode != storage_mode_compact)
|
|
|
|
{
|
|
|
|
if (!m_out_of_place)
|
|
|
|
{
|
|
|
|
// if no piece is out of place
|
|
|
|
// since we're in full allocation mode, we can
|
|
|
|
// forget the piece allocation tables
|
|
|
|
|
|
|
|
std::vector<int>().swap(m_piece_to_slot);
|
|
|
|
std::vector<int>().swap(m_slot_to_piece);
|
2008-03-08 07:06:31 +01:00
|
|
|
return check_init_storage(error);
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// in this case we're in full allocation mode, but
|
|
|
|
// we're resuming a compact allocated storage
|
|
|
|
m_state = state_expand_pieces;
|
|
|
|
m_current_slot = 0;
|
2008-03-08 07:06:31 +01:00
|
|
|
current_slot = m_current_slot;
|
2008-07-18 15:51:26 +02:00
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
2008-03-08 07:06:31 +01:00
|
|
|
return need_full_check;
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_unallocated_slots.empty())
|
|
|
|
{
|
|
|
|
switch_to_full_mode();
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
return check_init_storage(error);
|
2005-10-13 09:59:05 +02:00
|
|
|
}
|
2008-07-18 15:51:26 +02:00
|
|
|
|
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
2008-03-08 07:06:31 +01:00
|
|
|
return need_full_check;
|
2004-01-25 19:18:36 +01:00
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2009-02-14 05:31:08 +01:00
|
|
|
int piece_manager::skip_file() const
|
|
|
|
{
|
|
|
|
size_type file_offset = 0;
|
|
|
|
size_type current_offset = size_type(m_current_slot) * m_files.piece_length();
|
|
|
|
for (file_storage::iterator i = m_files.begin()
|
|
|
|
, end(m_files.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
file_offset += i->size;
|
|
|
|
if (file_offset > current_offset) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
TORRENT_ASSERT(file_offset > current_offset);
|
|
|
|
int ret = static_cast<int>(
|
|
|
|
(file_offset - current_offset + m_files.piece_length() - 1)
|
|
|
|
/ m_files.piece_length());
|
|
|
|
TORRENT_ASSERT(ret >= 1);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -1 = error, 0 = ok, >0 = skip this many pieces
|
2008-07-18 01:41:46 +02:00
|
|
|
int piece_manager::check_one_piece(int& have_piece)
|
2008-02-14 04:48:20 +01:00
|
|
|
{
|
|
|
|
// ------------------------
|
|
|
|
// DO THE FULL CHECK
|
|
|
|
// ------------------------
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
|
|
|
|
TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces());
|
2008-03-08 07:06:31 +01:00
|
|
|
TORRENT_ASSERT(have_piece == -1);
|
|
|
|
|
2008-02-14 04:48:20 +01:00
|
|
|
// initialization for the full check
|
|
|
|
if (m_hash_to_piece.empty())
|
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
for (int i = 0; i < m_files.num_pieces(); ++i)
|
2009-08-30 09:38:52 +02:00
|
|
|
m_hash_to_piece.insert(std::pair<const sha1_hash, int>(m_info->hash_for_piece(i), i));
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
|
|
|
|
2009-05-21 18:15:05 +02:00
|
|
|
partial_hash ph;
|
|
|
|
int num_read = 0;
|
|
|
|
int piece_size = m_files.piece_size(m_current_slot);
|
|
|
|
int small_piece_size = m_files.piece_size(m_files.num_pieces() - 1);
|
|
|
|
bool read_short = true;
|
|
|
|
sha1_hash small_hash;
|
|
|
|
if (piece_size == small_piece_size)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2009-05-21 18:15:05 +02:00
|
|
|
num_read = hash_for_slot(m_current_slot, ph, piece_size, 0, 0);
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
2009-05-21 18:15:05 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
num_read = hash_for_slot(m_current_slot, ph, piece_size
|
|
|
|
, small_piece_size, &small_hash);
|
|
|
|
}
|
|
|
|
read_short = num_read != piece_size;
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2009-05-21 18:15:05 +02:00
|
|
|
if (read_short)
|
2008-07-18 01:41:46 +02:00
|
|
|
{
|
|
|
|
if (m_storage->error()
|
2008-10-07 11:24:30 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2010-07-24 09:06:45 +02:00
|
|
|
&& m_storage->error() != error_code(ERROR_PATH_NOT_FOUND, get_system_category())
|
2009-01-11 03:02:34 +01:00
|
|
|
&& m_storage->error() != error_code(ERROR_FILE_NOT_FOUND, get_system_category())
|
2010-07-15 03:32:59 +02:00
|
|
|
&& m_storage->error() != error_code(ERROR_HANDLE_EOF, get_system_category())
|
|
|
|
&& m_storage->error() != error_code(ERROR_INVALID_HANDLE, get_system_category()))
|
2008-10-07 11:24:30 +02:00
|
|
|
#else
|
2008-07-18 01:41:46 +02:00
|
|
|
&& m_storage->error() != error_code(ENOENT, get_posix_category()))
|
2008-10-07 11:24:30 +02:00
|
|
|
#endif
|
2009-05-16 04:25:28 +02:00
|
|
|
{
|
2008-07-18 01:41:46 +02:00
|
|
|
return -1;
|
2009-05-16 04:25:28 +02:00
|
|
|
}
|
2009-05-21 18:15:05 +02:00
|
|
|
// if the file is incomplete, skip the rest of it
|
2009-02-14 05:31:08 +01:00
|
|
|
return skip_file();
|
2008-07-18 01:41:46 +02:00
|
|
|
}
|
|
|
|
|
2009-05-21 18:15:05 +02:00
|
|
|
sha1_hash large_hash = ph.h.final();
|
|
|
|
int piece_index = identify_data(large_hash, small_hash, m_current_slot);
|
2008-03-08 07:06:31 +01:00
|
|
|
|
|
|
|
if (piece_index >= 0) have_piece = piece_index;
|
2008-02-14 04:48:20 +01:00
|
|
|
|
|
|
|
if (piece_index != m_current_slot
|
|
|
|
&& piece_index >= 0)
|
|
|
|
m_out_of_place = true;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0);
|
|
|
|
|
|
|
|
const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
|
|
|
|
const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot;
|
|
|
|
|
|
|
|
// check if this piece should be swapped with any other slot
|
|
|
|
// this section will ensure that the storage is correctly sorted
|
|
|
|
// libtorrent will never leave the storage in a state that
|
|
|
|
// requires this sorting, but other clients may.
|
|
|
|
|
|
|
|
// example of worst case:
|
|
|
|
// | m_current_slot = 5
|
|
|
|
// V
|
|
|
|
// +---+- - - +---+- - - +---+- -
|
|
|
|
// | x | | 5 | | 3 | <- piece data in slots
|
|
|
|
// +---+- - - +---+- - - +---+- -
|
|
|
|
// 3 y 5 <- slot index
|
|
|
|
|
|
|
|
// in this example, the data in the m_current_slot (5)
|
|
|
|
// is piece 3. It has to be moved into slot 3. The data
|
|
|
|
// in slot y (piece 5) should be moved into the m_current_slot.
|
|
|
|
// and the data in slot 3 (piece x) should be moved to slot y.
|
|
|
|
|
|
|
|
// there are three possible cases.
|
|
|
|
// 1. There's another piece that should be placed into this slot
|
|
|
|
// 2. This piece should be placed into another slot.
|
|
|
|
// 3. There's another piece that should be placed into this slot
|
|
|
|
// and this piece should be placed into another slot
|
|
|
|
|
|
|
|
// swap piece_index with this slot
|
|
|
|
|
|
|
|
// case 1
|
|
|
|
if (this_should_move && !other_should_move)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(piece_index != m_current_slot);
|
|
|
|
|
|
|
|
const int other_slot = piece_index;
|
|
|
|
TORRENT_ASSERT(other_slot >= 0);
|
|
|
|
int other_piece = m_slot_to_piece[other_slot];
|
|
|
|
|
|
|
|
m_slot_to_piece[other_slot] = piece_index;
|
|
|
|
m_slot_to_piece[m_current_slot] = other_piece;
|
|
|
|
m_piece_to_slot[piece_index] = piece_index;
|
|
|
|
if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot;
|
|
|
|
|
|
|
|
if (other_piece == unassigned)
|
|
|
|
{
|
|
|
|
std::vector<int>::iterator i =
|
|
|
|
std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
|
|
|
|
TORRENT_ASSERT(i != m_free_slots.end());
|
|
|
|
if (m_storage_mode == storage_mode_compact)
|
|
|
|
{
|
|
|
|
m_free_slots.erase(i);
|
|
|
|
m_free_slots.push_back(m_current_slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret = false;
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece_index;
|
2008-02-14 04:48:20 +01:00
|
|
|
if (other_piece >= 0)
|
|
|
|
ret |= m_storage->swap_slots(other_slot, m_current_slot);
|
|
|
|
else
|
|
|
|
ret |= m_storage->move_slot(m_current_slot, other_slot);
|
|
|
|
|
2009-02-14 05:31:08 +01:00
|
|
|
if (ret) return skip_file();
|
2008-02-14 04:48:20 +01:00
|
|
|
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
|
|
|
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
|
|
|
}
|
|
|
|
// case 2
|
|
|
|
else if (!this_should_move && other_should_move)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(piece_index != m_current_slot);
|
|
|
|
|
|
|
|
const int other_piece = m_current_slot;
|
|
|
|
const int other_slot = m_piece_to_slot[other_piece];
|
|
|
|
TORRENT_ASSERT(other_slot >= 0);
|
|
|
|
|
|
|
|
m_slot_to_piece[m_current_slot] = other_piece;
|
|
|
|
m_slot_to_piece[other_slot] = piece_index;
|
|
|
|
m_piece_to_slot[other_piece] = m_current_slot;
|
|
|
|
|
|
|
|
if (piece_index == unassigned
|
|
|
|
&& m_storage_mode == storage_mode_compact)
|
|
|
|
m_free_slots.push_back(other_slot);
|
|
|
|
|
|
|
|
bool ret = false;
|
|
|
|
if (piece_index >= 0)
|
|
|
|
{
|
2008-02-25 11:28:53 +01:00
|
|
|
m_piece_to_slot[piece_index] = other_slot;
|
2008-02-14 04:48:20 +01:00
|
|
|
ret |= m_storage->swap_slots(other_slot, m_current_slot);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret |= m_storage->move_slot(other_slot, m_current_slot);
|
|
|
|
|
|
|
|
}
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = other_piece;
|
2009-02-14 05:31:08 +01:00
|
|
|
if (ret) return skip_file();
|
2008-02-14 04:48:20 +01:00
|
|
|
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
|
|
|
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
|
|
|
}
|
|
|
|
else if (this_should_move && other_should_move)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(piece_index != m_current_slot);
|
|
|
|
TORRENT_ASSERT(piece_index >= 0);
|
|
|
|
|
|
|
|
const int piece1 = m_slot_to_piece[piece_index];
|
|
|
|
const int piece2 = m_current_slot;
|
|
|
|
const int slot1 = piece_index;
|
|
|
|
const int slot2 = m_piece_to_slot[piece2];
|
|
|
|
|
|
|
|
TORRENT_ASSERT(slot1 >= 0);
|
|
|
|
TORRENT_ASSERT(slot2 >= 0);
|
|
|
|
TORRENT_ASSERT(piece2 >= 0);
|
|
|
|
|
|
|
|
if (slot1 == slot2)
|
|
|
|
{
|
|
|
|
// this means there are only two pieces involved in the swap
|
|
|
|
TORRENT_ASSERT(piece1 >= 0);
|
|
|
|
|
|
|
|
// movement diagram:
|
|
|
|
// +-------------------------------+
|
|
|
|
// | |
|
|
|
|
// +--> slot1 --> m_current_slot --+
|
|
|
|
|
|
|
|
m_slot_to_piece[slot1] = piece_index;
|
|
|
|
m_slot_to_piece[m_current_slot] = piece1;
|
|
|
|
|
|
|
|
m_piece_to_slot[piece_index] = slot1;
|
|
|
|
m_piece_to_slot[piece1] = m_current_slot;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(piece1 == m_current_slot);
|
|
|
|
TORRENT_ASSERT(piece_index == slot1);
|
|
|
|
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece_index;
|
2008-02-14 04:48:20 +01:00
|
|
|
m_storage->swap_slots(m_current_slot, slot1);
|
|
|
|
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
|
|
|
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(slot1 != slot2);
|
|
|
|
TORRENT_ASSERT(piece1 != piece2);
|
|
|
|
|
|
|
|
// movement diagram:
|
|
|
|
// +-----------------------------------------+
|
|
|
|
// | |
|
|
|
|
// +--> slot1 --> slot2 --> m_current_slot --+
|
|
|
|
|
|
|
|
m_slot_to_piece[slot1] = piece_index;
|
|
|
|
m_slot_to_piece[slot2] = piece1;
|
|
|
|
m_slot_to_piece[m_current_slot] = piece2;
|
|
|
|
|
|
|
|
m_piece_to_slot[piece_index] = slot1;
|
|
|
|
m_piece_to_slot[m_current_slot] = piece2;
|
|
|
|
|
|
|
|
if (piece1 == unassigned)
|
|
|
|
{
|
|
|
|
std::vector<int>::iterator i =
|
|
|
|
std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
|
|
|
|
TORRENT_ASSERT(i != m_free_slots.end());
|
|
|
|
if (m_storage_mode == storage_mode_compact)
|
|
|
|
{
|
|
|
|
m_free_slots.erase(i);
|
|
|
|
m_free_slots.push_back(slot2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret = false;
|
|
|
|
if (piece1 >= 0)
|
|
|
|
{
|
|
|
|
m_piece_to_slot[piece1] = slot2;
|
|
|
|
ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret |= m_storage->move_slot(m_current_slot, slot1);
|
|
|
|
ret |= m_storage->move_slot(slot2, m_current_slot);
|
|
|
|
}
|
|
|
|
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece_index;
|
2009-02-14 05:31:08 +01:00
|
|
|
if (ret) return skip_file();
|
2008-02-14 04:48:20 +01:00
|
|
|
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
|
|
|
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot);
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated);
|
|
|
|
TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
|
|
|
|
|
|
|
|
// the slot was identified as piece 'piece_index'
|
|
|
|
if (piece_index != unassigned)
|
|
|
|
m_piece_to_slot[piece_index] = m_current_slot;
|
|
|
|
else if (m_storage_mode == storage_mode_compact)
|
|
|
|
m_free_slots.push_back(m_current_slot);
|
|
|
|
|
|
|
|
m_slot_to_piece[m_current_slot] = piece_index;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
|
|
|
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
|
|
|
}
|
2009-02-17 01:11:38 +01:00
|
|
|
|
|
|
|
if (piece_index == unassigned)
|
|
|
|
{
|
|
|
|
// the data did not match any piece. Maybe we're reading
|
|
|
|
// from a sparse region, see if we are and skip
|
|
|
|
if (m_current_slot == m_files.num_pieces() -1) return 0;
|
|
|
|
|
|
|
|
int next_slot = m_storage->sparse_end(m_current_slot + 1);
|
|
|
|
if (next_slot > m_current_slot + 1) return next_slot - m_current_slot;
|
|
|
|
}
|
|
|
|
|
2008-07-18 01:41:46 +02:00
|
|
|
return 0;
|
2008-02-14 04:48:20 +01:00
|
|
|
}
|
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
void piece_manager::switch_to_full_mode()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
|
|
|
|
TORRENT_ASSERT(m_unallocated_slots.empty());
|
|
|
|
// we have allocated all slots, switch to
|
|
|
|
// full allocation mode in order to free
|
|
|
|
// some unnecessary memory.
|
|
|
|
m_storage_mode = storage_mode_sparse;
|
|
|
|
std::vector<int>().swap(m_unallocated_slots);
|
|
|
|
std::vector<int>().swap(m_free_slots);
|
|
|
|
std::vector<int>().swap(m_piece_to_slot);
|
|
|
|
std::vector<int>().swap(m_slot_to_piece);
|
|
|
|
}
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
int piece_manager::allocate_slot_for_piece(int piece_index)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-10-20 04:49:56 +02:00
|
|
|
mutex::scoped_lock lock(m_mutex);
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
if (m_storage_mode != storage_mode_compact) return piece_index;
|
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
INVARIANT_CHECK;
|
2003-12-10 01:24:16 +01:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(piece_index >= 0);
|
|
|
|
TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size());
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot.size() == m_slot_to_piece.size());
|
2003-12-07 02:26:57 +01:00
|
|
|
|
|
|
|
int slot_index = m_piece_to_slot[piece_index];
|
|
|
|
|
2004-01-16 17:19:27 +01:00
|
|
|
if (slot_index != has_no_slot)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(slot_index >= 0);
|
|
|
|
TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
|
2003-12-07 02:26:57 +01:00
|
|
|
return slot_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_free_slots.empty())
|
|
|
|
{
|
2009-12-25 17:02:45 +01:00
|
|
|
allocate_slots_impl(1, lock);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(!m_free_slots.empty());
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
std::vector<int>::iterator iter(
|
2003-12-07 02:26:57 +01:00
|
|
|
std::find(
|
|
|
|
m_free_slots.begin()
|
2003-12-07 06:53:04 +01:00
|
|
|
, m_free_slots.end()
|
|
|
|
, piece_index));
|
2003-12-07 02:26:57 +01:00
|
|
|
|
|
|
|
if (iter == m_free_slots.end())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_slot_to_piece[piece_index] != unassigned);
|
|
|
|
TORRENT_ASSERT(!m_free_slots.empty());
|
2003-12-07 02:26:57 +01:00
|
|
|
iter = m_free_slots.end() - 1;
|
|
|
|
|
|
|
|
// special case to make sure we don't use the last slot
|
|
|
|
// when we shouldn't, since it's smaller than ordinary slots
|
2008-05-28 10:44:40 +02:00
|
|
|
if (*iter == m_files.num_pieces() - 1 && piece_index != *iter)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-12-07 02:26:57 +01:00
|
|
|
if (m_free_slots.size() == 1)
|
2009-12-25 17:02:45 +01:00
|
|
|
allocate_slots_impl(1, lock);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_free_slots.size() > 1);
|
2004-01-26 11:29:00 +01:00
|
|
|
// assumes that all allocated slots
|
2003-12-07 02:26:57 +01:00
|
|
|
// are put at the end of the free_slots vector
|
|
|
|
iter = m_free_slots.end() - 1;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
slot_index = *iter;
|
2003-12-07 06:53:04 +01:00
|
|
|
m_free_slots.erase(iter);
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_slot_to_piece[slot_index] == unassigned);
|
2003-12-07 02:26:57 +01:00
|
|
|
|
|
|
|
m_slot_to_piece[slot_index] = piece_index;
|
|
|
|
m_piece_to_slot[piece_index] = slot_index;
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2003-12-07 02:26:57 +01:00
|
|
|
// there is another piece already assigned to
|
|
|
|
// the slot we are interested in, swap positions
|
2003-12-07 15:12:14 +01:00
|
|
|
if (slot_index != piece_index
|
|
|
|
&& m_slot_to_piece[piece_index] >= 0)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2009-04-04 11:52:25 +02:00
|
|
|
#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG && TORRENT_USE_IOSTREAM
|
2003-12-07 15:12:14 +01:00
|
|
|
std::stringstream s;
|
|
|
|
|
|
|
|
s << "there is another piece at our slot, swapping..";
|
|
|
|
|
|
|
|
s << "\n piece_index: ";
|
|
|
|
s << piece_index;
|
|
|
|
s << "\n slot_index: ";
|
|
|
|
s << slot_index;
|
|
|
|
s << "\n piece at our slot: ";
|
|
|
|
s << m_slot_to_piece[piece_index];
|
|
|
|
s << "\n";
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2003-12-07 15:12:14 +01:00
|
|
|
print_to_log(s.str());
|
|
|
|
debug_log();
|
2004-01-24 18:14:03 +01:00
|
|
|
#endif
|
2004-01-25 19:18:36 +01:00
|
|
|
|
|
|
|
int piece_at_our_slot = m_slot_to_piece[piece_index];
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_piece_to_slot[piece_at_our_slot] == piece_index);
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2003-12-07 02:26:57 +01:00
|
|
|
std::swap(
|
|
|
|
m_slot_to_piece[piece_index]
|
|
|
|
, m_slot_to_piece[slot_index]);
|
2003-10-30 00:28:09 +01:00
|
|
|
|
2003-12-07 02:26:57 +01:00
|
|
|
std::swap(
|
|
|
|
m_piece_to_slot[piece_index]
|
2003-12-07 15:19:04 +01:00
|
|
|
, m_piece_to_slot[piece_at_our_slot]);
|
2003-12-07 02:26:57 +01:00
|
|
|
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = piece_index;
|
2007-05-09 22:16:18 +02:00
|
|
|
m_storage->move_slot(piece_index, slot_index);
|
2004-01-17 21:04:19 +01:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index);
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[piece_index] == piece_index);
|
2004-01-16 16:07:01 +01:00
|
|
|
|
2003-12-07 02:26:57 +01:00
|
|
|
slot_index = piece_index;
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2008-11-29 22:33:21 +01:00
|
|
|
#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG
|
2003-12-07 15:12:14 +01:00
|
|
|
debug_log();
|
2004-01-17 21:04:19 +01:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(slot_index >= 0);
|
|
|
|
TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
|
2007-10-08 22:01:36 +02:00
|
|
|
|
2008-03-25 05:45:46 +01:00
|
|
|
if (m_free_slots.empty() && m_unallocated_slots.empty())
|
2007-10-08 22:01:36 +02:00
|
|
|
switch_to_full_mode();
|
|
|
|
|
2003-12-07 02:26:57 +01:00
|
|
|
return slot_index;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-09 09:55:03 +01:00
|
|
|
|
2009-12-25 17:02:45 +01:00
|
|
|
bool piece_manager::allocate_slots_impl(int num_slots, mutex::scoped_lock& l
|
|
|
|
, bool abort_on_disk)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(num_slots > 0);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2010-10-30 21:45:50 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
2008-03-08 07:06:31 +01:00
|
|
|
INVARIANT_CHECK;
|
2010-10-30 21:45:50 +02:00
|
|
|
#endif
|
2003-12-10 01:24:16 +01:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(!m_unallocated_slots.empty());
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
|
2005-10-13 09:59:05 +02:00
|
|
|
|
2007-04-17 23:54:40 +02:00
|
|
|
bool written = false;
|
2003-12-08 09:55:24 +01:00
|
|
|
|
2005-05-13 02:39:39 +02:00
|
|
|
for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 22:37:19 +01:00
|
|
|
int pos = m_unallocated_slots.front();
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_slot_to_piece[pos] == unallocated);
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[pos] != pos);
|
2003-12-07 02:26:57 +01:00
|
|
|
|
|
|
|
int new_free_slot = pos;
|
2004-01-16 17:19:27 +01:00
|
|
|
if (m_piece_to_slot[pos] != has_no_slot)
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2009-05-31 21:44:56 +02:00
|
|
|
m_last_piece = pos;
|
2003-12-07 02:26:57 +01:00
|
|
|
new_free_slot = m_piece_to_slot[pos];
|
2007-04-30 03:06:29 +02:00
|
|
|
m_storage->move_slot(new_free_slot, pos);
|
2003-12-07 02:26:57 +01:00
|
|
|
m_slot_to_piece[pos] = pos;
|
|
|
|
m_piece_to_slot[pos] = pos;
|
2007-04-30 03:06:29 +02:00
|
|
|
written = true;
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
2007-04-30 03:06:29 +02:00
|
|
|
m_unallocated_slots.erase(m_unallocated_slots.begin());
|
|
|
|
m_slot_to_piece[new_free_slot] = unassigned;
|
|
|
|
m_free_slots.push_back(new_free_slot);
|
2007-10-08 22:01:36 +02:00
|
|
|
if (abort_on_disk && written) break;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_free_slots.size() > 0);
|
2007-04-17 23:54:40 +02:00
|
|
|
return written;
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
2003-12-09 09:49:49 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
int piece_manager::slot_for(int piece) const
|
|
|
|
{
|
|
|
|
if (m_storage_mode != storage_mode_compact) return piece;
|
|
|
|
TORRENT_ASSERT(piece < int(m_piece_to_slot.size()));
|
|
|
|
TORRENT_ASSERT(piece >= 0);
|
|
|
|
return m_piece_to_slot[piece];
|
|
|
|
}
|
|
|
|
|
|
|
|
int piece_manager::piece_for(int slot) const
|
|
|
|
{
|
|
|
|
if (m_storage_mode != storage_mode_compact) return slot;
|
|
|
|
TORRENT_ASSERT(slot < int(m_slot_to_piece.size()));
|
|
|
|
TORRENT_ASSERT(slot >= 0);
|
|
|
|
return m_slot_to_piece[slot];
|
|
|
|
}
|
|
|
|
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2007-06-10 22:46:09 +02:00
|
|
|
void piece_manager::check_invariant() const
|
2003-12-07 02:26:57 +01:00
|
|
|
{
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
|
2008-03-16 13:42:59 +01:00
|
|
|
|
2008-03-25 05:45:46 +01:00
|
|
|
if (m_unallocated_slots.empty()
|
|
|
|
&& m_free_slots.empty()
|
|
|
|
&& m_state == state_finished)
|
2004-01-24 20:19:17 +01:00
|
|
|
{
|
2008-05-15 04:29:26 +02:00
|
|
|
TORRENT_ASSERT(m_storage_mode != storage_mode_compact
|
2008-05-28 10:44:40 +02:00
|
|
|
|| m_files.num_pieces() == 0);
|
2004-01-24 20:19:17 +01:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
|
|
|
|
if (m_storage_mode != storage_mode_compact)
|
2004-01-24 20:19:17 +01:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(m_unallocated_slots.empty());
|
|
|
|
TORRENT_ASSERT(m_free_slots.empty());
|
2004-01-24 20:19:17 +01:00
|
|
|
}
|
2007-10-08 22:01:36 +02:00
|
|
|
|
|
|
|
if (m_storage_mode != storage_mode_compact
|
|
|
|
&& m_state != state_expand_pieces
|
|
|
|
&& m_state != state_full_check)
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(m_piece_to_slot.empty());
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece.empty());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (m_piece_to_slot.empty()) return;
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces());
|
|
|
|
TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces());
|
2007-10-08 22:01:36 +02:00
|
|
|
|
|
|
|
for (std::vector<int>::const_iterator i = m_free_slots.begin();
|
|
|
|
i != m_free_slots.end(); ++i)
|
2004-01-25 13:37:15 +01:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
|
|
|
|
TORRENT_ASSERT(*i >= 0);
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[*i] == unassigned);
|
|
|
|
TORRENT_ASSERT(std::find(i+1, m_free_slots.end(), *i)
|
|
|
|
== m_free_slots.end());
|
2004-01-25 13:37:15 +01:00
|
|
|
}
|
2004-01-24 20:19:17 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
|
|
|
|
i != m_unallocated_slots.end(); ++i)
|
2004-01-25 13:37:15 +01:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
|
|
|
|
TORRENT_ASSERT(*i >= 0);
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[*i] == unallocated);
|
|
|
|
TORRENT_ASSERT(std::find(i+1, m_unallocated_slots.end(), *i)
|
|
|
|
== m_unallocated_slots.end());
|
2004-01-25 13:37:15 +01:00
|
|
|
}
|
2004-01-24 20:19:17 +01:00
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
for (int i = 0; i < m_files.num_pieces(); ++i)
|
2004-01-24 20:19:17 +01:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
// Check domain of piece_to_slot's elements
|
|
|
|
if (m_piece_to_slot[i] != has_no_slot)
|
2004-01-24 20:19:17 +01:00
|
|
|
{
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(m_piece_to_slot[i] >= 0);
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
|
2004-01-24 20:19:17 +01:00
|
|
|
}
|
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
// Check domain of slot_to_piece's elements
|
|
|
|
if (m_slot_to_piece[i] != unallocated
|
|
|
|
&& m_slot_to_piece[i] != unassigned)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[i] >= 0);
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
|
|
|
|
}
|
2003-12-10 01:24:16 +01:00
|
|
|
|
2007-10-08 22:01:36 +02:00
|
|
|
// do more detailed checks on piece_to_slot
|
|
|
|
if (m_piece_to_slot[i] >= 0)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[m_piece_to_slot[i]] == i);
|
|
|
|
if (m_piece_to_slot[i] != i)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[i] == has_no_slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
// do more detailed checks on slot_to_piece
|
|
|
|
|
|
|
|
if (m_slot_to_piece[i] >= 0)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
|
|
|
|
TORRENT_ASSERT(m_piece_to_slot[m_slot_to_piece[i]] == i);
|
2004-01-24 18:14:03 +01:00
|
|
|
#ifdef TORRENT_STORAGE_DEBUG
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(
|
|
|
|
std::find(
|
|
|
|
m_unallocated_slots.begin()
|
|
|
|
, m_unallocated_slots.end()
|
|
|
|
, i) == m_unallocated_slots.end()
|
|
|
|
);
|
|
|
|
TORRENT_ASSERT(
|
|
|
|
std::find(
|
|
|
|
m_free_slots.begin()
|
|
|
|
, m_free_slots.end()
|
|
|
|
, i) == m_free_slots.end()
|
|
|
|
);
|
2004-01-24 18:14:03 +01:00
|
|
|
#endif
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
else if (m_slot_to_piece[i] == unallocated)
|
|
|
|
{
|
2004-01-24 18:14:03 +01:00
|
|
|
#ifdef TORRENT_STORAGE_DEBUG
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(m_unallocated_slots.empty()
|
|
|
|
|| (std::find(
|
|
|
|
m_unallocated_slots.begin()
|
|
|
|
, m_unallocated_slots.end()
|
|
|
|
, i) != m_unallocated_slots.end())
|
|
|
|
);
|
2004-01-24 18:14:03 +01:00
|
|
|
#endif
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
else if (m_slot_to_piece[i] == unassigned)
|
|
|
|
{
|
2004-01-24 18:14:03 +01:00
|
|
|
#ifdef TORRENT_STORAGE_DEBUG
|
2007-10-08 22:01:36 +02:00
|
|
|
TORRENT_ASSERT(
|
|
|
|
std::find(
|
|
|
|
m_free_slots.begin()
|
|
|
|
, m_free_slots.end()
|
|
|
|
, i) != m_free_slots.end()
|
|
|
|
);
|
2004-01-24 18:14:03 +01:00
|
|
|
#endif
|
2007-10-08 22:01:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid");
|
|
|
|
}
|
2004-01-16 17:19:27 +01:00
|
|
|
}
|
2003-12-07 02:26:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-26 20:20:32 +02:00
|
|
|
#if defined(TORRENT_STORAGE_DEBUG) && TORRENT_USE_IOSTREAM
|
2007-06-10 22:46:09 +02:00
|
|
|
void piece_manager::debug_log() const
|
2003-12-07 15:12:14 +01:00
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
|
|
|
|
s << "index\tslot\tpiece\n";
|
|
|
|
|
2008-05-28 10:44:40 +02:00
|
|
|
for (int i = 0; i < m_files.num_pieces(); ++i)
|
2003-12-07 15:12:14 +01:00
|
|
|
{
|
|
|
|
s << i << "\t" << m_slot_to_piece[i] << "\t";
|
|
|
|
s << m_piece_to_slot[i] << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
s << "---------------------------------\n";
|
|
|
|
|
|
|
|
print_to_log(s.str());
|
|
|
|
}
|
2004-01-17 21:04:19 +01:00
|
|
|
#endif
|
2004-01-24 18:14:03 +01:00
|
|
|
#endif
|
2003-12-07 02:26:57 +01:00
|
|
|
} // namespace libtorrent
|
2003-12-01 23:09:58 +01:00
|
|
|
|