separated file_storage from torrent_info and create_torrent

This commit is contained in:
Arvid Norberg 2008-05-28 08:44:40 +00:00
parent cf37d8544d
commit 3910fe78de
31 changed files with 1859 additions and 1514 deletions

View File

@ -262,6 +262,7 @@ SOURCES =
create_torrent
disk_buffer_holder
entry
file_storage
lazy_bdecode
escape_string
gzip

194
docs/make_torrent.rst Normal file
View File

@ -0,0 +1,194 @@
=================
creating torrents
=================
:Author: Arvid Norberg, arvid@rasterbar.com
.. contents:: Table of contents
:depth: 2
:backlinks: none
overview
========
This section describes the functions and classes that are used
to create torrent files. It is a layered API with low level classes
and higher level convenience functions. A torrent is created in 4
steps:
1. first the files that will be part of the torrent are determined.
2. the torrent properties are set, such as tracker url, web seeds,
DHT nodes etc.
3. Read through all the files in the torrent, SHA-1 all the data
and set the piece hashes.
4. The torrent is bencoded into a file or buffer.
If there are a lot of files and or deep directoy hierarchies to
traverse, step one can be time consuming.
Typically step 3 is by far the most time consuming step, since it
requires to read all the bytes from all the files in the torrent.
All of these classes and functions are declared by including
``libtorrent/create_torrent.hpp``.
high level example
==================
::
file_storage fs;
// recursively adds files in directories
add_files(fs, "./my_torrent");
create_torrent t(fs);
t.add_tracker("http://my.tracker.com/announce");
t.set_creator("libtorrent example");
// reads the files and calculates the hashes
set_piece_hashes(t, ".");
ofstream out("my_torrent.torrent", std::ios_base::binary);
bencode(std::ostream_iterator<char>(out), t.generate());
add_files
=========
::
template <class Pred>
void add_files(file_storage& fs, boost::filesystem::path const& path, Pred p);
void add_files(file_storage& fs, boost::filesystem::path const& path);
Adds the file specified by ``path`` to the ``file_storage`` object. In case ``path``
refers to a diretory, files will be added recursively from the directory.
If specified, the predicate ``p`` is called once for every file and directory that
is encountered. files for which ``p`` returns true are added, and directories for
which ``p`` returns true are traversed. ``p`` must have the following signature::
bool Pred(boost::filesystem::path const& p);
The path that is passed in to the predicate is the full path of the file or
directory. If no predicate is specified, all files are added, and all directories
are traveresed.
The ".." directory is never traversed.
set_piece_hashes()
==================
::
template <class Fun>
void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f);
void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p);
This function will assume that the files added to the torrent file exists at path
``p``, read those files and hash the content and set the hashes in the ``create_torrent``
object. The optional function ``f`` is called in between every hash that is set. ``f``
must have the following signature::
void Fun(int);
file_storage
============
The ``file_storage`` class represents a file list and the piece
size. Everything necessary to interpret a regular bittorrent storage
file structure. Its synopsis::
class file_storage
{
public:
bool is_valid() const;
void add_file(file_entry const& e);
void add_file(fs::path const& p, size_type size);
void rename_file(int index, std::string const& new_filename);
std::vector<file_slice> map_block(int piece, size_type offset
, int size) const;
peer_request map_file(int file, size_type offset, int size) const;
typedef std::vector<file_entry>::const_iterator iterator;
typedef std::vector<file_entry>::const_reverse_iterator reverse_iterator;
iterator begin() const;
iterator end() const;
reverse_iterator rbegin();
reverse_iterator rend() const;
int num_files() const;
file_entry const& at(int index) const;
size_type total_size() const;
void set_num_pieces(int n);
int num_pieces() const;
void set_piece_length(int l);
int piece_length() const;
int piece_size(int index) const;
void set_name(std::string const& n);
const std::string& name() const;
void swap(file_storage& ti);
}
create_torrent
==============
The ``create_torrent`` class has the following synopsis::
struct create_torrent
{
create_torrent(file_storage& fs, int piece_size);
create_torrent(file_storage& fs);
entry generate() const;
file_storage const& files() const;
void set_comment(char const* str);
void set_creator(char const* str);
void set_hash(int index, sha1_hash const& h);
void add_url_seed(std::string const& url);
void add_node(std::pair<std::string, int> const& node);
void add_tracker(std::string const& url, int tier = 0);
int num_pieces() const;
int piece_length() const;
int piece_size(int i) const;
};
create_torrent()
----------------
::
create_torrent(file_storage& fs, int piece_size);
create_torrent(file_storage& fs);
The contrstructor that does not take a piece_size will calculate
a piece size such that the torrent file is roughly 40 kB.
generate()
----------
::
entry generate() const;
This function will generate the .torrent file as a bencode tree. In order to
generate the flat file, use the bencode() function.
It may be useful to add custom entries to the torrent file before bencoding it
and saving it to disk.

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,10 @@ The basic usage is as follows:
Each class and function is described in this manual.
For a description on how to create torrent files, see make_torrent_.
.. _make_torrent: make_torrent.html
network primitives
==================
@ -221,8 +225,7 @@ add_torrent()
::
typedef storage_interface* (&storage_constructor_type)(
boost::intrusive_ptr<torrent_info const>, fs::path const&
, file_pool&);
file_storage const&, fs::path const&, file_pool&);
struct add_torrent_params
{
@ -1090,6 +1093,10 @@ key is found, the return a pointer to it.
torrent_info
============
In previous versions of libtorrent, this class was also used for creating
torrent files. This functionality has been moved to ``create_torrent``, see
make_torrent_.
The ``torrent_info`` has the following synopsis::
class torrent_info
@ -1102,27 +1109,25 @@ The ``torrent_info`` has the following synopsis::
torrent_info(char const* filename);
void add_tracker(std::string const& url, int tier = 0);
std::vector<announce_entry> const& trackers() const;
typedef std::vector<file_entry>::const_iterator file_iterator;
typedef std::vector<file_entry>::const_reverse_iterator
reverse_file_iterator;
file_storage const& files() const;
bool remap_files(std::vector<file_entry> const& map);
typedef file_storage::iterator file_iterator;
typedef file_storage::reverse_iterator reverse_file_iterator;
file_iterator begin_files(bool storage = false) const;
file_iterator end_files(bool storage = false) const;
reverse_file_iterator rbegin_files(bool storage = false) const;
reverse_file_iterator rend_files(bool storage = false) const;
file_iterator begin_files() const;
file_iterator end_files() const;
reverse_file_iterator rbegin_files() const;
reverse_file_iterator rend_files() const;
int num_files(bool storage = false) const;
file_entry const& file_at(int index, bool storage = false) const;
int num_files() const;
file_entry const& file_at(int index) const;
std::vector<file_slice> map_block(int piece, size_type offset
, int size, bool storage = false) const;
, int size) const;
peer_request map_file(int file_index, size_type file_offset
, int size, bool storage = false) const;
std::vector<announce_entry> const& trackers() const;
, int size) const;
bool priv() const;
@ -1188,52 +1193,37 @@ add_tracker()
``add_tracker()`` adds a tracker to the announce-list. The ``tier`` determines the order in
which the trackers are to be tried. For more information see `trackers()`_.
remap_files()
-------------
files()
-------
::
bool remap_files(std::vector<file_entry> const& map);
file_storage const& file() const;
This call will create a new mapping of the data in this torrent to other files. The
``torrent_info`` maintains 2 views of the file storage. One that is true to the torrent
file, and one that represents what is actually saved on disk. This call will change
what the files on disk are called.
The each entry in the vector ``map`` is a ``file_entry``. The only fields in this struct
that are used in this case are ``path``, ``size`` and ``file_base``.
The return value indicates if the remap was successful or not. True means success and
false means failure. The sum of all the files passed in through ``map`` has to be exactly
the same as the total_size of the torrent. If the number of bytes that are mapped do not
match, false will be returned (this is the only case this function may fail).
Changing this mapping for an existing torrent will not move or rename files. If some files
should be renamed, this can be done before the torrent is added.
The ``file_storage`` object contains the information on how to map the pieces to
files. It is separated from the ``torrent_info`` object because when creating torrents
a storage object needs to be created without having a torrent file. When renaming files
in a storage, the storage needs to make its own copy of the ``file_storage`` in order
to make its mapping differ from the one in the torrent file.
For more information on the ``file_storage`` object, see the separate document on how
to create torrents.
begin_files() end_files() rbegin_files() rend_files()
-----------------------------------------------------
::
file_iterator begin_files(bool storage = false) const;
file_iterator end_files(bool storage = false) const;
reverse_file_iterator rbegin_files(bool storage = false) const;
reverse_file_iterator rend_files(bool storage = false) const;
file_iterator begin_files() const;
file_iterator end_files() const;
reverse_file_iterator rbegin_files() const;
reverse_file_iterator rend_files() const;
This class will need some explanation. First of all, to get a list of all files
in the torrent, you can use ``begin_files()``, ``end_files()``,
``rbegin_files()`` and ``rend_files()``. These will give you standard vector
iterators with the type ``file_entry``.
The ``storage`` parameter specifies which view of the files you want. The default
is false, which means you will see the content of the torrent file. If set to
true, you will see the file that the storage class uses to save the files to
disk. Typically these views are the same, but in case the files have been
remapped, they may differ. For more info, see `remap_files()`_.
::
struct file_entry
@ -1274,27 +1264,20 @@ num_files() file_at()
::
int num_files(bool storage = false) const;
file_entry const& file_at(int index, bool storage = false) const;
int num_files() const;
file_entry const& file_at(int index) const;
If you need index-access to files you can use the ``num_files()`` and ``file_at()``
to access files using indices.
The ``storage`` parameter specifies which view of the files you want. The default
is false, which means you will see the content of the torrent file. If set to
true, you will see the file that the storage class uses to save the files to
disk. Typically these views are the same, but in case the files have been
remapped, they may differ. For more info, see `remap_files()`_.
map_block()
-----------
::
std::vector<file_slice> map_block(int piece, size_type offset
, int size, bool storage = false) const;
, int size) const;
This function will map a piece index, a byte offset within that piece and
a size (in bytes) into the corresponding files with offsets where that data
@ -1316,12 +1299,6 @@ as argument. The ``offset`` is the byte offset in the file where the range
starts, and ``size`` is the number of bytes this range is. The size + offset
will never be greater than the file size.
The ``storage`` parameter specifies which view of the files you want. The default
is false, which means you will see the content of the torrent file. If set to
true, you will see the file that the storage class uses to save the files to
disk. Typically these views are the same, but in case the files have been
remapped, they may differ. For more info, see `remap_files()`_.
map_file()
----------
@ -1329,7 +1306,7 @@ map_file()
::
peer_request map_file(int file_index, size_type file_offset
, int size, bool storage = false) const;
, int size) const;
This function will map a range in a specific file into a range in the torrent.
The ``file_offset`` parameter is the offset in the file, given in bytes, where
@ -1366,12 +1343,6 @@ vector of those urls. If you're creating a torrent file, ``add_url_seed()``
adds one url to the list of url-seeds. Currently, the only transport protocol
supported for the url is http.
The ``storage`` parameter specifies which view of the files you want. The default
is false, which means you will see the content of the torrent file. If set to
true, you will see the file that the storage class uses to save the files to
disk. Typically these views are the same, but in case the files have been
remapped, they may differ. For more info, see `remap_files()`_.
See `HTTP seeding`_ for more information.

View File

@ -41,38 +41,38 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/file.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/create_torrent.hpp"
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/bind.hpp>
using namespace boost::filesystem;
using namespace libtorrent;
void add_files(create_torrent& t, path const& p, path const& l)
// do not include files and folders whose
// name starts with a .
bool file_filter(boost::filesystem::path const& filename)
{
if (l.leaf()[0] == '.') return;
path f(p / l);
if (is_directory(f))
{
for (directory_iterator i(f), end; i != end; ++i)
add_files(t, p, l / i->leaf());
}
else
{
std::cerr << "adding \"" << l.string() << "\"\n";
t.add_file(l, file_size(f));
}
if (filename.leaf()[0] == '.') return false;
std::cerr << filename << std::endl;
return true;
}
void print_progress(int i, int num)
{
std::cerr << "\r" << (i+1) << "/" << num;
}
int main(int argc, char* argv[])
{
using namespace libtorrent;
using namespace boost::filesystem;
int piece_size = 256 * 1024;
char const* creator_str = "libtorrent";
path::default_name_check(no_check);
if (argc != 4 && argc != 5)
@ -87,40 +87,20 @@ int main(int argc, char* argv[])
try
{
#endif
create_torrent t;
file_storage fs;
file_pool fp;
path full_path = complete(path(argv[3]));
int piece_size = 256 * 1024;
char const* creator_str = "libtorrent";
add_files(fs, full_path, file_filter);
add_files(t, full_path.branch_path(), full_path.leaf());
t.set_piece_size(piece_size);
create_torrent t(fs, piece_size);
t.add_tracker(argv[2]);
std::vector<char> tmp;
bencode(std::back_inserter(tmp), t.generate());
boost::intrusive_ptr<torrent_info> info(new torrent_info(&tmp[0], tmp.size()));
file_pool fp;
boost::scoped_ptr<storage_interface> st(
default_storage_constructor(info, full_path.branch_path(), fp));
// calculate the hash for all pieces
int num = t.num_pieces();
std::vector<char> buf(piece_size);
for (int i = 0; i < num; ++i)
{
st->read(&buf[0], i, 0, t.piece_size(i));
hasher h(&buf[0], t.piece_size(i));
t.set_hash(i, h.final());
std::cerr << "\r" << (i+1) << "/" << num;
}
set_piece_hashes(t, full_path.branch_path()
, boost::bind(&print_progress, _1, t.num_pieces()));
std::cerr << std::endl;
t.set_creator(creator_str);
if (argc == 5)
t.add_url_seed(argv[4]);
if (argc == 5) t.add_url_seed(argv[4]);
// create the torrent and print it to out
ofstream out(complete(path(argv[1])), std::ios_base::binary);

View File

@ -20,6 +20,7 @@ libtorrent/escape_string.hpp \
libtorrent/extensions.hpp \
libtorrent/file.hpp \
libtorrent/file_pool.hpp \
libtorrent/file_storage.hpp \
libtorrent/fingerprint.hpp \
libtorrent/GeoIP.h \
libtorrent/gzip.hpp \

View File

@ -53,6 +53,21 @@ namespace libtorrent
torrent_handle handle;
};
struct TORRENT_EXPORT file_renamed_alert: torrent_alert
{
file_renamed_alert(torrent_handle const& h
, std::string const& name_
, std::string const& msg)
: torrent_alert(h, alert::warning, msg)
, name(name_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new file_renamed_alert(*this)); }
std::string name;
};
struct TORRENT_EXPORT tracker_alert: torrent_alert
{
tracker_alert(torrent_handle const& h

View File

@ -35,6 +35,12 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/bencode.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/file_storage.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/hasher.hpp"
#include <vector>
#include <string>
#include <utility>
@ -44,8 +50,10 @@ POSSIBILITY OF SUCH DAMAGE.
#endif
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/optional.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
@ -56,26 +64,29 @@ namespace libtorrent
namespace fs = boost::filesystem;
namespace pt = boost::posix_time;
struct create_torrent
struct TORRENT_EXPORT create_torrent
{
create_torrent();
create_torrent(file_storage& fs, int piece_size);
create_torrent(file_storage& fs);
entry generate() const;
file_storage const& files() const { return m_files; }
void set_comment(char const* str);
void set_creator(char const* str);
void set_piece_size(int size);
void set_hash(int index, sha1_hash const& h);
void add_file(fs::path file, size_type size);
void add_url_seed(std::string const& url);
void add_node(std::pair<std::string, int> const& node);
void add_tracker(std::string const& url, int tier = 0);
int num_pieces() const { return m_num_pieces; }
int piece_length() const { return m_piece_length; }
int piece_size(int i) const;
int num_pieces() const { return m_files.num_pieces(); }
int piece_length() const { return m_files.piece_length(); }
int piece_size(int i) const { return m_files.piece_size(i); }
private:
file_storage& m_files;
// the urls to the trackers
typedef std::pair<std::string, int> announce_entry;
std::vector<announce_entry> m_urls;
@ -84,32 +95,15 @@ namespace libtorrent
std::vector<sha1_hash> m_piece_hash;
// the length of one piece
// if this is 0, the torrent_info is
// in an uninitialized state
int m_piece_length;
typedef std::pair<fs::path, size_type> file_entry;
// the list of files that this torrent consists of
std::vector<file_entry> m_files;
// dht nodes to add to the routing table/bootstrap from
typedef std::vector<std::pair<std::string, int> > nodes_t;
nodes_t m_nodes;
// the sum of all filesizes
size_type m_total_size;
// the number of pieces in the torrent
int m_num_pieces;
// the hash that identifies this torrent
// is mutable because it's calculated
// lazily
mutable sha1_hash m_info_hash;
std::string m_name;
// if a creation date is found in the torrent file
// this will be set to that, otherwise it'll be
// 1970, Jan 1
@ -134,6 +128,72 @@ namespace libtorrent
// be announced on the dht
bool m_private;
};
namespace detail
{
inline bool default_pred(boost::filesystem::path const&) { return true; }
inline void nop(int i) {}
template <class Pred>
void add_files_impl(file_storage& fs, boost::filesystem::path const& p
, boost::filesystem::path const& l, Pred pred)
{
using boost::filesystem::path;
using boost::filesystem::directory_iterator;
std::string const& leaf = l.leaf();
if (leaf == ".." || leaf == ".") return;
if (!pred(l)) return;
path f(p / l);
if (is_directory(f))
{
for (directory_iterator i(f), end; i != end; ++i)
add_files_impl(fs, p, l / i->leaf(), pred);
}
else
{
fs.add_file(l, file_size(f));
}
}
}
template <class Pred>
void add_files(file_storage& fs, boost::filesystem::path const& file, Pred p)
{
detail::add_files_impl(fs, complete(file).branch_path(), file.leaf(), p);
}
inline void add_files(file_storage& fs, boost::filesystem::path const& file)
{
detail::add_files_impl(fs, complete(file).branch_path(), file.leaf(), detail::default_pred);
}
template <class Fun>
void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f)
{
file_pool fp;
boost::scoped_ptr<storage_interface> st(
default_storage_constructor(const_cast<file_storage&>(t.files()), p, fp));
// calculate the hash for all pieces
int num = t.num_pieces();
std::vector<char> buf(t.piece_length());
for (int i = 0; i < num; ++i)
{
// read hits the disk and will block. Progress should
// be updated in between reads
st->read(&buf[0], i, 0, t.piece_size(i));
hasher h(&buf[0], t.piece_size(i));
t.set_hash(i, h.final());
f(i);
}
}
inline void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p)
{
set_piece_hashes(t, p, detail::nop);
}
}
#endif

View File

@ -82,6 +82,7 @@ namespace libtorrent
, check_fastresume
, check_files
, save_resume_data
, rename_file
};
action_t action;
@ -91,7 +92,7 @@ namespace libtorrent
boost::intrusive_ptr<piece_manager> storage;
// arguments used for read and write
int piece, offset;
// used for move_storage. On errors, this is set
// used for move_storage and rename_file. On errors, this is set
// to the error message
std::string str;

View File

@ -68,6 +68,7 @@ namespace libtorrent
boost::shared_ptr<file> open_file(void* st, fs::path const& p
, file::open_mode m, std::string& error);
void release(void* st);
void release(fs::path const& p);
void resize(int size);
private:

View File

@ -0,0 +1,146 @@
/*
Copyright (c) 2003-2008, Arvid Norberg
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.
*/
#ifndef TORRENT_FILE_STORAGE_HPP_INCLUDED
#define TORRENT_FILE_STORAGE_HPP_INCLUDED
#include <string>
#include <vector>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/filesystem/path.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/size_type.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/peer_request.hpp"
namespace libtorrent
{
namespace fs = boost::filesystem;
struct TORRENT_EXPORT file_entry
{
file_entry(): offset(0), size(0), file_base(0) {}
fs::path path;
size_type offset; // the offset of this file inside the torrent
size_type size; // the size of this file
// the offset in the file where the storage starts.
// This is always 0 unless parts of the torrent is
// compressed into a single file, such as a so-called part file.
size_type file_base;
};
struct TORRENT_EXPORT file_slice
{
int file_index;
size_type offset;
size_type size;
};
class TORRENT_EXPORT file_storage
{
friend class torrent_info;
public:
file_storage();
~file_storage() {}
bool is_valid() const { return m_piece_length > 0; }
void add_file(file_entry const& e);
void add_file(fs::path const& p, size_type size);
void rename_file(int index, std::string const& new_filename);
std::vector<file_slice> map_block(int piece, size_type offset
, int size) const;
peer_request map_file(int file, size_type offset, int size) const;
typedef std::vector<file_entry>::const_iterator iterator;
typedef std::vector<file_entry>::const_reverse_iterator reverse_iterator;
iterator begin() const { return m_files.begin(); }
iterator end() const { return m_files.end(); }
reverse_iterator rbegin() const { return m_files.rbegin(); }
reverse_iterator rend() const { return m_files.rend(); }
int num_files() const
{ return int(m_files.size()); }
file_entry const& at(int index) const
{
TORRENT_ASSERT(index >= 0 && index < int(m_files.size()));
return m_files[index];
}
size_type total_size() const { TORRENT_ASSERT(m_piece_length > 0); return m_total_size; }
void set_num_pieces(int n) { m_num_pieces = n; }
int num_pieces() const { TORRENT_ASSERT(m_piece_length > 0); return m_num_pieces; }
void set_piece_length(int l) { m_piece_length = l; }
int piece_length() const { TORRENT_ASSERT(m_piece_length > 0); return m_piece_length; }
int piece_size(int index) const;
void set_name(std::string const& n) { m_name = n; }
const std::string& name() const { TORRENT_ASSERT(m_piece_length > 0); return m_name; }
void swap(file_storage& ti)
{
using std::swap;
swap(ti.m_piece_length, m_piece_length);
swap(ti.m_files, m_files);
swap(ti.m_total_size, m_total_size);
swap(ti.m_num_pieces, m_num_pieces);
swap(ti.m_name, m_name);
}
private:
int m_piece_length;
// the list of files that this torrent consists of
std::vector<file_entry> m_files;
// the sum of all filesizes
size_type m_total_size;
// the number of pieces in the torrent
int m_num_pieces;
std::string m_name;
};
}
#endif // TORRENT_FILE_STORAGE_HPP_INCLUDED

View File

@ -87,11 +87,11 @@ namespace libtorrent
#endif
TORRENT_EXPORT std::vector<std::pair<size_type, std::time_t> > get_filesizes(
torrent_info const& t
file_storage const& t
, fs::path p);
TORRENT_EXPORT bool match_filesizes(
torrent_info const& t
file_storage const& t
, fs::path p
, std::vector<std::pair<size_type, std::time_t> > const& sizes
, bool compact_mode
@ -157,6 +157,9 @@ namespace libtorrent
// non-zero return value indicates an error
virtual bool release_files() = 0;
// this will rename the file specified by index.
virtual bool rename_file(int index, std::string const& new_filename) = 0;
// this will close all open files and delete them
// non-zero return value indicates an error
virtual bool delete_files() = 0;
@ -178,16 +181,12 @@ namespace libtorrent
};
typedef storage_interface* (&storage_constructor_type)(
boost::intrusive_ptr<torrent_info const>, fs::path const&
, file_pool&);
file_storage const&, fs::path const&, file_pool&);
TORRENT_EXPORT storage_interface* default_storage_constructor(
boost::intrusive_ptr<torrent_info const> ti
, fs::path const& path, file_pool& fp);
file_storage const&, fs::path const&, file_pool&);
TORRENT_EXPORT storage_interface* mapped_storage_constructor(
boost::intrusive_ptr<torrent_info const> ti
, fs::path const& path, file_pool& fp);
file_storage const&, fs::path const&, file_pool&);
struct disk_io_thread;
@ -201,7 +200,7 @@ namespace libtorrent
piece_manager(
boost::shared_ptr<void> const& torrent
, boost::intrusive_ptr<torrent_info const> ti
, boost::intrusive_ptr<torrent_info const> info
, fs::path const& path
, file_pool& fp
, disk_io_thread& io
@ -210,8 +209,7 @@ namespace libtorrent
~piece_manager();
torrent_info const* info() const { return m_info.get(); }
boost::intrusive_ptr<torrent_info const> info() const { return m_info; }
void write_resume_data(entry& rd) const;
void async_check_fastresume(entry const* resume_data
@ -219,6 +217,9 @@ namespace libtorrent
void async_check_files(boost::function<void(int, disk_io_job const&)> const& handler);
void async_rename_file(int index, std::string const& name
, boost::function<void(int, disk_io_job const&)> const& handler);
void async_read(
peer_request const& r
, boost::function<void(int, disk_io_job const&)> const& handler
@ -316,6 +317,8 @@ namespace libtorrent
int release_files_impl() { return m_storage->release_files(); }
int delete_files_impl() { return m_storage->delete_files(); }
int rename_file_impl(int index, std::string const& new_filename)
{ return m_storage->rename_file(index, new_filename); }
bool move_storage_impl(fs::path const& save_path);
@ -326,12 +329,13 @@ namespace libtorrent
void debug_log() const;
#endif
#endif
boost::intrusive_ptr<torrent_info const> m_info;
file_storage const& m_files;
boost::scoped_ptr<storage_interface> m_storage;
storage_mode_t m_storage_mode;
boost::intrusive_ptr<torrent_info const> m_info;
// slots that haven't had any file storage allocated
std::vector<int> m_unallocated_slots;
// slots that have file storage, but isn't assigned to a piece

View File

@ -565,8 +565,14 @@ namespace libtorrent
int max_uploads() const { return m_max_uploads; }
void set_max_connections(int limit);
int max_connections() const { return m_max_connections; }
void move_storage(fs::path const& save_path);
// renames the file with the given index to the new name
// the name may include a directory path
// returns false on failure
bool rename_file(int index, std::string const& name);
// unless this returns true, new connections must wait
// with their initialization.
bool ready_for_connections() const
@ -590,6 +596,7 @@ namespace libtorrent
void on_torrent_paused(int ret, disk_io_job const& j);
void on_storage_moved(int ret, disk_io_job const& j);
void on_save_resume_data(int ret, disk_io_job const& j);
void on_file_renamed(int ret, disk_io_job const& j);
void on_piece_verified(int ret, disk_io_job const& j
, boost::function<void(int)> f);

View File

@ -442,6 +442,7 @@ namespace libtorrent
// post condition: save_path() == save_path if true is returned
void move_storage(fs::path const& save_path) const;
void rename_file(int index, fs::path const& new_name) const;
sha1_hash info_hash() const;

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2003, Arvid Norberg
Copyright (c) 2003-2008, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -30,9 +30,8 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_TORRENT_INFO_HPP_INCLUDE
#define TORRENT_TORRENT_INFO_HPP_INCLUDE
#ifndef TORRENT_TORRENT_INFO_HPP_INCLUDED
#define TORRENT_TORRENT_INFO_HPP_INCLUDED
#include <string>
#include <vector>
@ -44,7 +43,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/optional.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#ifdef _MSC_VER
@ -56,44 +54,17 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/peer_request.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/time.hpp"
#include "libtorrent/intrusive_ptr_base.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/file_storage.hpp"
namespace libtorrent
{
namespace pt = boost::posix_time;
namespace gr = boost::gregorian;
namespace fs = boost::filesystem;
struct TORRENT_EXPORT file_entry
{
file_entry(): offset(0), size(0), file_base(0) {}
fs::path path;
size_type offset; // the offset of this file inside the torrent
size_type size; // the size of this file
// the offset in the file where the storage starts.
// This is always 0 unless parts of the torrent is
// compressed into a single file, such as a so-called part file.
size_type file_base;
// if the path was incorrectly encoded, this is
// the original corrupt encoded string. It is
// preserved in order to be able to reproduce
// the correct info-hash
boost::shared_ptr<const fs::path> orig_path;
};
struct TORRENT_EXPORT file_slice
{
int file_index;
size_type offset;
size_type size;
};
struct TORRENT_EXPORT announce_entry
{
announce_entry(std::string const& u): url(u), tier(0) {}
@ -101,10 +72,12 @@ namespace libtorrent
int tier;
};
#ifndef BOOST_NO_EXCEPTIONS
struct TORRENT_EXPORT invalid_torrent_file: std::exception
{
virtual const char* what() const throw() { return "invalid torrent file"; }
};
#endif
class TORRENT_EXPORT torrent_info : public intrusive_ptr_base<torrent_info>
{
@ -116,108 +89,56 @@ namespace libtorrent
torrent_info(char const* filename);
~torrent_info();
file_storage const& files() const { return m_files; }
void add_tracker(std::string const& url, int tier = 0);
std::vector<announce_entry> const& trackers() const { return m_urls; }
bool remap_files(std::vector<file_entry> const& map);
std::vector<file_slice> map_block(int piece, size_type offset
, int size, bool storage = false) const;
peer_request map_file(int file, size_type offset, int size
, bool storage = false) const;
std::vector<std::string> const& url_seeds() const
{ return m_url_seeds; }
void add_url_seed(std::string const& url)
{ m_url_seeds.push_back(url); }
typedef std::vector<file_entry>::const_iterator file_iterator;
typedef std::vector<file_entry>::const_reverse_iterator reverse_file_iterator;
// list the files in the torrent file
file_iterator begin_files(bool storage = false) const
{
if (!storage || m_remapped_files.empty())
return m_files.begin();
else
return m_remapped_files.begin();
}
file_iterator end_files(bool storage = false) const
{
if (!storage || m_remapped_files.empty())
return m_files.end();
else
return m_remapped_files.end();
}
reverse_file_iterator rbegin_files(bool storage = false) const
{
if (!storage || m_remapped_files.empty())
return m_files.rbegin();
else
return m_remapped_files.rbegin();
}
reverse_file_iterator rend_files(bool storage = false) const
{
if (!storage || m_remapped_files.empty())
return m_files.rend();
else
return m_remapped_files.rend();
}
int num_files(bool storage = false) const
{
TORRENT_ASSERT(m_piece_length > 0);
if (!storage || m_remapped_files.empty())
return int(m_files.size());
else
return int(m_remapped_files.size());
}
file_entry const& file_at(int index, bool storage = false) const
{
if (!storage || m_remapped_files.empty())
{
TORRENT_ASSERT(index >= 0 && index < (int)m_files.size());
return m_files[index];
}
else
{
TORRENT_ASSERT(index >= 0 && index < (int)m_remapped_files.size());
return m_remapped_files[index];
}
}
std::vector<announce_entry> const& trackers() const { return m_urls; }
size_type total_size() const { TORRENT_ASSERT(m_piece_length > 0); return m_total_size; }
int piece_length() const { TORRENT_ASSERT(m_piece_length > 0); return m_piece_length; }
int num_pieces() const { TORRENT_ASSERT(m_piece_length > 0); return m_num_pieces; }
size_type total_size() const { return m_files.total_size(); }
int piece_length() const { return m_files.piece_length(); }
int num_pieces() const { return m_files.num_pieces(); }
const sha1_hash& info_hash() const { return m_info_hash; }
const std::string& name() const { TORRENT_ASSERT(m_piece_length > 0); return m_name; }
const std::string& name() const { return m_files.name(); }
typedef file_storage::iterator file_iterator;
typedef file_storage::reverse_iterator reverse_file_iterator;
file_iterator begin_files() const { return m_files.begin(); }
file_iterator end_files() const { return m_files.end(); }
reverse_file_iterator rbegin_files() const { return m_files.rbegin(); }
reverse_file_iterator rend_files() const { return m_files.rend(); }
int num_files() const { return m_files.num_files(); }
file_entry const& file_at(int index) const { return m_files.at(index); }
std::vector<file_slice> map_block(int piece, size_type offset, int size) const
{ return m_files.map_block(piece, offset, size); }
peer_request map_file(int file, size_type offset, int size) const
{ return m_files.map_file(file, offset, size); }
// ------- start deprecation -------
// these functions will be removed in a future version
torrent_info(entry const& torrent_file) TORRENT_DEPRECATED;
void print(std::ostream& os) const TORRENT_DEPRECATED;
// ------- end deprecation -------
bool is_valid() const { return m_piece_length > 0; }
bool is_valid() const { return m_files.is_valid(); }
bool priv() const { return m_private; }
int piece_size(int index) const;
int piece_size(int index) const { return m_files.piece_size(index); }
sha1_hash hash_for_piece(int index) const
{
return sha1_hash(hash_for_piece_ptr(index));
}
{ return sha1_hash(hash_for_piece_ptr(index)); }
char const* hash_for_piece_ptr(int index) const
{
TORRENT_ASSERT(index >= 0);
TORRENT_ASSERT(index < m_num_pieces);
TORRENT_ASSERT(index < m_files.num_pieces());
TORRENT_ASSERT(m_piece_hashes);
TORRENT_ASSERT(m_piece_hashes >= m_info_section.get());
TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size);
@ -261,41 +182,18 @@ namespace libtorrent
bool parse_torrent_file(lazy_entry const& libtorrent, std::string& error);
file_storage m_files;
// the urls to the trackers
std::vector<announce_entry> m_urls;
std::vector<std::string> m_url_seeds;
// the length of one piece
// if this is 0, the torrent_info is
// in an uninitialized state
int m_piece_length;
// the list of files that this torrent consists of
std::vector<file_entry> m_files;
// this vector is typically empty. If it is not
// empty, it means the user has re-mapped the
// files in this torrent to different names
// on disk. This is only used when reading and
// writing the disk.
std::vector<file_entry> m_remapped_files;
nodes_t m_nodes;
// the sum of all filesizes
size_type m_total_size;
// the number of pieces in the torrent
int m_num_pieces;
// the hash that identifies this torrent
// is mutable because it's calculated
// lazily
sha1_hash m_info_hash;
std::string m_name;
// if a creation date is found in the torrent file
// this will be set to that, otherwise it'll be
// 1970, Jan 1

View File

@ -24,7 +24,7 @@ logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp
socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \
disk_io_thread.cpp ut_metadata.cpp magnet_uri.cpp udp_socket.cpp smart_ban.cpp \
http_parser.cpp gzip.cpp disk_buffer_holder.cpp create_torrent.cpp GeoIP.c \
parse_url.cpp $(kademlia_sources)
parse_url.cpp file_storage.cpp $(kademlia_sources)
noinst_HEADERS = \
$(top_srcdir)/include/libtorrent/alert.hpp \
@ -51,6 +51,7 @@ $(top_srcdir)/include/libtorrent/extensions/logger.hpp \
$(top_srcdir)/include/libtorrent/extensions/ut_pex.hpp \
$(top_srcdir)/include/libtorrent/file.hpp \
$(top_srcdir)/include/libtorrent/file_pool.hpp \
$(top_srcdir)/include/libtorrent/file_storage.hpp \
$(top_srcdir)/include/libtorrent/fingerprint.hpp \
$(top_srcdir)/include/libtorrent/gzip.hpp \
$(top_srcdir)/include/libtorrent/hasher.hpp \

View File

@ -31,7 +31,8 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/storage.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
@ -41,22 +42,61 @@ namespace gr = boost::gregorian;
namespace libtorrent
{
create_torrent::create_torrent()
: m_piece_length(0)
, m_total_size(0)
, m_num_pieces(0)
, m_info_hash()
, m_name()
create_torrent::create_torrent(file_storage& fs, int size)
: m_files(fs)
, m_creation_date(pt::second_clock::universal_time())
, m_multifile(false)
, m_multifile(fs.num_files() > 1)
, m_private(false)
{}
{
TORRENT_ASSERT(fs.num_files() > 0);
if (!m_multifile && m_files.at(0).path.has_branch_path()) m_multifile = true;
// make sure the size is an even power of 2
#ifndef NDEBUG
for (int i = 0; i < 32; ++i)
{
if (size & (1 << i))
{
TORRENT_ASSERT((size & ~(1 << i)) == 0);
break;
}
}
#endif
m_files.set_piece_length(size);
m_files.set_num_pieces(static_cast<int>(
(m_files.total_size() + m_files.piece_length() - 1) / m_files.piece_length()));
m_piece_hash.resize(m_files.num_pieces());
}
create_torrent::create_torrent(file_storage& fs)
: m_files(fs)
, m_creation_date(pt::second_clock::universal_time())
, m_multifile(fs.num_files() > 1)
, m_private(false)
{
TORRENT_ASSERT(fs.num_files() > 0);
if (!m_multifile && m_files.at(0).path.has_branch_path()) m_multifile = true;
const int target_size = 40 * 1024;
int size = fs.total_size() / (target_size / 20);
for (int i = 4*1024*1024; i > 16*1024; i /= 2)
{
if (size < i) continue;
size = i;
break;
}
m_files.set_piece_length(size);
m_files.set_num_pieces(static_cast<int>(
(m_files.total_size() + m_files.piece_length() - 1) / m_files.piece_length()));
m_piece_hash.resize(m_files.num_pieces());
}
entry create_torrent::generate() const
{
TORRENT_ASSERT(m_piece_length > 0);
TORRENT_ASSERT(m_files.piece_length() > 0);
if (m_files.empty())
if (m_files.num_files() == 0)
{
// TODO: throw something here
// throw
@ -128,15 +168,13 @@ namespace libtorrent
}
entry& info = dict["info"];
info["name"] = m_name;
info["name"] = m_files.name();
if (m_private) info["private"] = 1;
if (!m_multifile)
{
info["length"] = m_files.front().second;
info["length"] = m_files.at(0).size;
}
else
{
@ -144,19 +182,19 @@ namespace libtorrent
{
entry& files = info["files"];
for (std::vector<file_entry>::const_iterator i = m_files.begin();
for (file_storage::iterator i = m_files.begin();
i != m_files.end(); ++i)
{
files.list().push_back(entry());
entry& file_e = files.list().back();
file_e["length"] = i->second;
file_e["length"] = i->size;
entry& path_e = file_e["path"];
TORRENT_ASSERT(i->first.has_branch_path());
TORRENT_ASSERT(*i->first.begin() == m_name);
TORRENT_ASSERT(i->path.has_branch_path());
TORRENT_ASSERT(*i->path.begin() == m_files.name());
for (fs::path::iterator j = boost::next(i->first.begin());
j != i->first.end(); ++j)
for (fs::path::iterator j = boost::next(i->path.begin());
j != i->path.end(); ++j)
{
path_e.list().push_back(entry(*j));
}
@ -164,7 +202,7 @@ namespace libtorrent
}
}
info["piece length"] = m_piece_length;
info["piece length"] = m_files.piece_length();
entry& pieces = info["pieces"];
std::string& p = pieces.string();
@ -183,21 +221,6 @@ namespace libtorrent
}
int create_torrent::piece_size(int index) const
{
TORRENT_ASSERT(index >= 0 && index < num_pieces());
if (index == num_pieces()-1)
{
int size = int(m_total_size
- (num_pieces() - 1) * piece_length());
TORRENT_ASSERT(size > 0);
TORRENT_ASSERT(size <= piece_length());
return int(size);
}
else
return piece_length();
}
void create_torrent::add_tracker(std::string const& url, int tier)
{
m_urls.push_back(announce_entry(url, tier));
@ -207,32 +230,6 @@ namespace libtorrent
, bind(&announce_entry::second, _1) < bind(&announce_entry::second, _2));
}
void create_torrent::set_piece_size(int size)
{
// make sure the size is an even power of 2
#ifndef NDEBUG
for (int i = 0; i < 32; ++i)
{
if (size & (1 << i))
{
TORRENT_ASSERT((size & ~(1 << i)) == 0);
break;
}
}
#endif
m_piece_length = size;
m_num_pieces = static_cast<int>(
(m_total_size + m_piece_length - 1) / m_piece_length);
int old_num_pieces = static_cast<int>(m_piece_hash.size());
m_piece_hash.resize(m_num_pieces);
for (int i = old_num_pieces; i < m_num_pieces; ++i)
{
m_piece_hash[i].clear();
}
}
void create_torrent::set_hash(int index, sha1_hash const& h)
{
TORRENT_ASSERT(index >= 0);
@ -240,46 +237,6 @@ namespace libtorrent
m_piece_hash[index] = h;
}
void create_torrent::add_file(fs::path file, size_type size)
{
// TORRENT_ASSERT(file.begin() != file.end());
if (!file.has_branch_path())
{
// you have already added at least one file with a
// path to the file (branch_path), which means that
// all the other files need to be in the same top
// directory as the first file.
TORRENT_ASSERT(m_files.empty());
TORRENT_ASSERT(!m_multifile);
m_name = file.string();
}
else
{
#ifndef NDEBUG
if (!m_files.empty())
TORRENT_ASSERT(m_name == *file.begin());
#endif
m_multifile = true;
m_name = *file.begin();
}
m_files.push_back(file_entry(file, size));
m_total_size += size;
if (m_piece_length == 0)
m_piece_length = 256 * 1024;
m_num_pieces = int((m_total_size + m_piece_length - 1) / m_piece_length);
int old_num_pieces = int(m_piece_hash.size());
m_piece_hash.resize(m_num_pieces);
if (m_num_pieces > old_num_pieces)
std::for_each(m_piece_hash.begin() + old_num_pieces
, m_piece_hash.end(), boost::bind(&sha1_hash::clear, _1));
}
void create_torrent::add_node(std::pair<std::string, int> const& node)
{
m_nodes.push_back(node);
@ -299,5 +256,6 @@ namespace libtorrent
{
m_created_by = str;
}
}

View File

@ -1049,6 +1049,13 @@ namespace libtorrent
ret = 0;
break;
}
case disk_io_job::rename_file:
{
#ifdef TORRENT_DISK_STATS
m_log << log_time() << " rename file" << std::endl;
#endif
ret = j.storage->rename_file_impl(j.piece, j.str);
}
}
#ifndef BOOST_NO_EXCEPTIONS
} catch (std::exception& e)

View File

@ -115,6 +115,16 @@ namespace libtorrent
return e.file_ptr;
}
void file_pool::release(fs::path const& p)
{
boost::mutex::scoped_lock l(m_mutex);
typedef nth_index<file_set, 0>::type path_view;
path_view& pt = get<0>(m_files);
path_view::iterator i = pt.find(p);
if (i != pt.end()) pt.erase(i);
}
void file_pool::release(void* st)
{
boost::mutex::scoped_lock l(m_mutex);

150
src/file_storage.cpp Normal file
View File

@ -0,0 +1,150 @@
/*
Copyright (c) 2003-2008, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/pch.hpp"
#include "libtorrent/file_storage.hpp"
namespace libtorrent
{
file_storage::file_storage()
: m_piece_length(0)
, m_total_size(0)
, m_num_pieces(0)
{}
int file_storage::piece_size(int index) const
{
TORRENT_ASSERT(index >= 0 && index < num_pieces());
if (index == num_pieces()-1)
{
int size = int(total_size()
- size_type(num_pieces() - 1) * piece_length());
TORRENT_ASSERT(size > 0);
TORRENT_ASSERT(size <= piece_length());
return int(size);
}
else
return piece_length();
}
void file_storage::rename_file(int index, std::string const& new_filename)
{
TORRENT_ASSERT(index >= 0 && index < int(m_files.size()));
m_files[index].path = new_filename;
}
std::vector<file_slice> file_storage::map_block(int piece, size_type offset
, int size_) const
{
TORRENT_ASSERT(num_files() > 0);
std::vector<file_slice> ret;
size_type start = piece * (size_type)m_piece_length + offset;
size_type size = size_;
TORRENT_ASSERT(start + size <= m_total_size);
// find the file iterator and file offset
// TODO: make a vector that can map piece -> file index in O(1)
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
int counter = 0;
for (file_iter = begin();; ++counter, ++file_iter)
{
TORRENT_ASSERT(file_iter != end());
if (file_offset < file_iter->size)
{
file_slice f;
f.file_index = counter;
f.offset = file_offset + file_iter->file_base;
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
size -= f.size;
file_offset += f.size;
ret.push_back(f);
}
TORRENT_ASSERT(size >= 0);
if (size <= 0) break;
file_offset -= file_iter->size;
}
return ret;
}
peer_request file_storage::map_file(int file_index, size_type file_offset
, int size) const
{
TORRENT_ASSERT(file_index < num_files());
TORRENT_ASSERT(file_index >= 0);
size_type offset = file_offset + at(file_index).offset;
peer_request ret;
ret.piece = int(offset / piece_length());
ret.start = int(offset - ret.piece * piece_length());
ret.length = size;
return ret;
}
void file_storage::add_file(fs::path const& file, size_type size)
{
TORRENT_ASSERT(size >= 0);
if (!file.has_branch_path())
{
// you have already added at least one file with a
// path to the file (branch_path), which means that
// all the other files need to be in the same top
// directory as the first file.
TORRENT_ASSERT(m_files.empty());
m_name = file.string();
}
else
{
if (m_files.empty())
m_name = *file.begin();
}
TORRENT_ASSERT(m_name == *file.begin());
file_entry e;
m_files.push_back(e);
m_files.back().size = size;
m_files.back().path = file;
m_files.back().offset = m_total_size;
m_total_size += size;
}
void file_storage::add_file(file_entry const& e)
{
add_file(e.path, e.size);
}
}

View File

@ -262,8 +262,8 @@ namespace libtorrent
struct mapped_storage: storage_interface
{
mapped_storage(boost::intrusive_ptr<torrent_info const> const& info, fs::path save_path)
: m_info(info)
mapped_storage(file_storage const& fs, fs::path save_path)
: m_files(fs)
, m_save_path(save_path)
{}
@ -272,9 +272,9 @@ namespace libtorrent
int read(char* buf, int slot, int offset, int size)
{
TORRENT_ASSERT(buf != 0);
TORRENT_ASSERT(slot >= 0 && slot < m_info->num_pieces());
TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces());
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(offset < m_info->piece_size(slot));
TORRENT_ASSERT(offset < m_files.piece_size(slot));
TORRENT_ASSERT(size > 0);
size_type result = -1;
@ -283,17 +283,17 @@ namespace libtorrent
#ifndef NDEBUG
std::vector<file_slice> slices
= m_info->map_block(slot, offset, size, true);
= files().map_block(slot, offset, size);
TORRENT_ASSERT(!slices.empty());
#endif
size_type start = slot * (size_type)m_info->piece_length() + offset;
TORRENT_ASSERT(start + size <= m_info->total_size());
size_type start = slot * (size_type)m_files.piece_length() + offset;
TORRENT_ASSERT(start + size <= m_files.total_size());
// find the file iterator and file offset
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
for (file_iter = m_info->begin_files(true);;)
for (file_iter = files().begin();;)
{
if (file_offset < file_iter->size)
break;
@ -333,7 +333,7 @@ namespace libtorrent
TORRENT_ASSERT(int(slices.size()) > counter);
size_type slice_size = slices[counter].size;
TORRENT_ASSERT(slice_size == read_bytes);
TORRENT_ASSERT(m_info->file_at(slices[counter].file_index, true).path
TORRENT_ASSERT(files().at(slices[counter].file_index).path
== file_iter->path);
#endif
@ -353,7 +353,7 @@ namespace libtorrent
{
++file_iter;
// skip empty files
while (file_iter != m_info->end_files(true) && file_iter->size == 0)
while (file_iter != files().end() && file_iter->size == 0)
++file_iter;
#ifndef NDEBUG
@ -390,24 +390,24 @@ namespace libtorrent
int write(const char* buf, int slot, int offset, int size)
{
TORRENT_ASSERT(buf != 0);
TORRENT_ASSERT(slot >= 0 && slot < m_info->num_pieces());
TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces());
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(offset < m_info->piece_size(slot));
TORRENT_ASSERT(offset < m_files.piece_size(slot));
TORRENT_ASSERT(size > 0);
#ifndef NDEBUG
std::vector<file_slice> slices
= m_info->map_block(slot, offset, size, true);
= files().map_block(slot, offset, size);
TORRENT_ASSERT(!slices.empty());
#endif
size_type start = slot * (size_type)m_info->piece_length() + offset;
TORRENT_ASSERT(start + size <= m_info->total_size());
size_type start = slot * (size_type)m_files.piece_length() + offset;
TORRENT_ASSERT(start + size <= m_files.total_size());
// find the file iterator and file offset
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
for (file_iter = m_info->begin_files(true);;)
for (file_iter = files().begin();;)
{
if (file_offset < file_iter->size)
break;
@ -449,7 +449,7 @@ namespace libtorrent
TORRENT_ASSERT(int(slices.size()) > counter);
size_type slice_size = slices[counter].size;
TORRENT_ASSERT(slice_size == write_bytes);
TORRENT_ASSERT(m_info->file_at(slices[counter].file_index, true).path
TORRENT_ASSERT(files().at(slices[counter].file_index).path
== file_iter->path);
#endif
@ -468,7 +468,7 @@ namespace libtorrent
if (left_to_write > 0)
{
++file_iter;
while (file_iter != m_info->end_files(true) && file_iter->size == 0)
while (file_iter != files().end() && file_iter->size == 0)
++file_iter;
#ifndef NDEBUG
// empty files are not returned by map_block, so if
@ -533,11 +533,11 @@ namespace libtorrent
m_pool.release(this);
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400
old_path = safe_convert((m_save_path / m_info->name()).string());
new_path = safe_convert((save_path / m_info->name()).string());
old_path = safe_convert((m_save_path / files().name()).string());
new_path = safe_convert((save_path / files().name()).string());
#else
old_path = m_save_path / m_info->name();
new_path = save_path / m_info->name();
old_path = m_save_path / files().name();
new_path = save_path / files().name();
#endif
try
@ -604,7 +604,7 @@ namespace libtorrent
}
entry::list_type const& slots = slots_ent->list();
bool seed = int(slots.size()) == m_info->num_pieces()
bool seed = int(slots.size()) == files().num_pieces()
&& std::find_if(slots.begin(), slots.end()
, boost::bind<bool>(std::less<int>()
, boost::bind((size_type const& (entry::*)() const)
@ -617,11 +617,11 @@ namespace libtorrent
if (seed)
{
if (m_info->num_files(true) != (int)file_sizes.size())
if (files().num_files() != (int)file_sizes.size())
{
error = "the number of files does not match the torrent (num: "
+ boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
+ boost::lexical_cast<std::string>(m_info->num_files(true)) + ")";
+ boost::lexical_cast<std::string>(files().num_files()) + ")";
return false;
}
@ -629,8 +629,8 @@ namespace libtorrent
fs = file_sizes.begin();
// the resume data says we have the entire torrent
// make sure the file sizes are the right ones
for (torrent_info::file_iterator i = m_info->begin_files(true)
, end(m_info->end_files(true)); i != end; ++i, ++fs)
for (file_storage::iterator i = files().begin()
, end(files().end()); i != end; ++i, ++fs)
{
if (i->size != fs->first)
{
@ -643,7 +643,7 @@ namespace libtorrent
return true;
}
return match_filesizes(*m_info, m_save_path, file_sizes
return match_filesizes(files(), m_save_path, file_sizes
, !full_allocation_mode, &error);
}
@ -655,7 +655,7 @@ namespace libtorrent
return true;
}
std::vector<std::pair<size_type, std::time_t> > file_sizes
= get_filesizes(*m_info, m_save_path);
= get_filesizes(m_files, m_save_path);
entry::list_type& fl = rd["file sizes"].list();
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
@ -672,7 +672,7 @@ namespace libtorrent
bool move_slot(int src_slot, int dst_slot)
{
// TODO: this can be optimized by mapping both slots and do a straight memcpy
int piece_size = m_info->piece_size(dst_slot);
int piece_size = m_files.piece_size(dst_slot);
m_scratch_buffer.resize(piece_size);
size_type ret1 = read(&m_scratch_buffer[0], src_slot, 0, piece_size);
size_type ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
@ -683,9 +683,9 @@ namespace libtorrent
{
// TODO: this can be optimized by mapping both slots and do a straight memcpy
// the size of the target slot is the size of the piece
int piece_size = m_info->piece_length();
int piece1_size = m_info->piece_size(slot2);
int piece2_size = m_info->piece_size(slot1);
int piece_size = m_files.piece_length();
int piece1_size = m_files.piece_size(slot2);
int piece2_size = m_files.piece_size(slot1);
m_scratch_buffer.resize(piece_size * 2);
size_type ret1 = read(&m_scratch_buffer[0], slot1, 0, piece1_size);
size_type ret2 = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
@ -699,10 +699,10 @@ namespace libtorrent
{
// TODO: this can be optimized by mapping both slots and do a straight memcpy
// the size of the target slot is the size of the piece
int piece_size = m_info->piece_length();
int piece1_size = m_info->piece_size(slot2);
int piece2_size = m_info->piece_size(slot3);
int piece3_size = m_info->piece_size(slot1);
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);
m_scratch_buffer.resize(piece_size * 2);
size_type ret1 = read(&m_scratch_buffer[0], slot1, 0, piece1_size);
size_type ret2 = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
@ -745,6 +745,39 @@ namespace libtorrent
#endif
}
bool rename_file(int index, std::string const& new_filename)
{
if (index < 0 || index >= m_files.num_files()) return true;
fs::path old_name = m_save_path / files().at(index).path;
m_pool.release(this);
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400
fs::wpath old_path = safe_convert(old_name);
fs::wpath new_path = safe_convert(m_save_path / new_filename);
#else
fs::path const& old_path = old_name;
fs::path new_path = m_save_path / new_filename;
#endif
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
rename(old_path, new_path);
if (!m_mapped_files)
{ m_mapped_files.reset(new file_storage(m_files)); }
m_mapped_files->rename_file(index, new_filename);
#ifndef BOOST_NO_EXCEPTIONS
}
catch (std::exception& e)
{
set_error(old_name.string(), e.what());
return true;
}
#endif
return false;
}
bool release_files()
{
m_pool.release(this);
@ -764,8 +797,8 @@ namespace libtorrent
// delete the files from disk
std::set<std::string> directories;
typedef std::set<std::string>::iterator iter_t;
for (torrent_info::file_iterator i = m_info->begin_files(true)
, end(m_info->end_files(true)); i != end; ++i)
for (file_storage::iterator i = m_files.begin()
, end(m_files.end()); i != end; ++i)
{
std::string p = (m_save_path / i->path).string();
fs::path bp = i->path.branch_path();
@ -804,7 +837,10 @@ namespace libtorrent
private:
boost::intrusive_ptr<torrent_info const> m_info;
file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; }
boost::scoped_ptr<file_storage> m_mapped_files;
file_storage const& m_files;
fs::path m_save_path;
// temporary storage for moving pieces
@ -813,10 +849,10 @@ namespace libtorrent
static mapped_file_pool m_pool;
};
storage_interface* mapped_storage_constructor(boost::intrusive_ptr<torrent_info const> ti
storage_interface* mapped_storage_constructor(file_storage const& fs
, fs::path const& path, file_pool& fp)
{
return new mapped_storage(ti, path);
return new mapped_storage(fs, path);
}
mapped_file_pool mapped_storage::m_pool;

View File

@ -1661,7 +1661,7 @@ namespace aux {
{
TORRENT_ASSERT(!params.save_path.empty());
if (params.ti && params.ti->num_files() == 0)
if (params.ti && params.ti->files().num_files() == 0)
{
#ifndef BOOST_NO_EXCEPTIONS
throw std::runtime_error("no files in torrent");

View File

@ -251,12 +251,12 @@ namespace libtorrent
{
std::vector<std::pair<size_type, std::time_t> > get_filesizes(
torrent_info const& t, fs::path p)
file_storage const& s, fs::path p)
{
p = complete(p);
std::vector<std::pair<size_type, std::time_t> > sizes;
for (torrent_info::file_iterator i = t.begin_files(true);
i != t.end_files(true); ++i)
for (file_storage::iterator i = s.begin()
, end(s.end());i != end; ++i)
{
size_type size = 0;
std::time_t time = 0;
@ -289,13 +289,13 @@ namespace libtorrent
// pieces, so any older version of the resume data will
// still be a correct subset of the actual data on disk.
bool match_filesizes(
torrent_info const& t
file_storage const& fs
, fs::path p
, std::vector<std::pair<size_type, std::time_t> > const& sizes
, bool compact_mode
, std::string* error)
{
if ((int)sizes.size() != t.num_files(true))
if ((int)sizes.size() != fs.num_files())
{
if (error) *error = "mismatching number of files";
return false;
@ -304,8 +304,8 @@ namespace libtorrent
std::vector<std::pair<size_type, std::time_t> >::const_iterator s
= sizes.begin();
for (torrent_info::file_iterator i = t.begin_files(true);
i != t.end_files(true); ++i, ++s)
for (file_storage::iterator i = fs.begin()
, end(fs.end());i != end; ++i, ++s)
{
size_type size = 0;
std::time_t time = 0;
@ -354,15 +354,16 @@ namespace libtorrent
class storage : public storage_interface, boost::noncopyable
{
public:
storage(boost::intrusive_ptr<torrent_info const> info, fs::path const& path, file_pool& fp)
: m_info(info)
, m_files(fp)
storage(file_storage const& fs, fs::path const& path, file_pool& fp)
: m_files(fs)
, m_pool(fp)
{
TORRENT_ASSERT(info->begin_files(true) != info->end_files(true));
TORRENT_ASSERT(m_files.begin() != m_files.end());
m_save_path = fs::complete(path);
TORRENT_ASSERT(m_save_path.is_complete());
}
bool rename_file(int index, std::string const& new_filename);
bool release_files();
bool delete_files();
bool initialize(bool allocate_files);
@ -379,14 +380,18 @@ namespace libtorrent
int read_impl(char* buf, int slot, int offset, int size, bool fill_zero);
~storage()
{ m_files.release(this); }
{ m_pool.release(this); }
file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; }
boost::scoped_ptr<file_storage> m_mapped_files;
file_storage const& m_files;
boost::intrusive_ptr<torrent_info const> m_info;
fs::path m_save_path;
// the file pool is typically stored in
// the session, to make all storage
// instances use the same pool
file_pool& m_files;
file_pool& m_pool;
// temporary storage for moving pieces
buffer m_scratch_buffer;
@ -399,7 +404,7 @@ namespace libtorrent
hasher whole;
int slot_size1 = piece_size;
m_scratch_buffer.resize(slot_size1);
int read_result = read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, true);
read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, true);
if (ph.offset > 0)
partial.update(&m_scratch_buffer[0], ph.offset);
whole.update(&m_scratch_buffer[0], slot_size1);
@ -426,8 +431,8 @@ namespace libtorrent
{
// first, create all missing directories
fs::path last_path;
for (torrent_info::file_iterator file_iter = m_info->begin_files(true),
end_iter = m_info->end_files(true); file_iter != end_iter; ++file_iter)
for (file_storage::iterator file_iter = files().begin(),
end_iter = files().end(); file_iter != end_iter; ++file_iter)
{
fs::path dir = (m_save_path / file_iter->path).branch_path();
@ -475,7 +480,7 @@ namespace libtorrent
if (allocate_files)
{
std::string error;
boost::shared_ptr<file> f = m_files.open_file(this
boost::shared_ptr<file> f = m_pool.open_file(this
, m_save_path / file_iter->path, file::in | file::out
, error);
if (f && f->error().empty())
@ -491,13 +496,46 @@ namespace libtorrent
#endif
}
// close files that were opened in write mode
m_files.release(this);
m_pool.release(this);
return false;
}
bool storage::rename_file(int index, std::string const& new_filename)
{
if (index < 0 || index >= m_files.num_files()) return true;
fs::path old_name = m_save_path / files().at(index).path;
m_pool.release(old_name);
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400
fs::wpath old_path = safe_convert(old_name);
fs::wpath new_path = safe_convert(m_save_path / new_filename);
#else
fs::path const& old_path = old_name;
fs::path new_path = m_save_path / new_filename;
#endif
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
rename(old_path, new_path);
if (!m_mapped_files)
{ m_mapped_files.reset(new file_storage(m_files)); }
m_mapped_files->rename_file(index, new_filename);
#ifndef BOOST_NO_EXCEPTIONS
}
catch (std::exception& e)
{
set_error(old_name.string(), e.what());
return true;
}
#endif
return false;
}
bool storage::release_files()
{
m_files.release(this);
m_pool.release(this);
buffer().swap(m_scratch_buffer);
return false;
}
@ -505,7 +543,7 @@ namespace libtorrent
bool storage::delete_files()
{
// make sure we don't have the files open
m_files.release(this);
m_pool.release(this);
buffer().swap(m_scratch_buffer);
int result = 0;
@ -515,8 +553,8 @@ namespace libtorrent
// delete the files from disk
std::set<std::string> directories;
typedef std::set<std::string>::iterator iter_t;
for (torrent_info::file_iterator i = m_info->begin_files(true)
, end(m_info->end_files(true)); i != end; ++i)
for (file_storage::iterator i = files().begin()
, end(files().end()); i != end; ++i)
{
std::string p = (m_save_path / i->path).string();
fs::path bp = i->path.branch_path();
@ -584,7 +622,7 @@ namespace libtorrent
TORRENT_ASSERT(rd.type() == entry::dictionary_t);
std::vector<std::pair<size_type, std::time_t> > file_sizes
= get_filesizes(*m_info, m_save_path);
= get_filesizes(files(), m_save_path);
entry::list_type& fl = rd["file sizes"].list();
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
@ -642,7 +680,7 @@ namespace libtorrent
}
entry::list_type const& slots = slots_ent->list();
bool seed = int(slots.size()) == m_info->num_pieces()
bool seed = int(slots.size()) == m_files.num_pieces()
&& std::find_if(slots.begin(), slots.end()
, boost::bind<bool>(std::less<int>()
, boost::bind((size_type const& (entry::*)() const)
@ -655,11 +693,11 @@ namespace libtorrent
if (seed)
{
if (m_info->num_files(true) != (int)file_sizes.size())
if (m_files.num_files() != (int)file_sizes.size())
{
error = "the number of files does not match the torrent (num: "
+ boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
+ boost::lexical_cast<std::string>(m_info->num_files(true)) + ")";
+ boost::lexical_cast<std::string>(m_files.num_files()) + ")";
return false;
}
@ -667,8 +705,8 @@ namespace libtorrent
fs = file_sizes.begin();
// the resume data says we have the entire torrent
// make sure the file sizes are the right ones
for (torrent_info::file_iterator i = m_info->begin_files(true)
, end(m_info->end_files(true)); i != end; ++i, ++fs)
for (file_storage::iterator i = files().begin()
, end(files().end()); i != end; ++i, ++fs)
{
if (i->size != fs->first)
{
@ -680,7 +718,7 @@ namespace libtorrent
}
}
return match_filesizes(*m_info, m_save_path, file_sizes
return match_filesizes(files(), m_save_path, file_sizes
, !full_allocation_mode, &error);
}
@ -716,14 +754,14 @@ namespace libtorrent
return false;
#endif
m_files.release(this);
m_pool.release(this);
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400
old_path = safe_convert((m_save_path / m_info->name()).string());
new_path = safe_convert((save_path / m_info->name()).string());
old_path = safe_convert((m_save_path / files().name()).string());
new_path = safe_convert((save_path / files().name()).string());
#else
old_path = m_save_path / m_info->name();
new_path = save_path / m_info->name();
old_path = m_save_path / files().name();
new_path = save_path / files().name();
#endif
#ifndef BOOST_NO_EXCEPTIONS
@ -740,16 +778,20 @@ namespace libtorrent
return true;
#ifndef BOOST_NO_EXCEPTIONS
}
catch (std::exception&) {}
return false;
catch (std::exception& e)
{
set_error((m_save_path / files().name()).string(), e.what());
return true;
}
#endif
return false;
}
#ifndef NDEBUG
/*
void storage::shuffle()
{
int num_pieces = m_info->num_pieces();
int num_pieces = files().num_pieces();
std::vector<int> pieces(num_pieces);
for (std::vector<int>::iterator i = pieces.begin();
@ -766,7 +808,7 @@ namespace libtorrent
{
const int slot_index = targets[i];
const int piece_index = pieces[i];
const int slot_size =static_cast<int>(m_info->piece_size(slot_index));
const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
std::vector<char> buf(slot_size);
read(&buf[0], piece_index, 0, slot_size);
write(&buf[0], slot_index, 0, slot_size);
@ -777,7 +819,7 @@ namespace libtorrent
bool storage::move_slot(int src_slot, int dst_slot)
{
int piece_size = m_info->piece_size(dst_slot);
int piece_size = m_files.piece_size(dst_slot);
m_scratch_buffer.resize(piece_size);
int ret1 = read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true);
int ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
@ -787,9 +829,9 @@ namespace libtorrent
bool storage::swap_slots(int slot1, int slot2)
{
// the size of the target slot is the size of the piece
int piece_size = m_info->piece_length();
int piece1_size = m_info->piece_size(slot2);
int piece2_size = m_info->piece_size(slot1);
int piece_size = m_files.piece_length();
int piece1_size = m_files.piece_size(slot2);
int piece2_size = m_files.piece_size(slot1);
m_scratch_buffer.resize(piece_size * 2);
int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
@ -802,10 +844,10 @@ namespace libtorrent
bool storage::swap_slots3(int slot1, int slot2, int slot3)
{
// the size of the target slot is the size of the piece
int piece_size = m_info->piece_length();
int piece1_size = m_info->piece_size(slot2);
int piece2_size = m_info->piece_size(slot3);
int piece3_size = m_info->piece_size(slot1);
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);
m_scratch_buffer.resize(piece_size * 2);
int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
@ -835,25 +877,25 @@ namespace libtorrent
, bool fill_zero)
{
TORRENT_ASSERT(buf != 0);
TORRENT_ASSERT(slot >= 0 && slot < m_info->num_pieces());
TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces());
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(offset < m_info->piece_size(slot));
TORRENT_ASSERT(offset < m_files.piece_size(slot));
TORRENT_ASSERT(size > 0);
#ifndef NDEBUG
std::vector<file_slice> slices
= m_info->map_block(slot, offset, size, true);
= files().map_block(slot, offset, size);
TORRENT_ASSERT(!slices.empty());
#endif
size_type start = slot * (size_type)m_info->piece_length() + offset;
TORRENT_ASSERT(start + size <= m_info->total_size());
size_type start = slot * (size_type)m_files.piece_length() + offset;
TORRENT_ASSERT(start + size <= m_files.total_size());
// find the file iterator and file offset
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
for (file_iter = m_info->begin_files(true);;)
for (file_iter = files().begin();;)
{
if (file_offset < file_iter->size)
break;
@ -864,7 +906,7 @@ namespace libtorrent
int buf_pos = 0;
std::string error;
boost::shared_ptr<file> in(m_files.open_file(
boost::shared_ptr<file> in(m_pool.open_file(
this, m_save_path / file_iter->path, file::in
, error));
if (!in)
@ -899,7 +941,7 @@ namespace libtorrent
#endif
int left_to_read = size;
int slot_size = static_cast<int>(m_info->piece_size(slot));
int slot_size = static_cast<int>(m_files.piece_size(slot));
if (offset + left_to_read > slot_size)
left_to_read = slot_size - offset;
@ -924,7 +966,7 @@ namespace libtorrent
TORRENT_ASSERT(int(slices.size()) > counter);
size_type slice_size = slices[counter].size;
TORRENT_ASSERT(slice_size == read_bytes);
TORRENT_ASSERT(m_info->file_at(slices[counter].file_index, true).path
TORRENT_ASSERT(files().at(slices[counter].file_index).path
== file_iter->path);
#endif
@ -961,7 +1003,7 @@ namespace libtorrent
file_offset = 0;
std::string error;
in = m_files.open_file(
in = m_pool.open_file(
this, path, file::in, error);
if (!in)
{
@ -997,35 +1039,35 @@ namespace libtorrent
{
TORRENT_ASSERT(buf != 0);
TORRENT_ASSERT(slot >= 0);
TORRENT_ASSERT(slot < m_info->num_pieces());
TORRENT_ASSERT(slot < m_files.num_pieces());
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(size > 0);
#ifndef NDEBUG
std::vector<file_slice> slices
= m_info->map_block(slot, offset, size, true);
= files().map_block(slot, offset, size);
TORRENT_ASSERT(!slices.empty());
#endif
size_type start = slot * (size_type)m_info->piece_length() + offset;
size_type start = slot * (size_type)m_files.piece_length() + offset;
// find the file iterator and file offset
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
for (file_iter = m_info->begin_files(true);;)
for (file_iter = files().begin();;)
{
if (file_offset < file_iter->size)
break;
file_offset -= file_iter->size;
++file_iter;
TORRENT_ASSERT(file_iter != m_info->end_files(true));
TORRENT_ASSERT(file_iter != files().end());
}
fs::path p(m_save_path / file_iter->path);
std::string error;
boost::shared_ptr<file> out = m_files.open_file(
boost::shared_ptr<file> out = m_pool.open_file(
this, p, file::out | file::in, error);
if (!out)
@ -1050,7 +1092,7 @@ namespace libtorrent
}
int left_to_write = size;
int slot_size = static_cast<int>(m_info->piece_size(slot));
int slot_size = static_cast<int>(m_files.piece_size(slot));
if (offset + left_to_write > slot_size)
left_to_write = slot_size - offset;
@ -1074,7 +1116,7 @@ namespace libtorrent
{
TORRENT_ASSERT(int(slices.size()) > counter);
TORRENT_ASSERT(slices[counter].size == write_bytes);
TORRENT_ASSERT(m_info->file_at(slices[counter].file_index, true).path
TORRENT_ASSERT(files().at(slices[counter].file_index).path
== file_iter->path);
TORRENT_ASSERT(buf_pos >= 0);
@ -1101,11 +1143,11 @@ namespace libtorrent
#endif
++file_iter;
TORRENT_ASSERT(file_iter != m_info->end_files(true));
TORRENT_ASSERT(file_iter != files().end());
fs::path p = m_save_path / file_iter->path;
file_offset = 0;
std::string error;
out = m_files.open_file(
out = m_pool.open_file(
this, p, file::out | file::in, error);
if (!out)
@ -1131,25 +1173,26 @@ namespace libtorrent
return size;
}
storage_interface* default_storage_constructor(boost::intrusive_ptr<torrent_info const> ti
storage_interface* default_storage_constructor(file_storage const& fs
, fs::path const& path, file_pool& fp)
{
return new storage(ti, path, fp);
return new storage(fs, path, fp);
}
// -- piece_manager -----------------------------------------------------
piece_manager::piece_manager(
boost::shared_ptr<void> const& torrent
, boost::intrusive_ptr<torrent_info const> ti
, boost::intrusive_ptr<torrent_info const> info
, fs::path const& save_path
, file_pool& fp
, disk_io_thread& io
, storage_constructor_type sc
, storage_mode_t sm)
: m_storage(sc(ti, save_path, fp))
: m_info(info)
, m_files(m_info->files())
, m_storage(sc(m_files, save_path, fp))
, m_storage_mode(sm)
, m_info(ti)
, m_save_path(complete(save_path))
, m_state(state_none)
, m_current_slot(0)
@ -1213,6 +1256,17 @@ namespace libtorrent
m_io_thread.add_job(j, handler);
}
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);
}
void piece_manager::async_check_files(
boost::function<void(int, disk_io_job const&)> const& handler)
{
@ -1241,6 +1295,8 @@ namespace libtorrent
m_io_thread.add_job(j, handler);
#ifndef NDEBUG
boost::recursive_mutex::scoped_lock l(m_mutex);
// if this assert is hit, it suggests
// that check_files was not successful
TORRENT_ASSERT(slot_for(r.piece) >= 0);
#endif
}
@ -1295,7 +1351,7 @@ namespace libtorrent
int slot = slot_for(piece);
TORRENT_ASSERT(slot != has_no_slot);
return m_storage->hash_for_slot(slot, ph, m_info->piece_size(piece));
return m_storage->hash_for_slot(slot, ph, m_files.piece_size(piece));
}
bool piece_manager::move_storage_impl(fs::path const& save_path)
@ -1373,7 +1429,7 @@ namespace libtorrent
TORRENT_ASSERT(buf);
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(size > 0);
TORRENT_ASSERT(piece_index >= 0 && piece_index < m_info->num_pieces());
TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces());
int slot = allocate_slot_for_piece(piece_index);
int ret = m_storage->write(buf, slot, offset, size);
@ -1455,9 +1511,9 @@ namespace libtorrent
{
// INVARIANT_CHECK;
const int piece_size = static_cast<int>(m_info->piece_length());
const int last_piece_size = static_cast<int>(m_info->piece_size(
m_info->num_pieces() - 1));
const int piece_size = static_cast<int>(m_files.piece_length());
const int last_piece_size = static_cast<int>(m_files.piece_size(
m_files.num_pieces() - 1));
TORRENT_ASSERT((int)piece_data.size() >= last_piece_size);
@ -1579,8 +1635,8 @@ namespace libtorrent
int piece_manager::check_no_fastresume(std::string& error)
{
torrent_info::file_iterator i = m_info->begin_files(true);
torrent_info::file_iterator end = m_info->end_files(true);
file_storage::iterator i = m_files.begin();
file_storage::iterator end = m_files.end();
for (; i != end; ++i)
{
@ -1612,9 +1668,9 @@ namespace libtorrent
{
m_state = state_full_check;
m_piece_to_slot.clear();
m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot);
m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
m_slot_to_piece.clear();
m_slot_to_piece.resize(m_info->num_pieces(), unallocated);
m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
if (m_storage_mode == storage_mode_compact)
{
m_unallocated_slots.clear();
@ -1629,12 +1685,12 @@ namespace libtorrent
// in compact mode without checking, we need to
// populate the unallocated list
TORRENT_ASSERT(m_unallocated_slots.empty());
for (int i = 0, end(m_info->num_pieces()); i < end; ++i)
for (int i = 0, end(m_files.num_pieces()); i < end; ++i)
m_unallocated_slots.push_back(i);
m_piece_to_slot.clear();
m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot);
m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
m_slot_to_piece.clear();
m_slot_to_piece.resize(m_info->num_pieces(), unallocated);
m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
}
return check_init_storage(error);
@ -1675,7 +1731,7 @@ namespace libtorrent
INVARIANT_CHECK;
TORRENT_ASSERT(m_info->piece_length() > 0);
TORRENT_ASSERT(m_files.piece_length() > 0);
// if we don't have any resume data, return
if (rd.type() == entry::undefined_t) return check_no_fastresume(error);
@ -1686,38 +1742,11 @@ namespace libtorrent
return check_no_fastresume(error);
}
entry const* file_format = rd.find_key("file-format");
if (file_format == 0 || file_format->type() != entry::string_t)
{
error = "missing file format tag";
return check_no_fastresume(error);
}
if (file_format->string() != "libtorrent resume file")
{
error = "invalid file format tag";
return check_no_fastresume(error);
}
entry const* info_hash = rd.find_key("info-hash");
if (info_hash == 0 || info_hash->type() != entry::string_t)
{
error = "missing info-hash";
return check_no_fastresume(error);
}
if (sha1_hash(info_hash->string()) != m_info->info_hash())
{
error = "mismatching info-hash";
return check_no_fastresume(error);
}
int block_size = (std::min)(16 * 1024, m_info->piece_length());
int block_size = (std::min)(16 * 1024, m_files.piece_length());
entry const* blocks_per_piece_ent = rd.find_key("blocks per piece");
if (blocks_per_piece_ent != 0
&& blocks_per_piece_ent->type() == entry::int_t
&& blocks_per_piece_ent->integer() != m_info->piece_length() / block_size)
&& blocks_per_piece_ent->integer() != m_files.piece_length() / block_size)
{
error = "invalid 'blocks per piece' entry";
return check_no_fastresume(error);
@ -1746,17 +1775,17 @@ namespace libtorrent
return check_no_fastresume(error);
}
if ((int)slots->list().size() > m_info->num_pieces())
if ((int)slots->list().size() > m_files.num_pieces())
{
error = "file has more slots than torrent (slots: "
+ boost::lexical_cast<std::string>(slots->list().size()) + " size: "
+ boost::lexical_cast<std::string>(m_info->num_pieces()) + " )";
+ boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
return check_no_fastresume(error);
}
if (storage_mode == storage_mode_compact)
{
int num_pieces = int(m_info->num_pieces());
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);
int slot = 0;
@ -1877,7 +1906,7 @@ namespace libtorrent
// is finished
int piece_manager::check_files(int& current_slot, int& have_piece, std::string& error)
{
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_info->num_pieces());
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
current_slot = m_current_slot;
have_piece = -1;
@ -1894,9 +1923,9 @@ namespace libtorrent
if (other_piece >= 0)
{
if (m_scratch_buffer2.empty())
m_scratch_buffer2.resize(m_info->piece_length());
m_scratch_buffer2.resize(m_files.piece_length());
int piece_size = m_info->piece_size(other_piece);
int piece_size = m_files.piece_size(other_piece);
if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size)
!= piece_size)
{
@ -1910,7 +1939,7 @@ namespace libtorrent
// the slot where this piece belongs is
// free. Just move the piece there.
int piece_size = m_info->piece_size(piece);
int piece_size = m_files.piece_size(piece);
if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
{
error = m_storage->error();
@ -1926,19 +1955,19 @@ namespace libtorrent
return need_full_check;
}
while (m_current_slot < m_info->num_pieces()
while (m_current_slot < m_files.num_pieces()
&& (m_slot_to_piece[m_current_slot] == m_current_slot
|| m_slot_to_piece[m_current_slot] < 0))
{
++m_current_slot;
}
if (m_current_slot == m_info->num_pieces())
if (m_current_slot == m_files.num_pieces())
{
return check_init_storage(error);
}
TORRENT_ASSERT(m_current_slot < m_info->num_pieces());
TORRENT_ASSERT(m_current_slot < m_files.num_pieces());
int piece = m_slot_to_piece[m_current_slot];
TORRENT_ASSERT(piece >= 0);
@ -1949,9 +1978,9 @@ namespace libtorrent
// where this one goes. Store it in the scratch
// buffer until next iteration.
if (m_scratch_buffer.empty())
m_scratch_buffer.resize(m_info->piece_length());
m_scratch_buffer.resize(m_files.piece_length());
int piece_size = m_info->piece_size(other_piece);
int piece_size = m_files.piece_size(other_piece);
if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
{
error = m_storage->error();
@ -1975,7 +2004,7 @@ namespace libtorrent
TORRENT_ASSERT(m_state == state_full_check);
bool skip = check_one_piece(have_piece);
TORRENT_ASSERT(m_current_slot <= m_info->num_pieces());
TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
if (skip)
{
@ -1984,9 +2013,9 @@ namespace libtorrent
// completely. We should skip all pieces belonging to that file.
// find the file that failed, and skip all the pieces in that file
size_type file_offset = 0;
size_type current_offset = size_type(m_current_slot) * m_info->piece_length();
for (torrent_info::file_iterator i = m_info->begin_files(true);
i != m_info->end_files(true); ++i)
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;
@ -1994,8 +2023,8 @@ namespace libtorrent
TORRENT_ASSERT(file_offset > current_offset);
int skip_blocks = static_cast<int>(
(file_offset - current_offset + m_info->piece_length() - 1)
/ m_info->piece_length());
(file_offset - current_offset + m_files.piece_length() - 1)
/ m_files.piece_length());
if (m_storage_mode == storage_mode_compact)
{
@ -2008,15 +2037,15 @@ namespace libtorrent
// current slot will increase by one at the end of the for-loop too
m_current_slot += skip_blocks - 1;
TORRENT_ASSERT(m_current_slot <= m_info->num_pieces());
TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
}
++m_current_slot;
current_slot = m_current_slot;
if (m_current_slot >= m_info->num_pieces())
if (m_current_slot >= m_files.num_pieces())
{
TORRENT_ASSERT(m_current_slot == m_info->num_pieces());
TORRENT_ASSERT(m_current_slot == m_files.num_pieces());
// clear the memory we've been using
std::vector<char>().swap(m_piece_data);
@ -2059,19 +2088,19 @@ namespace libtorrent
// DO THE FULL CHECK
// ------------------------
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_info->num_pieces());
TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_info->num_pieces());
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces());
TORRENT_ASSERT(have_piece == -1);
// initialization for the full check
if (m_hash_to_piece.empty())
{
for (int i = 0; i < m_info->num_pieces(); ++i)
for (int i = 0; i < m_files.num_pieces(); ++i)
m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
}
m_piece_data.resize(int(m_info->piece_length()));
int piece_size = m_info->piece_size(m_current_slot);
m_piece_data.resize(int(m_files.piece_length()));
int piece_size = m_files.piece_size(m_current_slot);
int num_read = m_storage->read(&m_piece_data[0]
, m_current_slot, 0, piece_size);
@ -2348,7 +2377,7 @@ namespace libtorrent
// special case to make sure we don't use the last slot
// when we shouldn't, since it's smaller than ordinary slots
if (*iter == m_info->num_pieces() - 1 && piece_index != *iter)
if (*iter == m_files.num_pieces() - 1 && piece_index != *iter)
{
if (m_free_slots.size() == 1)
allocate_slots(1);
@ -2481,14 +2510,14 @@ namespace libtorrent
{
boost::recursive_mutex::scoped_lock lock(m_mutex);
TORRENT_ASSERT(m_current_slot <= m_info->num_pieces());
TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
if (m_unallocated_slots.empty()
&& m_free_slots.empty()
&& m_state == state_finished)
{
TORRENT_ASSERT(m_storage_mode != storage_mode_compact
|| m_info->num_pieces() == 0);
|| m_files.num_pieces() == 0);
}
if (m_storage_mode != storage_mode_compact)
@ -2508,8 +2537,8 @@ namespace libtorrent
{
if (m_piece_to_slot.empty()) return;
TORRENT_ASSERT((int)m_piece_to_slot.size() == m_info->num_pieces());
TORRENT_ASSERT((int)m_slot_to_piece.size() == m_info->num_pieces());
TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces());
TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces());
for (std::vector<int>::const_iterator i = m_free_slots.begin();
i != m_free_slots.end(); ++i)
@ -2531,7 +2560,7 @@ namespace libtorrent
== m_unallocated_slots.end());
}
for (int i = 0; i < m_info->num_pieces(); ++i)
for (int i = 0; i < m_files.num_pieces(); ++i)
{
// Check domain of piece_to_slot's elements
if (m_piece_to_slot[i] != has_no_slot)
@ -2620,7 +2649,7 @@ namespace libtorrent
s << "index\tslot\tpiece\n";
for (int i = 0; i < m_info->num_pieces(); ++i)
for (int i = 0; i < m_files.num_pieces(); ++i)
{
s << i << "\t" << m_slot_to_piece[i] << "\t";
s << m_piece_to_slot[i] << "\n";

View File

@ -424,8 +424,34 @@ namespace libtorrent
m_state = torrent_status::queued_for_checking;
if (m_resume_data.type() == entry::dictionary_t) read_resume_data(m_resume_data);
if (m_resume_data.type() == entry::dictionary_t)
{
char const* error = 0;
entry const* file_format = m_resume_data.find_key("file-format");
if (file_format->string() != "libtorrent resume file")
error = "invalid file format tag";
entry const* info_hash = m_resume_data.find_key("info-hash");
if (!error && (info_hash == 0 || info_hash->type() != entry::string_t))
error = "missing info-hash";
if (!error && sha1_hash(info_hash->string()) != m_torrent_file->info_hash())
error = "mismatching info-hash";
if (error && m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), error));
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << "fastresume data for "
<< torrent_file().name() << " rejected: "
<< error << "\n";
#endif
}
if (error) m_resume_data = entry();
else read_resume_data(m_resume_data);
}
m_storage->async_check_fastresume(&m_resume_data
, bind(&torrent::on_resume_data_checked
, shared_from_this(), _1, _2));
@ -1425,6 +1451,21 @@ namespace libtorrent
}
}
void torrent::on_file_renamed(int ret, disk_io_job const& j)
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
if (alerts().should_post(alert::warning))
{
if (ret == 0)
alerts().post_alert(file_renamed_alert(get_handle(), j.str
, "renamed file: " + j.str));
else
alerts().post_alert(file_renamed_alert(get_handle(), "", j.str));
}
}
void torrent::on_torrent_paused(int ret, disk_io_job const& j)
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
@ -1619,7 +1660,7 @@ namespace libtorrent
for (int i = 0; i < int(files.size()); ++i)
{
size_type start = position;
size_type size = m_torrent_file->file_at(i).size;
size_type size = m_torrent_file->files().at(i).size;
if (size == 0) continue;
position += size;
// mark all pieces of the file with this file's priority
@ -1750,7 +1791,7 @@ namespace libtorrent
for (int i = 0; i < (int)bitmask.size(); ++i)
{
size_type start = position;
position += m_torrent_file->file_at(i).size;
position += m_torrent_file->files().at(i).size;
// is the file selected for download?
if (!bitmask[i])
{
@ -3148,6 +3189,20 @@ namespace libtorrent
return m_save_path;
}
bool torrent::rename_file(int index, std::string const& name)
{
INVARIANT_CHECK;
TORRENT_ASSERT(index >= 0);
TORRENT_ASSERT(index < m_torrent_file->num_files());
if (!m_owning_storage.get()) return false;
m_owning_storage->async_rename_file(index, name
, bind(&torrent::on_file_renamed, shared_from_this(), _1, _2));
return true;
}
void torrent::move_storage(fs::path const& save_path)
{
INVARIANT_CHECK;
@ -3770,8 +3825,8 @@ namespace libtorrent
for (int i = 0; i < m_torrent_file->num_files(); ++i)
{
peer_request ret = m_torrent_file->map_file(i, 0, 0);
size_type size = m_torrent_file->file_at(i).size;
peer_request ret = m_torrent_file->files().map_file(i, 0, 0);
size_type size = m_torrent_file->files().at(i).size;
// zero sized files are considered
// 100% done all the time
@ -3793,7 +3848,7 @@ namespace libtorrent
}
TORRENT_ASSERT(size == 0);
fp[i] = static_cast<float>(done) / m_torrent_file->file_at(i).size;
fp[i] = static_cast<float>(done) / m_torrent_file->files().at(i).size;
}
}

View File

@ -215,6 +215,12 @@ namespace libtorrent
TORRENT_FORWARD(move_storage(save_path));
}
void torrent_handle::rename_file(int index, fs::path const& new_name) const
{
INVARIANT_CHECK;
TORRENT_FORWARD(rename_file(index, new_name.string()));
}
void torrent_handle::add_extension(
boost::function<boost::shared_ptr<torrent_plugin>(torrent*, void*)> const& ext
, void* userdata)

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2008, Arvid Norberg
Copyright (c) 2003-2008, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -153,11 +153,7 @@ namespace
// save the original encoding and replace the
// commonly used path with the correctly
// encoded string
if (!valid_encoding)
{
target.orig_path.reset(new fs::path(target.path));
target.path = tmp_path;
}
if (!valid_encoding) target.path = tmp_path;
}
bool extract_single_file(lazy_entry const& dict, file_entry& target
@ -194,40 +190,26 @@ namespace
return true;
}
bool extract_files(lazy_entry const& list, std::vector<file_entry>& target
bool extract_files(lazy_entry const& list, file_storage& target
, std::string const& root_dir)
{
size_type offset = 0;
if (list.type() != lazy_entry::list_t) return false;
for (int i = 0, end(list.list_size()); i < end; ++i)
{
target.push_back(file_entry());
if (!extract_single_file(*list.list_at(i), target.back(), root_dir))
file_entry e;
if (!extract_single_file(*list.list_at(i), e, root_dir))
return false;
target.back().offset = offset;
offset += target.back().size;
target.add_file(e);
}
return true;
}
/*
void remove_dir(fs::path& p)
{
TORRENT_ASSERT(p.begin() != p.end());
path tmp;
for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i)
tmp /= *i;
p = tmp;
}
*/
}
namespace libtorrent
{
// standard constructor that parses a torrent file
torrent_info::torrent_info(entry const& torrent_file)
: m_num_pieces(0)
, m_creation_date(pt::ptime(pt::not_a_date_time))
: m_creation_date(pt::ptime(pt::not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_info_section_size(0)
@ -249,8 +231,7 @@ namespace libtorrent
}
torrent_info::torrent_info(lazy_entry const& torrent_file)
: m_num_pieces(0)
, m_creation_date(pt::ptime(pt::not_a_date_time))
: m_creation_date(pt::ptime(pt::not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_info_section_size(0)
@ -266,8 +247,7 @@ namespace libtorrent
}
torrent_info::torrent_info(char const* buffer, int size)
: m_num_pieces(0)
, m_creation_date(pt::ptime(pt::not_a_date_time))
: m_creation_date(pt::ptime(pt::not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_info_section_size(0)
@ -289,22 +269,16 @@ namespace libtorrent
// just the necessary to use it with piece manager
// used for torrents with no metadata
torrent_info::torrent_info(sha1_hash const& info_hash)
: m_piece_length(0)
, m_total_size(0)
, m_num_pieces(0)
, m_info_hash(info_hash)
, m_name()
: m_info_hash(info_hash)
, m_creation_date(pt::second_clock::universal_time())
, m_multifile(false)
, m_private(false)
, m_info_section_size(0)
, m_piece_hashes(0)
{
}
{}
torrent_info::torrent_info(char const* filename)
: m_num_pieces(0)
, m_creation_date(pt::ptime(pt::not_a_date_time))
: m_creation_date(pt::ptime(pt::not_a_date_time))
, m_multifile(false)
, m_private(false)
{
@ -335,11 +309,8 @@ namespace libtorrent
m_urls.swap(ti.m_urls);
m_url_seeds.swap(ti.m_url_seeds);
m_files.swap(ti.m_files);
m_files.swap(ti.m_remapped_files);
m_nodes.swap(ti.m_nodes);
swap(m_num_pieces, ti.m_num_pieces);
swap(m_info_hash, ti.m_info_hash);
m_name.swap(ti.m_name);
swap(m_creation_date, ti.m_creation_date);
m_comment.swap(ti.m_comment);
m_created_by.swap(ti.m_created_by);
@ -373,27 +344,27 @@ namespace libtorrent
TORRENT_ASSERT(section.first[m_info_section_size-1] == 'e');
// extract piece length
m_piece_length = info.dict_find_int_value("piece length", -1);
if (m_piece_length <= 0)
int piece_length = info.dict_find_int_value("piece length", -1);
if (piece_length <= 0)
{
error = "invalid or missing 'piece length' entry in torrent file";
return false;
}
m_files.set_piece_length(piece_length);
// extract file name (or the directory name if it's a multifile libtorrent)
m_name = info.dict_find_string_value("name.utf-8");
if (m_name.empty()) m_name = info.dict_find_string_value("name");
if (m_name.empty())
std::string name = info.dict_find_string_value("name.utf-8");
if (name.empty()) name = info.dict_find_string_value("name");
if (name.empty())
{
error = "invalid name in torrent file";
error = "missing name in torrent file";
return false;
}
fs::path tmp = m_name;
fs::path tmp = name;
if (tmp.is_complete())
{
m_name = tmp.leaf();
name = tmp.leaf();
}
else if (tmp.has_branch_path())
{
@ -404,9 +375,9 @@ namespace libtorrent
if (*i == "." || *i == "..") continue;
p /= *i;
}
m_name = p.string();
name = p.string();
}
if (m_name == ".." || m_name == ".")
if (name == ".." || name == ".")
{
error = "invalid 'name' of torrent (possible exploit attempt)";
return false;
@ -419,7 +390,7 @@ namespace libtorrent
// if there's no list of files, there has to be a length
// field.
file_entry e;
e.path = m_name;
e.path = name;
e.offset = 0;
e.size = info.dict_find_int_value("length", -1);
if (e.size < 0)
@ -427,29 +398,26 @@ namespace libtorrent
error = "invalid length of torrent";
return false;
}
m_files.push_back(e);
m_files.add_file(e);
m_multifile = false;
}
else
{
if (!extract_files(*i, m_files, m_name))
if (!extract_files(*i, m_files, name))
{
error = "failed to parse files from torrent file";
return false;
}
m_multifile = true;
}
// calculate total size of all pieces
m_total_size = 0;
for (std::vector<file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i)
m_total_size += i->size;
m_files.set_name(name);
// extract sha-1 hashes for all pieces
// we want this division to round upwards, that's why we have the
// extra addition
m_num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
m_files.set_num_pieces(int((m_files.total_size() + m_files.piece_length() - 1)
/ m_files.piece_length()));
lazy_entry const* pieces = info.dict_find("pieces");
if (pieces == 0 || pieces->type() != lazy_entry::string_t)
@ -458,7 +426,7 @@ namespace libtorrent
return false;
}
if (pieces->string_length() != m_num_pieces * 20)
if (pieces->string_length() != m_files.num_pieces() * 20)
{
error = "incorrect number of piece hashes in torrent file";
return false;
@ -616,122 +584,11 @@ namespace libtorrent
os << "number of pieces: " << num_pieces() << "\n";
os << "piece length: " << piece_length() << "\n";
os << "files:\n";
for (file_iterator i = begin_files(); i != end_files(); ++i)
for (file_storage::iterator i = m_files.begin(); i != m_files.end(); ++i)
os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n";
}
// ------- end deprecation -------
int torrent_info::piece_size(int index) const
{
TORRENT_ASSERT(index >= 0 && index < num_pieces());
if (index == num_pieces()-1)
{
int size = int(total_size()
- size_type(num_pieces() - 1) * piece_length());
TORRENT_ASSERT(size > 0);
TORRENT_ASSERT(size <= piece_length());
return int(size);
}
else
return piece_length();
}
bool torrent_info::remap_files(std::vector<file_entry> const& map)
{
size_type offset = 0;
m_remapped_files.resize(map.size());
for (int i = 0; i < int(map.size()); ++i)
{
file_entry& fe = m_remapped_files[i];
fe.path = map[i].path;
fe.offset = offset;
fe.size = map[i].size;
fe.file_base = map[i].file_base;
fe.orig_path.reset();
offset += fe.size;
}
if (offset != total_size())
{
m_remapped_files.clear();
return false;
}
#ifndef NDEBUG
std::vector<file_entry> map2(m_remapped_files);
std::sort(map2.begin(), map2.end()
, bind(&file_entry::file_base, _1) < bind(&file_entry::file_base, _2));
std::stable_sort(map2.begin(), map2.end()
, bind(&file_entry::path, _1) < bind(&file_entry::path, _2));
fs::path last_path;
size_type last_end = 0;
for (std::vector<file_entry>::iterator i = map2.begin(), end(map2.end());
i != end; ++i)
{
if (last_path == i->path)
{
assert(last_end <= i->file_base);
}
last_end = i->file_base + i->size;
last_path = i->path;
}
#endif
return true;
}
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
, int size_, bool storage) const
{
TORRENT_ASSERT(num_files() > 0);
std::vector<file_slice> ret;
size_type start = piece * (size_type)m_piece_length + offset;
size_type size = size_;
TORRENT_ASSERT(start + size <= m_total_size);
// find the file iterator and file offset
// TODO: make a vector that can map piece -> file index in O(1)
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
int counter = 0;
for (file_iter = begin_files(storage);; ++counter, ++file_iter)
{
TORRENT_ASSERT(file_iter != end_files(storage));
if (file_offset < file_iter->size)
{
file_slice f;
f.file_index = counter;
f.offset = file_offset + file_iter->file_base;
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
size -= f.size;
file_offset += f.size;
ret.push_back(f);
}
TORRENT_ASSERT(size >= 0);
if (size <= 0) break;
file_offset -= file_iter->size;
}
return ret;
}
peer_request torrent_info::map_file(int file_index, size_type file_offset
, int size, bool storage) const
{
TORRENT_ASSERT(file_index < num_files(storage));
TORRENT_ASSERT(file_index >= 0);
size_type offset = file_offset + file_at(file_index, storage).offset;
peer_request ret;
ret.piece = int(offset / piece_length());
ret.start = int(offset - ret.piece * piece_length());
ret.length = size;
return ret;
}
}

View File

@ -242,7 +242,7 @@ namespace libtorrent
}
else
{
std::vector<file_slice> files = info.map_block(r.piece, r.start
std::vector<file_slice> files = info.files().map_block(r.piece, r.start
, r.length);
for (std::vector<file_slice>::iterator i = files.begin();
@ -254,13 +254,13 @@ namespace libtorrent
if (using_proxy)
{
request += m_url;
std::string path = info.file_at(f.file_index).path.string();
std::string path = info.files().at(f.file_index).path.string();
request += escape_path(path.c_str(), path.length());
}
else
{
std::string path = m_path;
path += info.file_at(f.file_index).path.string();
path += info.files().at(f.file_index).path.string();
request += escape_path(path.c_str(), path.length());
}
request += " HTTP/1.1\r\n";
@ -426,7 +426,7 @@ namespace libtorrent
int file_index = m_file_requests.front();
torrent_info const& info = t->torrent_file();
std::string path = info.file_at(file_index).path.string();
std::string path = info.files().at(file_index).path.string();
path = escape_path(path.c_str(), path.length());
size_t i = location.rfind(path);
if (i == std::string::npos)
@ -511,7 +511,7 @@ namespace libtorrent
}
int file_index = m_file_requests.front();
peer_request in_range = info.map_file(file_index, range_start
peer_request in_range = info.files().map_file(file_index, range_start
, int(range_end - range_start));
peer_request front_request = m_requests.front();

View File

@ -170,10 +170,10 @@ boost::intrusive_ptr<torrent_info> create_torrent(std::ostream* file)
using namespace boost::filesystem;
libtorrent::create_torrent t;
file_storage fs;
int total_size = 2 * 1024 * 1024;
t.add_file(path("temporary"), total_size);
t.set_piece_size(16 * 1024);
fs.add_file(path("temporary"), total_size);
libtorrent::create_torrent t(fs, 16 * 1024);
t.add_tracker(tracker_url);
std::vector<char> piece(16 * 1024);

View File

@ -51,9 +51,11 @@ void on_check_files(int ret, disk_io_job const& j)
}
void run_storage_tests(boost::intrusive_ptr<torrent_info> info
, file_storage& fs
, path const& test_path
, libtorrent::storage_mode_t storage_mode)
{
TORRENT_ASSERT(fs.num_files() > 0);
create_directory(test_path / "temp_storage");
int num_pieces = (1 + 612 + 17 + piece_size - 1) / piece_size;
@ -64,7 +66,7 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
{ // avoid having two storages use the same files
file_pool fp;
boost::scoped_ptr<storage_interface> s(
default_storage_constructor(info, test_path, fp));
default_storage_constructor(fs, test_path, fp));
// write piece 1 (in slot 0)
s->write(piece1, 0, 0, half);
@ -100,12 +102,17 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
entry frd;
pm->async_check_fastresume(&frd, &on_check_resume_data);
ios.reset();
ios.run();
pm->async_check_files(&on_check_files);
for (int i = 0; i < 4; ++i)
{
ios.reset();
ios.run_one();
}
// test move_storage
boost::function<void(int, disk_io_job const&)> none;
TEST_CHECK(exists(test_path / "temp_storage"));
pm->async_move_storage(test_path / "temp_storage2", none);
@ -117,6 +124,15 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
TEST_CHECK(!exists(test_path / "temp_storage2/temp_storage"));
remove_all(test_path / "temp_storage2");
// test rename_file
remove(test_path / "part0");
TEST_CHECK(exists(test_path / "temp_storage/test1.tmp"));
TEST_CHECK(!exists(test_path / "part0"));
pm->async_rename_file(0, "part0", none);
test_sleep(2000);
TEST_CHECK(!exists(test_path / "temp_storage/test1.tmp"));
TEST_CHECK(exists(test_path / "part0"));
peer_request r;
r.piece = 0;
r.start = 0;
@ -128,6 +144,10 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
pm->async_read(r, bind(&on_read_piece, _1, _2, piece2, piece_size));
pm->async_release_files(none);
pm->async_rename_file(0, "temp_storage/test1.tmp", none);
test_sleep(1000);
TEST_CHECK(!exists(test_path / "part0"));
ios.run();
io.join();
@ -136,13 +156,13 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
void test_remove(path const& test_path)
{
libtorrent::create_torrent t;
t.set_piece_size(4);
t.add_file("temp_storage/test1.tmp", 8);
t.add_file("temp_storage/folder1/test2.tmp", 8);
t.add_file("temp_storage/folder2/test3.tmp", 0);
t.add_file("temp_storage/_folder3/test4.tmp", 0);
t.add_file("temp_storage/_folder3/subfolder/test5.tmp", 8);
file_storage fs;
fs.add_file("temp_storage/test1.tmp", 8);
fs.add_file("temp_storage/folder1/test2.tmp", 8);
fs.add_file("temp_storage/folder2/test3.tmp", 0);
fs.add_file("temp_storage/_folder3/test4.tmp", 0);
fs.add_file("temp_storage/_folder3/subfolder/test5.tmp", 8);
libtorrent::create_torrent t(fs, 4);
char buf[4] = {0, 0, 0, 0};
sha1_hash h = hasher(buf, 4).final();
@ -152,7 +172,7 @@ void test_remove(path const& test_path)
file_pool fp;
boost::scoped_ptr<storage_interface> s(
default_storage_constructor(info, test_path, fp));
default_storage_constructor(fs, test_path, fp));
// allocate the files and create the directories
s->initialize(true);
@ -172,14 +192,15 @@ void run_test(path const& test_path)
boost::intrusive_ptr<torrent_info> info;
{
libtorrent::create_torrent t;
t.set_piece_size(piece_size);
t.add_file("temp_storage/test1.tmp", 17);
t.add_file("temp_storage/test2.tmp", 612);
t.add_file("temp_storage/test3.tmp", 0);
t.add_file("temp_storage/test4.tmp", 0);
t.add_file("temp_storage/test5.tmp", 1);
remove_all(test_path / "temp_storage");
file_storage fs;
fs.add_file("temp_storage/test1.tmp", 17);
fs.add_file("temp_storage/test2.tmp", 612);
fs.add_file("temp_storage/test3.tmp", 0);
fs.add_file("temp_storage/test4.tmp", 0);
fs.add_file("temp_storage/test5.tmp", 1);
libtorrent::create_torrent t(fs, piece_size);
t.set_hash(0, hasher(piece0, piece_size).final());
t.set_hash(1, hasher(piece1, piece_size).final());
t.set_hash(2, hasher(piece2, piece_size).final());
@ -188,7 +209,7 @@ void run_test(path const& test_path)
info = new torrent_info(t.generate());
std::cerr << "=== test 1 ===" << std::endl;
run_storage_tests(info, test_path, storage_mode_compact);
run_storage_tests(info, fs, test_path, storage_mode_compact);
// make sure the files have the correct size
std::cerr << file_size(test_path / "temp_storage" / "test1.tmp") << std::endl;
@ -200,63 +221,39 @@ void run_test(path const& test_path)
remove_all(test_path / "temp_storage");
}
// ==============================================
// make sure remap_files works
std::vector<file_entry> map;
file_entry fe;
fe.path = "temp_storage/test.tmp";
fe.size = 17;
fe.file_base = 612 + 1;
map.push_back(fe);
fe.path = "temp_storage/test.tmp";
fe.size = 612 + 1;
fe.file_base = 0;
map.push_back(fe);
bool ret = info->remap_files(map);
TEST_CHECK(ret);
std::cerr << "=== test 2 ===" << std::endl;
run_storage_tests(info, test_path, storage_mode_compact);
std::cerr << file_size(test_path / "temp_storage" / "test.tmp") << std::endl;
TEST_CHECK(file_size(test_path / "temp_storage" / "test.tmp") == 17 + 612 + 1);
remove_all(test_path / "temp_storage");
// ==============================================
{
libtorrent::create_torrent t;
t.set_piece_size(piece_size);
t.add_file("temp_storage/test1.tmp", 17 + 612 + 1);
file_storage fs;
fs.add_file("temp_storage/test1.tmp", 17 + 612 + 1);
libtorrent::create_torrent t(fs, piece_size);
TEST_CHECK(fs.begin()->path == "temp_storage/test1.tmp");
t.set_hash(0, hasher(piece0, piece_size).final());
t.set_hash(1, hasher(piece1, piece_size).final());
t.set_hash(2, hasher(piece2, piece_size).final());
info = new torrent_info(t.generate());
std::cerr << "=== test 3 ===" << std::endl;
run_storage_tests(info, test_path, storage_mode_compact);
run_storage_tests(info, fs, test_path, storage_mode_compact);
// 48 = piece_size * 3
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == 48);
remove_all(test_path / "temp_storage");
}
// ==============================================
std::cerr << "=== test 4 ===" << std::endl;
run_storage_tests(info, test_path, storage_mode_allocate);
run_storage_tests(info, fs, test_path, storage_mode_allocate);
std::cerr << file_size(test_path / "temp_storage" / "test1.tmp") << std::endl;
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == 17 + 612 + 1);
remove_all(test_path / "temp_storage");
}
// ==============================================

View File

@ -14,12 +14,12 @@ int test_main()
session ses(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48130, 48140));
libtorrent::create_torrent t;
file_storage fs;
size_type file_size = 1 * 1024 * 1024 * 1024;
t.add_file("test_torrent/tmp1", file_size);
t.add_file("test_torrent/tmp2", file_size);
t.add_file("test_torrent/tmp3", file_size);
t.set_piece_size(4 * 1024 * 1024);
fs.add_file("test_torrent/tmp1", file_size);
fs.add_file("test_torrent/tmp2", file_size);
fs.add_file("test_torrent/tmp3", file_size);
libtorrent::create_torrent t(fs, 4 * 1024 * 1024);
t.add_tracker("http://non-existing.com/announce");
std::vector<char> piece(4 * 1024 * 1024);

View File

@ -15,22 +15,6 @@
using namespace boost::filesystem;
using namespace libtorrent;
void add_files(libtorrent::create_torrent& t, path const& p, path const& l)
{
if (l.leaf()[0] == '.') return;
path f(p / l);
if (is_directory(f))
{
for (directory_iterator i(f), end; i != end; ++i)
add_files(t, p, l / i->leaf());
}
else
{
std::cerr << "adding \"" << l.string() << "\"\n";
t.add_file(l, file_size(f));
}
}
// proxy: 0=none, 1=socks4, 2=socks5, 3=socks5_pw 4=http 5=http_pw
void test_transfer(boost::intrusive_ptr<torrent_info> torrent_file, int proxy)
{
@ -87,9 +71,6 @@ int test_main()
using namespace libtorrent;
using namespace boost::filesystem;
libtorrent::create_torrent t;
t.add_url_seed("http://127.0.0.1:8000/");
create_directory("test_torrent");
char random_data[300000];
std::srand(std::time(0));
@ -102,7 +83,11 @@ int test_main()
std::ofstream("./test_torrent/test6").write(random_data, 300000);
std::ofstream("./test_torrent/test7").write(random_data, 300000);
add_files(t, complete("."), "test_torrent");
file_storage fs;
add_files(fs, path("test_torrent"));
libtorrent::create_torrent t(fs, 16 * 1024);
t.add_url_seed("http://127.0.0.1:8000/");
start_web_server(8000);
@ -111,20 +96,17 @@ int test_main()
std::vector<char> buf(t.piece_length());
file_pool fp;
boost::intrusive_ptr<torrent_info> torrent_file(new torrent_info(t.generate()));
boost::scoped_ptr<storage_interface> s(default_storage_constructor(
torrent_file, ".", fp));
fs, ".", fp));
for (int i = 0; i < num; ++i)
{
s->read(&buf[0], i, 0, t.piece_size(i));
hasher h(&buf[0], t.piece_size(i));
s->read(&buf[0], i, 0, fs.piece_size(i));
hasher h(&buf[0], fs.piece_size(i));
t.set_hash(i, h.final());
}
entry e = t.generate();
torrent_file = new torrent_info(e);
s.reset(default_storage_constructor(torrent_file, ".", fp));
boost::intrusive_ptr<torrent_info> torrent_file(new torrent_info(t.generate()));
for (int i = 0; i < 6; ++i)
test_transfer(torrent_file, i);