Coverage Report

Created: 2025-06-12 06:27

/src/libtorrent/src/file_storage.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2008-2022, Arvid Norberg
4
Copyright (c) 2009, Georg Rudoy
5
Copyright (c) 2016-2018, 2020, Alden Torres
6
Copyright (c) 2017-2019, Steven Siloti
7
Copyright (c) 2022, Konstantin Morozov
8
All rights reserved.
9
10
Redistribution and use in source and binary forms, with or without
11
modification, are permitted provided that the following conditions
12
are met:
13
14
    * Redistributions of source code must retain the above copyright
15
      notice, this list of conditions and the following disclaimer.
16
    * Redistributions in binary form must reproduce the above copyright
17
      notice, this list of conditions and the following disclaimer in
18
      the documentation and/or other materials provided with the distribution.
19
    * Neither the name of the author nor the names of its
20
      contributors may be used to endorse or promote products derived
21
      from this software without specific prior written permission.
22
23
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
POSSIBILITY OF SUCH DAMAGE.
34
35
*/
36
37
#include "libtorrent/file_storage.hpp"
38
#include "libtorrent/string_util.hpp" // for allocate_string_copy
39
#include "libtorrent/utf8.hpp"
40
#include "libtorrent/index_range.hpp"
41
#include "libtorrent/aux_/path.hpp"
42
#include "libtorrent/aux_/numeric_cast.hpp"
43
#include "libtorrent/disk_interface.hpp" // for default_block_size
44
#include "libtorrent/aux_/merkle.hpp"
45
#include "libtorrent/aux_/throw.hpp"
46
47
#include "libtorrent/aux_/disable_warnings_push.hpp"
48
#include <boost/crc.hpp>
49
#include "libtorrent/aux_/disable_warnings_pop.hpp"
50
51
#include <cstdio>
52
#include <cinttypes>
53
#include <algorithm>
54
#include <functional>
55
#include <set>
56
#include <atomic>
57
58
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
59
#define TORRENT_SEPARATOR '\\'
60
#else
61
12.7M
#define TORRENT_SEPARATOR '/'
62
#endif
63
64
using namespace std::placeholders;
65
66
namespace libtorrent {
67
68
  constexpr file_flags_t file_storage::flag_pad_file;
69
  constexpr file_flags_t file_storage::flag_hidden;
70
  constexpr file_flags_t file_storage::flag_executable;
71
  constexpr file_flags_t file_storage::flag_symlink;
72
73
#if TORRENT_ABI_VERSION == 1
74
  constexpr file_flags_t file_storage::pad_file;
75
  constexpr file_flags_t file_storage::attribute_hidden;
76
  constexpr file_flags_t file_storage::attribute_executable;
77
  constexpr file_flags_t file_storage::attribute_symlink;
78
#endif
79
80
25.9k
  file_storage::file_storage() = default;
81
29.0k
  file_storage::~file_storage() = default;
82
83
  // even though this copy constructor and the copy assignment
84
  // operator are identical to what the compiler would have
85
  // generated, they are put here to explicitly make them part
86
  // of libtorrent and properly exported by the .dll.
87
3.12k
  file_storage::file_storage(file_storage const&) = default;
88
4
  file_storage& file_storage::operator=(file_storage const&) & = default;
89
0
  file_storage::file_storage(file_storage&&) noexcept = default;
90
0
  file_storage& file_storage::operator=(file_storage&&) & = default;
91
92
  void file_storage::reserve(int num_files)
93
4.95k
  {
94
4.95k
    m_files.reserve(num_files);
95
4.95k
  }
96
97
  int file_storage::piece_size(piece_index_t const index) const
98
10.1k
  {
99
10.1k
    TORRENT_ASSERT_PRECOND(index >= piece_index_t(0) && index < end_piece());
100
10.1k
    if (index == last_piece())
101
67
    {
102
67
      std::int64_t const size_except_last
103
67
        = (num_pieces() - 1) * std::int64_t(piece_length());
104
67
      std::int64_t const size = total_size() - size_except_last;
105
67
      TORRENT_ASSERT(size > 0);
106
67
      TORRENT_ASSERT(size <= piece_length());
107
67
      return int(size);
108
67
    }
109
10.0k
    else
110
10.0k
      return piece_length();
111
10.1k
  }
112
113
constexpr aux::path_index_t aux::file_entry::no_path;
114
constexpr aux::path_index_t aux::file_entry::path_is_absolute;
115
116
namespace {
117
118
  bool compare_file_offset(aux::file_entry const& lhs
119
    , aux::file_entry const& rhs)
120
2.28k
  {
121
2.28k
    return lhs.offset < rhs.offset;
122
2.28k
  }
123
124
}
125
126
  int file_storage::piece_size2(piece_index_t const index) const
127
469
  {
128
469
    TORRENT_ASSERT_PRECOND(index >= piece_index_t{} && index < end_piece());
129
469
    TORRENT_ASSERT(max_file_offset / piece_length() > static_cast<int>(index));
130
    // find the file iterator and file offset
131
469
    aux::file_entry target;
132
469
    TORRENT_ASSERT(max_file_offset / piece_length() > static_cast<int>(index));
133
469
    target.offset = aux::numeric_cast<std::uint64_t>(std::int64_t(piece_length()) * static_cast<int>(index));
134
469
    TORRENT_ASSERT(!compare_file_offset(target, m_files.front()));
135
136
469
    auto const file_iter = std::upper_bound(
137
469
      m_files.begin(), m_files.end(), target, compare_file_offset);
138
139
469
    TORRENT_ASSERT(file_iter != m_files.begin());
140
469
    if (file_iter == m_files.end()) return piece_size(index);
141
142
    // this static cast is safe because the resulting value is capped by
143
    // piece_length(), which fits in an int
144
0
    return static_cast<int>(
145
0
      std::min(static_cast<std::uint64_t>(piece_length()), file_iter->offset - target.offset));
146
469
  }
147
148
  int file_storage::blocks_in_piece2(piece_index_t const index) const
149
469
  {
150
    // the number of default_block_size in a piece size, rounding up
151
469
    return (piece_size2(index) + default_block_size - 1) / default_block_size;
152
469
  }
153
154
  int file_storage::blocks_per_piece() const
155
2.77k
  {
156
2.77k
    return (m_piece_length + default_block_size - 1) / default_block_size;
157
2.77k
  }
158
159
  // path is supposed to include the name of the torrent itself.
160
  // or an absolute path, to move a file outside of the download directory
161
  void file_storage::update_path_index(aux::file_entry& e
162
    , std::string const& path, bool const set_name)
163
341k
  {
164
341k
    if (is_complete(path))
165
117
    {
166
117
      TORRENT_ASSERT(set_name);
167
117
      e.set_name(path);
168
117
      e.path_index = aux::file_entry::path_is_absolute;
169
117
      return;
170
117
    }
171
172
341k
    TORRENT_ASSERT(path[0] != '/');
173
174
    // split the string into the leaf filename
175
    // and the branch path
176
341k
    string_view leaf;
177
341k
    string_view branch_path;
178
341k
    std::tie(branch_path, leaf) = rsplit_path(path);
179
180
341k
    if (branch_path.empty())
181
1.77k
    {
182
1.77k
      if (set_name) e.set_name(leaf);
183
1.77k
      e.path_index = aux::file_entry::no_path;
184
1.77k
      return;
185
1.77k
    }
186
187
    // if the path *does* contain the name of the torrent (as we expect)
188
    // strip it before adding it to m_paths
189
339k
    if (lsplit_path(branch_path).first == m_name)
190
143k
    {
191
143k
      branch_path = lsplit_path(branch_path).second;
192
      // strip duplicate separators
193
144k
      while (!branch_path.empty() && (branch_path.front() == TORRENT_SEPARATOR
194
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
195
        || branch_path.front() == '/'
196
#endif
197
45.9k
        ))
198
691
        branch_path.remove_prefix(1);
199
143k
      e.no_root_dir = false;
200
143k
    }
201
196k
    else
202
196k
    {
203
196k
      e.no_root_dir = true;
204
196k
    }
205
206
339k
    e.path_index = get_or_add_path(branch_path);
207
339k
    if (set_name) e.set_name(leaf);
208
339k
  }
209
210
  aux::path_index_t file_storage::get_or_add_path(string_view const path)
211
339k
  {
212
    // do we already have this path in the path list?
213
339k
    auto const p = std::find(m_paths.rbegin(), m_paths.rend(), path);
214
215
339k
    if (p == m_paths.rend())
216
26.4k
    {
217
      // no, we don't. add it
218
26.4k
      auto const ret = m_paths.end_index();
219
26.4k
      TORRENT_ASSERT(path.size() == 0 || path[0] != '/');
220
26.4k
      m_paths.emplace_back(path.data(), path.size());
221
26.4k
      return ret;
222
26.4k
    }
223
313k
    else
224
313k
    {
225
      // yes we do. use it
226
313k
      return aux::path_index_t{aux::numeric_cast<std::uint32_t>(
227
313k
        p.base() - m_paths.begin() - 1)};
228
313k
    }
229
339k
  }
230
231
#if TORRENT_ABI_VERSION == 1
232
0
  file_entry::file_entry(): offset(0), size(0)
233
0
    , mtime(0), pad_file(false), hidden_attribute(false)
234
0
    , executable_attribute(false)
235
0
    , symlink_attribute(false)
236
0
  {}
237
238
0
  file_entry::~file_entry() = default;
239
#endif // TORRENT_ABI_VERSION
240
241
namespace aux {
242
243
  file_entry::file_entry()
244
328k
    : offset(0)
245
328k
    , symlink_index(not_a_symlink)
246
328k
    , no_root_dir(false)
247
328k
    , size(0)
248
328k
    , name_len(name_is_owned)
249
328k
    , pad_file(false)
250
328k
    , hidden_attribute(false)
251
328k
    , executable_attribute(false)
252
328k
    , symlink_attribute(false)
253
328k
  {}
254
255
  file_entry::~file_entry()
256
407k
  {
257
407k
    if (name_len == name_is_owned) delete[] name;
258
407k
  }
259
260
  file_entry::file_entry(file_entry const& fe)
261
78.7k
    : offset(fe.offset)
262
78.7k
    , symlink_index(fe.symlink_index)
263
78.7k
    , no_root_dir(fe.no_root_dir)
264
78.7k
    , size(fe.size)
265
78.7k
    , name_len(fe.name_len)
266
78.7k
    , pad_file(fe.pad_file)
267
78.7k
    , hidden_attribute(fe.hidden_attribute)
268
78.7k
    , executable_attribute(fe.executable_attribute)
269
78.7k
    , symlink_attribute(fe.symlink_attribute)
270
78.7k
    , root(fe.root)
271
78.7k
    , path_index(fe.path_index)
272
78.7k
  {
273
78.7k
    bool const borrow = fe.name_len != name_is_owned;
274
78.7k
    set_name(fe.filename(), borrow);
275
78.7k
  }
276
277
  file_entry& file_entry::operator=(file_entry const& fe) &
278
0
  {
279
0
    if (&fe == this) return *this;
280
0
    offset = fe.offset;
281
0
    size = fe.size;
282
0
    path_index = fe.path_index;
283
0
    symlink_index = fe.symlink_index;
284
0
    pad_file = fe.pad_file;
285
0
    hidden_attribute = fe.hidden_attribute;
286
0
    executable_attribute = fe.executable_attribute;
287
0
    symlink_attribute = fe.symlink_attribute;
288
0
    no_root_dir = fe.no_root_dir;
289
0
    root = fe.root;
290
291
    // if the name is not owned, don't allocate memory, we can point into the
292
    // same metadata buffer
293
0
    bool const borrow = fe.name_len != name_is_owned;
294
0
    set_name(fe.filename(), borrow);
295
296
0
    return *this;
297
0
  }
298
299
  file_entry::file_entry(file_entry&& fe) noexcept
300
4
    : offset(fe.offset)
301
4
    , symlink_index(fe.symlink_index)
302
4
    , no_root_dir(fe.no_root_dir)
303
4
    , size(fe.size)
304
4
    , name_len(fe.name_len)
305
4
    , pad_file(fe.pad_file)
306
4
    , hidden_attribute(fe.hidden_attribute)
307
4
    , executable_attribute(fe.executable_attribute)
308
4
    , symlink_attribute(fe.symlink_attribute)
309
4
    , name(fe.name)
310
4
    , root(fe.root)
311
4
    , path_index(fe.path_index)
312
4
  {
313
4
    fe.name_len = 0;
314
4
    fe.name = nullptr;
315
4
  }
316
317
  file_entry& file_entry::operator=(file_entry&& fe) & noexcept
318
0
  {
319
0
    if (&fe == this) return *this;
320
0
    offset = fe.offset;
321
0
    size = fe.size;
322
0
    path_index = fe.path_index;
323
0
    symlink_index = fe.symlink_index;
324
0
    pad_file = fe.pad_file;
325
0
    hidden_attribute = fe.hidden_attribute;
326
0
    executable_attribute = fe.executable_attribute;
327
0
    symlink_attribute = fe.symlink_attribute;
328
0
    no_root_dir = fe.no_root_dir;
329
330
0
    if (name_len == name_is_owned) delete[] name;
331
332
0
    name = fe.name;
333
0
    root = fe.root;
334
0
    name_len = fe.name_len;
335
336
0
    fe.name_len = 0;
337
0
    fe.name = nullptr;
338
0
    return *this;
339
0
  }
340
341
  // if borrow_string is true, don't take ownership over n, just
342
  // point to it.
343
  // if borrow_string is false, n will be copied and owned by the
344
  // file_entry.
345
  void file_entry::set_name(string_view n, bool const borrow_string)
346
420k
  {
347
    // free the current string, before assigning the new one
348
420k
    if (name_len == name_is_owned) delete[] name;
349
420k
    if (n.empty())
350
67
    {
351
67
      TORRENT_ASSERT(borrow_string == false);
352
67
      name = nullptr;
353
67
    }
354
420k
    else if (borrow_string)
355
25.7k
    {
356
      // we have limited space in the length field. truncate string
357
      // if it's too long
358
25.7k
      if (n.size() >= name_is_owned) n = n.substr(name_is_owned - 1);
359
360
25.7k
      name = n.data();
361
25.7k
      name_len = aux::numeric_cast<std::uint64_t>(n.size());
362
25.7k
    }
363
394k
    else
364
394k
    {
365
394k
      name = allocate_string_copy(n);
366
394k
      name_len = name_is_owned;
367
394k
    }
368
420k
  }
369
370
  string_view file_entry::filename() const
371
1.57M
  {
372
1.57M
    if (name_len != name_is_owned) return {name, std::size_t(name_len)};
373
1.48M
    return name ? string_view(name) : string_view();
374
1.57M
  }
375
376
} // aux namespace
377
378
#if TORRENT_ABI_VERSION == 1
379
380
  void file_storage::add_file_borrow(char const* filename, int filename_len
381
    , std::string const& path, std::int64_t file_size, file_flags_t file_flags
382
    , char const* filehash, std::int64_t mtime, string_view symlink_path)
383
0
  {
384
0
    TORRENT_ASSERT(filename_len >= 0);
385
0
    add_file_borrow({filename, std::size_t(filename_len)}, path, file_size
386
0
      , file_flags, filehash, mtime, symlink_path);
387
0
  }
388
389
  void file_storage::add_file(file_entry const& fe, char const* filehash)
390
0
  {
391
0
    file_flags_t flags = {};
392
0
    if (fe.pad_file) flags |= file_storage::flag_pad_file;
393
0
    if (fe.hidden_attribute) flags |= file_storage::flag_hidden;
394
0
    if (fe.executable_attribute) flags |= file_storage::flag_executable;
395
0
    if (fe.symlink_attribute) flags |= file_storage::flag_symlink;
396
397
0
    add_file_borrow({}, fe.path, fe.size, flags, filehash, fe.mtime
398
0
      , fe.symlink_path);
399
0
  }
400
#endif // TORRENT_ABI_VERSION
401
402
  void file_storage::rename_file(file_index_t const index
403
    , std::string const& new_filename)
404
14.5k
  {
405
14.5k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
406
14.5k
    update_path_index(m_files[index], new_filename);
407
14.5k
  }
408
409
#if TORRENT_ABI_VERSION == 1
410
  file_storage::iterator file_storage::file_at_offset_deprecated(std::int64_t offset) const
411
0
  {
412
    // find the file iterator and file offset
413
0
    aux::file_entry target;
414
0
    TORRENT_ASSERT(offset <= max_file_offset);
415
0
    target.offset = aux::numeric_cast<std::uint64_t>(offset);
416
0
    TORRENT_ASSERT(!compare_file_offset(target, m_files.front()));
417
418
0
    auto file_iter = std::upper_bound(
419
0
      begin_deprecated(), end_deprecated(), target, compare_file_offset);
420
421
0
    TORRENT_ASSERT(file_iter != begin_deprecated());
422
0
    --file_iter;
423
0
    return file_iter;
424
0
  }
425
426
  file_storage::iterator file_storage::file_at_offset(std::int64_t offset) const
427
0
  {
428
0
    return file_at_offset_deprecated(offset);
429
0
  }
430
#endif
431
432
  file_index_t file_storage::file_index_at_offset(std::int64_t const offset) const
433
672
  {
434
672
    TORRENT_ASSERT_PRECOND(offset >= 0);
435
672
    TORRENT_ASSERT_PRECOND(offset < m_total_size);
436
672
    TORRENT_ASSERT(offset <= max_file_offset);
437
    // find the file iterator and file offset
438
672
    aux::file_entry target;
439
672
    target.offset = aux::numeric_cast<std::uint64_t>(offset);
440
672
    TORRENT_ASSERT(!compare_file_offset(target, m_files.front()));
441
442
672
    auto file_iter = std::upper_bound(
443
672
      m_files.begin(), m_files.end(), target, compare_file_offset);
444
445
672
    TORRENT_ASSERT(file_iter != m_files.begin());
446
672
    --file_iter;
447
672
    return file_index_t{int(file_iter - m_files.begin())};
448
672
  }
449
450
  file_index_t file_storage::file_index_at_piece(piece_index_t const piece) const
451
0
  {
452
0
    return file_index_at_offset(static_cast<int>(piece) * std::int64_t(piece_length()));
453
0
  }
454
455
  file_index_t file_storage::file_index_for_root(sha256_hash const& root_hash) const
456
220
  {
457
    // TODO: maybe it would be nice to have a better index here
458
220
    for (file_index_t const i : file_range())
459
220
    {
460
220
      if (root(i) == root_hash) return i;
461
220
    }
462
144
    return file_index_t{-1};
463
220
  }
464
465
  piece_index_t file_storage::piece_index_at_file(file_index_t f) const
466
0
  {
467
0
    return piece_index_t{aux::numeric_cast<int>(file_offset(f) / piece_length())};
468
0
  }
469
470
#if TORRENT_ABI_VERSION <= 2
471
  char const* file_storage::file_name_ptr(file_index_t const index) const
472
0
  {
473
0
    return m_files[index].name;
474
0
  }
475
476
  int file_storage::file_name_len(file_index_t const index) const
477
0
  {
478
0
    if (m_files[index].name_len == aux::file_entry::name_is_owned)
479
0
      return -1;
480
0
    return m_files[index].name_len;
481
0
  }
482
#endif
483
484
  std::vector<file_slice> file_storage::map_block(piece_index_t const piece
485
    , std::int64_t const offset, std::int64_t size) const
486
0
  {
487
0
    TORRENT_ASSERT_PRECOND(piece >= piece_index_t{0});
488
0
    TORRENT_ASSERT_PRECOND(piece < end_piece());
489
0
    TORRENT_ASSERT_PRECOND(num_files() > 0);
490
0
    TORRENT_ASSERT_PRECOND(size >= 0);
491
0
    std::vector<file_slice> ret;
492
493
0
    if (m_files.empty()) return ret;
494
495
    // find the file iterator and file offset
496
0
    aux::file_entry target;
497
0
    TORRENT_ASSERT(max_file_offset / m_piece_length > static_cast<int>(piece));
498
0
    target.offset = aux::numeric_cast<std::uint64_t>(static_cast<int>(piece) * std::int64_t(m_piece_length) + offset);
499
0
    TORRENT_ASSERT_PRECOND(std::int64_t(target.offset) <= m_total_size - size);
500
0
    TORRENT_ASSERT(!compare_file_offset(target, m_files.front()));
501
502
    // in case the size is past the end, fix it up
503
0
    if (std::int64_t(target.offset) > m_total_size - size)
504
0
      size = m_total_size - std::int64_t(target.offset);
505
506
0
    auto file_iter = std::upper_bound(
507
0
      m_files.begin(), m_files.end(), target, compare_file_offset);
508
509
0
    TORRENT_ASSERT(file_iter != m_files.begin());
510
0
    --file_iter;
511
512
0
    std::int64_t file_offset = target.offset - file_iter->offset;
513
0
    for (; size > 0; file_offset -= file_iter->size, ++file_iter)
514
0
    {
515
0
      TORRENT_ASSERT(file_iter != m_files.end());
516
0
      if (file_offset < std::int64_t(file_iter->size))
517
0
      {
518
0
        file_slice f{};
519
0
        f.file_index = file_index_t(int(file_iter - m_files.begin()));
520
0
        f.offset = file_offset;
521
0
        f.size = std::min(std::int64_t(file_iter->size) - file_offset, std::int64_t(size));
522
0
        TORRENT_ASSERT(f.size <= size);
523
0
        size -= f.size;
524
0
        file_offset += f.size;
525
0
        ret.push_back(f);
526
0
      }
527
528
0
      TORRENT_ASSERT(size >= 0);
529
0
    }
530
0
    return ret;
531
0
  }
532
533
#if TORRENT_ABI_VERSION == 1
534
  file_entry file_storage::at(int index) const
535
0
  {
536
0
    return at_deprecated(index);
537
0
  }
538
539
  aux::file_entry const& file_storage::internal_at(int const index) const
540
0
  {
541
0
    TORRENT_ASSERT(index >= 0);
542
0
    TORRENT_ASSERT(index < int(m_files.size()));
543
0
    return m_files[file_index_t(index)];
544
0
  }
545
546
  file_entry file_storage::at_deprecated(int index) const
547
0
  {
548
0
    TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size()));
549
0
    file_entry ret;
550
0
    aux::file_entry const& ife = m_files[index];
551
0
    ret.path = file_path(index);
552
0
    ret.offset = ife.offset;
553
0
    ret.size = ife.size;
554
0
    ret.mtime = mtime(index);
555
0
    ret.pad_file = ife.pad_file;
556
0
    ret.hidden_attribute = ife.hidden_attribute;
557
0
    ret.executable_attribute = ife.executable_attribute;
558
0
    ret.symlink_attribute = ife.symlink_attribute;
559
0
    if (ife.symlink_index != aux::file_entry::not_a_symlink)
560
0
      ret.symlink_path = symlink(index);
561
0
    ret.filehash = hash(index);
562
0
    return ret;
563
0
  }
564
#endif // TORRENT_ABI_VERSION
565
566
  int file_storage::num_files() const noexcept
567
42.6k
  { return int(m_files.size()); }
568
569
  // returns the index of the one-past-end file in the file storage
570
  file_index_t file_storage::end_file() const noexcept
571
1.52M
  { return m_files.end_index(); }
572
573
  file_index_t file_storage::last_file() const noexcept
574
2.29k
  { return --m_files.end_index(); }
575
576
  index_range<file_index_t> file_storage::file_range() const noexcept
577
18.4k
  { return m_files.range(); }
578
579
  index_range<piece_index_t> file_storage::piece_range() const noexcept
580
4
  { return {piece_index_t{0}, end_piece()}; }
581
582
  peer_request file_storage::map_file(file_index_t const file_index
583
    , std::int64_t const file_offset, int const size) const
584
122
  {
585
122
    TORRENT_ASSERT_PRECOND(file_index < end_file());
586
122
    TORRENT_ASSERT(m_num_pieces >= 0);
587
588
122
    peer_request ret{};
589
122
    if (file_index >= end_file())
590
0
    {
591
0
      ret.piece = end_piece();
592
0
      ret.start = 0;
593
0
      ret.length = 0;
594
0
      return ret;
595
0
    }
596
597
122
    std::int64_t const offset = file_offset + this->file_offset(file_index);
598
599
122
    if (offset >= total_size())
600
0
    {
601
0
      ret.piece = end_piece();
602
0
      ret.start = 0;
603
0
      ret.length = 0;
604
0
    }
605
122
    else
606
122
    {
607
122
      ret.piece = piece_index_t(int(offset / piece_length()));
608
122
      ret.start = int(offset % piece_length());
609
122
      ret.length = size;
610
122
      if (offset + size > total_size())
611
0
        ret.length = int(total_size() - offset);
612
122
    }
613
122
    return ret;
614
122
  }
615
616
#ifndef BOOST_NO_EXCEPTIONS
617
  void file_storage::add_file(std::string const& path, std::int64_t const file_size
618
    , file_flags_t const file_flags, std::time_t const mtime, string_view const symlink_path
619
    , char const* root_hash)
620
561
  {
621
561
    error_code ec;
622
561
    add_file_borrow(ec, {}, path, file_size, file_flags, nullptr, mtime
623
561
      , symlink_path, root_hash);
624
561
    if (ec) aux::throw_ex<system_error>(ec);
625
561
  }
626
627
  void file_storage::add_file_borrow(string_view filename
628
    , std::string const& path, std::int64_t const file_size
629
    , file_flags_t const file_flags, char const* filehash
630
    , std::int64_t const mtime, string_view const symlink_path
631
    , char const* root_hash)
632
0
  {
633
0
    error_code ec;
634
0
    add_file_borrow(ec, filename, path, file_size
635
0
      , file_flags, filehash, mtime, symlink_path, root_hash);
636
0
    if (ec) aux::throw_ex<system_error>(ec);
637
0
  }
638
#endif // BOOST_NO_EXCEPTIONS
639
640
  void file_storage::add_file(error_code& ec, std::string const& path
641
    , std::int64_t const file_size, file_flags_t const file_flags, std::time_t const mtime
642
    , string_view symlink_path, char const* root_hash)
643
0
  {
644
0
    add_file_borrow(ec, {}, path, file_size, file_flags, nullptr, mtime
645
0
      , symlink_path, root_hash);
646
0
  }
647
648
  void file_storage::add_file_borrow(error_code& ec, string_view filename
649
    , std::string const& path, std::int64_t const file_size
650
    , file_flags_t const file_flags, char const* filehash
651
    , std::int64_t const mtime, string_view const symlink_path
652
    , char const* root_hash)
653
327k
  {
654
327k
    TORRENT_ASSERT_PRECOND(file_size >= 0);
655
327k
    TORRENT_ASSERT_PRECOND(!is_complete(filename));
656
657
327k
    if (file_size > max_file_size)
658
0
    {
659
0
      ec = make_error_code(boost::system::errc::file_too_large);
660
0
      return;
661
0
    }
662
663
327k
    if (max_file_offset - m_total_size < file_size)
664
0
    {
665
0
      ec = make_error_code(errors::torrent_invalid_length);
666
0
      return;
667
0
    }
668
669
327k
    if (!filename.empty())
670
18.2k
    {
671
18.2k
      if (filename.size() >= (1 << 12))
672
0
      {
673
0
        ec = make_error_code(boost::system::errc::filename_too_long);
674
0
        return;
675
0
      }
676
18.2k
    }
677
309k
    else if (lt::filename(path).size() >= (1 << 12))
678
109
    {
679
109
      ec = make_error_code(boost::system::errc::filename_too_long);
680
109
      return;
681
109
    }
682
683
327k
    if (!has_parent_path(path))
684
1.70k
    {
685
      // you have already added at least one file with a
686
      // path to the file (branch_path), which means that
687
      // all the other files need to be in the same top
688
      // directory as the first file.
689
1.70k
      TORRENT_ASSERT_PRECOND(m_files.empty());
690
1.70k
      m_name = path;
691
1.70k
    }
692
325k
    else
693
325k
    {
694
325k
      if (m_files.empty())
695
5.27k
        m_name = lsplit_path(path).first.to_string();
696
325k
    }
697
698
    // files without a root_hash are assumed to be v1, except symlinks. They
699
    // don't have a root hash and can be either v1 or v2
700
327k
    if (symlink_path.empty() && file_size > 0)
701
10.1k
    {
702
10.1k
      bool const v2 = (root_hash != nullptr);
703
      // This condition is true of all files we've added so far have been
704
      // symlinks. i.e. this is the first "real" file we're adding.
705
      // or if m_total_size == 0, all files we've added so far have been
706
      // empty (which also are are v1/v2-ambigous)
707
10.1k
      if (m_files.size() == m_symlinks.size() || m_total_size == 0)
708
3.53k
      {
709
3.53k
        m_v2 = v2;
710
3.53k
      }
711
6.62k
      else if (m_v2 != v2)
712
0
      {
713
        // you cannot mix v1 and v2 files when building torrent_storage. Either
714
        // all files are v1 or all files are v2
715
0
        ec = m_v2 ? make_error_code(errors::torrent_missing_pieces_root)
716
0
          : make_error_code(errors::torrent_inconsistent_files);
717
0
        return;
718
0
      }
719
10.1k
    }
720
721
327k
    m_files.emplace_back();
722
327k
    aux::file_entry& e = m_files.back();
723
724
    // the last argument specified whether the function should also set
725
    // the filename. If it does, it will copy the leaf filename from path.
726
    // if filename is empty, we should copy it. If it isn't, we're borrowing
727
    // it and we can save the copy by setting it after this call to
728
    // update_path_index().
729
327k
    update_path_index(e, path, filename.empty());
730
731
    // filename is allowed to be empty, in which case we just use path
732
327k
    if (!filename.empty())
733
18.2k
      e.set_name(filename, true);
734
735
327k
    e.size = aux::numeric_cast<std::uint64_t>(file_size);
736
327k
    e.offset = aux::numeric_cast<std::uint64_t>(m_total_size);
737
327k
    e.pad_file = bool(file_flags & file_storage::flag_pad_file);
738
327k
    e.hidden_attribute = bool(file_flags & file_storage::flag_hidden);
739
327k
    e.executable_attribute = bool(file_flags & file_storage::flag_executable);
740
327k
    e.symlink_attribute = bool(file_flags & file_storage::flag_symlink);
741
327k
    e.root = root_hash;
742
743
327k
    if (filehash)
744
861
    {
745
861
      if (m_file_hashes.size() < m_files.size()) m_file_hashes.resize(m_files.size());
746
861
      m_file_hashes[last_file()] = filehash;
747
861
    }
748
327k
    if (!symlink_path.empty()
749
327k
      && m_symlinks.size() < aux::file_entry::not_a_symlink - 1)
750
141k
    {
751
141k
      e.symlink_index = m_symlinks.size();
752
141k
      m_symlinks.emplace_back(symlink_path.to_string());
753
141k
    }
754
185k
    else
755
185k
    {
756
185k
      e.symlink_attribute = false;
757
185k
    }
758
327k
    if (mtime)
759
1.43k
    {
760
1.43k
      if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size());
761
1.43k
      m_mtime[last_file()] = std::time_t(mtime);
762
1.43k
    }
763
764
327k
    m_total_size += e.size;
765
766
    // when making v2 torrents, pad the end of each file (if necessary) to
767
    // ensure it ends on a piece boundary.
768
    // we do this at the end of files rather in-front of files to conform to
769
    // the BEP52 reference implementation
770
327k
    if (m_v2 && (m_total_size % piece_length()) != 0)
771
0
    {
772
0
      auto const pad_size = piece_length() - (m_total_size % piece_length());
773
0
      TORRENT_ASSERT(int(pad_size) != piece_length());
774
0
      TORRENT_ASSERT(int(pad_size) > 0);
775
0
      if (m_total_size > max_file_offset - pad_size)
776
0
      {
777
0
        ec = make_error_code(errors::torrent_invalid_length);
778
0
        return;
779
0
      }
780
781
0
      m_files.emplace_back();
782
      // e is invalid from here down!
783
0
      auto& pad = m_files.back();
784
0
      pad.size = static_cast<std::uint64_t>(pad_size);
785
0
      TORRENT_ASSERT(m_total_size <= max_file_offset);
786
0
      TORRENT_ASSERT(m_total_size > 0);
787
0
      pad.offset = static_cast<std::uint64_t>(m_total_size);
788
0
      pad.path_index = get_or_add_path(".pad");
789
0
      char name[30];
790
0
      std::snprintf(name, sizeof(name), "%" PRIu64
791
0
        , pad.size);
792
0
      pad.set_name(name);
793
0
      pad.pad_file = true;
794
0
      m_total_size += pad_size;
795
0
    }
796
327k
  }
797
798
  // this is here for backwards compatibility with hybrid torrents created
799
  // with libtorrent 2.0.0-2.0.7, which would not add tail-padding
800
  void file_storage::remove_tail_padding()
801
0
  {
802
0
    file_index_t f = end_file();
803
0
    while (f > file_index_t{0})
804
0
    {
805
0
      --f;
806
      // empty files and symlinks are skipped
807
0
      if (file_size(f) == 0) continue;
808
0
      if (pad_file_at(f))
809
0
      {
810
0
        m_total_size -= file_size(f);
811
0
        m_files.erase(m_files.begin() + int(f));
812
0
        while (f < end_file())
813
0
        {
814
0
          m_files[f].offset = static_cast<std::uint64_t>(m_total_size);
815
0
          TORRENT_ASSERT(m_files[f].size == 0);
816
0
          ++f;
817
0
        }
818
0
      }
819
      // if the last non-empty file isn't a pad file, don't do anything
820
0
      return;
821
0
    }
822
    // nothing found
823
0
  }
824
825
  sha1_hash file_storage::hash(file_index_t const index) const
826
0
  {
827
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
828
0
    if (index >= m_file_hashes.end_index()) return sha1_hash();
829
0
    return sha1_hash(m_file_hashes[index]);
830
0
  }
831
832
  sha256_hash file_storage::root(file_index_t const index) const
833
291
  {
834
291
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
835
291
    if (m_files[index].root == nullptr) return sha256_hash();
836
291
    return sha256_hash(m_files[index].root);
837
291
  }
838
839
  char const* file_storage::root_ptr(file_index_t const index) const
840
1.88k
  {
841
1.88k
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
842
1.88k
    return m_files[index].root;
843
1.88k
  }
844
845
  std::string file_storage::symlink(file_index_t const index) const
846
0
  {
847
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
848
0
    aux::file_entry const& fe = m_files[index];
849
0
    if (fe.symlink_index == aux::file_entry::not_a_symlink)
850
0
      return {};
851
852
0
    TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size()));
853
854
0
    auto const& link = m_symlinks[fe.symlink_index];
855
856
0
    std::string ret;
857
0
    ret.reserve(m_name.size() + link.size() + 1);
858
0
    ret.assign(m_name);
859
0
    append_path(ret, link);
860
0
    return ret;
861
0
  }
862
863
  std::string const& file_storage::internal_symlink(file_index_t const index) const
864
0
  {
865
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
866
0
    aux::file_entry const& fe = m_files[index];
867
0
    TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size()));
868
869
0
    return m_symlinks[fe.symlink_index];
870
0
  }
871
872
  std::time_t file_storage::mtime(file_index_t const index) const
873
0
  {
874
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
875
0
    if (index >= m_mtime.end_index()) return 0;
876
0
    return m_mtime[index];
877
0
  }
878
879
namespace {
880
881
    template <class CRC>
882
    void process_string_lowercase(CRC& crc, string_view str)
883
216k
    {
884
216k
      for (char const c : str)
885
23.1M
        crc.process_byte(to_lower(c) & 0xff);
886
216k
    }
887
888
    template <class CRC>
889
    void process_path_lowercase(
890
      std::unordered_set<std::uint32_t>& table
891
      , CRC crc, string_view str)
892
11.6k
    {
893
11.6k
      if (str.empty()) return;
894
10.4k
      for (char const c : str)
895
12.6M
      {
896
12.6M
        if (c == TORRENT_SEPARATOR)
897
6.11M
          table.insert(crc.checksum());
898
12.6M
        crc.process_byte(to_lower(c) & 0xff);
899
12.6M
      }
900
10.4k
      table.insert(crc.checksum());
901
10.4k
    }
902
  }
903
904
  void file_storage::all_path_hashes(
905
    std::unordered_set<std::uint32_t>& table) const
906
2.70k
  {
907
2.70k
    boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
908
909
2.70k
    if (!m_name.empty())
910
2.70k
    {
911
2.70k
      process_string_lowercase(crc, m_name);
912
2.70k
      TORRENT_ASSERT(m_name[m_name.size() - 1] != TORRENT_SEPARATOR);
913
2.70k
      crc.process_byte(TORRENT_SEPARATOR);
914
2.70k
    }
915
916
2.70k
    for (auto const& p : m_paths)
917
11.6k
      process_path_lowercase(table, crc, p);
918
2.70k
  }
919
920
  std::uint32_t file_storage::file_path_hash(file_index_t const index
921
    , std::string const& save_path) const
922
98.0k
  {
923
98.0k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
924
98.0k
    aux::file_entry const& fe = m_files[index];
925
926
98.0k
    boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
927
928
98.0k
    if (fe.path_index == aux::file_entry::path_is_absolute)
929
0
    {
930
0
      process_string_lowercase(crc, fe.filename());
931
0
    }
932
98.0k
    else if (fe.path_index == aux::file_entry::no_path)
933
983
    {
934
983
      if (!save_path.empty())
935
0
      {
936
0
        process_string_lowercase(crc, save_path);
937
0
        TORRENT_ASSERT(save_path[save_path.size() - 1] != TORRENT_SEPARATOR);
938
0
        crc.process_byte(TORRENT_SEPARATOR);
939
0
      }
940
983
      process_string_lowercase(crc, fe.filename());
941
983
    }
942
97.1k
    else if (fe.no_root_dir)
943
44.0k
    {
944
44.0k
      if (!save_path.empty())
945
0
      {
946
0
        process_string_lowercase(crc, save_path);
947
0
        TORRENT_ASSERT(save_path[save_path.size() - 1] != TORRENT_SEPARATOR);
948
0
        crc.process_byte(TORRENT_SEPARATOR);
949
0
      }
950
44.0k
      std::string const& p = m_paths[fe.path_index];
951
44.0k
      if (!p.empty())
952
44.0k
      {
953
44.0k
        process_string_lowercase(crc, p);
954
44.0k
        TORRENT_ASSERT(p[p.size() - 1] != TORRENT_SEPARATOR);
955
44.0k
        crc.process_byte(TORRENT_SEPARATOR);
956
44.0k
      }
957
44.0k
      process_string_lowercase(crc, fe.filename());
958
44.0k
    }
959
53.0k
    else
960
53.0k
    {
961
53.0k
      if (!save_path.empty())
962
0
      {
963
0
        process_string_lowercase(crc, save_path);
964
0
        TORRENT_ASSERT(save_path[save_path.size() - 1] != TORRENT_SEPARATOR);
965
0
        crc.process_byte(TORRENT_SEPARATOR);
966
0
      }
967
53.0k
      process_string_lowercase(crc, m_name);
968
53.0k
      TORRENT_ASSERT(m_name.size() > 0);
969
53.0k
      TORRENT_ASSERT(m_name[m_name.size() - 1] != TORRENT_SEPARATOR);
970
53.0k
      crc.process_byte(TORRENT_SEPARATOR);
971
972
53.0k
      std::string const& p = m_paths[fe.path_index];
973
53.0k
      if (!p.empty())
974
18.4k
      {
975
18.4k
        process_string_lowercase(crc, p);
976
18.4k
        TORRENT_ASSERT(p.size() > 0);
977
18.4k
        TORRENT_ASSERT(p[p.size() - 1] != TORRENT_SEPARATOR);
978
18.4k
        crc.process_byte(TORRENT_SEPARATOR);
979
18.4k
      }
980
53.0k
      process_string_lowercase(crc, fe.filename());
981
53.0k
    }
982
983
98.0k
    return crc.checksum();
984
98.0k
  }
985
986
  std::string file_storage::file_path(file_index_t const index, std::string const& save_path) const
987
169k
  {
988
169k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
989
169k
    aux::file_entry const& fe = m_files[index];
990
991
169k
    std::string ret;
992
993
169k
    if (fe.path_index == aux::file_entry::path_is_absolute)
994
0
    {
995
0
      ret = fe.filename().to_string();
996
0
    }
997
169k
    else if (fe.path_index == aux::file_entry::no_path)
998
19
    {
999
19
      ret.reserve(save_path.size() + fe.filename().size() + 1);
1000
19
      ret.assign(save_path);
1001
19
      append_path(ret, fe.filename());
1002
19
    }
1003
169k
    else if (fe.no_root_dir)
1004
76.7k
    {
1005
76.7k
      std::string const& p = m_paths[fe.path_index];
1006
1007
76.7k
      ret.reserve(save_path.size() + p.size() + fe.filename().size() + 2);
1008
76.7k
      ret.assign(save_path);
1009
76.7k
      append_path(ret, p);
1010
76.7k
      append_path(ret, fe.filename());
1011
76.7k
    }
1012
92.8k
    else
1013
92.8k
    {
1014
92.8k
      std::string const& p = m_paths[fe.path_index];
1015
1016
92.8k
      ret.reserve(save_path.size() + m_name.size() + p.size() + fe.filename().size() + 3);
1017
92.8k
      ret.assign(save_path);
1018
92.8k
      append_path(ret, m_name);
1019
92.8k
      append_path(ret, p);
1020
92.8k
      append_path(ret, fe.filename());
1021
92.8k
    }
1022
1023
    // a single return statement, just to make NRVO more likely to kick in
1024
169k
    return ret;
1025
169k
  }
1026
1027
  std::string file_storage::internal_file_path(file_index_t const index) const
1028
529k
  {
1029
529k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1030
529k
    aux::file_entry const& fe = m_files[index];
1031
1032
529k
    if (fe.path_index != aux::file_entry::path_is_absolute
1033
529k
      && fe.path_index != aux::file_entry::no_path)
1034
529k
    {
1035
529k
      std::string ret;
1036
529k
      std::string const& p = m_paths[fe.path_index];
1037
529k
      ret.reserve(p.size() + fe.filename().size() + 2);
1038
529k
      append_path(ret, p);
1039
529k
      append_path(ret, fe.filename());
1040
529k
      return ret;
1041
529k
    }
1042
0
    else
1043
0
    {
1044
0
      return fe.filename().to_string();
1045
0
    }
1046
529k
  }
1047
1048
  string_view file_storage::file_name(file_index_t const index) const
1049
0
  {
1050
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1051
0
    aux::file_entry const& fe = m_files[index];
1052
0
    return fe.filename();
1053
0
  }
1054
1055
  std::int64_t file_storage::file_size(file_index_t const index) const
1056
179k
  {
1057
179k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1058
179k
    return m_files[index].size;
1059
179k
  }
1060
1061
  bool file_storage::pad_file_at(file_index_t const index) const
1062
10.9k
  {
1063
10.9k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1064
10.9k
    return m_files[index].pad_file;
1065
10.9k
  }
1066
1067
  std::int64_t file_storage::file_offset(file_index_t const index) const
1068
17.7k
  {
1069
17.7k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1070
17.7k
    return m_files[index].offset;
1071
17.7k
  }
1072
1073
  int file_storage::file_num_pieces(file_index_t const index) const
1074
552
  {
1075
552
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1076
552
    TORRENT_ASSERT_PRECOND(m_piece_length > 0);
1077
552
    auto const& f = m_files[index];
1078
1079
552
    if (f.size == 0) return 0;
1080
1081
    // this function only works for v2 torrents, where files are guaranteed to
1082
    // be aligned to pieces
1083
552
    TORRENT_ASSERT(f.pad_file == false);
1084
552
    TORRENT_ASSERT((static_cast<std::int64_t>(f.offset) % m_piece_length) == 0);
1085
552
    return aux::numeric_cast<int>(
1086
552
      (static_cast<std::int64_t>(f.size) + m_piece_length - 1) / m_piece_length);
1087
552
  }
1088
1089
  index_range<piece_index_t::diff_type> file_storage::file_piece_range(file_index_t const file) const
1090
2
  {
1091
2
    return {piece_index_t::diff_type{0}, piece_index_t::diff_type{file_num_pieces(file)}};
1092
2
  }
1093
1094
  int file_storage::file_num_blocks(file_index_t const index) const
1095
3.03k
  {
1096
3.03k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1097
3.03k
    TORRENT_ASSERT_PRECOND(m_piece_length > 0);
1098
3.03k
    auto const& f = m_files[index];
1099
1100
3.03k
    if (f.size == 0) return 0;
1101
1102
    // this function only works for v2 torrents, where files are guaranteed to
1103
    // be aligned to pieces
1104
3.03k
    TORRENT_ASSERT(f.pad_file == false);
1105
3.03k
    TORRENT_ASSERT((static_cast<std::int64_t>(f.offset) % m_piece_length) == 0);
1106
3.03k
    return int((f.size + default_block_size - 1) / default_block_size);
1107
3.03k
  }
1108
1109
  int file_storage::file_first_piece_node(file_index_t index) const
1110
0
  {
1111
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1112
0
    TORRENT_ASSERT_PRECOND(m_piece_length > 0);
1113
0
    int const piece_layer_size = merkle_num_leafs(file_num_pieces(index));
1114
0
    return merkle_num_nodes(piece_layer_size) - piece_layer_size;
1115
0
  }
1116
1117
  int file_storage::file_first_block_node(file_index_t index) const
1118
0
  {
1119
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1120
0
    TORRENT_ASSERT_PRECOND(m_piece_length > 0);
1121
0
    int const leaf_layer_size = merkle_num_leafs(file_num_blocks(index));
1122
0
    return merkle_num_nodes(leaf_layer_size) - leaf_layer_size;
1123
0
  }
1124
1125
  file_flags_t file_storage::file_flags(file_index_t const index) const
1126
477k
  {
1127
477k
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1128
477k
    aux::file_entry const& fe = m_files[index];
1129
477k
    return (fe.pad_file ? file_storage::flag_pad_file : file_flags_t{})
1130
477k
      | (fe.hidden_attribute ? file_storage::flag_hidden : file_flags_t{})
1131
477k
      | (fe.executable_attribute ? file_storage::flag_executable : file_flags_t{})
1132
477k
      | (fe.symlink_attribute ? file_storage::flag_symlink : file_flags_t{});
1133
477k
  }
1134
1135
  bool file_storage::file_absolute_path(file_index_t const index) const
1136
0
  {
1137
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t(0) && index < end_file());
1138
0
    aux::file_entry const& fe = m_files[index];
1139
0
    return fe.path_index == aux::file_entry::path_is_absolute;
1140
0
  }
1141
1142
#if TORRENT_ABI_VERSION == 1
1143
  sha1_hash file_storage::hash(aux::file_entry const& fe) const
1144
0
  {
1145
0
    int const index = int(&fe - &m_files.front());
1146
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
1147
0
    if (index >= int(m_file_hashes.size())) return sha1_hash(nullptr);
1148
0
    return sha1_hash(m_file_hashes[index]);
1149
0
  }
1150
1151
  std::string file_storage::symlink(aux::file_entry const& fe) const
1152
0
  {
1153
0
    TORRENT_ASSERT_PRECOND(fe.symlink_index < int(m_symlinks.size()));
1154
0
    return m_symlinks[fe.symlink_index];
1155
0
  }
1156
1157
  std::time_t file_storage::mtime(aux::file_entry const& fe) const
1158
0
  {
1159
0
    int const index = int(&fe - &m_files.front());
1160
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
1161
0
    if (index >= int(m_mtime.size())) return 0;
1162
0
    return m_mtime[index];
1163
0
  }
1164
1165
  int file_storage::file_index(aux::file_entry const& fe) const
1166
0
  {
1167
0
    int const index = int(&fe - &m_files.front());
1168
0
    TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size()));
1169
0
    return index;
1170
0
  }
1171
1172
  std::string file_storage::file_path(aux::file_entry const& fe
1173
    , std::string const& save_path) const
1174
0
  {
1175
0
    int const index = int(&fe - &m_files.front());
1176
0
    TORRENT_ASSERT_PRECOND(index >= file_index_t{} && index < end_file());
1177
0
    return file_path(index, save_path);
1178
0
  }
1179
1180
  std::string file_storage::file_name(aux::file_entry const& fe) const
1181
0
  {
1182
0
    return fe.filename().to_string();
1183
0
  }
1184
1185
  std::int64_t file_storage::file_size(aux::file_entry const& fe) const
1186
0
  {
1187
0
    return fe.size;
1188
0
  }
1189
1190
  bool file_storage::pad_file_at(aux::file_entry const& fe) const
1191
0
  {
1192
0
    return fe.pad_file;
1193
0
  }
1194
1195
  std::int64_t file_storage::file_offset(aux::file_entry const& fe) const
1196
0
  {
1197
0
    return fe.offset;
1198
0
  }
1199
1200
  file_entry file_storage::at(file_storage::iterator i) const
1201
0
  { return at_deprecated(int(i - m_files.begin())); }
1202
#endif // TORRENT_ABI_VERSION
1203
1204
  void file_storage::swap(file_storage& ti) noexcept
1205
2.70k
  {
1206
2.70k
    using std::swap;
1207
2.70k
    swap(ti.m_files, m_files);
1208
2.70k
    swap(ti.m_file_hashes, m_file_hashes);
1209
2.70k
    swap(ti.m_symlinks, m_symlinks);
1210
2.70k
    swap(ti.m_mtime, m_mtime);
1211
2.70k
    swap(ti.m_paths, m_paths);
1212
2.70k
    swap(ti.m_name, m_name);
1213
2.70k
    swap(ti.m_total_size, m_total_size);
1214
2.70k
    swap(ti.m_num_pieces, m_num_pieces);
1215
2.70k
    swap(ti.m_piece_length, m_piece_length);
1216
2.70k
    swap(ti.m_v2, m_v2);
1217
2.70k
  }
1218
1219
  void file_storage::canonicalize()
1220
0
  {
1221
0
    canonicalize_impl(false);
1222
0
  }
1223
1224
  void file_storage::canonicalize_impl(bool const backwards_compatible)
1225
4
  {
1226
4
    TORRENT_ASSERT(piece_length() >= 16 * 1024);
1227
1228
    // use this vector to track the new ordering of files
1229
    // this allows the use of STL algorithms despite them
1230
    // not supporting a custom swap functor
1231
4
    aux::vector<file_index_t, file_index_t> new_order(end_file());
1232
4
    for (auto i : file_range())
1233
4
      new_order[i] = i;
1234
1235
    // remove any existing pad files
1236
4
    {
1237
4
      auto pad_begin = std::partition(new_order.begin(), new_order.end()
1238
4
        , [this](file_index_t i) { return !m_files[i].pad_file; });
1239
4
      new_order.erase(pad_begin, new_order.end());
1240
4
    }
1241
1242
    // TODO: this would be more efficient if m_paths was sorted first, such
1243
    // that a lower path index always meant sorted-before
1244
1245
    // sort files by path/name
1246
4
    std::sort(new_order.begin(), new_order.end()
1247
4
      , [this](file_index_t l, file_index_t r)
1248
4
    {
1249
      // assuming m_paths are unique!
1250
0
      auto const& lf = m_files[l];
1251
0
      auto const& rf = m_files[r];
1252
0
      if (lf.path_index != rf.path_index)
1253
0
      {
1254
0
        int const ret = path_compare(m_paths[lf.path_index], lf.filename()
1255
0
          , m_paths[rf.path_index], rf.filename());
1256
0
        if (ret != 0) return ret < 0;
1257
0
      }
1258
0
      return lf.filename() < rf.filename();
1259
0
    });
1260
1261
4
    aux::vector<aux::file_entry, file_index_t> new_files;
1262
4
    aux::vector<char const*, file_index_t> new_file_hashes;
1263
4
    aux::vector<std::time_t, file_index_t> new_mtime;
1264
1265
    // reserve enough space for the worst case after padding
1266
4
    new_files.reserve(new_order.size() * 2 - 1);
1267
4
    if (!m_file_hashes.empty())
1268
0
      new_file_hashes.reserve(new_order.size() * 2 - 1);
1269
4
    if (!m_mtime.empty())
1270
0
      new_mtime.reserve(new_order.size() * 2 - 1);
1271
1272
    // re-compute offsets and insert pad files as necessary
1273
4
    std::int64_t off = 0;
1274
1275
4
    auto add_pad_file = [&](file_index_t const i) {
1276
0
      if ((off % piece_length()) != 0 && m_files[i].size > 0)
1277
0
      {
1278
0
        auto const pad_size = piece_length() - (off % piece_length());
1279
0
        TORRENT_ASSERT(pad_size < piece_length());
1280
0
        TORRENT_ASSERT(pad_size > 0);
1281
0
        new_files.emplace_back();
1282
0
        auto& pad = new_files.back();
1283
0
        pad.size = static_cast<std::uint64_t>(pad_size);
1284
0
        pad.offset = static_cast<std::uint64_t>(off);
1285
0
        off += pad_size;
1286
0
        pad.path_index = get_or_add_path(".pad");
1287
0
        char name[30];
1288
0
        std::snprintf(name, sizeof(name), "%" PRIu64, pad.size);
1289
0
        pad.set_name(name);
1290
0
        pad.pad_file = true;
1291
1292
0
        if (!m_file_hashes.empty())
1293
0
          new_file_hashes.push_back(nullptr);
1294
0
        if (!m_mtime.empty())
1295
0
          new_mtime.push_back(0);
1296
0
      }
1297
0
    };
1298
1299
4
    for (file_index_t i : new_order)
1300
4
    {
1301
4
      if (backwards_compatible)
1302
0
        add_pad_file(i);
1303
1304
4
      TORRENT_ASSERT(!m_files[i].pad_file);
1305
4
      new_files.emplace_back(std::move(m_files[i]));
1306
1307
4
      if (i < m_file_hashes.end_index())
1308
0
        new_file_hashes.push_back(m_file_hashes[i]);
1309
4
      else if (!m_file_hashes.empty())
1310
0
        new_file_hashes.push_back(nullptr);
1311
1312
4
      if (i < m_mtime.end_index())
1313
0
        new_mtime.push_back(m_mtime[i]);
1314
4
      else if (!m_mtime.empty())
1315
0
        new_mtime.push_back(0);
1316
1317
4
      auto& file = new_files.back();
1318
4
      TORRENT_ASSERT(off < max_file_offset - static_cast<std::int64_t>(file.size));
1319
4
      file.offset = static_cast<std::uint64_t>(off);
1320
4
      off += file.size;
1321
1322
      // we don't pad single-file torrents. That would make it impossible
1323
      // to have single-file hybrid torrents.
1324
4
      if (!backwards_compatible && num_files() > 1)
1325
0
        add_pad_file(i);
1326
4
    }
1327
1328
4
    m_files = std::move(new_files);
1329
4
    m_file_hashes = std::move(new_file_hashes);
1330
4
    m_mtime = std::move(new_mtime);
1331
1332
4
    m_total_size = off;
1333
4
  }
1334
1335
  void file_storage::sanitize_symlinks()
1336
4.82k
  {
1337
    // symlinks are unusual, this function is optimized assuming there are no
1338
    // symbolic links in the torrent. If we find one symbolic link, we'll
1339
    // build the hash table of files it's allowed to refer to, but don't pay
1340
    // that price up-front.
1341
4.82k
    std::unordered_map<std::string, file_index_t> file_map;
1342
4.82k
    bool file_map_initialized = false;
1343
1344
    // lazily instantiated set of all valid directories a symlink may point to
1345
    // TODO: in C++17 this could be string_view
1346
4.82k
    std::unordered_set<std::string> dir_map;
1347
4.82k
    bool dir_map_initialized = false;
1348
1349
    // symbolic links that points to directories
1350
4.82k
    std::unordered_map<std::string, std::string> dir_links;
1351
1352
    // we validate symlinks in (potentially) 2 passes over the files.
1353
    // remaining symlinks to validate after the first pass
1354
4.82k
    std::vector<file_index_t> symlinks_to_validate;
1355
1356
4.82k
    for (auto const i : file_range())
1357
323k
    {
1358
323k
      if (!(file_flags(i) & file_storage::flag_symlink)) continue;
1359
1360
141k
      if (!file_map_initialized)
1361
2.90k
      {
1362
2.90k
        for (auto const j : file_range())
1363
303k
          file_map.insert({internal_file_path(j), j});
1364
2.90k
        file_map_initialized = true;
1365
2.90k
      }
1366
1367
141k
      aux::file_entry const& fe = m_files[i];
1368
141k
      TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size()));
1369
1370
      // symlink targets are only allowed to point to files or directories in
1371
      // this torrent.
1372
141k
      {
1373
141k
        std::string target = m_symlinks[fe.symlink_index];
1374
1375
141k
        if (is_complete(target))
1376
0
        {
1377
          // a symlink target is not allowed to be an absolute path, ever
1378
          // this symlink is invalid, make it point to itself
1379
0
          m_symlinks[fe.symlink_index] = internal_file_path(i);
1380
0
          continue;
1381
0
        }
1382
1383
141k
        auto const iter = file_map.find(target);
1384
141k
        if (iter != file_map.end())
1385
6.89k
        {
1386
6.89k
          m_symlinks[fe.symlink_index] = target;
1387
6.89k
          if (file_flags(iter->second) & file_storage::flag_symlink)
1388
3.94k
          {
1389
            // we don't know whether this symlink is a file or a
1390
            // directory, so make the conservative assumption that it's a
1391
            // directory
1392
3.94k
            dir_links[internal_file_path(i)] = target;
1393
3.94k
          }
1394
6.89k
          continue;
1395
6.89k
        }
1396
1397
        // it may point to a directory that doesn't have any files (but only
1398
        // other directories), in which case it won't show up in m_paths
1399
134k
        if (!dir_map_initialized)
1400
2.79k
        {
1401
2.79k
          for (auto const& p : m_paths)
1402
2.99M
            for (string_view pv = p; !pv.empty(); pv = rsplit_path(pv).first)
1403
2.97M
              dir_map.insert(pv.to_string());
1404
2.79k
          dir_map_initialized = true;
1405
2.79k
        }
1406
1407
134k
        if (dir_map.count(target))
1408
30.1k
        {
1409
          // it points to a sub directory within the torrent, that's OK
1410
30.1k
          m_symlinks[fe.symlink_index] = target;
1411
30.1k
          dir_links[internal_file_path(i)] = target;
1412
30.1k
          continue;
1413
30.1k
        }
1414
1415
134k
      }
1416
1417
      // for backwards compatibility, allow paths relative to the link as
1418
      // well
1419
104k
      if (fe.path_index < aux::file_entry::path_is_absolute)
1420
104k
      {
1421
104k
        std::string target = m_paths[fe.path_index];
1422
104k
        append_path(target, m_symlinks[fe.symlink_index]);
1423
        // if it points to a directory, that's OK
1424
104k
        auto const it = std::find(m_paths.begin(), m_paths.end(), target);
1425
104k
        if (it != m_paths.end())
1426
2.13k
        {
1427
2.13k
          m_symlinks[fe.symlink_index] = *it;
1428
2.13k
          dir_links[internal_file_path(i)] = *it;
1429
2.13k
          continue;
1430
2.13k
        }
1431
1432
102k
        if (dir_map.count(target))
1433
4.12k
        {
1434
          // it points to a sub directory within the torrent, that's OK
1435
4.12k
          m_symlinks[fe.symlink_index] = target;
1436
4.12k
          dir_links[internal_file_path(i)] = target;
1437
4.12k
          continue;
1438
4.12k
        }
1439
1440
98.1k
        auto const iter = file_map.find(target);
1441
98.1k
        if (iter != file_map.end())
1442
5.52k
        {
1443
5.52k
          m_symlinks[fe.symlink_index] = target;
1444
5.52k
          if (file_flags(iter->second) & file_storage::flag_symlink)
1445
4.98k
          {
1446
            // we don't know whether this symlink is a file or a
1447
            // directory, so make the conservative assumption that it's a
1448
            // directory
1449
4.98k
            dir_links[internal_file_path(i)] = target;
1450
4.98k
          }
1451
5.52k
          continue;
1452
5.52k
        }
1453
98.1k
      }
1454
1455
      // we don't know whether this symlink is a file or a
1456
      // directory, so make the conservative assumption that it's a
1457
      // directory
1458
92.6k
      dir_links[internal_file_path(i)] = m_symlinks[fe.symlink_index];
1459
92.6k
      symlinks_to_validate.push_back(i);
1460
92.6k
    }
1461
1462
    // in case there were some "complex" symlinks, we nee a second pass to
1463
    // validate those. For example, symlinks whose target rely on other
1464
    // symlinks
1465
4.82k
    for (auto const i : symlinks_to_validate)
1466
92.6k
    {
1467
92.6k
      aux::file_entry const& fe = m_files[i];
1468
92.6k
      TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size()));
1469
1470
92.6k
      std::string target = m_symlinks[fe.symlink_index];
1471
1472
      // to avoid getting stuck in an infinite loop, we only allow traversing
1473
      // a symlink once
1474
92.6k
      std::set<std::string> traversed;
1475
1476
      // this is where we check every path element for existence. If it's not
1477
      // among the concrete paths, it may be a symlink, which is also OK
1478
      // note that we won't iterate through this for the last step, where the
1479
      // filename is included. The filename is validated after the loop
1480
92.6k
      for (string_view branch = lsplit_path(target).first;
1481
1.22M
        branch.size() < target.size();
1482
1.12M
        branch = lsplit_path(target, branch.size() + 1).first)
1483
1.18M
      {
1484
1.18M
        auto branch_temp = branch.to_string();
1485
        // this is a concrete directory
1486
1.18M
        if (dir_map.count(branch_temp)) continue;
1487
1488
104k
        auto const iter = dir_links.find(branch_temp);
1489
104k
        if (iter == dir_links.end()) goto failed;
1490
61.6k
        if (traversed.count(branch_temp)) goto failed;
1491
42.2k
        traversed.insert(std::move(branch_temp));
1492
1493
        // this path element is a symlink. substitute the branch so far by
1494
        // the link target
1495
42.2k
        target = combine_path(iter->second, target.substr(branch.size() + 1));
1496
1497
        // start over with the new (concrete) path
1498
42.2k
        branch = {};
1499
42.2k
      }
1500
1501
      // the final (resolved) target must be a valid file
1502
      // or directory
1503
30.6k
      if (file_map.count(target) == 0
1504
30.6k
        && dir_map.count(target) == 0) goto failed;
1505
1506
      // this is OK
1507
5.15k
      continue;
1508
1509
87.4k
failed:
1510
1511
      // this symlink is invalid, make it point to itself
1512
87.4k
      m_symlinks[fe.symlink_index] = internal_file_path(i);
1513
87.4k
    }
1514
4.82k
  }
1515
1516
namespace aux {
1517
1518
  bool files_compatible(file_storage const& lhs, file_storage const& rhs)
1519
4
  {
1520
4
    if (lhs.num_files() != rhs.num_files())
1521
0
      return false;
1522
1523
4
    if (lhs.total_size() != rhs.total_size())
1524
0
      return false;
1525
1526
4
    if (lhs.piece_length() != rhs.piece_length())
1527
0
      return false;
1528
1529
    // for compatibility, only non-empty and non-pad files matter.
1530
    // those files all need to match in index, name, size and offset
1531
4
    for (file_index_t i : lhs.file_range())
1532
4
    {
1533
4
      bool const lhs_relevant = !lhs.pad_file_at(i) && lhs.file_size(i) > 0;
1534
4
      bool const rhs_relevant = !rhs.pad_file_at(i) && rhs.file_size(i) > 0;
1535
1536
4
      if (lhs_relevant != rhs_relevant)
1537
0
        return false;
1538
1539
4
      if (!lhs_relevant) continue;
1540
1541
      // we deliberately ignore file attributes like "hidden",
1542
      // "executable" and mtime here. It's not critical they match
1543
4
      if (lhs.pad_file_at(i) != rhs.pad_file_at(i)
1544
4
        || lhs.file_size(i) != rhs.file_size(i)
1545
4
        || lhs.file_path(i) != rhs.file_path(i)
1546
4
        || lhs.file_offset(i) != rhs.file_offset(i))
1547
0
      {
1548
0
        return false;
1549
0
      }
1550
1551
4
      if ((lhs.file_flags(i) & file_storage::flag_symlink)
1552
4
        && lhs.symlink(i) != rhs.symlink(i))
1553
0
      {
1554
0
        return false;
1555
0
      }
1556
4
    }
1557
4
    return true;
1558
4
  }
1559
1560
  std::tuple<piece_index_t, piece_index_t>
1561
  file_piece_range_exclusive(file_storage const& fs, file_index_t const file)
1562
0
  {
1563
0
    peer_request const range = fs.map_file(file, 0, 1);
1564
0
    std::int64_t const file_size = fs.file_size(file);
1565
0
    std::int64_t const piece_size = fs.piece_length();
1566
0
    piece_index_t const begin_piece = range.start == 0 ? range.piece : piece_index_t(static_cast<int>(range.piece) + 1);
1567
    // the last piece is potentially smaller than the other pieces, so the
1568
    // generic logic doesn't really work. If this file is the last file, the
1569
    // last piece doesn't overlap with any other file and it's entirely
1570
    // contained within the last file.
1571
0
    piece_index_t const end_piece = (file == file_index_t(fs.num_files() - 1))
1572
0
      ? piece_index_t(fs.num_pieces())
1573
0
      : piece_index_t(int((static_cast<int>(range.piece) * piece_size + range.start + file_size + 1) / piece_size));
1574
0
    return std::make_tuple(begin_piece, end_piece);
1575
0
  }
1576
1577
  std::tuple<piece_index_t, piece_index_t>
1578
  file_piece_range_inclusive(file_storage const& fs, file_index_t const file)
1579
122
  {
1580
122
    peer_request const range = fs.map_file(file, 0, 1);
1581
122
    std::int64_t const file_size = fs.file_size(file);
1582
122
    std::int64_t const piece_size = fs.piece_length();
1583
122
    piece_index_t const end_piece = piece_index_t(int((static_cast<int>(range.piece)
1584
122
      * piece_size + range.start + file_size - 1) / piece_size + 1));
1585
122
    return std::make_tuple(range.piece, end_piece);
1586
122
  }
1587
1588
  int calc_num_pieces(file_storage const& fs)
1589
4
  {
1590
4
    return aux::numeric_cast<int>(
1591
4
      (fs.total_size() + fs.piece_length() - 1) / fs.piece_length());
1592
4
  }
1593
1594
  std::int64_t size_on_disk(file_storage const& fs)
1595
1.88k
  {
1596
1.88k
    std::int64_t ret = 0;
1597
1.88k
    for (file_index_t i : fs.file_range())
1598
1.88k
    {
1599
1.88k
      if (fs.pad_file_at(i)) continue;
1600
1.88k
      ret += fs.file_size(i);
1601
1.88k
    }
1602
1.88k
    return ret;
1603
1.88k
  }
1604
1605
  } // namespace aux
1606
}