1 /* 2 3 Copyright (c) 2003-2018, Arvid Norberg, Daniel Wallin 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 10 * Redistributions of source code must retain the above copyright 11 notice, this list of conditions and the following disclaimer. 12 * Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions and the following disclaimer in 14 the documentation and/or other materials provided with the distribution. 15 * Neither the name of the author nor the names of its 16 contributors may be used to endorse or promote products derived 17 from this software without specific prior written permission. 18 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 POSSIBILITY OF SUCH DAMAGE. 30 31 */ 32 33 #include "libtorrent/config.hpp" 34 #include "libtorrent/error_code.hpp" 35 #include "libtorrent/aux_/storage_utils.hpp" 36 37 #include <ctime> 38 #include <algorithm> 39 #include <numeric> 40 #include <set> 41 #include <functional> 42 #include <cstdio> 43 44 #include "libtorrent/aux_/disable_warnings_push.hpp" 45 46 #if defined(__APPLE__) 47 // for getattrlist() 48 #include <sys/attr.h> 49 #include <unistd.h> 50 // for statfs() 51 #include <sys/param.h> 52 #include <sys/mount.h> 53 #endif 54 55 #if defined(__linux__) 56 #include <sys/statfs.h> 57 #endif 58 59 #if defined(__FreeBSD__) || defined(__DragonFly__) 60 // for statfs() 61 #include <sys/param.h> 62 #include <sys/mount.h> 63 #endif 64 65 #if TORRENT_HAS_SYMLINK 66 #include <unistd.h> // for symlink() 67 #endif 68 69 #include "libtorrent/aux_/disable_warnings_pop.hpp" 70 71 #include "libtorrent/storage.hpp" 72 #include "libtorrent/torrent.hpp" 73 #include "libtorrent/aux_/path.hpp" 74 #include "libtorrent/invariant_check.hpp" 75 #include "libtorrent/file_pool.hpp" 76 #include "libtorrent/aux_/session_impl.hpp" 77 #include "libtorrent/disk_buffer_holder.hpp" 78 #include "libtorrent/stat_cache.hpp" 79 #include "libtorrent/hex.hpp" // to_hex 80 //#include "libtorrent/aux_/escape_string.hpp" 81 82 namespace libtorrent { 83 default_storage(storage_params const & params,file_pool & pool)84 default_storage::default_storage(storage_params const& params 85 , file_pool& pool) 86 : storage_interface(params.files) 87 , m_file_priority(params.priorities) 88 , m_pool(pool) 89 , m_allocate_files(params.mode == storage_mode_allocate) 90 { 91 if (params.mapped_files) m_mapped_files.reset(new file_storage(*params.mapped_files)); 92 93 TORRENT_ASSERT(files().num_files() > 0); 94 m_save_path = complete(params.path); 95 m_part_file_name = "." + aux::to_hex(params.info_hash) + ".parts"; 96 } 97 ~default_storage()98 default_storage::~default_storage() 99 { 100 error_code ec; 101 if (m_part_file) m_part_file->flush_metadata(ec); 102 103 // this may be called from a different 104 // thread than the disk thread 105 m_pool.release(storage_index()); 106 } 107 need_partfile()108 void default_storage::need_partfile() 109 { 110 if (m_part_file) return; 111 112 m_part_file.reset(new part_file( 113 m_save_path, m_part_file_name 114 , files().num_pieces(), files().piece_length())); 115 } 116 set_file_priority(aux::vector<download_priority_t,file_index_t> & prio,storage_error & ec)117 void default_storage::set_file_priority( 118 aux::vector<download_priority_t, file_index_t>& prio 119 , storage_error& ec) 120 { 121 // extend our file priorities in case it's truncated 122 // the default assumed priority is 4 (the default) 123 if (prio.size() > m_file_priority.size()) 124 m_file_priority.resize(prio.size(), default_priority); 125 126 file_storage const& fs = files(); 127 for (file_index_t i(0); i < prio.end_index(); ++i) 128 { 129 // pad files always have priority 0. 130 if (fs.pad_file_at(i)) continue; 131 132 download_priority_t const old_prio = m_file_priority[i]; 133 download_priority_t new_prio = prio[i]; 134 if (old_prio == dont_download && new_prio != dont_download) 135 { 136 // move stuff out of the part file 137 file_handle f = open_file(i, open_mode::read_write, ec); 138 if (ec) 139 { 140 prio = m_file_priority; 141 return; 142 } 143 144 if (m_part_file && use_partfile(i)) 145 { 146 m_part_file->export_file([&f, &ec](std::int64_t file_offset, span<char> buf) 147 { 148 iovec_t const v = {buf.data(), buf.size()}; 149 std::int64_t const ret = f->writev(file_offset, v, ec.ec); 150 TORRENT_UNUSED(ret); 151 TORRENT_ASSERT(ec || ret == std::int64_t(v.size())); 152 }, fs.file_offset(i), fs.file_size(i), ec.ec); 153 154 if (ec) 155 { 156 ec.file(i); 157 ec.operation = operation_t::partfile_write; 158 prio = m_file_priority; 159 return; 160 } 161 } 162 } 163 else if (old_prio != dont_download && new_prio == dont_download) 164 { 165 // move stuff into the part file 166 // this is not implemented yet. 167 // so we just don't use a partfile for this file 168 169 std::string const fp = fs.file_path(i, m_save_path); 170 if (exists(fp)) use_partfile(i, false); 171 /* 172 file_handle f = open_file(i, open_mode::read_only, ec); 173 if (ec.ec != boost::system::errc::no_such_file_or_directory) 174 { 175 if (ec) 176 { 177 prio = m_file_priority; 178 return; 179 } 180 181 need_partfile(); 182 183 m_part_file->import_file(*f, fs.file_offset(i), fs.file_size(i), ec.ec); 184 if (ec) 185 { 186 ec.file(i); 187 ec.operation = operation_t::partfile_read; 188 prio = m_file_priority; 189 return; 190 } 191 // remove the file 192 std::string p = fs.file_path(i, m_save_path); 193 delete_one_file(p, ec.ec); 194 if (ec) 195 { 196 ec.file(i); 197 ec.operation = operation_t::file_remove; 198 prio = m_file_priority; 199 return; 200 } 201 } 202 */ 203 } 204 ec.ec.clear(); 205 m_file_priority[i] = new_prio; 206 207 if (m_file_priority[i] == dont_download && use_partfile(i)) 208 { 209 need_partfile(); 210 } 211 } 212 if (m_part_file) m_part_file->flush_metadata(ec.ec); 213 if (ec) 214 { 215 ec.file(torrent_status::error_file_partfile); 216 ec.operation = operation_t::partfile_write; 217 } 218 } 219 use_partfile(file_index_t const index) const220 bool default_storage::use_partfile(file_index_t const index) const 221 { 222 TORRENT_ASSERT_VAL(index >= file_index_t{}, index); 223 if (index >= m_use_partfile.end_index()) return true; 224 return m_use_partfile[index]; 225 } 226 use_partfile(file_index_t const index,bool const b)227 void default_storage::use_partfile(file_index_t const index, bool const b) 228 { 229 if (index >= m_use_partfile.end_index()) m_use_partfile.resize(static_cast<int>(index) + 1, true); 230 m_use_partfile[index] = b; 231 } 232 initialize(storage_error & ec)233 void default_storage::initialize(storage_error& ec) 234 { 235 m_stat_cache.reserve(files().num_files()); 236 237 #ifdef TORRENT_WINDOWS 238 // don't do full file allocations on network drives 239 auto const file_name = convert_to_native_path_string(m_save_path); 240 int const drive_type = GetDriveTypeW(file_name.c_str()); 241 242 if (drive_type == DRIVE_REMOTE) 243 m_allocate_files = false; 244 #endif 245 246 { 247 std::unique_lock<std::mutex> l(m_file_created_mutex); 248 m_file_created.resize(files().num_files(), false); 249 } 250 251 file_storage const& fs = files(); 252 // if some files have priority 0, we need to check if they exist on the 253 // filesystem, in which case we won't use a partfile for them. 254 // this is to be backwards compatible with previous versions of 255 // libtorrent, when part files were not supported. 256 for (file_index_t i(0); i < m_file_priority.end_index(); ++i) 257 { 258 if (m_file_priority[i] != dont_download || fs.pad_file_at(i)) 259 continue; 260 261 file_status s; 262 std::string const file_path = fs.file_path(i, m_save_path); 263 error_code err; 264 stat_file(file_path, &s, err); 265 if (!err) 266 { 267 use_partfile(i, false); 268 } 269 else 270 { 271 need_partfile(); 272 } 273 } 274 275 // first, create all missing directories 276 std::string last_path; 277 for (auto const file_index : fs.file_range()) 278 { 279 // ignore files that have priority 0 280 if (m_file_priority.end_index() > file_index 281 && m_file_priority[file_index] == dont_download) 282 { 283 continue; 284 } 285 286 // ignore pad files 287 if (fs.pad_file_at(file_index)) continue; 288 289 // this is just to see if the file exists 290 error_code err; 291 m_stat_cache.get_filesize(file_index, fs, m_save_path, err); 292 293 if (err && err != boost::system::errc::no_such_file_or_directory) 294 { 295 ec.file(file_index); 296 ec.operation = operation_t::file_stat; 297 ec.ec = err; 298 break; 299 } 300 301 // if the file is empty and doesn't already exist, create it 302 // deliberately don't truncate files that already exist 303 // if a file is supposed to have size 0, but already exists, we will 304 // never truncate it to 0. 305 if (fs.file_size(file_index) == 0 306 && err == boost::system::errc::no_such_file_or_directory) 307 { 308 std::string dir = parent_path(fs.file_path(file_index, m_save_path)); 309 310 if (dir != last_path) 311 { 312 last_path = dir; 313 314 create_directories(last_path, ec.ec); 315 if (ec.ec) 316 { 317 ec.file(file_index); 318 ec.operation = operation_t::mkdir; 319 break; 320 } 321 } 322 ec.ec.clear(); 323 324 #if TORRENT_HAS_SYMLINK 325 // create symlinks 326 if (fs.file_flags(file_index) & file_storage::flag_symlink) 327 { 328 // we make the symlink target relative to the link itself 329 std::string const target = lexically_relative( 330 parent_path(fs.file_path(file_index)), fs.symlink(file_index)); 331 std::string const link = fs.file_path(file_index, m_save_path); 332 if (::symlink(target.c_str(), link.c_str()) != 0) 333 { 334 int const error = errno; 335 if (error == EEXIST) 336 { 337 // if the file exist, it may be a symlink already. if so, 338 // just verify the link target is what it's supposed to be 339 // note that readlink() does not null terminate the buffer 340 char buffer[512]; 341 auto const ret = ::readlink(link.c_str(), buffer, sizeof(buffer)); 342 if (ret <= 0 || target != string_view(buffer, std::size_t(ret))) 343 { 344 ec.ec = error_code(error, generic_category()); 345 ec.file(file_index); 346 ec.operation = operation_t::symlink; 347 return; 348 } 349 } 350 else 351 { 352 ec.ec = error_code(error, generic_category()); 353 ec.file(file_index); 354 ec.operation = operation_t::symlink; 355 return; 356 } 357 } 358 } 359 else 360 #endif 361 { 362 // just creating the file is enough to make it zero-sized. If 363 // there's a race here and some other process truncates the file, 364 // it's not a problem, we won't access empty files ever again 365 file_handle f = open_file(file_index, open_mode::read_write 366 | open_mode::random_access, ec); 367 if (ec) return; 368 } 369 } 370 ec.ec.clear(); 371 } 372 373 // close files that were opened in write mode 374 m_pool.release(storage_index()); 375 } 376 has_any_file(storage_error & ec)377 bool default_storage::has_any_file(storage_error& ec) 378 { 379 m_stat_cache.reserve(files().num_files()); 380 381 if (aux::has_any_file(files(), m_save_path, m_stat_cache, ec)) 382 return true; 383 384 if (ec) return false; 385 386 file_status s; 387 stat_file(combine_path(m_save_path, m_part_file_name), &s, ec.ec); 388 if (!ec) return true; 389 390 // the part file not existing is expected 391 if (ec && ec.ec == boost::system::errc::no_such_file_or_directory) 392 ec.ec.clear(); 393 394 if (ec) 395 { 396 ec.file(torrent_status::error_file_partfile); 397 ec.operation = operation_t::file_stat; 398 return false; 399 } 400 return false; 401 } 402 rename_file(file_index_t const index,std::string const & new_filename,storage_error & ec)403 void default_storage::rename_file(file_index_t const index, std::string const& new_filename 404 , storage_error& ec) 405 { 406 if (index < file_index_t(0) || index >= files().end_file()) return; 407 std::string old_name = files().file_path(index, m_save_path); 408 m_pool.release(storage_index(), index); 409 410 // if the old file doesn't exist, just succeed and change the filename 411 // that will be created. This shortcut is important because the 412 // destination directory may not exist yet, which would cause a failure 413 // even though we're not moving a file (yet). It's better for it to 414 // fail later when we try to write to the file the first time, because 415 // the user then will have had a chance to make the destination directory 416 // valid. 417 if (exists(old_name, ec.ec)) 418 { 419 std::string new_path; 420 if (is_complete(new_filename)) new_path = new_filename; 421 else new_path = combine_path(m_save_path, new_filename); 422 std::string new_dir = parent_path(new_path); 423 424 // create any missing directories that the new filename 425 // lands in 426 create_directories(new_dir, ec.ec); 427 if (ec.ec) 428 { 429 ec.file(index); 430 ec.operation = operation_t::file_rename; 431 return; 432 } 433 434 rename(old_name, new_path, ec.ec); 435 436 // if old_name doesn't exist, that's not an error 437 // here. Once we start writing to the file, it will 438 // be written to the new filename 439 if (ec.ec == boost::system::errc::no_such_file_or_directory) 440 ec.ec.clear(); 441 442 if (ec) 443 { 444 ec.ec.clear(); 445 copy_file(old_name, new_path, ec.ec); 446 447 if (ec) 448 { 449 ec.file(index); 450 ec.operation = operation_t::file_rename; 451 return; 452 } 453 454 error_code ignore; 455 remove(old_name, ignore); 456 } 457 } 458 else if (ec.ec) 459 { 460 // if exists fails, report that error 461 ec.file(index); 462 ec.operation = operation_t::file_rename; 463 return; 464 } 465 466 // if old path doesn't exist, just rename the file 467 // in our file_storage, so that when it is created 468 // it will get the new name 469 if (!m_mapped_files) 470 { m_mapped_files.reset(new file_storage(files())); } 471 m_mapped_files->rename_file(index, new_filename); 472 } 473 release_files(storage_error &)474 void default_storage::release_files(storage_error&) 475 { 476 if (m_part_file) 477 { 478 error_code ignore; 479 m_part_file->flush_metadata(ignore); 480 } 481 482 // make sure we don't have the files open 483 m_pool.release(storage_index()); 484 485 // make sure we can pick up new files added to the download directory when 486 // we start the torrent again 487 m_stat_cache.clear(); 488 } 489 delete_files(remove_flags_t const options,storage_error & ec)490 void default_storage::delete_files(remove_flags_t const options, storage_error& ec) 491 { 492 // make sure we don't have the files open 493 m_pool.release(storage_index()); 494 495 // if there's a part file open, make sure to destruct it to have it 496 // release the underlying part file. Otherwise we may not be able to 497 // delete it 498 if (m_part_file) m_part_file.reset(); 499 500 aux::delete_files(files(), m_save_path, m_part_file_name, options, ec); 501 } 502 verify_resume_data(add_torrent_params const & rd,aux::vector<std::string,file_index_t> const & links,storage_error & ec)503 bool default_storage::verify_resume_data(add_torrent_params const& rd 504 , aux::vector<std::string, file_index_t> const& links 505 , storage_error& ec) 506 { 507 return aux::verify_resume_data(rd, links, files() 508 , m_file_priority, m_stat_cache, m_save_path, ec); 509 } 510 move_storage(std::string const & sp,move_flags_t const flags,storage_error & ec)511 status_t default_storage::move_storage(std::string const& sp 512 , move_flags_t const flags, storage_error& ec) 513 { 514 m_pool.release(storage_index()); 515 516 status_t ret; 517 std::tie(ret, m_save_path) = aux::move_storage(files(), m_save_path, sp 518 , m_part_file.get(), flags, ec); 519 520 // clear the stat cache in case the new location has new files 521 m_stat_cache.clear(); 522 523 return ret; 524 } 525 readv(span<iovec_t const> bufs,piece_index_t const piece,int const offset,open_mode_t const flags,storage_error & error)526 int default_storage::readv(span<iovec_t const> bufs 527 , piece_index_t const piece, int const offset 528 , open_mode_t const flags, storage_error& error) 529 { 530 #ifdef TORRENT_SIMULATE_SLOW_READ 531 std::this_thread::sleep_for(seconds(1)); 532 #endif 533 return readwritev(files(), bufs, piece, offset, error 534 , [this, flags](file_index_t const file_index 535 , std::int64_t const file_offset 536 , span<iovec_t const> vec, storage_error& ec) 537 { 538 if (files().pad_file_at(file_index)) 539 { 540 // reading from a pad file yields zeroes 541 aux::clear_bufs(vec); 542 return bufs_size(vec); 543 } 544 545 if (file_index < m_file_priority.end_index() 546 && m_file_priority[file_index] == dont_download 547 && use_partfile(file_index)) 548 { 549 TORRENT_ASSERT(m_part_file); 550 551 error_code e; 552 peer_request map = files().map_file(file_index 553 , file_offset, 0); 554 int const ret = m_part_file->readv(vec 555 , map.piece, map.start, e); 556 557 if (e) 558 { 559 ec.ec = e; 560 ec.file(file_index); 561 ec.operation = operation_t::partfile_read; 562 return -1; 563 } 564 return ret; 565 } 566 567 file_handle handle = open_file(file_index 568 , open_mode::read_only | flags, ec); 569 if (ec) return -1; 570 571 error_code e; 572 int const ret = int(handle->readv(file_offset 573 , vec, e, flags)); 574 575 // set this unconditionally in case the upper layer would like to treat 576 // short reads as errors 577 ec.operation = operation_t::file_read; 578 579 // we either get an error or 0 or more bytes read 580 TORRENT_ASSERT(e || ret >= 0); 581 TORRENT_ASSERT(ret <= bufs_size(vec)); 582 583 if (e) 584 { 585 ec.ec = e; 586 ec.file(file_index); 587 return -1; 588 } 589 590 return ret; 591 }); 592 } 593 writev(span<iovec_t const> bufs,piece_index_t const piece,int const offset,open_mode_t const flags,storage_error & error)594 int default_storage::writev(span<iovec_t const> bufs 595 , piece_index_t const piece, int const offset 596 , open_mode_t const flags, storage_error& error) 597 { 598 return readwritev(files(), bufs, piece, offset, error 599 , [this, flags](file_index_t const file_index 600 , std::int64_t const file_offset 601 , span<iovec_t const> vec, storage_error& ec) 602 { 603 if (files().pad_file_at(file_index)) 604 { 605 // writing to a pad-file is a no-op 606 return bufs_size(vec); 607 } 608 609 if (file_index < m_file_priority.end_index() 610 && m_file_priority[file_index] == dont_download 611 && use_partfile(file_index)) 612 { 613 TORRENT_ASSERT(m_part_file); 614 615 error_code e; 616 peer_request map = files().map_file(file_index 617 , file_offset, 0); 618 int const ret = m_part_file->writev(vec 619 , map.piece, map.start, e); 620 621 if (e) 622 { 623 ec.ec = e; 624 ec.file(file_index); 625 ec.operation = operation_t::partfile_write; 626 return -1; 627 } 628 return ret; 629 } 630 631 // invalidate our stat cache for this file, since 632 // we're writing to it 633 m_stat_cache.set_dirty(file_index); 634 635 file_handle handle = open_file(file_index 636 , open_mode::read_write, ec); 637 if (ec) return -1; 638 639 error_code e; 640 int const ret = int(handle->writev(file_offset 641 , vec, e, flags)); 642 643 // set this unconditionally in case the upper layer would like to treat 644 // short reads as errors 645 ec.operation = operation_t::file_write; 646 647 // we either get an error or 0 or more bytes read 648 TORRENT_ASSERT(e || ret >= 0); 649 TORRENT_ASSERT(ret <= bufs_size(vec)); 650 651 if (e) 652 { 653 ec.ec = e; 654 ec.file(file_index); 655 return -1; 656 } 657 658 return ret; 659 }); 660 } 661 open_file(file_index_t const file,open_mode_t mode,storage_error & ec) const662 file_handle default_storage::open_file(file_index_t const file 663 , open_mode_t mode, storage_error& ec) const 664 { 665 file_handle h = open_file_impl(file, mode, ec.ec); 666 if (((mode & open_mode::rw_mask) != open_mode::read_only) 667 && ec.ec == boost::system::errc::no_such_file_or_directory) 668 { 669 // this means the directory the file is in doesn't exist. 670 // so create it 671 ec.ec.clear(); 672 std::string path = files().file_path(file, m_save_path); 673 create_directories(parent_path(path), ec.ec); 674 675 if (ec.ec) 676 { 677 ec.file(file); 678 ec.operation = operation_t::mkdir; 679 return file_handle(); 680 } 681 682 // if the directory creation failed, don't try to open the file again 683 // but actually just fail 684 h = open_file_impl(file, mode, ec.ec); 685 } 686 if (ec.ec) 687 { 688 ec.file(file); 689 ec.operation = operation_t::file_open; 690 return file_handle(); 691 } 692 TORRENT_ASSERT(h); 693 694 if ((mode & open_mode::rw_mask) != open_mode::read_only) 695 { 696 std::unique_lock<std::mutex> l(m_file_created_mutex); 697 if (m_file_created.size() != files().num_files()) 698 m_file_created.resize(files().num_files(), false); 699 700 TORRENT_ASSERT(int(m_file_created.size()) == files().num_files()); 701 TORRENT_ASSERT(file < m_file_created.end_index()); 702 // if this is the first time we open this file for writing, 703 // and we have m_allocate_files enabled, set the final size of 704 // the file right away, to allocate it on the filesystem. 705 if (m_file_created[file] == false) 706 { 707 m_file_created.set_bit(file); 708 l.unlock(); 709 710 // if we're allocating files or if the file exists and is greater 711 // than what it's supposed to be, truncate it to its correct size 712 std::int64_t const size = files().file_size(file); 713 error_code e; 714 bool const need_truncate = h->get_size(e) > size; 715 if (e) 716 { 717 ec.ec = e; 718 ec.file(file); 719 ec.operation = operation_t::file_stat; 720 return h; 721 } 722 723 if (m_allocate_files || need_truncate) 724 { 725 h->set_size(size, e); 726 if (e) 727 { 728 ec.ec = e; 729 ec.file(file); 730 ec.operation = operation_t::file_fallocate; 731 return h; 732 } 733 m_stat_cache.set_dirty(file); 734 } 735 } 736 } 737 return h; 738 } 739 open_file_impl(file_index_t file,open_mode_t mode,error_code & ec) const740 file_handle default_storage::open_file_impl(file_index_t file, open_mode_t mode 741 , error_code& ec) const 742 { 743 if (!m_allocate_files) mode |= open_mode::sparse; 744 745 // files with priority 0 should always be sparse 746 if (m_file_priority.end_index() > file 747 && m_file_priority[file] == dont_download) 748 { 749 mode |= open_mode::sparse; 750 } 751 752 if (m_settings.load() && settings().get_bool(settings_pack::no_atime_storage)) mode |= open_mode::no_atime; 753 754 // if we have a cache already, don't store the data twice by leaving it in the OS cache as well 755 if (m_settings.load() 756 && settings().get_int(settings_pack::disk_io_write_mode) 757 == settings_pack::disable_os_cache) 758 { 759 mode |= open_mode::no_cache; 760 } 761 762 file_handle ret = m_pool.open_file(storage_index(), m_save_path, file 763 , files(), mode, ec); 764 return ret; 765 } 766 tick()767 bool default_storage::tick() 768 { 769 error_code ec; 770 if (m_part_file) m_part_file->flush_metadata(ec); 771 772 return false; 773 } 774 default_storage_constructor(storage_params const & params,file_pool & pool)775 storage_interface* default_storage_constructor(storage_params const& params 776 , file_pool& pool) 777 { 778 return new default_storage(params, pool); 779 } 780 781 // -- disabled_storage -------------------------------------------------- 782 783 namespace { 784 785 // this storage implementation does not write anything to disk 786 // and it pretends to read, and just leaves garbage in the buffers 787 // this is useful when simulating many clients on the same machine 788 // or when running stress tests and want to take the cost of the 789 // disk I/O out of the picture. This cannot be used for any kind 790 // of normal bittorrent operation, since it will just send garbage 791 // to peers and throw away all the data it downloads. It would end 792 // up being banned immediately 793 class disabled_storage final : public storage_interface 794 { 795 public: disabled_storage(file_storage const & fs)796 explicit disabled_storage(file_storage const& fs) : storage_interface(fs) {} 797 has_any_file(storage_error &)798 bool has_any_file(storage_error&) override { return false; } set_file_priority(aux::vector<download_priority_t,file_index_t> &,storage_error &)799 void set_file_priority(aux::vector<download_priority_t, file_index_t>& 800 , storage_error&) override {} rename_file(file_index_t,std::string const &,storage_error &)801 void rename_file(file_index_t, std::string const&, storage_error&) override {} release_files(storage_error &)802 void release_files(storage_error&) override {} delete_files(remove_flags_t,storage_error &)803 void delete_files(remove_flags_t, storage_error&) override {} initialize(storage_error &)804 void initialize(storage_error&) override {} move_storage(std::string const &,move_flags_t,storage_error &)805 status_t move_storage(std::string const&, move_flags_t, storage_error&) override { return status_t::no_error; } 806 readv(span<iovec_t const> bufs,piece_index_t,int,open_mode_t,storage_error &)807 int readv(span<iovec_t const> bufs 808 , piece_index_t, int, open_mode_t, storage_error&) override 809 { 810 return bufs_size(bufs); 811 } writev(span<iovec_t const> bufs,piece_index_t,int,open_mode_t,storage_error &)812 int writev(span<iovec_t const> bufs 813 , piece_index_t, int, open_mode_t, storage_error&) override 814 { 815 return bufs_size(bufs); 816 } 817 verify_resume_data(add_torrent_params const &,aux::vector<std::string,file_index_t> const &,storage_error &)818 bool verify_resume_data(add_torrent_params const& 819 , aux::vector<std::string, file_index_t> const& 820 , storage_error&) override { return false; } 821 }; 822 } 823 disabled_storage_constructor(storage_params const & params,file_pool &)824 storage_interface* disabled_storage_constructor(storage_params const& params, file_pool&) 825 { 826 return new disabled_storage(params.files); 827 } 828 829 // -- zero_storage ------------------------------------------------------ 830 831 namespace { 832 833 // this storage implementation always reads zeroes, and always discards 834 // anything written to it 835 struct zero_storage final : storage_interface 836 { zero_storagelibtorrent::__anon023f1a3b0511::zero_storage837 explicit zero_storage(file_storage const& fs) : storage_interface(fs) {} initializelibtorrent::__anon023f1a3b0511::zero_storage838 void initialize(storage_error&) override {} 839 readvlibtorrent::__anon023f1a3b0511::zero_storage840 int readv(span<iovec_t const> bufs 841 , piece_index_t, int, open_mode_t, storage_error&) override 842 { 843 int ret = 0; 844 for (auto const& b : bufs) 845 { 846 std::memset(b.data(), 0, std::size_t(b.size())); 847 ret += int(b.size()); 848 } 849 return ret; 850 } writevlibtorrent::__anon023f1a3b0511::zero_storage851 int writev(span<iovec_t const> bufs 852 , piece_index_t, int, open_mode_t, storage_error&) override 853 { 854 return std::accumulate(bufs.begin(), bufs.end(), 0 855 , [](int const acc, iovec_t const& b) { return acc + int(b.size()); }); 856 } 857 has_any_filelibtorrent::__anon023f1a3b0511::zero_storage858 bool has_any_file(storage_error&) override { return false; } set_file_prioritylibtorrent::__anon023f1a3b0511::zero_storage859 void set_file_priority(aux::vector<download_priority_t, file_index_t>& /* prio */ 860 , storage_error&) override {} move_storagelibtorrent::__anon023f1a3b0511::zero_storage861 status_t move_storage(std::string const& /* save_path */ 862 , move_flags_t, storage_error&) override { return status_t::no_error; } verify_resume_datalibtorrent::__anon023f1a3b0511::zero_storage863 bool verify_resume_data(add_torrent_params const& /* rd */ 864 , aux::vector<std::string, file_index_t> const& /* links */ 865 , storage_error&) override 866 { return false; } release_fileslibtorrent::__anon023f1a3b0511::zero_storage867 void release_files(storage_error&) override {} rename_filelibtorrent::__anon023f1a3b0511::zero_storage868 void rename_file(file_index_t 869 , std::string const& /* new_filename */, storage_error&) override {} delete_fileslibtorrent::__anon023f1a3b0511::zero_storage870 void delete_files(remove_flags_t, storage_error&) override {} 871 }; 872 } 873 zero_storage_constructor(storage_params const & params,file_pool &)874 storage_interface* zero_storage_constructor(storage_params const& params, file_pool&) 875 { 876 return new zero_storage(params.files); 877 } 878 879 } // namespace libtorrent 880