diff --git a/ChangeLog b/ChangeLog index 0829382b4..c6f126554 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ + * added add_piece() function to inject data from external sources * add_tracker() function added to torrent_handle * if there is no working tracker, current_tracker is the tracker that is currently being tried diff --git a/docs/manual.rst b/docs/manual.rst index 957e80528..4af647c60 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1717,6 +1717,9 @@ Its declaration looks like this:: void rename_file(int index, boost::filesystem::wpath) const; storage_interface* get_storage_impl() const; + enum flags_t { overwrite_existing = 1 }; + void add_piece(int piece, char const* data, int flags = 0) const; + sha1_hash info_hash() const; bool operator==(torrent_handle const&) const; @@ -1868,6 +1871,27 @@ get_storage_impl() Returns the storage implementation for this torrent. This depends on the storage contructor function that was passed to ``session::add_torrent``. +add_piece() +----------- + + :: + + enum flags_t { overwrite_existing = 1 }; + void add_piece(int piece, char const* data, int flags = 0) const; + +This function will write ``data`` to the storage as piece ``piece``, as if it had +been downloaded from a peer. ``data`` is expected to point to a buffer of as many +bytes as the size of the specified piece. The data in the buffer is copied and +passed on to the disk IO thread to be written at a later point. + +By default, data that's already been downloaded is not overwritten by this buffer. If +you trust this data to be correct (and pass the piece hash check) you may pass the +``overwrite_existing`` flag. This will instruct libtorrent to overwrite any data that +may already have been downloaded with this data. + +Since the data is written asynchronously, you may know that is passed or failed the +hash check by waiting for ``piece_finished_alert`` or ``has_failed_alert``. + force_reannounce() ------------------ diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index f6c640f0b..aac562aaf 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -169,6 +169,11 @@ namespace libtorrent int seed_rank(session_settings const& s) const; + enum flags_t { overwrite_existing = 1 }; + void add_piece(int piece, char const* data, int flags = 0); + void on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p); + storage_mode_t storage_mode() const { return m_storage_mode; } storage_interface* get_storage() { diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 14ff76326..2a21894b4 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -314,6 +314,9 @@ namespace libtorrent torrent_handle() {} + enum flags_t { overwrite_existing = 1 }; + void add_piece(int piece, char const* data, int flags = 0) const; + void get_full_peer_list(std::vector& v) const; void get_peer_info(std::vector& v) const; torrent_status status() const; diff --git a/src/torrent.cpp b/src/torrent.cpp index e8d3fe706..10793cf50 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -370,6 +370,71 @@ namespace libtorrent if (!m_connections.empty()) disconnect_all(); } + + void torrent::add_piece(int piece, char const* data, int flags) + { + TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); + int piece_size = m_torrent_file->piece_size(piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + peer_request p; + p.piece = piece; + p.start = 0; + picker().inc_refcount(piece); + for (int i = 0; i < blocks_in_piece; ++i, p.start += m_block_size) + { + if (picker().is_finished(piece_block(piece, i)) + && (flags & torrent::overwrite_existing) == 0) + continue; + + p.length = (std::min)(piece_size - p.start, m_block_size); + char* buffer = m_ses.allocate_disk_buffer(); + // out of memory + if (buffer == 0) return; + disk_buffer_holder holder(m_ses, buffer); + std::memcpy(buffer, data + p.start, p.length); + filesystem().async_write(p, holder, bind(&torrent::on_disk_write_complete + , shared_from_this(), _1, _2, p)); + piece_block block(piece, i); + picker().mark_as_downloading(block, 0, piece_picker::fast); + picker().mark_as_writing(block, 0); + } + picker().dec_refcount(piece); + } + + void torrent::on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_abort) + { + piece_block block_finished(p.piece, p.start / m_block_size); + return; + } + + piece_block block_finished(p.piece, p.start / m_block_size); + + if (ret == -1) + { + if (has_picker()) picker().write_failed(block_finished); + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.str)); + set_error(j.str); + pause(); + return; + } + picker().mark_as_finished(block_finished, 0); + + // did we just finish the piece? + if (picker().is_piece_finished(p.piece)) + { + async_verify_piece(p.piece, bind(&torrent::piece_finished, shared_from_this() + , p.piece, _1)); + } + } peer_request torrent::to_req(piece_block const& p) { diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 8b0eb431b..d15698600 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -535,6 +535,12 @@ namespace libtorrent TORRENT_FORWARD(add_tracker(url)); } + void torrent_handle::add_piece(int piece, char const* data, int flags) const + { + INVARIANT_CHECK; + TORRENT_FORWARD(add_piece(piece, data, flags)); + } + storage_interface* torrent_handle::get_storage_impl() const { INVARIANT_CHECK; diff --git a/test/test_torrent.cpp b/test/test_torrent.cpp index bcc1df407..d7f09e910 100644 --- a/test/test_torrent.cpp +++ b/test/test_torrent.cpp @@ -49,11 +49,28 @@ void test_running_torrent(boost::intrusive_ptr info, size_type fil TEST_CHECK(st.total_wanted == file_size); std::cout << "total_wanted_done: " << st.total_wanted_done << " : 0" << std::endl; TEST_CHECK(st.total_wanted_done == 0); + + if (info->num_pieces() > 0) + { + h.piece_priority(0, 1); + st = h.status(); + TEST_CHECK(st.pieces[0] == false); + std::vector piece(info->piece_length()); + for (int i = 0; i < int(piece.size()); ++i) + piece[i] = (i % 26) + 'A'; + h.add_piece(0, &piece[0]); + test_sleep(10000); + st = h.status(); + TEST_CHECK(st.pieces[0] == true); + } } int test_main() { { + remove("test_torrent_dir2/tmp1"); + remove("test_torrent_dir2/tmp2"); + remove("test_torrent_dir2/tmp3"); file_storage fs; size_type file_size = 1 * 1024 * 1024 * 1024; fs.add_file("test_torrent_dir2/tmp1", file_size); @@ -69,6 +86,7 @@ int test_main() // calculate the hash for all pieces sha1_hash ph = hasher(&piece[0], piece.size()).final(); int num = t.num_pieces(); + TEST_CHECK(t.num_pieces() > 0); for (int i = 0; i < num; ++i) t.set_hash(i, ph); @@ -76,6 +94,7 @@ int test_main() std::back_insert_iterator > out(tmp); bencode(out, t.generate()); boost::intrusive_ptr info(new torrent_info(&tmp[0], tmp.size())); + TEST_CHECK(info->num_pieces() > 0); test_running_torrent(info, file_size); }