2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
29 #include "customstorage.h"
31 #include <libtorrent/download_priority.hpp>
33 #include "base/utils/fs.h"
36 #ifdef QBT_USES_LIBTORRENT2
37 #include <libtorrent/mmap_disk_io.hpp>
38 #include <libtorrent/posix_disk_io.hpp>
39 #include <libtorrent/session.hpp>
41 std::unique_ptr
<lt::disk_interface
> customDiskIOConstructor(
42 lt::io_context
&ioContext
, const lt::settings_interface
&settings
, lt::counters
&counters
)
44 return std::make_unique
<CustomDiskIOThread
>(lt::default_disk_io_constructor(ioContext
, settings
, counters
));
47 std::unique_ptr
<lt::disk_interface
> customPosixDiskIOConstructor(
48 lt::io_context
&ioContext
, const lt::settings_interface
&settings
, lt::counters
&counters
)
50 return std::make_unique
<CustomDiskIOThread
>(lt::posix_disk_io_constructor(ioContext
, settings
, counters
));
53 std::unique_ptr
<lt::disk_interface
> customMMapDiskIOConstructor(
54 lt::io_context
&ioContext
, const lt::settings_interface
&settings
, lt::counters
&counters
)
56 return std::make_unique
<CustomDiskIOThread
>(lt::mmap_disk_io_constructor(ioContext
, settings
, counters
));
59 CustomDiskIOThread::CustomDiskIOThread(std::unique_ptr
<libtorrent::disk_interface
> nativeDiskIOThread
)
60 : m_nativeDiskIO
{std::move(nativeDiskIOThread
)}
64 lt::storage_holder
CustomDiskIOThread::new_torrent(const lt::storage_params
&storageParams
, const std::shared_ptr
<void> &torrent
)
66 lt::storage_holder storageHolder
= m_nativeDiskIO
->new_torrent(storageParams
, torrent
);
68 const Path savePath
{storageParams
.path
};
69 m_storageData
[storageHolder
] =
72 storageParams
.mapped_files
? *storageParams
.mapped_files
: storageParams
.files
,
73 storageParams
.priorities
79 void CustomDiskIOThread::remove_torrent(lt::storage_index_t storage
)
81 m_nativeDiskIO
->remove_torrent(storage
);
84 void CustomDiskIOThread::async_read(lt::storage_index_t storage
, const lt::peer_request
&peerRequest
85 , std::function
<void (lt::disk_buffer_holder
, const lt::storage_error
&)> handler
86 , lt::disk_job_flags_t flags
)
88 m_nativeDiskIO
->async_read(storage
, peerRequest
, std::move(handler
), flags
);
91 bool CustomDiskIOThread::async_write(lt::storage_index_t storage
, const lt::peer_request
&peerRequest
92 , const char *buf
, std::shared_ptr
<lt::disk_observer
> diskObserver
93 , std::function
<void (const lt::storage_error
&)> handler
, lt::disk_job_flags_t flags
)
95 return m_nativeDiskIO
->async_write(storage
, peerRequest
, buf
, std::move(diskObserver
), std::move(handler
), flags
);
98 void CustomDiskIOThread::async_hash(lt::storage_index_t storage
, lt::piece_index_t piece
99 , lt::span
<lt::sha256_hash
> hash
, lt::disk_job_flags_t flags
100 , std::function
<void (lt::piece_index_t
, const lt::sha1_hash
&, const lt::storage_error
&)> handler
)
102 m_nativeDiskIO
->async_hash(storage
, piece
, hash
, flags
, std::move(handler
));
105 void CustomDiskIOThread::async_hash2(lt::storage_index_t storage
, lt::piece_index_t piece
106 , int offset
, lt::disk_job_flags_t flags
107 , std::function
<void (lt::piece_index_t
, const lt::sha256_hash
&, const lt::storage_error
&)> handler
)
109 m_nativeDiskIO
->async_hash2(storage
, piece
, offset
, flags
, std::move(handler
));
112 void CustomDiskIOThread::async_move_storage(lt::storage_index_t storage
, std::string path
, lt::move_flags_t flags
113 , std::function
<void (lt::status_t
, const std::string
&, const lt::storage_error
&)> handler
)
115 const Path newSavePath
{path
};
117 if (flags
== lt::move_flags_t::dont_replace
)
118 handleCompleteFiles(storage
, newSavePath
);
120 m_nativeDiskIO
->async_move_storage(storage
, path
, flags
121 , [=, this, handler
= std::move(handler
)](lt::status_t status
, const std::string
&path
, const lt::storage_error
&error
)
123 #if LIBTORRENT_VERSION_NUM < 20100
124 if ((status
!= lt::status_t::fatal_disk_error
) && (status
!= lt::status_t::file_exist
))
126 if ((status
!= lt::disk_status::fatal_disk_error
) && (status
!= lt::disk_status::file_exist
))
128 m_storageData
[storage
].savePath
= newSavePath
;
130 handler(status
, path
, error
);
134 void CustomDiskIOThread::async_release_files(lt::storage_index_t storage
, std::function
<void ()> handler
)
136 m_nativeDiskIO
->async_release_files(storage
, std::move(handler
));
139 void CustomDiskIOThread::async_check_files(lt::storage_index_t storage
, const lt::add_torrent_params
*resume_data
140 , lt::aux::vector
<std::string
, lt::file_index_t
> links
141 , std::function
<void (lt::status_t
, const lt::storage_error
&)> handler
)
143 handleCompleteFiles(storage
, m_storageData
[storage
].savePath
);
144 m_nativeDiskIO
->async_check_files(storage
, resume_data
, std::move(links
), std::move(handler
));
147 void CustomDiskIOThread::async_stop_torrent(lt::storage_index_t storage
, std::function
<void ()> handler
)
149 m_nativeDiskIO
->async_stop_torrent(storage
, std::move(handler
));
152 void CustomDiskIOThread::async_rename_file(lt::storage_index_t storage
, lt::file_index_t index
, std::string name
153 , std::function
<void (const std::string
&, lt::file_index_t
, const lt::storage_error
&)> handler
)
155 m_nativeDiskIO
->async_rename_file(storage
, index
, name
156 , [=, this, handler
= std::move(handler
)](const std::string
&name
, lt::file_index_t index
, const lt::storage_error
&error
)
159 m_storageData
[storage
].files
.rename_file(index
, name
);
160 handler(name
, index
, error
);
164 void CustomDiskIOThread::async_delete_files(lt::storage_index_t storage
, lt::remove_flags_t options
165 , std::function
<void (const lt::storage_error
&)> handler
)
167 m_nativeDiskIO
->async_delete_files(storage
, options
, std::move(handler
));
170 void CustomDiskIOThread::async_set_file_priority(lt::storage_index_t storage
, lt::aux::vector
<lt::download_priority_t
, lt::file_index_t
> priorities
171 , std::function
<void (const lt::storage_error
&, lt::aux::vector
<lt::download_priority_t
, lt::file_index_t
>)> handler
)
173 m_nativeDiskIO
->async_set_file_priority(storage
, std::move(priorities
)
174 , [=, this, handler
= std::move(handler
)](const lt::storage_error
&error
, const lt::aux::vector
<lt::download_priority_t
, lt::file_index_t
> &priorities
)
176 m_storageData
[storage
].filePriorities
= priorities
;
177 handler(error
, priorities
);
181 void CustomDiskIOThread::async_clear_piece(lt::storage_index_t storage
, lt::piece_index_t index
182 , std::function
<void (lt::piece_index_t
)> handler
)
184 m_nativeDiskIO
->async_clear_piece(storage
, index
, std::move(handler
));
187 void CustomDiskIOThread::update_stats_counters(lt::counters
&counters
) const
189 m_nativeDiskIO
->update_stats_counters(counters
);
192 std::vector
<lt::open_file_state
> CustomDiskIOThread::get_status(lt::storage_index_t index
) const
194 return m_nativeDiskIO
->get_status(index
);
197 void CustomDiskIOThread::abort(bool wait
)
199 m_nativeDiskIO
->abort(wait
);
202 void CustomDiskIOThread::submit_jobs()
204 m_nativeDiskIO
->submit_jobs();
207 void CustomDiskIOThread::settings_updated()
209 m_nativeDiskIO
->settings_updated();
212 void CustomDiskIOThread::handleCompleteFiles(lt::storage_index_t storage
, const Path
&savePath
)
214 const StorageData storageData
= m_storageData
[storage
];
215 const lt::file_storage
&fileStorage
= storageData
.files
;
216 for (const lt::file_index_t fileIndex
: fileStorage
.file_range())
218 // ignore files that have priority 0
219 if ((storageData
.filePriorities
.end_index() > fileIndex
) && (storageData
.filePriorities
[fileIndex
] == lt::dont_download
))
223 if (fileStorage
.pad_file_at(fileIndex
)) continue;
225 const Path filePath
{fileStorage
.file_path(fileIndex
)};
226 if (filePath
.hasExtension(QB_EXT
))
228 const Path incompleteFilePath
= savePath
/ filePath
;
229 const Path completeFilePath
= incompleteFilePath
.removedExtension(QB_EXT
);
230 if (completeFilePath
.exists())
232 Utils::Fs::removeFile(incompleteFilePath
);
233 Utils::Fs::renameFile(completeFilePath
, incompleteFilePath
);
241 lt::storage_interface
*customStorageConstructor(const lt::storage_params
¶ms
, lt::file_pool
&pool
)
243 return new CustomStorage(params
, pool
);
246 CustomStorage::CustomStorage(const lt::storage_params
¶ms
, lt::file_pool
&filePool
)
247 : lt::default_storage(params
, filePool
)
248 , m_savePath
{params
.path
}
252 bool CustomStorage::verify_resume_data(const lt::add_torrent_params
&rd
, const lt::aux::vector
<std::string
, lt::file_index_t
> &links
, lt::storage_error
&ec
)
254 handleCompleteFiles(m_savePath
);
255 return lt::default_storage::verify_resume_data(rd
, links
, ec
);
258 void CustomStorage::set_file_priority(lt::aux::vector
<lt::download_priority_t
, lt::file_index_t
> &priorities
, lt::storage_error
&ec
)
260 m_filePriorities
= priorities
;
261 lt::default_storage::set_file_priority(priorities
, ec
);
264 lt::status_t
CustomStorage::move_storage(const std::string
&savePath
, lt::move_flags_t flags
, lt::storage_error
&ec
)
266 const Path newSavePath
{savePath
};
268 if (flags
== lt::move_flags_t::dont_replace
)
269 handleCompleteFiles(newSavePath
);
271 const lt::status_t ret
= lt::default_storage::move_storage(savePath
, flags
, ec
);
272 if ((ret
!= lt::status_t::fatal_disk_error
) && (ret
!= lt::status_t::file_exist
))
273 m_savePath
= newSavePath
;
278 void CustomStorage::handleCompleteFiles(const Path
&savePath
)
280 const lt::file_storage
&fileStorage
= files();
281 for (const lt::file_index_t fileIndex
: fileStorage
.file_range())
283 // ignore files that have priority 0
284 if ((m_filePriorities
.end_index() > fileIndex
) && (m_filePriorities
[fileIndex
] == lt::dont_download
))
288 if (fileStorage
.pad_file_at(fileIndex
)) continue;
290 const Path filePath
{fileStorage
.file_path(fileIndex
)};
291 if (filePath
.hasExtension(QB_EXT
))
293 const Path incompleteFilePath
= savePath
/ filePath
;
294 const Path completeFilePath
= incompleteFilePath
.removedExtension(QB_EXT
);
295 if (completeFilePath
.exists())
297 Utils::Fs::removeFile(incompleteFilePath
);
298 Utils::Fs::renameFile(completeFilePath
, incompleteFilePath
);