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