summarylogtreecommitdiffstats
path: root/backport_lt_all_02-honor_system_file_allocate_fix.patch
diff options
context:
space:
mode:
Diffstat (limited to 'backport_lt_all_02-honor_system_file_allocate_fix.patch')
-rw-r--r--backport_lt_all_02-honor_system_file_allocate_fix.patch340
1 files changed, 340 insertions, 0 deletions
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