summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO22
-rw-r--r--PKGBUILD50
-rw-r--r--backport_lt_all_01-partially_done_and_choke_group_fix.patch282
-rw-r--r--backport_lt_all_02-honor_system_file_allocate_fix.patch340
-rw-r--r--lt-ps_all_02-better-bencode-errors_all.patch40
5 files changed, 734 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..c06eee3f9619
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,22 @@
+pkgbase = libtorrent-ps-ch
+ pkgdesc = BitTorrent library written in C++ for use with rtorrent-ps-ch
+ pkgver = 1.8.3
+ pkgrel = 1
+ url = https://github.com/rakshasa/libtorrent
+ arch = any
+ license = GPL2
+ depends = openssl
+ provides = libtorrent
+ conflicts = libtorrent
+ conflicts = libtorrent-ps
+ source = https://github.com/rakshasa/libtorrent/archive/v0.13.8.tar.gz
+ source = backport_lt_all_01-partially_done_and_choke_group_fix.patch
+ source = backport_lt_all_02-honor_system_file_allocate_fix.patch
+ source = lt-ps_all_02-better-bencode-errors_all.patch
+ md5sums = dd184eadb8b449ddc6c3498a93ddd568
+ md5sums = 2a8eb09877e81e3e72bd544c27b45dbb
+ md5sums = 4797c5abf04dca2468178bc85244d076
+ md5sums = dfae06b33afa738211619040810be8b4
+
+pkgname = libtorrent-ps-ch
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..d22e5ec39137
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,50 @@
+# Maintainer: qdesjardin <qdesjardin gmail com>
+
+_pkgname=libtorrent
+pkgname=libtorrent-ps-ch
+_pkgver=0.13.8
+pkgver=1.8.3
+pkgrel=1
+pkgdesc='BitTorrent library written in C++ for use with rtorrent-ps-ch'
+license=('GPL2')
+arch=('any')
+url='https://github.com/rakshasa/libtorrent'
+depends=('openssl')
+provides=('libtorrent')
+conflicts=('libtorrent' 'libtorrent-ps')
+source=("https://github.com/rakshasa/$_pkgname/archive/v$_pkgver.tar.gz"
+ 'backport_lt_all_01-partially_done_and_choke_group_fix.patch'
+ 'backport_lt_all_02-honor_system_file_allocate_fix.patch'
+ 'lt-ps_all_02-better-bencode-errors_all.patch')
+md5sums=('dd184eadb8b449ddc6c3498a93ddd568'
+ '2a8eb09877e81e3e72bd544c27b45dbb'
+ '4797c5abf04dca2468178bc85244d076'
+ 'dfae06b33afa738211619040810be8b4')
+
+prepare() {
+ cd "$srcdir/$_pkgname-$_pkgver"
+
+ for corepatch in "$srcdir"/lt-ps_*.patch; do
+ test ! -e "$corepatch" || { msg2 "$(basename $corepatch)"; patch -uNp1 -i "$corepatch"; }
+ done
+
+ for backport in "$srcdir"/{backport,misc}_lt_*.patch; do
+ test ! -e "$backport" || { msg2 "$(basename $backport)"; patch -uNp1 -i "$backport"; }
+ done
+
+ ./autogen.sh
+}
+
+build() {
+ cd "$srcdir/$_pkgname-$_pkgver"
+
+ ./configure \
+ --prefix=/usr \
+ --disable-debug
+ make
+}
+
+package() {
+ cd "$srcdir/$_pkgname-$_pkgver"
+ make DESTDIR="$pkgdir" install
+}
diff --git a/backport_lt_all_01-partially_done_and_choke_group_fix.patch b/backport_lt_all_01-partially_done_and_choke_group_fix.patch
new file mode 100644
index 000000000000..7c812bdf9e3e
--- /dev/null
+++ b/backport_lt_all_01-partially_done_and_choke_group_fix.patch
@@ -0,0 +1,282 @@
+--- a/src/download/download_wrapper.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/download/download_wrapper.cc 2017-04-30 21:25:15.511379804 +0100
+@@ -228,6 +228,7 @@ DownloadWrapper::receive_hash_done(Chunk
+ priority_queue_erase(&taskScheduler, &m_main->delay_partially_done());
+ priority_queue_erase(&taskScheduler, &m_main->delay_partially_restarted());
+ priority_queue_insert(&taskScheduler, &m_main->delay_partially_done(), cachedTime);
++ finished_download();
+ }
+
+ if (!m_main->have_queue()->empty() && m_main->have_queue()->front().first >= cachedTime)
+@@ -325,8 +326,10 @@ DownloadWrapper::receive_tick(uint32_t t
+
+ void
+ DownloadWrapper::receive_update_priorities() {
+- if (m_main->chunk_selector()->empty())
++ if (m_main->chunk_selector()->empty()) {
++ file_list()->set_selected_size_bytes();
+ return;
++ }
+
+ data()->mutable_high_priority()->clear();
+ data()->mutable_normal_priority()->clear();
+@@ -373,11 +376,15 @@ DownloadWrapper::receive_update_prioriti
+ priority_queue_erase(&taskScheduler, &m_main->delay_partially_done());
+ priority_queue_erase(&taskScheduler, &m_main->delay_partially_restarted());
+
+- if (was_partial)
++ if (was_partial) {
+ priority_queue_insert(&taskScheduler, &m_main->delay_partially_done(), cachedTime);
+- else
++ finished_download();
++ } else {
+ priority_queue_insert(&taskScheduler, &m_main->delay_partially_restarted(), cachedTime);
++ }
+ }
++
++ file_list()->set_selected_size_bytes();
+ }
+
+ void
+--- a/src/manager.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/manager.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -98,7 +98,7 @@ Manager::Manager() :
+ m_connectionManager->listen()->slot_accepted() =
+ std::bind(&HandshakeManager::add_incoming, m_handshakeManager, std::placeholders::_1, std::placeholders::_2);
+
+- m_resourceManager->push_group("default");
++ m_resourceManager->push_group("default_leech");
+ m_resourceManager->group_back()->up_queue()->set_heuristics(choke_queue::HEURISTICS_UPLOAD_LEECH);
+ m_resourceManager->group_back()->down_queue()->set_heuristics(choke_queue::HEURISTICS_DOWNLOAD_LEECH);
+ }
+--- a/src/protocol/handshake_manager.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/protocol/handshake_manager.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -232,7 +232,7 @@ HandshakeManager::receive_succeeded(Hand
+
+ if (!download->info()->is_active())
+ reason = e_handshake_inactive_download;
+- else if (download->file_list()->is_done() && handshake->bitfield()->is_all_set())
++ else if (download->file_list()->data()->is_partially_done() && handshake->bitfield()->is_all_set())
+ reason = e_handshake_unwanted_connection;
+ else
+ reason = e_handshake_duplicate;
+--- a/src/protocol/peer_connection_base.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/protocol/peer_connection_base.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -197,7 +197,7 @@ PeerConnectionBase::initialize(DownloadM
+
+ m_peerChunks.download_cache()->clear();
+
+- if (!m_download->file_list()->is_done()) {
++ if (!m_download->file_list()->data()->is_partially_done()) {
+ m_sendInterested = true;
+ m_downInterested = true;
+ }
+--- a/src/protocol/peer_connection_leech.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/protocol/peer_connection_leech.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -691,7 +691,7 @@ PeerConnection<type>::read_have_chunk(ui
+ m_download->choke_group()->up_queue()->set_not_queued(this, &m_upChoke);
+ }
+
+- if (type != Download::CONNECTION_LEECH || m_download->file_list()->is_done())
++ if (type != Download::CONNECTION_LEECH || m_download->file_list()->data()->is_partially_done())
+ return;
+
+ if (is_down_interested()) {
+--- a/src/torrent/data/file_list.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/data/file_list.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -86,6 +86,7 @@ FileList::FileList() :
+ m_isOpen(false),
+
+ m_torrentSize(0),
++ m_selectedSize(0),
+ m_chunkSize(0),
+ m_maxFileSize(~uint64_t()) {
+ }
+@@ -98,6 +99,7 @@ FileList::~FileList() {
+
+ base_type::clear();
+ m_torrentSize = 0;
++ m_selectedSize = 0;
+ }
+
+ bool
+@@ -193,6 +195,65 @@ FileList::set_max_file_size(uint64_t siz
+ m_maxFileSize = size;
+ }
+
++// This function should be called from receive_update_priorities().
++// It offloads continous calculation by updating the m_selectedSize property.
++// Its purpose is to replace size_bytes() with selected_size_bytes()
++// by taking into account partial downloads.
++void
++FileList::set_selected_size_bytes(uint64_t bytes) {
++ if (bytes > 0) {
++ m_selectedSize = bytes;
++ return;
++ }
++
++ if (is_done()) {
++ m_selectedSize = m_torrentSize;
++ return;
++ }
++
++ uint64_t completedBytes = completed_bytes();
++
++ if (data()->is_partially_done()) {
++ m_selectedSize = completedBytes;
++ return;
++ }
++
++ uint32_t selectedSizeChunks = 0;
++ uint32_t prevChunk = -1;
++ bool areAllFilesSelected = true;
++ bool isLastFileSelected = false;
++
++ for (FileList::const_iterator itr = begin(), last = end(); itr != last; itr++) {
++
++ if ((*itr)->priority() != PRIORITY_OFF) {
++ selectedSizeChunks += (*itr)->size_chunks() - ((*itr)->range_first() == prevChunk ? 1 : 0);
++ prevChunk = (*itr)->range_second() - 1;
++
++ if (itr == end() - 1)
++ isLastFileSelected = true;
++ } else {
++ areAllFilesSelected = false;
++ }
++
++ }
++
++ if (areAllFilesSelected) {
++ m_selectedSize = m_torrentSize;
++ return;
++ }
++
++ uint64_t selectedSizeBytes = (uint64_t)selectedSizeChunks * (uint64_t)m_chunkSize;
++
++ // Dealing with size of last chunk as it's usually smaller than the rest.
++ uint64_t remainder = m_torrentSize % (uint64_t)m_chunkSize;
++
++ if (isLastFileSelected && remainder != 0)
++ selectedSizeBytes = selectedSizeBytes - (uint64_t)m_chunkSize + remainder;
++
++ // Set completed bytes if some files (e.g. all of them) were set to Off later.
++ m_selectedSize = (selectedSizeBytes < completedBytes ? completedBytes : selectedSizeBytes);
++}
++
+ // This function should really ensure that we arn't dealing files
+ // spread over multiple mount-points.
+ uint64_t
+@@ -376,6 +432,7 @@ FileList::initialize(uint64_t torrentSiz
+
+ m_chunkSize = chunkSize;
+ m_torrentSize = torrentSize;
++ m_selectedSize = torrentSize;
+ m_rootDir = ".";
+
+ m_data.mutable_completed_bitfield()->set_size_bits((size_bytes() + chunk_size() - 1) / chunk_size());
+@@ -725,6 +782,7 @@ FileList::reset_filesize(int64_t size) {
+ close();
+ m_chunkSize = size;
+ m_torrentSize = size;
++ m_selectedSize = size;
+ (*begin())->set_size_bytes(size);
+ (*begin())->set_range(m_chunkSize);
+
+--- a/src/torrent/data/file_list.h 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/data/file_list.h 2017-04-30 21:15:43.000000000 +0100
+@@ -109,6 +109,9 @@ public:
+ uint64_t size_bytes() const { return m_torrentSize; }
+ uint32_t size_chunks() const { return bitfield()->size_bits(); }
+
++ uint64_t selected_size_bytes() const { return m_selectedSize; }
++ void set_selected_size_bytes(uint64_t bytes = 0);
++
+ uint32_t completed_chunks() const { return bitfield()->size_set(); }
+ uint64_t completed_bytes() const;
+ uint64_t left_bytes() const;
+@@ -187,6 +190,7 @@ private:
+ uint64_t m_torrentSize;
+ uint32_t m_chunkSize;
+ uint64_t m_maxFileSize;
++ uint64_t m_selectedSize;
+
+ std::string m_rootDir;
+
+--- a/src/torrent/download.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/download.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -565,32 +565,6 @@ Download::set_connection_type(Connection
+ m_ptr->set_connection_type(t);
+ }
+
+-Download::HeuristicType
+-Download::upload_choke_heuristic() const {
+- return (Download::HeuristicType)m_ptr->main()->choke_group()->up_queue()->heuristics();
+-}
+-
+-void
+-Download::set_upload_choke_heuristic(HeuristicType t) {
+- if ((choke_queue::heuristics_enum)t >= choke_queue::HEURISTICS_MAX_SIZE)
+- throw input_error("Invalid heuristics value.");
+-
+- m_ptr->main()->choke_group()->up_queue()->set_heuristics((choke_queue::heuristics_enum)t);
+-}
+-
+-Download::HeuristicType
+-Download::download_choke_heuristic() const {
+- return (Download::HeuristicType)m_ptr->main()->choke_group()->down_queue()->heuristics();
+-}
+-
+-void
+-Download::set_download_choke_heuristic(HeuristicType t) {
+- if ((choke_queue::heuristics_enum)t >= choke_queue::HEURISTICS_MAX_SIZE)
+- throw input_error("Invalid heuristics value.");
+-
+- m_ptr->main()->choke_group()->down_queue()->set_heuristics((choke_queue::heuristics_enum)t);
+-}
+-
+ void
+ Download::update_priorities() {
+ m_ptr->receive_update_priorities();
+--- a/src/torrent/download.h 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/download.h 2017-04-30 21:15:43.000000000 +0100
+@@ -183,15 +183,6 @@ public:
+ ConnectionType connection_type() const;
+ void set_connection_type(ConnectionType t);
+
+- typedef enum {
+- } HeuristicType;
+-
+- HeuristicType upload_choke_heuristic() const;
+- void set_upload_choke_heuristic(HeuristicType t);
+-
+- HeuristicType download_choke_heuristic() const;
+- void set_download_choke_heuristic(HeuristicType t);
+-
+ // Call this when you want the modifications of the download priorities
+ // in the entries to take effect. It is slightly expensive as it rechecks
+ // all the peer bitfields to see if we are still interested.
+--- a/src/torrent/peer/connection_list.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/peer/connection_list.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -79,7 +79,7 @@ ConnectionList::clear() {
+
+ bool
+ ConnectionList::want_connection(PeerInfo* p, Bitfield* bitfield) {
+- if (m_download->file_list()->is_done() || m_download->initial_seeding() != NULL)
++ if (m_download->file_list()->data()->is_partially_done() || m_download->initial_seeding() != NULL)
+ return !bitfield->is_all_set();
+
+ if (!m_download->info()->is_accepting_seeders())
+--- a/src/torrent/utils/resume.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/utils/resume.cc 2017-04-30 21:15:43.000000000 +0100
+@@ -254,10 +254,7 @@ resume_save_progress(Download download,
+
+ // } else if ((*listItr)->completed_chunks() == (*listItr)->size_chunks()) {
+
+- } else if (fileList->bitfield()->is_all_set()) {
+- // Currently only checking if we're finished. This needs to be
+- // smarter when it comes to downloading partial torrents, etc.
+-
++ } else if (download.data()->is_partially_done()) {
+ // This assumes the syncs are properly called before
+ // resume_save_progress gets called after finishing a torrent.
+ filesItr->insert_key("mtime", (int64_t)fs.modified_time());
diff --git a/backport_lt_all_02-honor_system_file_allocate_fix.patch b/backport_lt_all_02-honor_system_file_allocate_fix.patch
new file mode 100644
index 000000000000..0029cdc374a7
--- /dev/null
+++ b/backport_lt_all_02-honor_system_file_allocate_fix.patch
@@ -0,0 +1,340 @@
+--- a/src/download/download_wrapper.cc 2017-04-30 21:25:15.511379804 +0100
++++ a/src/download/download_wrapper.cc 2017-04-30 22:10:12.910100074 +0100
+@@ -46,6 +46,7 @@
+ #include "protocol/handshake_manager.h"
+ #include "protocol/peer_connection_base.h"
+ #include "torrent/exceptions.h"
++#include "torrent/download.h"
+ #include "torrent/object.h"
+ #include "torrent/tracker_list.h"
+ #include "torrent/data/file.h"
+@@ -103,7 +104,7 @@ DownloadWrapper::~DownloadWrapper() {
+ }
+
+ void
+-DownloadWrapper::initialize(const std::string& hash, const std::string& id) {
++DownloadWrapper::initialize(const std::string& hash, const std::string& id, int flags) {
+ char hashObfuscated[20];
+ sha1_salt("req2", 4, hash.c_str(), hash.length(), hashObfuscated);
+
+@@ -124,7 +125,7 @@ DownloadWrapper::initialize(const std::s
+
+ // Connect various signals and slots.
+ m_hashChecker->slot_check_chunk() = std::bind(&DownloadWrapper::check_chunk_hash, this, std::placeholders::_1);
+- m_hashChecker->delay_checked().slot() = std::bind(&DownloadWrapper::receive_initial_hash, this);
++ m_hashChecker->delay_checked().slot() = std::bind(&DownloadWrapper::receive_initial_hash, this, flags);
+ }
+
+ void
+@@ -156,7 +157,7 @@ DownloadWrapper::is_stopped() const {
+ }
+
+ void
+-DownloadWrapper::receive_initial_hash() {
++DownloadWrapper::receive_initial_hash(int flags) {
+ if (info()->is_active())
+ throw internal_error("DownloadWrapper::receive_initial_hash() but we're in a bad state.");
+
+@@ -172,7 +173,7 @@ DownloadWrapper::receive_initial_hash()
+ // Initialize the ChunkSelector here so that no chunks will be
+ // marked by HashTorrent that are not accounted for.
+ m_main->chunk_selector()->initialize(m_main->chunk_statistics());
+- receive_update_priorities();
++ receive_update_priorities(flags);
+ }
+
+ if (data()->slot_initial_hash())
+@@ -325,7 +326,7 @@ DownloadWrapper::receive_tick(uint32_t t
+ }
+
+ void
+-DownloadWrapper::receive_update_priorities() {
++DownloadWrapper::receive_update_priorities(int flags) {
+ if (m_main->chunk_selector()->empty()) {
+ file_list()->set_selected_size_bytes();
+ return;
+@@ -335,9 +336,15 @@ DownloadWrapper::receive_update_prioriti
+ data()->mutable_normal_priority()->clear();
+
+ for (FileList::iterator itr = m_main->file_list()->begin(); itr != m_main->file_list()->end(); ++itr) {
++ // Unset fallocate flag by default.
++ (*itr)->unset_flags(File::flag_fallocate);
++
+ switch ((*itr)->priority()) {
+ case PRIORITY_NORMAL:
+ {
++ if (flags & torrent::Download::open_enable_fallocate)
++ (*itr)->set_flags(File::flag_fallocate);
++
+ File::range_type range = (*itr)->range();
+
+ if ((*itr)->has_flags(File::flag_prioritize_first) && range.first != range.second) {
+@@ -354,6 +361,9 @@ DownloadWrapper::receive_update_prioriti
+ break;
+ }
+ case PRIORITY_HIGH:
++ if (flags & torrent::Download::open_enable_fallocate)
++ (*itr)->set_flags(File::flag_fallocate);
++
+ data()->mutable_high_priority()->insert((*itr)->range().first, (*itr)->range().second);
+ break;
+ default:
+--- a/src/download/download_wrapper.h 2016-12-12 08:49:18.000000000 +0000
++++ a/src/download/download_wrapper.h 2017-04-30 22:06:19.000000000 +0100
+@@ -64,7 +64,7 @@ public:
+ ChunkList* chunk_list() { return m_main->chunk_list(); }
+
+ // Initialize hash checker and various download stuff.
+- void initialize(const std::string& hash, const std::string& id);
++ void initialize(const std::string& hash, const std::string& id, int flags = 0);
+
+ void close();
+
+@@ -91,7 +91,7 @@ public:
+ // Internal:
+ //
+
+- void receive_initial_hash();
++ void receive_initial_hash(int flags = 0);
+ void receive_hash_done(ChunkHandle handle, const char* hash);
+
+ void check_chunk_hash(ChunkHandle handle);
+@@ -102,7 +102,7 @@ public:
+
+ void receive_tick(uint32_t ticks);
+
+- void receive_update_priorities();
++ void receive_update_priorities(int flags = 0);
+
+ private:
+ DownloadWrapper(const DownloadWrapper&);
+--- a/src/torrent/data/file.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/data/file.cc 2017-04-30 22:06:19.000000000 +0100
+@@ -184,6 +184,7 @@ File::resize_file() {
+ if (m_flags & flag_fallocate) {
+ flags |= SocketFile::flag_fallocate;
+ flags |= SocketFile::flag_fallocate_blocking;
++ m_flags &= ~flag_fallocate;
+ }
+
+ return SocketFile(m_fd).set_size(m_size, flags);
+--- a/src/torrent/data/file.h 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/data/file.h 2017-04-30 22:06:19.000000000 +0100
+@@ -68,8 +68,11 @@ public:
+
+ bool is_create_queued() const { return m_flags & flag_create_queued; }
+ bool is_resize_queued() const { return m_flags & flag_resize_queued; }
++ bool is_fallocatable() const { return m_flags & flag_fallocate; }
+ bool is_previously_created() const { return m_flags & flag_previously_created; }
+
++ bool is_fallocatable_file() { return has_flags(flag_resize_queued) && has_flags(flag_fallocate); }
++
+ bool has_flags(int flags) { return m_flags & flags; }
+
+ void set_flags(int flags);
+--- a/src/torrent/data/file_list.cc 2017-04-30 21:15:43.000000000 +0100
++++ a/src/torrent/data/file_list.cc 2017-04-30 22:06:19.000000000 +0100
+@@ -259,9 +259,9 @@ FileList::set_selected_size_bytes() {
+ uint64_t
+ FileList::free_diskspace() const {
+ uint64_t freeDiskspace = std::numeric_limits<uint64_t>::max();
++ rak::fs_stat stat;
+
+ for (path_list::const_iterator itr = m_indirectLinks.begin(), last = m_indirectLinks.end(); itr != last; ++itr) {
+- rak::fs_stat stat;
+
+ if (!stat.update(*itr))
+ continue;
+@@ -269,9 +269,74 @@ FileList::free_diskspace() const {
+ freeDiskspace = std::min<uint64_t>(freeDiskspace, stat.bytes_avail());
+ }
+
++ // Check the base directory of download if files haven't been created yet
++ if (freeDiskspace == std::numeric_limits<uint64_t>::max()) {
++ std::string dirPath = is_multi_file() ? m_rootDir.substr(0, m_rootDir.find_last_of("\\/")) : m_rootDir;
++
++ if (stat.update(dirPath))
++ freeDiskspace = std::min<uint64_t>(freeDiskspace, stat.bytes_avail());
++ }
++
+ return freeDiskspace != std::numeric_limits<uint64_t>::max() ? freeDiskspace : 0;
+ }
+
++uint64_t
++FileList::allocatable_size_bytes() const {
++ uint64_t allocatableSizeBytes = 0;
++
++ if (data()->is_partially_done())
++ return allocatableSizeBytes;
++
++ uint32_t allocatableSizeChunks = 0;
++ uint32_t prevChunk = -1;
++ bool areAllFilesAllocatable = true;
++ bool isLastFileAllocatable = false;
++
++ for (FileList::const_iterator itr = begin(), last = end(); itr != last; itr++) {
++
++ // Checks flag_fallocate and flag_resize_queued as well, it will take care of restarting client.
++ if ((*itr)->is_fallocatable_file()) {
++ allocatableSizeChunks += (*itr)->size_chunks() - ((*itr)->range_first() == prevChunk ? 1 : 0);
++ prevChunk = (*itr)->range_second() - 1;
++
++ if (itr == end() - 1)
++ isLastFileAllocatable = true;
++ } else {
++ areAllFilesAllocatable = false;
++ }
++
++ }
++
++ if (areAllFilesAllocatable)
++ return m_torrentSize;
++
++ allocatableSizeBytes = (uint64_t)allocatableSizeChunks * (uint64_t)m_chunkSize;
++
++ // Dealing with size of last chunk as it's usually smaller than the rest.
++ uint64_t reminder = m_torrentSize % (uint64_t)m_chunkSize;
++
++ if (isLastFileAllocatable && reminder != 0)
++ allocatableSizeBytes = allocatableSizeBytes - (uint64_t)m_chunkSize + reminder;
++
++ return allocatableSizeBytes;
++}
++
++bool
++FileList::is_enough_diskspace() const {
++ uint64_t allocatable_size = allocatable_size_bytes();
++
++ if (allocatable_size > 0) {
++ uint64_t free_disk_space = free_diskspace();
++
++ if (free_disk_space < allocatable_size) {
++ LT_LOG_FL(INFO, "File allocation is set and not enough disk space to start torrent: allocatable size:%i free space:%i", allocatable_size, free_disk_space);
++ return false;
++ }
++ }
++
++ return true;
++}
++
+ FileList::iterator_range
+ FileList::split(iterator position, split_type* first, split_type* last) {
+ if (is_open())
+@@ -642,7 +707,12 @@ FileList::open_file(File* node, const Pa
+ return false;
+ }
+
+- return node->prepare(MemoryChunk::prot_read, 0);
++ // File allocation will be done if fallocate flag is set,
++ // create zero-length file otherwise.
++ if (node->has_flags(File::flag_fallocate))
++ return node->prepare(MemoryChunk::prot_write, 0);
++ else
++ return node->prepare(MemoryChunk::prot_read, 0);
+ }
+
+ MemoryChunk
+--- a/src/torrent/data/file_list.h 2017-04-30 21:15:43.000000000 +0100
++++ a/src/torrent/data/file_list.h 2017-04-30 22:06:19.000000000 +0100
+@@ -107,6 +107,7 @@ public:
+
+ size_t size_files() const { return base_type::size(); }
+ uint64_t size_bytes() const { return m_torrentSize; }
++ uint64_t allocatable_size_bytes() const;
+ uint32_t size_chunks() const { return bitfield()->size_bits(); }
+
+ uint64_t selected_size_bytes() const { return m_selectedSize; }
+@@ -135,6 +136,10 @@ public:
+ // of free diskspace will be returned.
+ uint64_t free_diskspace() const;
+
++ // Determines whether there is enough disk space for fallocating
++ // selected files.
++ bool is_enough_diskspace() const;
++
+ // List of directories in the torrent that might be on different
+ // volumes as they are links, including the root directory. Used by
+ // 'free_diskspace()'.
+--- a/src/torrent/download.cc 2017-04-30 21:15:43.000000000 +0100
++++ a/src/torrent/download.cc 2017-05-14 21:28:05.138916583 +0100
+@@ -137,7 +137,13 @@ Download::start(int flags) {
+ throw internal_error("Tried to start a download with empty bitfield.");
+
+ if (info->is_active())
+- return;
++ throw internal_error("Tried to start an already started download.");
++
++ // Don't start the download if there's not enough disk space for it
++ // when the open_enable_fallocate was set and at least one of the
++ // files has fallocate flag (libtorrent would crash otherwise)
++ if (flags & open_enable_fallocate && !m_ptr->main()->file_list()->is_enough_diskspace())
++ throw internal_error("Tried to start a download with not enough disk space for it.");
+
+ LT_LOG_THIS(INFO, "Starting torrent: flags:%0x.", flags);
+
+@@ -145,9 +151,9 @@ Download::start(int flags) {
+
+ // file_list()->open(flags);
+
+- // If the FileList::open_no_create flag was not set, our new
+- // behavior is to create all zero-length files with
+- // flag_queued_create set.
++ // If the open_enable_fallocate or the FileList::open_no_create
++ // flag was not set, then create all zero-length files with
++ // flag_create_queued set.
+ file_list()->open(flags & ~FileList::open_no_create);
+
+ if (m_ptr->connection_type() == CONNECTION_INITIAL_SEED) {
+@@ -566,8 +572,8 @@ Download::set_connection_type(Connection
+ }
+
+ void
+-Download::update_priorities() {
+- m_ptr->receive_update_priorities();
++Download::update_priorities(int flags) {
++ m_ptr->receive_update_priorities(flags);
+ }
+
+ void
+--- a/src/torrent/download.h 2017-04-30 21:15:43.000000000 +0100
++++ a/src/torrent/download.h 2017-05-14 21:25:52.249636506 +0100
+@@ -186,7 +186,7 @@ public:
+ // Call this when you want the modifications of the download priorities
+ // in the entries to take effect. It is slightly expensive as it rechecks
+ // all the peer bitfields to see if we are still interested.
+- void update_priorities();
++ void update_priorities(int flags = 0);
+
+ void add_peer(const sockaddr* addr, int port);
+
+--- a/src/torrent/torrent.cc 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/torrent.cc 2017-04-30 22:06:19.000000000 +0100
+@@ -164,7 +164,7 @@ encoding_list() {
+ }
+
+ Download
+-download_add(Object* object) {
++download_add(Object* object, int flags) {
+ std::auto_ptr<DownloadWrapper> download(new DownloadWrapper);
+
+ DownloadConstructor ctor;
+@@ -190,7 +190,7 @@ download_add(Object* object) {
+ }
+
+ download->set_hash_queue(manager->hash_queue());
+- download->initialize(infoHash, PEER_NAME + rak::generate_random<std::string>(20 - std::string(PEER_NAME).size()));
++ download->initialize(infoHash, PEER_NAME + rak::generate_random<std::string>(20 - std::string(PEER_NAME).size()), flags);
+
+ // Add trackers, etc, after setting the info hash so that log
+ // entries look sane.
+--- a/src/torrent/torrent.h 2016-12-12 08:49:18.000000000 +0000
++++ a/src/torrent/torrent.h 2017-04-30 22:06:19.000000000 +0100
+@@ -93,7 +93,7 @@ EncodingList* encoding_list() LIBT
+ // is done by 'download_remove'.
+ //
+ // Might consider redesigning that...
+-Download download_add(Object* s) LIBTORRENT_EXPORT;
++Download download_add(Object* s, int flags = 0) LIBTORRENT_EXPORT;
+ void download_remove(Download d) LIBTORRENT_EXPORT;
+
+ // Add all downloads to dlist. The client is responsible for clearing
diff --git a/lt-ps_all_02-better-bencode-errors_all.patch b/lt-ps_all_02-better-bencode-errors_all.patch
new file mode 100644
index 000000000000..6cfd192b70cf
--- /dev/null
+++ b/lt-ps_all_02-better-bencode-errors_all.patch
@@ -0,0 +1,40 @@
+diff --git a/src/torrent/object_raw_bencode.h b/src/torrent/object_raw_bencode.h
+index 5e82fff8..4f49386a 100644
+--- a/src/torrent/object_raw_bencode.h
++++ b/src/torrent/object_raw_bencode.h
+@@ -156,7 +156,7 @@ public:
+ inline std::string
+ raw_bencode::as_value_string() const {
+ if (!is_value())
+- throw bencode_error("Wrong object type.");
++ throw bencode_error("Wrong object type - not a value.");
+
+ return std::string(data() + 1, size() - 2);
+ }
+@@ -164,7 +164,7 @@ raw_bencode::as_value_string() const {
+ inline raw_string
+ raw_bencode::as_raw_string() const {
+ if (!is_raw_string())
+- throw bencode_error("Wrong object type.");
++ throw bencode_error("Wrong object type - not a raw string.");
+
+ const_iterator itr = std::find(begin(), end(), ':');
+
+@@ -177,7 +177,7 @@ raw_bencode::as_raw_string() const {
+ inline raw_list
+ raw_bencode::as_raw_list() const {
+ if (!is_raw_list())
+- throw bencode_error("Wrong object type.");
++ throw bencode_error("Wrong object type - not a raw list.");
+
+ return raw_list(m_data + 1, m_size - 2);
+ }
+@@ -185,7 +185,7 @@ raw_bencode::as_raw_list() const {
+ inline raw_map
+ raw_bencode::as_raw_map() const {
+ if (!is_raw_map())
+- throw bencode_error("Wrong object type.");
++ throw bencode_error("Wrong object type - not a raw map.");
+
+ return raw_map(m_data + 1, m_size - 2);
+ }