1 /* 2 3 Copyright (c) 2003-2018, Arvid Norberg 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/file_storage.hpp" 34 #include "libtorrent/string_util.hpp" // for allocate_string_copy 35 #include "libtorrent/utf8.hpp" 36 #include "libtorrent/index_range.hpp" 37 #include "libtorrent/aux_/path.hpp" 38 #include "libtorrent/aux_/numeric_cast.hpp" 39 40 #include "libtorrent/aux_/disable_warnings_push.hpp" 41 #include <boost/crc.hpp> 42 #include "libtorrent/aux_/disable_warnings_pop.hpp" 43 44 #include <cstdio> 45 #include <algorithm> 46 #include <functional> 47 #include <set> 48 #include <atomic> 49 50 #if TORRENT_ABI_VERSION == 1 && defined TORRENT_WINDOWS 51 #include "libtorrent/aux_/escape_string.hpp" 52 #endif 53 54 #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) 55 #define TORRENT_SEPARATOR '\\' 56 #else 57 #define TORRENT_SEPARATOR '/' 58 #endif 59 60 using namespace std::placeholders; 61 62 namespace libtorrent { 63 64 constexpr file_flags_t file_storage::flag_pad_file; 65 constexpr file_flags_t file_storage::flag_hidden; 66 constexpr file_flags_t file_storage::flag_executable; 67 constexpr file_flags_t file_storage::flag_symlink; 68 69 #if TORRENT_ABI_VERSION == 1 70 constexpr file_flags_t file_storage::pad_file; 71 constexpr file_flags_t file_storage::attribute_hidden; 72 constexpr file_flags_t file_storage::attribute_executable; 73 constexpr file_flags_t file_storage::attribute_symlink; 74 #endif 75 file_storage()76 file_storage::file_storage() 77 : m_piece_length(0) 78 , m_num_pieces(0) 79 , m_total_size(0) 80 {} 81 82 file_storage::~file_storage() = default; 83 84 // even though this copy constructor and the copy assignment 85 // operator are identical to what the compiler would have 86 // generated, they are put here to explicitly make them part 87 // of libtorrent and properly exported by the .dll. 88 file_storage::file_storage(file_storage const&) = default; 89 file_storage& file_storage::operator=(file_storage const&) = default; 90 file_storage::file_storage(file_storage&&) noexcept = default; 91 file_storage& file_storage::operator=(file_storage&&) = default; 92 reserve(int num_files)93 void file_storage::reserve(int num_files) 94 { 95 m_files.reserve(num_files); 96 } 97 piece_size(piece_index_t const index) const98 int file_storage::piece_size(piece_index_t const index) const 99 { 100 TORRENT_ASSERT_PRECOND(index >= piece_index_t(0) && index < end_piece()); 101 if (index == last_piece()) 102 { 103 std::int64_t const size_except_last 104 = (num_pieces() - 1) * std::int64_t(piece_length()); 105 std::int64_t const size = total_size() - size_except_last; 106 TORRENT_ASSERT(size > 0); 107 TORRENT_ASSERT(size <= piece_length()); 108 return int(size); 109 } 110 else 111 return piece_length(); 112 } 113 114 namespace { 115 compare_file_offset(internal_file_entry const & lhs,internal_file_entry const & rhs)116 bool compare_file_offset(internal_file_entry const& lhs 117 , internal_file_entry const& rhs) 118 { 119 return lhs.offset < rhs.offset; 120 } 121 122 } 123 124 // path is supposed to include the name of the torrent itself. 125 // or an absolute path, to move a file outside of the download directory update_path_index(internal_file_entry & e,std::string const & path,bool const set_name)126 void file_storage::update_path_index(internal_file_entry& e 127 , std::string const& path, bool const set_name) 128 { 129 if (is_complete(path)) 130 { 131 TORRENT_ASSERT(set_name); 132 e.set_name(path); 133 e.path_index = -2; 134 return; 135 } 136 137 TORRENT_ASSERT(path[0] != '/'); 138 139 // split the string into the leaf filename 140 // and the branch path 141 string_view leaf; 142 string_view branch_path; 143 std::tie(branch_path, leaf) = rsplit_path(path); 144 145 if (branch_path.empty()) 146 { 147 if (set_name) e.set_name(leaf); 148 e.path_index = -1; 149 return; 150 } 151 152 // if the path *does* contain the name of the torrent (as we expect) 153 // strip it before adding it to m_paths 154 if (lsplit_path(branch_path).first == m_name) 155 { 156 branch_path = lsplit_path(branch_path).second; 157 // strip duplicate separators 158 while (!branch_path.empty() && (branch_path.front() == TORRENT_SEPARATOR 159 #if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) 160 || branch_path.front() == '/' 161 #endif 162 )) 163 branch_path.remove_prefix(1); 164 e.no_root_dir = false; 165 } 166 else 167 { 168 e.no_root_dir = true; 169 } 170 171 e.path_index = get_or_add_path(branch_path); 172 if (set_name) e.set_name(leaf); 173 } 174 get_or_add_path(string_view const path)175 int file_storage::get_or_add_path(string_view const path) 176 { 177 // do we already have this path in the path list? 178 auto const p = std::find(m_paths.rbegin(), m_paths.rend(), path); 179 180 if (p == m_paths.rend()) 181 { 182 // no, we don't. add it 183 int const ret = int(m_paths.size()); 184 TORRENT_ASSERT(path.size() == 0 || path[0] != '/'); 185 m_paths.emplace_back(path.data(), path.size()); 186 return ret; 187 } 188 else 189 { 190 // yes we do. use it 191 return int(p.base() - m_paths.begin() - 1); 192 } 193 } 194 195 #if TORRENT_ABI_VERSION == 1 file_entry()196 file_entry::file_entry(): offset(0), size(0) 197 , mtime(0), pad_file(false), hidden_attribute(false) 198 , executable_attribute(false) 199 , symlink_attribute(false) 200 {} 201 202 file_entry::~file_entry() = default; 203 #endif // TORRENT_ABI_VERSION 204 internal_file_entry()205 internal_file_entry::internal_file_entry() 206 : offset(0) 207 , symlink_index(not_a_symlink) 208 , no_root_dir(false) 209 , size(0) 210 , name_len(name_is_owned) 211 , pad_file(false) 212 , hidden_attribute(false) 213 , executable_attribute(false) 214 , symlink_attribute(false) 215 , name(nullptr) 216 , path_index(-1) 217 {} 218 ~internal_file_entry()219 internal_file_entry::~internal_file_entry() 220 { 221 if (name_len == name_is_owned) delete[] name; 222 } 223 internal_file_entry(internal_file_entry const & fe)224 internal_file_entry::internal_file_entry(internal_file_entry const& fe) 225 : offset(fe.offset) 226 , symlink_index(fe.symlink_index) 227 , no_root_dir(fe.no_root_dir) 228 , size(fe.size) 229 , name_len(fe.name_len) 230 , pad_file(fe.pad_file) 231 , hidden_attribute(fe.hidden_attribute) 232 , executable_attribute(fe.executable_attribute) 233 , symlink_attribute(fe.symlink_attribute) 234 , name(nullptr) 235 , path_index(fe.path_index) 236 { 237 bool const borrow = fe.name_len != name_is_owned; 238 set_name(fe.filename(), borrow); 239 } 240 operator =(internal_file_entry const & fe)241 internal_file_entry& internal_file_entry::operator=(internal_file_entry const& fe) & 242 { 243 if (&fe == this) return *this; 244 offset = fe.offset; 245 size = fe.size; 246 path_index = fe.path_index; 247 symlink_index = fe.symlink_index; 248 pad_file = fe.pad_file; 249 hidden_attribute = fe.hidden_attribute; 250 executable_attribute = fe.executable_attribute; 251 symlink_attribute = fe.symlink_attribute; 252 no_root_dir = fe.no_root_dir; 253 // if the name is not owned, don't allocate memory, we can point into the 254 // same metadata buffer 255 bool const borrow = fe.name_len != name_is_owned; 256 set_name(fe.filename(), borrow); 257 return *this; 258 } 259 internal_file_entry(internal_file_entry && fe)260 internal_file_entry::internal_file_entry(internal_file_entry&& fe) noexcept 261 : offset(fe.offset) 262 , symlink_index(fe.symlink_index) 263 , no_root_dir(fe.no_root_dir) 264 , size(fe.size) 265 , name_len(fe.name_len) 266 , pad_file(fe.pad_file) 267 , hidden_attribute(fe.hidden_attribute) 268 , executable_attribute(fe.executable_attribute) 269 , symlink_attribute(fe.symlink_attribute) 270 , name(fe.name) 271 , path_index(fe.path_index) 272 { 273 fe.name_len = 0; 274 fe.name = nullptr; 275 } 276 operator =(internal_file_entry && fe)277 internal_file_entry& internal_file_entry::operator=(internal_file_entry&& fe) & noexcept 278 { 279 if (&fe == this) return *this; 280 offset = fe.offset; 281 size = fe.size; 282 path_index = fe.path_index; 283 symlink_index = fe.symlink_index; 284 pad_file = fe.pad_file; 285 hidden_attribute = fe.hidden_attribute; 286 executable_attribute = fe.executable_attribute; 287 symlink_attribute = fe.symlink_attribute; 288 no_root_dir = fe.no_root_dir; 289 name = fe.name; 290 name_len = fe.name_len; 291 292 fe.name_len = 0; 293 fe.name = nullptr; 294 return *this; 295 } 296 297 // if borrow_string is true, don't take ownership over n, just 298 // point to it. 299 // if borrow_string is false, n will be copied and owned by the 300 // internal_file_entry. set_name(string_view n,bool const borrow_string)301 void internal_file_entry::set_name(string_view n, bool const borrow_string) 302 { 303 // free the current string, before assigning the new one 304 if (name_len == name_is_owned) delete[] name; 305 if (n.empty()) 306 { 307 TORRENT_ASSERT(borrow_string == false); 308 name = nullptr; 309 } 310 else if (borrow_string) 311 { 312 // we have limited space in the length field. truncate string 313 // if it's too long 314 if (n.size() >= name_is_owned) n = n.substr(name_is_owned - 1); 315 316 name = n.data(); 317 name_len = aux::numeric_cast<std::uint64_t>(n.size()); 318 } 319 else 320 { 321 name = allocate_string_copy(n); 322 name_len = name_is_owned; 323 } 324 } 325 filename() const326 string_view internal_file_entry::filename() const 327 { 328 if (name_len != name_is_owned) return {name, std::size_t(name_len)}; 329 return name ? string_view(name) : string_view(); 330 } 331 apply_pointer_offset(std::ptrdiff_t const off)332 void file_storage::apply_pointer_offset(std::ptrdiff_t const off) 333 { 334 for (auto& f : m_files) 335 { 336 if (f.name_len == internal_file_entry::name_is_owned) continue; 337 f.name += off; 338 } 339 340 for (auto& h : m_file_hashes) 341 { 342 if (h == nullptr) continue; 343 h += off; 344 } 345 } 346 347 #if TORRENT_ABI_VERSION == 1 348 add_file_borrow(char const * filename,int filename_len,std::string const & path,std::int64_t file_size,file_flags_t file_flags,char const * filehash,std::int64_t mtime,string_view symlink_path)349 void file_storage::add_file_borrow(char const* filename, int filename_len 350 , std::string const& path, std::int64_t file_size, file_flags_t file_flags 351 , char const* filehash, std::int64_t mtime, string_view symlink_path) 352 { 353 TORRENT_ASSERT(filename_len >= 0); 354 add_file_borrow({filename, std::size_t(filename_len)}, path, file_size 355 , file_flags, filehash, mtime, symlink_path); 356 } 357 add_file(file_entry const & fe,char const * filehash)358 void file_storage::add_file(file_entry const& fe, char const* filehash) 359 { 360 file_flags_t flags = {}; 361 if (fe.pad_file) flags |= file_storage::flag_pad_file; 362 if (fe.hidden_attribute) flags |= file_storage::flag_hidden; 363 if (fe.executable_attribute) flags |= file_storage::flag_executable; 364 if (fe.symlink_attribute) flags |= file_storage::flag_symlink; 365 366 add_file_borrow({}, fe.path, fe.size, flags, filehash, fe.mtime 367 , fe.symlink_path); 368 } 369 370 #if defined TORRENT_WINDOWS set_name(std::wstring const & n)371 void file_storage::set_name(std::wstring const& n) 372 { 373 m_name = convert_from_wstring(n); 374 } 375 rename_file_deprecated(file_index_t index,std::wstring const & new_filename)376 void file_storage::rename_file_deprecated(file_index_t index, std::wstring const& new_filename) 377 { 378 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 379 update_path_index(m_files[index], convert_from_wstring(new_filename)); 380 } 381 add_file(std::wstring const & file,std::int64_t file_size,file_flags_t const file_flags,std::time_t mtime,string_view symlink_path)382 void file_storage::add_file(std::wstring const& file, std::int64_t file_size 383 , file_flags_t const file_flags, std::time_t mtime, string_view symlink_path) 384 { 385 add_file(convert_from_wstring(file), file_size, file_flags, mtime, symlink_path); 386 } 387 rename_file(file_index_t index,std::wstring const & new_filename)388 void file_storage::rename_file(file_index_t index, std::wstring const& new_filename) 389 { 390 rename_file_deprecated(index, new_filename); 391 } 392 #endif // TORRENT_WINDOWS 393 #endif // TORRENT_ABI_VERSION 394 rename_file(file_index_t const index,std::string const & new_filename)395 void file_storage::rename_file(file_index_t const index 396 , std::string const& new_filename) 397 { 398 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 399 update_path_index(m_files[index], new_filename); 400 } 401 402 #if TORRENT_ABI_VERSION == 1 file_at_offset_deprecated(std::int64_t offset) const403 file_storage::iterator file_storage::file_at_offset_deprecated(std::int64_t offset) const 404 { 405 // find the file iterator and file offset 406 internal_file_entry target; 407 target.offset = aux::numeric_cast<std::uint64_t>(offset); 408 TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); 409 410 auto file_iter = std::upper_bound( 411 begin_deprecated(), end_deprecated(), target, compare_file_offset); 412 413 TORRENT_ASSERT(file_iter != begin_deprecated()); 414 --file_iter; 415 return file_iter; 416 } 417 file_at_offset(std::int64_t offset) const418 file_storage::iterator file_storage::file_at_offset(std::int64_t offset) const 419 { 420 return file_at_offset_deprecated(offset); 421 } 422 #endif 423 file_index_at_offset(std::int64_t const offset) const424 file_index_t file_storage::file_index_at_offset(std::int64_t const offset) const 425 { 426 TORRENT_ASSERT_PRECOND(offset >= 0); 427 TORRENT_ASSERT_PRECOND(offset < m_total_size); 428 // find the file iterator and file offset 429 internal_file_entry target; 430 target.offset = aux::numeric_cast<std::uint64_t>(offset); 431 TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); 432 433 auto file_iter = std::upper_bound( 434 m_files.begin(), m_files.end(), target, compare_file_offset); 435 436 TORRENT_ASSERT(file_iter != m_files.begin()); 437 --file_iter; 438 return file_index_t(int(file_iter - m_files.begin())); 439 } 440 file_name_ptr(file_index_t const index) const441 char const* file_storage::file_name_ptr(file_index_t const index) const 442 { 443 return m_files[index].name; 444 } 445 file_name_len(file_index_t const index) const446 int file_storage::file_name_len(file_index_t const index) const 447 { 448 if (m_files[index].name_len == internal_file_entry::name_is_owned) 449 return -1; 450 return m_files[index].name_len; 451 } 452 map_block(piece_index_t const piece,std::int64_t const offset,int size) const453 std::vector<file_slice> file_storage::map_block(piece_index_t const piece 454 , std::int64_t const offset, int size) const 455 { 456 TORRENT_ASSERT_PRECOND(piece >= piece_index_t{0}); 457 TORRENT_ASSERT_PRECOND(piece < end_piece()); 458 TORRENT_ASSERT_PRECOND(num_files() > 0); 459 std::vector<file_slice> ret; 460 461 if (m_files.empty()) return ret; 462 463 // find the file iterator and file offset 464 internal_file_entry target; 465 target.offset = aux::numeric_cast<std::uint64_t>(static_cast<int>(piece) * std::int64_t(m_piece_length) + offset); 466 TORRENT_ASSERT_PRECOND(std::int64_t(target.offset) + size <= m_total_size); 467 TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); 468 469 // in case the size is past the end, fix it up 470 if (std::int64_t(target.offset) + size > m_total_size) 471 size = aux::numeric_cast<int>(m_total_size - std::int64_t(target.offset)); 472 473 auto file_iter = std::upper_bound( 474 m_files.begin(), m_files.end(), target, compare_file_offset); 475 476 TORRENT_ASSERT(file_iter != m_files.begin()); 477 --file_iter; 478 479 std::int64_t file_offset = target.offset - file_iter->offset; 480 for (; size > 0; file_offset -= file_iter->size, ++file_iter) 481 { 482 TORRENT_ASSERT(file_iter != m_files.end()); 483 if (file_offset < std::int64_t(file_iter->size)) 484 { 485 file_slice f{}; 486 f.file_index = file_index_t(int(file_iter - m_files.begin())); 487 f.offset = file_offset; 488 f.size = std::min(std::int64_t(file_iter->size) - file_offset, std::int64_t(size)); 489 TORRENT_ASSERT(f.size <= size); 490 size -= int(f.size); 491 file_offset += f.size; 492 ret.push_back(f); 493 } 494 495 TORRENT_ASSERT(size >= 0); 496 } 497 return ret; 498 } 499 500 #if TORRENT_ABI_VERSION == 1 at(int index) const501 file_entry file_storage::at(int index) const 502 { 503 return at_deprecated(index); 504 } 505 internal_at(int const index) const506 internal_file_entry const& file_storage::internal_at(int const index) const 507 { 508 TORRENT_ASSERT(index >= 0); 509 TORRENT_ASSERT(index < int(m_files.size())); 510 return m_files[file_index_t(index)]; 511 } 512 at_deprecated(int index) const513 file_entry file_storage::at_deprecated(int index) const 514 { 515 TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); 516 file_entry ret; 517 internal_file_entry const& ife = m_files[index]; 518 ret.path = file_path(index); 519 ret.offset = ife.offset; 520 ret.size = ife.size; 521 ret.mtime = mtime(index); 522 ret.pad_file = ife.pad_file; 523 ret.hidden_attribute = ife.hidden_attribute; 524 ret.executable_attribute = ife.executable_attribute; 525 ret.symlink_attribute = ife.symlink_attribute; 526 if (ife.symlink_index != internal_file_entry::not_a_symlink) 527 ret.symlink_path = symlink(index); 528 ret.filehash = hash(index); 529 return ret; 530 } 531 #endif // TORRENT_ABI_VERSION 532 num_files() const533 int file_storage::num_files() const noexcept 534 { return int(m_files.size()); } 535 536 // returns the index of the one-past-end file in the file storage end_file() const537 file_index_t file_storage::end_file() const noexcept 538 { return m_files.end_index(); } 539 last_file() const540 file_index_t file_storage::last_file() const noexcept 541 { return --m_files.end_index(); } 542 file_range() const543 index_range<file_index_t> file_storage::file_range() const noexcept 544 { return m_files.range(); } 545 piece_range() const546 index_range<piece_index_t> file_storage::piece_range() const noexcept 547 { return {piece_index_t{0}, end_piece()}; } 548 map_file(file_index_t const file_index,std::int64_t const file_offset,int const size) const549 peer_request file_storage::map_file(file_index_t const file_index 550 , std::int64_t const file_offset, int const size) const 551 { 552 TORRENT_ASSERT_PRECOND(file_index < end_file()); 553 TORRENT_ASSERT(m_num_pieces >= 0); 554 555 peer_request ret{}; 556 if (file_index >= end_file()) 557 { 558 ret.piece = end_piece(); 559 ret.start = 0; 560 ret.length = 0; 561 return ret; 562 } 563 564 std::int64_t const offset = file_offset + this->file_offset(file_index); 565 566 if (offset >= total_size()) 567 { 568 ret.piece = end_piece(); 569 ret.start = 0; 570 ret.length = 0; 571 } 572 else 573 { 574 ret.piece = piece_index_t(int(offset / piece_length())); 575 ret.start = int(offset % piece_length()); 576 ret.length = size; 577 if (offset + size > total_size()) 578 ret.length = int(total_size() - offset); 579 } 580 return ret; 581 } 582 add_file(std::string const & path,std::int64_t file_size,file_flags_t const file_flags,std::time_t mtime,string_view symlink_path)583 void file_storage::add_file(std::string const& path, std::int64_t file_size 584 , file_flags_t const file_flags, std::time_t mtime, string_view symlink_path) 585 { 586 add_file_borrow({}, path, file_size, file_flags, nullptr, mtime 587 , symlink_path); 588 } 589 add_file_borrow(string_view filename,std::string const & path,std::int64_t const file_size,file_flags_t const file_flags,char const * filehash,std::int64_t const mtime,string_view symlink_path)590 void file_storage::add_file_borrow(string_view filename 591 , std::string const& path, std::int64_t const file_size 592 , file_flags_t const file_flags, char const* filehash 593 , std::int64_t const mtime, string_view symlink_path) 594 { 595 TORRENT_ASSERT_PRECOND(file_size >= 0); 596 TORRENT_ASSERT_PRECOND(!is_complete(filename)); 597 if (!has_parent_path(path)) 598 { 599 // you have already added at least one file with a 600 // path to the file (branch_path), which means that 601 // all the other files need to be in the same top 602 // directory as the first file. 603 TORRENT_ASSERT_PRECOND(m_files.empty()); 604 m_name = path; 605 } 606 else 607 { 608 if (m_files.empty()) 609 m_name = lsplit_path(path).first.to_string(); 610 } 611 612 // this is poor-man's emplace_back() 613 m_files.resize(m_files.size() + 1); 614 internal_file_entry& e = m_files.back(); 615 616 // the last argument specified whether the function should also set 617 // the filename. If it does, it will copy the leaf filename from path. 618 // if filename is empty, we should copy it. If it isn't, we're borrowing 619 // it and we can save the copy by setting it after this call to 620 // update_path_index(). 621 update_path_index(e, path, filename.empty()); 622 623 // filename is allowed to be empty, in which case we just use path 624 if (!filename.empty()) 625 e.set_name(filename, true); 626 627 e.size = aux::numeric_cast<std::uint64_t>(file_size); 628 e.offset = aux::numeric_cast<std::uint64_t>(m_total_size); 629 e.pad_file = bool(file_flags & file_storage::flag_pad_file); 630 e.hidden_attribute = bool(file_flags & file_storage::flag_hidden); 631 e.executable_attribute = bool(file_flags & file_storage::flag_executable); 632 e.symlink_attribute = bool(file_flags & file_storage::flag_symlink); 633 634 if (filehash) 635 { 636 if (m_file_hashes.size() < m_files.size()) m_file_hashes.resize(m_files.size()); 637 m_file_hashes[last_file()] = filehash; 638 } 639 if (!symlink_path.empty() 640 && m_symlinks.size() < internal_file_entry::not_a_symlink - 1) 641 { 642 e.symlink_index = m_symlinks.size(); 643 m_symlinks.emplace_back(symlink_path.to_string()); 644 } 645 else 646 { 647 e.symlink_attribute = false; 648 } 649 if (mtime) 650 { 651 if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size()); 652 m_mtime[last_file()] = std::time_t(mtime); 653 } 654 655 m_total_size += e.size; 656 } 657 hash(file_index_t const index) const658 sha1_hash file_storage::hash(file_index_t const index) const 659 { 660 if (index >= m_file_hashes.end_index()) return sha1_hash(); 661 return sha1_hash(m_file_hashes[index]); 662 } 663 symlink(file_index_t const index) const664 std::string const& file_storage::symlink(file_index_t const index) const 665 { 666 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 667 internal_file_entry const& fe = m_files[index]; 668 // TODO: 3 this is a hack to retain ABI compatibility with 1.2.1 669 // in next major release, make this return by value 670 static std::string storage[4]; 671 static std::atomic<size_t> counter{0}; 672 673 if (fe.symlink_index == internal_file_entry::not_a_symlink) 674 { 675 std::string& ret = storage[(counter++) % 4]; 676 ret.clear(); 677 return ret; 678 } 679 680 TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); 681 682 auto const& link = m_symlinks[fe.symlink_index]; 683 684 std::string& ret = storage[(counter++) % 4]; 685 ret.reserve(m_name.size() + link.size() + 1); 686 ret.assign(m_name); 687 append_path(ret, link); 688 return ret; 689 } 690 internal_symlink(file_index_t const index) const691 std::string const& file_storage::internal_symlink(file_index_t const index) const 692 { 693 TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file()); 694 internal_file_entry const& fe = m_files[index]; 695 TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); 696 697 return m_symlinks[fe.symlink_index]; 698 } 699 mtime(file_index_t const index) const700 std::time_t file_storage::mtime(file_index_t const index) const 701 { 702 if (index >= m_mtime.end_index()) return 0; 703 return m_mtime[index]; 704 } 705 706 namespace { 707 708 template <class CRC> process_string_lowercase(CRC & crc,string_view str)709 void process_string_lowercase(CRC& crc, string_view str) 710 { 711 for (char const c : str) 712 crc.process_byte(to_lower(c) & 0xff); 713 } 714 715 template <class CRC> process_path_lowercase(std::unordered_set<std::uint32_t> & table,CRC crc,string_view str)716 void process_path_lowercase( 717 std::unordered_set<std::uint32_t>& table 718 , CRC crc, string_view str) 719 { 720 if (str.empty()) return; 721 for (char const c : str) 722 { 723 if (c == TORRENT_SEPARATOR) 724 table.insert(crc.checksum()); 725 crc.process_byte(to_lower(c) & 0xff); 726 } 727 table.insert(crc.checksum()); 728 } 729 } 730 all_path_hashes(std::unordered_set<std::uint32_t> & table) const731 void file_storage::all_path_hashes( 732 std::unordered_set<std::uint32_t>& table) const 733 { 734 boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; 735 736 if (!m_name.empty()) 737 { 738 process_string_lowercase(crc, m_name); 739 TORRENT_ASSERT(m_name[m_name.size() - 1] != TORRENT_SEPARATOR); 740 crc.process_byte(TORRENT_SEPARATOR); 741 } 742 743 for (auto const& p : m_paths) 744 process_path_lowercase(table, crc, p); 745 } 746 file_path_hash(file_index_t const index,std::string const & save_path) const747 std::uint32_t file_storage::file_path_hash(file_index_t const index 748 , std::string const& save_path) const 749 { 750 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 751 internal_file_entry const& fe = m_files[index]; 752 753 boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; 754 755 if (fe.path_index == -2) 756 { 757 // -2 means this is an absolute path filename 758 process_string_lowercase(crc, fe.filename()); 759 } 760 else if (fe.path_index == -1) 761 { 762 // -1 means no path 763 if (!save_path.empty()) 764 { 765 process_string_lowercase(crc, save_path); 766 TORRENT_ASSERT(save_path[save_path.size() - 1] != TORRENT_SEPARATOR); 767 crc.process_byte(TORRENT_SEPARATOR); 768 } 769 process_string_lowercase(crc, fe.filename()); 770 } 771 else if (fe.no_root_dir) 772 { 773 if (!save_path.empty()) 774 { 775 process_string_lowercase(crc, save_path); 776 TORRENT_ASSERT(save_path[save_path.size() - 1] != TORRENT_SEPARATOR); 777 crc.process_byte(TORRENT_SEPARATOR); 778 } 779 std::string const& p = m_paths[fe.path_index]; 780 if (!p.empty()) 781 { 782 process_string_lowercase(crc, p); 783 TORRENT_ASSERT(p[p.size() - 1] != TORRENT_SEPARATOR); 784 crc.process_byte(TORRENT_SEPARATOR); 785 } 786 process_string_lowercase(crc, fe.filename()); 787 } 788 else 789 { 790 if (!save_path.empty()) 791 { 792 process_string_lowercase(crc, save_path); 793 TORRENT_ASSERT(save_path[save_path.size() - 1] != TORRENT_SEPARATOR); 794 crc.process_byte(TORRENT_SEPARATOR); 795 } 796 process_string_lowercase(crc, m_name); 797 TORRENT_ASSERT(m_name.size() > 0); 798 TORRENT_ASSERT(m_name[m_name.size() - 1] != TORRENT_SEPARATOR); 799 crc.process_byte(TORRENT_SEPARATOR); 800 801 std::string const& p = m_paths[fe.path_index]; 802 if (!p.empty()) 803 { 804 process_string_lowercase(crc, p); 805 TORRENT_ASSERT(p.size() > 0); 806 TORRENT_ASSERT(p[p.size() - 1] != TORRENT_SEPARATOR); 807 crc.process_byte(TORRENT_SEPARATOR); 808 } 809 process_string_lowercase(crc, fe.filename()); 810 } 811 812 return crc.checksum(); 813 } 814 file_path(file_index_t const index,std::string const & save_path) const815 std::string file_storage::file_path(file_index_t const index, std::string const& save_path) const 816 { 817 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 818 internal_file_entry const& fe = m_files[index]; 819 820 std::string ret; 821 822 // -2 means this is an absolute path filename 823 if (fe.path_index == -2) 824 { 825 ret = fe.filename().to_string(); 826 } 827 else if (fe.path_index == -1) 828 { 829 // -1 means no path 830 ret.reserve(save_path.size() + fe.filename().size() + 1); 831 ret.assign(save_path); 832 append_path(ret, fe.filename()); 833 } 834 else if (fe.no_root_dir) 835 { 836 std::string const& p = m_paths[fe.path_index]; 837 838 ret.reserve(save_path.size() + p.size() + fe.filename().size() + 2); 839 ret.assign(save_path); 840 append_path(ret, p); 841 append_path(ret, fe.filename()); 842 } 843 else 844 { 845 std::string const& p = m_paths[fe.path_index]; 846 847 ret.reserve(save_path.size() + m_name.size() + p.size() + fe.filename().size() + 3); 848 ret.assign(save_path); 849 append_path(ret, m_name); 850 append_path(ret, p); 851 append_path(ret, fe.filename()); 852 } 853 854 // a single return statement, just to make NRVO more likely to kick in 855 return ret; 856 } 857 internal_file_path(file_index_t const index) const858 std::string file_storage::internal_file_path(file_index_t const index) const 859 { 860 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 861 internal_file_entry const& fe = m_files[index]; 862 863 if (fe.path_index >= 0) 864 { 865 std::string ret; 866 std::string const& p = m_paths[fe.path_index]; 867 ret.reserve(p.size() + fe.filename().size() + 2); 868 append_path(ret, p); 869 append_path(ret, fe.filename()); 870 return ret; 871 } 872 else 873 { 874 return fe.filename().to_string(); 875 } 876 } 877 file_name(file_index_t const index) const878 string_view file_storage::file_name(file_index_t const index) const 879 { 880 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 881 internal_file_entry const& fe = m_files[index]; 882 return fe.filename(); 883 } 884 file_size(file_index_t const index) const885 std::int64_t file_storage::file_size(file_index_t const index) const 886 { 887 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 888 return m_files[index].size; 889 } 890 pad_file_at(file_index_t const index) const891 bool file_storage::pad_file_at(file_index_t const index) const 892 { 893 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 894 return m_files[index].pad_file; 895 } 896 file_offset(file_index_t const index) const897 std::int64_t file_storage::file_offset(file_index_t const index) const 898 { 899 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 900 return m_files[index].offset; 901 } 902 file_flags(file_index_t const index) const903 file_flags_t file_storage::file_flags(file_index_t const index) const 904 { 905 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 906 internal_file_entry const& fe = m_files[index]; 907 return (fe.pad_file ? file_storage::flag_pad_file : file_flags_t{}) 908 | (fe.hidden_attribute ? file_storage::flag_hidden : file_flags_t{}) 909 | (fe.executable_attribute ? file_storage::flag_executable : file_flags_t{}) 910 | (fe.symlink_attribute ? file_storage::flag_symlink : file_flags_t{}); 911 } 912 file_absolute_path(file_index_t const index) const913 bool file_storage::file_absolute_path(file_index_t const index) const 914 { 915 TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file()); 916 internal_file_entry const& fe = m_files[index]; 917 return fe.path_index == -2; 918 } 919 920 #if TORRENT_ABI_VERSION == 1 hash(internal_file_entry const & fe) const921 sha1_hash file_storage::hash(internal_file_entry const& fe) const 922 { 923 int index = int(&fe - &m_files[0]); 924 if (index >= int(m_file_hashes.size())) return sha1_hash(nullptr); 925 return sha1_hash(m_file_hashes[index]); 926 } 927 symlink(internal_file_entry const & fe) const928 std::string const& file_storage::symlink(internal_file_entry const& fe) const 929 { 930 TORRENT_ASSERT_PRECOND(fe.symlink_index < int(m_symlinks.size())); 931 return m_symlinks[fe.symlink_index]; 932 } 933 mtime(internal_file_entry const & fe) const934 std::time_t file_storage::mtime(internal_file_entry const& fe) const 935 { 936 int index = int(&fe - &m_files[0]); 937 if (index >= int(m_mtime.size())) return 0; 938 return m_mtime[index]; 939 } 940 file_index(internal_file_entry const & fe) const941 int file_storage::file_index(internal_file_entry const& fe) const 942 { 943 int index = int(&fe - &m_files[0]); 944 TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); 945 return index; 946 } 947 file_path(internal_file_entry const & fe,std::string const & save_path) const948 std::string file_storage::file_path(internal_file_entry const& fe 949 , std::string const& save_path) const 950 { 951 int const index = int(&fe - &m_files[0]); 952 return file_path(index, save_path); 953 } 954 file_name(internal_file_entry const & fe) const955 std::string file_storage::file_name(internal_file_entry const& fe) const 956 { 957 return fe.filename().to_string(); 958 } 959 file_size(internal_file_entry const & fe) const960 std::int64_t file_storage::file_size(internal_file_entry const& fe) const 961 { 962 return fe.size; 963 } 964 pad_file_at(internal_file_entry const & fe) const965 bool file_storage::pad_file_at(internal_file_entry const& fe) const 966 { 967 return fe.pad_file; 968 } 969 file_offset(internal_file_entry const & fe) const970 std::int64_t file_storage::file_offset(internal_file_entry const& fe) const 971 { 972 return fe.offset; 973 } 974 at(file_storage::iterator i) const975 file_entry file_storage::at(file_storage::iterator i) const 976 { return at_deprecated(int(i - m_files.begin())); } 977 #endif // TORRENT_ABI_VERSION 978 reorder_file(int const index,int const dst)979 void file_storage::reorder_file(int const index, int const dst) 980 { 981 TORRENT_ASSERT(index < int(m_files.size())); 982 TORRENT_ASSERT(dst < int(m_files.size())); 983 TORRENT_ASSERT(dst < index); 984 985 std::iter_swap(m_files.begin() + index, m_files.begin() + dst); 986 if (!m_mtime.empty()) 987 { 988 TORRENT_ASSERT(m_mtime.size() == m_files.size()); 989 if (int(m_mtime.size()) < index) m_mtime.resize(index + 1, 0); 990 std::iter_swap(m_mtime.begin() + dst, m_mtime.begin() + index); 991 } 992 if (!m_file_hashes.empty()) 993 { 994 TORRENT_ASSERT(m_file_hashes.size() == m_files.size()); 995 if (int(m_file_hashes.size()) < index) m_file_hashes.resize(index + 1, nullptr); 996 std::iter_swap(m_file_hashes.begin() + dst, m_file_hashes.begin() + index); 997 } 998 } 999 swap(file_storage & ti)1000 void file_storage::swap(file_storage& ti) noexcept 1001 { 1002 using std::swap; 1003 swap(ti.m_files, m_files); 1004 swap(ti.m_file_hashes, m_file_hashes); 1005 swap(ti.m_symlinks, m_symlinks); 1006 swap(ti.m_mtime, m_mtime); 1007 swap(ti.m_paths, m_paths); 1008 swap(ti.m_name, m_name); 1009 swap(ti.m_total_size, m_total_size); 1010 swap(ti.m_num_pieces, m_num_pieces); 1011 swap(ti.m_piece_length, m_piece_length); 1012 } 1013 optimize(int const pad_file_limit,int alignment,bool const tail_padding)1014 void file_storage::optimize(int const pad_file_limit, int alignment 1015 , bool const tail_padding) 1016 { 1017 if (alignment == -1) 1018 alignment = m_piece_length; 1019 1020 // TODO: padfiles should be removed 1021 1022 std::int64_t off = 0; 1023 int padding_file = 0; 1024 for (auto i = m_files.begin(); i != m_files.end(); ++i) 1025 { 1026 if ((off % alignment) == 0) 1027 { 1028 // this file position is aligned, pick the largest 1029 // available file to put here. If we encounter a file whose size is 1030 // divisible by `alignment`, we pick that immediately, since that 1031 // will not affect whether we're at an aligned position and will 1032 // improve packing of files 1033 auto best_match = i; 1034 for (auto k = i; k != m_files.end(); ++k) 1035 { 1036 // a file whose size fits the alignment always takes priority, 1037 // since it will let us keep placing aligned files 1038 if ((k->size % aux::numeric_cast<std::uint64_t>(alignment)) == 0) 1039 { 1040 best_match = k; 1041 break; 1042 } 1043 // otherwise, pick the largest file, to have as many bytes be 1044 // aligned. 1045 if (best_match->size < k->size) best_match = k; 1046 } 1047 1048 if (best_match != i) 1049 { 1050 int const index = int(best_match - m_files.begin()); 1051 int const cur_index = int(i - m_files.begin()); 1052 reorder_file(index, cur_index); 1053 i = m_files.begin() + cur_index; 1054 } 1055 } 1056 else if (pad_file_limit >= 0 1057 && i->size > std::uint32_t(pad_file_limit) 1058 && i->pad_file == false) 1059 { 1060 // if we have pad files enabled, and this file is 1061 // not piece-aligned and the file size exceeds the 1062 // limit, and it's not a padding file itself. 1063 // so add a padding file in front of it 1064 int const pad_size = alignment - (off % alignment); 1065 1066 // find the largest file that fits in pad_size 1067 auto best_match = m_files.end(); 1068 1069 // if pad_file_limit is 0, it means all files are padded, there's 1070 // no point in trying to find smaller files to use as filling 1071 if (pad_file_limit > 0) 1072 { 1073 for (auto j = i + 1; j < m_files.end(); ++j) 1074 { 1075 if (j->size > std::uint32_t(pad_size)) continue; 1076 if (best_match == m_files.end() || j->size > best_match->size) 1077 best_match = j; 1078 } 1079 1080 if (best_match != m_files.end()) 1081 { 1082 // we found one 1083 // We cannot have found i, because i->size > pad_file_limit 1084 // which is forced to be no less than alignment. We only 1085 // look for files <= pad_size, which never is greater than 1086 // alignment 1087 TORRENT_ASSERT(best_match != i); 1088 int index = int(best_match - m_files.begin()); 1089 int cur_index = int(i - m_files.begin()); 1090 reorder_file(index, cur_index); 1091 i = m_files.begin() + cur_index; 1092 i->offset = aux::numeric_cast<std::uint64_t>(off); 1093 off += i->size; 1094 continue; 1095 } 1096 } 1097 1098 // we could not find a file that fits in pad_size 1099 // add a padding file 1100 // note that i will be set to point to the 1101 // new pad file. Once we're done adding it, we need 1102 // to increment i to point to the current file again 1103 // first add the pad file to the end of the file list 1104 // then swap it in place. This minimizes the amount 1105 // of copying of internal_file_entry, which is somewhat 1106 // expensive (until we have move semantics) 1107 add_pad_file(pad_size, i, off, padding_file); 1108 1109 TORRENT_ASSERT((off % alignment) == 0); 1110 continue; 1111 } 1112 i->offset = aux::numeric_cast<std::uint64_t>(off); 1113 off += i->size; 1114 1115 if (tail_padding 1116 && i->size > std::uint32_t(pad_file_limit) 1117 && (off % alignment) != 0) 1118 { 1119 // skip the file we just put in place, so we put the pad 1120 // file after it 1121 ++i; 1122 1123 // tail-padding is enabled, and the offset after this file is not 1124 // aligned. The last file must be padded too, in order to match an 1125 // equivalent tail-padded file. 1126 add_pad_file(alignment - (off % alignment), i, off, padding_file); 1127 1128 TORRENT_ASSERT((off % alignment) == 0); 1129 1130 if (i == m_files.end()) break; 1131 } 1132 } 1133 m_total_size = off; 1134 } 1135 add_pad_file(int const size,std::vector<internal_file_entry>::iterator & i,std::int64_t & offset,int & pad_file_counter)1136 void file_storage::add_pad_file(int const size 1137 , std::vector<internal_file_entry>::iterator& i 1138 , std::int64_t& offset 1139 , int& pad_file_counter) 1140 { 1141 int const cur_index = int(i - m_files.begin()); 1142 int const index = int(m_files.size()); 1143 m_files.push_back(internal_file_entry()); 1144 internal_file_entry& e = m_files.back(); 1145 // i may have been invalidated, refresh it 1146 i = m_files.begin() + cur_index; 1147 e.size = aux::numeric_cast<std::uint64_t>(size); 1148 e.offset = aux::numeric_cast<std::uint64_t>(offset); 1149 e.path_index = get_or_add_path(".pad"); 1150 char name[15]; 1151 std::snprintf(name, sizeof(name), "%d", pad_file_counter); 1152 e.set_name(name); 1153 e.pad_file = true; 1154 offset += size; 1155 ++pad_file_counter; 1156 1157 if (!m_mtime.empty()) m_mtime.resize(index + 1, 0); 1158 if (!m_file_hashes.empty()) m_file_hashes.resize(index + 1, nullptr); 1159 1160 if (index != cur_index) reorder_file(index, cur_index); 1161 } 1162 sanitize_symlinks()1163 void file_storage::sanitize_symlinks() 1164 { 1165 // symlinks are unusual, this function is optimized assuming there are no 1166 // symbolic links in the torrent. If we find one symbolic link, we'll 1167 // build the hash table of files it's allowed to refer to, but don't pay 1168 // that price up-front. 1169 std::unordered_map<std::string, file_index_t> file_map; 1170 bool file_map_initialized = false; 1171 1172 // lazily instantiated set of all valid directories a symlink may point to 1173 // TODO: in C++17 this could be string_view 1174 std::unordered_set<std::string> dir_map; 1175 bool dir_map_initialized = false; 1176 1177 // symbolic links that points to directories 1178 std::unordered_map<std::string, std::string> dir_links; 1179 1180 // we validate symlinks in (potentially) 2 passes over the files. 1181 // remaining symlinks to validate after the first pass 1182 std::vector<file_index_t> symlinks_to_validate; 1183 1184 for (auto const i : file_range()) 1185 { 1186 if (!(file_flags(i) & file_storage::flag_symlink)) continue; 1187 1188 if (!file_map_initialized) 1189 { 1190 for (auto const j : file_range()) 1191 file_map.insert({internal_file_path(j), j}); 1192 file_map_initialized = true; 1193 } 1194 1195 internal_file_entry const& fe = m_files[i]; 1196 TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); 1197 1198 // symlink targets are only allowed to point to files or directories in 1199 // this torrent. 1200 { 1201 std::string target = m_symlinks[fe.symlink_index]; 1202 1203 if (is_complete(target)) 1204 { 1205 // a symlink target is not allowed to be an absolute path, ever 1206 // this symlink is invalid, make it point to itself 1207 m_symlinks[fe.symlink_index] = internal_file_path(i); 1208 continue; 1209 } 1210 1211 auto const iter = file_map.find(target); 1212 if (iter != file_map.end()) 1213 { 1214 m_symlinks[fe.symlink_index] = target; 1215 if (file_flags(iter->second) & file_storage::flag_symlink) 1216 { 1217 // we don't know whether this symlink is a file or a 1218 // directory, so make the conservative assumption that it's a 1219 // directory 1220 dir_links[internal_file_path(i)] = target; 1221 } 1222 continue; 1223 } 1224 1225 // it may point to a directory that doesn't have any files (but only 1226 // other directories), in which case it won't show up in m_paths 1227 if (!dir_map_initialized) 1228 { 1229 for (auto const& p : m_paths) 1230 for (string_view pv = p; !pv.empty(); pv = rsplit_path(pv).first) 1231 dir_map.insert(pv.to_string()); 1232 dir_map_initialized = true; 1233 } 1234 1235 if (dir_map.count(target)) 1236 { 1237 // it points to a sub directory within the torrent, that's OK 1238 m_symlinks[fe.symlink_index] = target; 1239 dir_links[internal_file_path(i)] = target; 1240 continue; 1241 } 1242 1243 } 1244 1245 // for backwards compatibility, allow paths relative to the link as 1246 // well 1247 if (fe.path_index >= 0) 1248 { 1249 std::string target = m_paths[fe.path_index]; 1250 append_path(target, m_symlinks[fe.symlink_index]); 1251 // if it points to a directory, that's OK 1252 auto const it = std::find(m_paths.begin(), m_paths.end(), target); 1253 if (it != m_paths.end()) 1254 { 1255 m_symlinks[fe.symlink_index] = *it; 1256 dir_links[internal_file_path(i)] = *it; 1257 continue; 1258 } 1259 1260 if (dir_map.count(target)) 1261 { 1262 // it points to a sub directory within the torrent, that's OK 1263 m_symlinks[fe.symlink_index] = target; 1264 dir_links[internal_file_path(i)] = target; 1265 continue; 1266 } 1267 1268 auto const iter = file_map.find(target); 1269 if (iter != file_map.end()) 1270 { 1271 m_symlinks[fe.symlink_index] = target; 1272 if (file_flags(iter->second) & file_storage::flag_symlink) 1273 { 1274 // we don't know whether this symlink is a file or a 1275 // directory, so make the conservative assumption that it's a 1276 // directory 1277 dir_links[internal_file_path(i)] = target; 1278 } 1279 continue; 1280 } 1281 } 1282 1283 // we don't know whether this symlink is a file or a 1284 // directory, so make the conservative assumption that it's a 1285 // directory 1286 dir_links[internal_file_path(i)] = m_symlinks[fe.symlink_index]; 1287 symlinks_to_validate.push_back(i); 1288 } 1289 1290 // in case there were some "complex" symlinks, we nee a second pass to 1291 // validate those. For example, symlinks whose target rely on other 1292 // symlinks 1293 for (auto const i : symlinks_to_validate) 1294 { 1295 internal_file_entry const& fe = m_files[i]; 1296 TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); 1297 1298 std::string target = m_symlinks[fe.symlink_index]; 1299 1300 // to avoid getting stuck in an infinite loop, we only allow traversing 1301 // a symlink once 1302 std::set<std::string> traversed; 1303 1304 // this is where we check every path element for existence. If it's not 1305 // among the concrete paths, it may be a symlink, which is also OK 1306 // note that we won't iterate through this for the last step, where the 1307 // filename is included. The filename is validated after the loop 1308 for (string_view branch = lsplit_path(target).first; 1309 branch.size() < target.size(); 1310 branch = lsplit_path(target, branch.size() + 1).first) 1311 { 1312 // this is a concrete directory 1313 if (dir_map.count(branch.to_string())) continue; 1314 1315 auto const iter = dir_links.find(branch.to_string()); 1316 if (iter == dir_links.end()) goto failed; 1317 if (traversed.count(branch.to_string())) goto failed; 1318 traversed.insert(branch.to_string()); 1319 1320 // this path element is a symlink. substitute the branch so far by 1321 // the link target 1322 target = combine_path(iter->second, target.substr(branch.size() + 1)); 1323 1324 // start over with the new (concrete) path 1325 branch = {}; 1326 } 1327 1328 // the final (resolved) target must be a valid file 1329 // or directory 1330 if (file_map.count(target) == 0 1331 && dir_map.count(target) == 0) goto failed; 1332 1333 // this is OK 1334 continue; 1335 1336 failed: 1337 1338 // this symlink is invalid, make it point to itself 1339 m_symlinks[fe.symlink_index] = internal_file_path(i); 1340 } 1341 } 1342 1343 1344 namespace aux { 1345 1346 std::tuple<piece_index_t, piece_index_t> file_piece_range_exclusive(file_storage const & fs,file_index_t const file)1347 file_piece_range_exclusive(file_storage const& fs, file_index_t const file) 1348 { 1349 peer_request const range = fs.map_file(file, 0, 1); 1350 std::int64_t const file_size = fs.file_size(file); 1351 std::int64_t const piece_size = fs.piece_length(); 1352 piece_index_t const begin_piece = range.start == 0 ? range.piece : piece_index_t(static_cast<int>(range.piece) + 1); 1353 // the last piece is potentially smaller than the other pieces, so the 1354 // generic logic doesn't really work. If this file is the last file, the 1355 // last piece doesn't overlap with any other file and it's entirely 1356 // contained within the last file. 1357 piece_index_t const end_piece = (file == file_index_t(fs.num_files() - 1)) 1358 ? piece_index_t(fs.num_pieces()) 1359 : piece_index_t(int((static_cast<int>(range.piece) * piece_size + range.start + file_size + 1) / piece_size)); 1360 return std::make_tuple(begin_piece, end_piece); 1361 } 1362 1363 std::tuple<piece_index_t, piece_index_t> file_piece_range_inclusive(file_storage const & fs,file_index_t const file)1364 file_piece_range_inclusive(file_storage const& fs, file_index_t const file) 1365 { 1366 peer_request const range = fs.map_file(file, 0, 1); 1367 std::int64_t const file_size = fs.file_size(file); 1368 std::int64_t const piece_size = fs.piece_length(); 1369 piece_index_t const end_piece = piece_index_t(int((static_cast<int>(range.piece) 1370 * piece_size + range.start + file_size - 1) / piece_size + 1)); 1371 return std::make_tuple(range.piece, end_piece); 1372 } 1373 1374 } // namespace aux 1375 } 1376