Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix honoring system.file.allocate.set=1 rtorrent config setting - clean base #152

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/download/download_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);

Expand All @@ -124,7 +125,7 @@ DownloadWrapper::initialize(const std::string& hash, const std::string& id) {

// 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
Expand Down Expand Up @@ -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.");

Expand All @@ -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())
Expand Down Expand Up @@ -324,17 +325,23 @@ DownloadWrapper::receive_tick(uint32_t ticks) {
}

void
DownloadWrapper::receive_update_priorities() {
DownloadWrapper::receive_update_priorities(int flags) {
if (m_main->chunk_selector()->empty())
return;

data()->mutable_high_priority()->clear();
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) {
Expand All @@ -351,6 +358,9 @@ DownloadWrapper::receive_update_priorities() {
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:
Expand Down
6 changes: 3 additions & 3 deletions src/download/download_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class DownloadWrapper {
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();

Expand All @@ -91,7 +91,7 @@ class DownloadWrapper {
// 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);
Expand All @@ -102,7 +102,7 @@ class DownloadWrapper {

void receive_tick(uint32_t ticks);

void receive_update_priorities();
void receive_update_priorities(int flags = 0);

private:
DownloadWrapper(const DownloadWrapper&);
Expand Down
1 change: 1 addition & 0 deletions src/torrent/data/file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/torrent/data/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ class LIBTORRENT_EXPORT lt_cacheline_aligned File {

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);
Expand Down
74 changes: 72 additions & 2 deletions src/torrent/data/file_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,19 +198,84 @@ FileList::set_max_file_size(uint64_t size) {
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;

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())
Expand Down Expand Up @@ -580,7 +645,12 @@ FileList::open_file(File* node, const Path& lastPath, int flags) {
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
Expand Down
5 changes: 5 additions & 0 deletions src/torrent/data/file_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class LIBTORRENT_EXPORT FileList : private std::vector<File*> {

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(); }

uint32_t completed_chunks() const { return bitfield()->size_set(); }
Expand All @@ -132,6 +133,10 @@ class LIBTORRENT_EXPORT FileList : private std::vector<File*> {
// 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()'.
Expand Down
18 changes: 12 additions & 6 deletions src/torrent/download.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,23 @@ 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);

m_ptr->data()->verify_wanted_chunks("Download::start(...)");

// 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) {
Expand Down Expand Up @@ -592,8 +598,8 @@ Download::set_download_choke_heuristic(HeuristicType t) {
}

void
Download::update_priorities() {
m_ptr->receive_update_priorities();
Download::update_priorities(int flags) {
m_ptr->receive_update_priorities(flags);
}

void
Expand Down
2 changes: 1 addition & 1 deletion src/torrent/download.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class LIBTORRENT_EXPORT Download {
// 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);

Expand Down
4 changes: 2 additions & 2 deletions src/torrent/torrent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/torrent/torrent.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ EncodingList* encoding_list() LIBTORRENT_EXPORT;
// 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
Expand Down