diff options
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.patch | 340 |
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 |