/src/libtorrent/src/resolve_links.cpp
Line | Count | Source |
1 | | /* |
2 | | |
3 | | Copyright (c) 2015-2019, 2022, Arvid Norberg |
4 | | Copyright (c) 2016-2017, Alden Torres |
5 | | All rights reserved. |
6 | | |
7 | | Redistribution and use in source and binary forms, with or without |
8 | | modification, are permitted provided that the following conditions |
9 | | are met: |
10 | | |
11 | | * Redistributions of source code must retain the above copyright |
12 | | notice, this list of conditions and the following disclaimer. |
13 | | * Redistributions in binary form must reproduce the above copyright |
14 | | notice, this list of conditions and the following disclaimer in |
15 | | the documentation and/or other materials provided with the distribution. |
16 | | * Neither the name of the author nor the names of its |
17 | | contributors may be used to endorse or promote products derived |
18 | | from this software without specific prior written permission. |
19 | | |
20 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
21 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
24 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | | POSSIBILITY OF SUCH DAMAGE. |
31 | | |
32 | | */ |
33 | | |
34 | | #include "libtorrent/resolve_links.hpp" |
35 | | #include "libtorrent/torrent_info.hpp" |
36 | | #include "libtorrent/aux_/numeric_cast.hpp" |
37 | | |
38 | | namespace libtorrent { |
39 | | |
40 | | #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS |
41 | | resolve_links::resolve_links(std::shared_ptr<torrent_info> ti) |
42 | 0 | : m_torrent_file(std::move(ti)) |
43 | 0 | { |
44 | 0 | TORRENT_ASSERT(m_torrent_file); |
45 | |
|
46 | 0 | int const piece_size = m_torrent_file->piece_length(); |
47 | |
|
48 | 0 | bool const v1 = m_torrent_file->v1(); |
49 | 0 | bool const v2 = m_torrent_file->v2(); |
50 | |
|
51 | 0 | file_storage const& fs = m_torrent_file->files(); |
52 | 0 | m_file_sizes.reserve(aux::numeric_cast<std::size_t>(fs.num_files())); |
53 | 0 | for (auto const i : fs.file_range()) |
54 | 0 | { |
55 | | // don't match pad-files, and don't match files that aren't aligned to |
56 | | // pieces. Files are matched by comparing piece hashes, so pieces must |
57 | | // be aligned and the same size |
58 | 0 | if (fs.pad_file_at(i)) continue; |
59 | 0 | if ((fs.file_offset(i) % piece_size) != 0) continue; |
60 | | |
61 | 0 | if (v1) |
62 | 0 | m_file_sizes.insert({fs.file_size(i), i}); |
63 | |
|
64 | 0 | if (v2) |
65 | 0 | m_file_roots.insert({fs.root(i), i}); |
66 | 0 | } |
67 | |
|
68 | 0 | m_links.resize(m_torrent_file->num_files()); |
69 | 0 | } |
70 | | |
71 | | void resolve_links::match(std::shared_ptr<const torrent_info> const& ti |
72 | | , std::string const& save_path) |
73 | 0 | { |
74 | 0 | if (!ti) return; |
75 | | |
76 | 0 | if (m_torrent_file->v2() && ti->v2()) |
77 | 0 | { |
78 | 0 | match_v2(ti, save_path); |
79 | 0 | } |
80 | |
|
81 | 0 | if (m_torrent_file->v1() && ti->v1()) |
82 | 0 | { |
83 | 0 | match_v1(ti, save_path); |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | void resolve_links::match_v1(std::shared_ptr<const torrent_info> const& ti |
88 | | , std::string const& save_path) |
89 | 0 | { |
90 | | // only torrents with the same piece size |
91 | 0 | if (ti->piece_length() != m_torrent_file->piece_length()) return; |
92 | | |
93 | 0 | int const piece_size = ti->piece_length(); |
94 | |
|
95 | 0 | file_storage const& fs = ti->files(); |
96 | 0 | for (auto const i : fs.file_range()) |
97 | 0 | { |
98 | | // for every file in the other torrent, see if we have one that match |
99 | | // it in m_torrent_file |
100 | | |
101 | | // if the file base is not aligned to pieces, we're not going to match |
102 | | // it anyway (we only compare piece hashes) |
103 | 0 | if ((fs.file_offset(i) % piece_size) != 0) continue; |
104 | 0 | if (fs.pad_file_at(i)) continue; |
105 | | |
106 | 0 | std::int64_t const file_size = fs.file_size(i); |
107 | |
|
108 | 0 | auto const range = m_file_sizes.equal_range(file_size); |
109 | 0 | for (auto iter = range.first; iter != range.second; ++iter) |
110 | 0 | { |
111 | 0 | auto const idx = iter->second; |
112 | 0 | TORRENT_ASSERT(idx >= file_index_t(0)); |
113 | 0 | TORRENT_ASSERT(idx < m_torrent_file->files().end_file()); |
114 | | |
115 | | // if we already have found a duplicate for this file, no need |
116 | | // to keep looking |
117 | 0 | if (m_links[idx].ti) continue; |
118 | | |
119 | | // files are aligned and have the same size, now start comparing |
120 | | // piece hashes, to see if the files are identical |
121 | | |
122 | | // the pieces of the incoming file |
123 | 0 | piece_index_t their_piece = fs.map_file(i, 0, 0).piece; |
124 | | // the pieces of "this" file (from m_torrent_file) |
125 | 0 | piece_index_t our_piece = m_torrent_file->files().map_file( |
126 | 0 | idx, 0, 0).piece; |
127 | |
|
128 | 0 | int const num_pieces = int((file_size + piece_size - 1) / piece_size); |
129 | |
|
130 | 0 | bool match = true; |
131 | 0 | for (int p = 0; p < num_pieces; ++p, ++their_piece, ++our_piece) |
132 | 0 | { |
133 | 0 | if (m_torrent_file->hash_for_piece(our_piece) |
134 | 0 | != ti->hash_for_piece(their_piece)) |
135 | 0 | { |
136 | 0 | match = false; |
137 | 0 | break; |
138 | 0 | } |
139 | 0 | } |
140 | 0 | if (!match) continue; |
141 | | |
142 | 0 | m_links[idx].ti = ti; |
143 | 0 | m_links[idx].save_path = save_path; |
144 | 0 | m_links[idx].file_idx = i; |
145 | | |
146 | | // since we have a duplicate for this file, we may as well remove |
147 | | // it from the file-size map, so we won't find it again. |
148 | 0 | m_file_sizes.erase(iter); |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | | void resolve_links::match_v2(std::shared_ptr<const torrent_info> const& ti |
155 | | , std::string const& save_path) |
156 | 0 | { |
157 | 0 | file_storage const& fs = ti->files(); |
158 | 0 | for (auto const i : fs.file_range()) |
159 | 0 | { |
160 | | // for every file in the other torrent, see if we have one that match |
161 | | // it in m_torrent_file |
162 | 0 | if (fs.pad_file_at(i)) continue; |
163 | | |
164 | 0 | auto const iter = m_file_roots.find(fs.root(i)); |
165 | 0 | if (iter == m_file_roots.end()) continue; |
166 | | |
167 | 0 | auto const idx = iter->second; |
168 | |
|
169 | 0 | TORRENT_ASSERT(idx >= file_index_t(0)); |
170 | 0 | TORRENT_ASSERT(idx < m_torrent_file->files().end_file()); |
171 | | |
172 | | // if we already have found a duplicate for this file, no need |
173 | | // to keep looking |
174 | 0 | if (m_links[idx].ti) continue; |
175 | | |
176 | 0 | m_links[idx].ti = ti; |
177 | 0 | m_links[idx].save_path = save_path; |
178 | 0 | m_links[idx].file_idx = i; |
179 | | |
180 | | // since we have a duplicate for this file, we may as well remove |
181 | | // it from the file-size map, so we won't find it again. |
182 | 0 | m_file_roots.erase(iter); |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | #endif // TORRENT_DISABLE_MUTABLE_TORRENTS |
187 | | |
188 | | } // namespace libtorrent |