summarylogtreecommitdiffstats
path: root/backport_lt_all_02-honor_system_file_allocate_fix.patch
blob: 0029cdc374a74b7d2978b528a5f02014efaf9424 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
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