Coverage Report

Created: 2026-03-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/file.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2018, d-komarov
4
Copyright (c) 2004-2005, 2007-2020, 2022, Arvid Norberg
5
Copyright (c) 2016, Alden Torres
6
Copyright (c) 2016-2017, Andrei Kurushin
7
Copyright (c) 2016-2017, Steven Siloti
8
Copyright (c) 2020, Tiger Wang
9
All rights reserved.
10
11
Redistribution and use in source and binary forms, with or without
12
modification, are permitted provided that the following conditions
13
are met:
14
15
    * Redistributions of source code must retain the above copyright
16
      notice, this list of conditions and the following disclaimer.
17
    * Redistributions in binary form must reproduce the above copyright
18
      notice, this list of conditions and the following disclaimer in
19
      the documentation and/or other materials provided with the distribution.
20
    * Neither the name of the author nor the names of its
21
      contributors may be used to endorse or promote products derived
22
      from this software without specific prior written permission.
23
24
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
POSSIBILITY OF SUCH DAMAGE.
35
36
*/
37
38
#include "libtorrent/config.hpp"
39
#include "libtorrent/span.hpp"
40
#include <mutex> // for call_once
41
42
#ifdef __GNUC__
43
#pragma GCC diagnostic push
44
#pragma GCC diagnostic ignored "-Wunused-macros"
45
#endif
46
47
#ifdef __clang__
48
#pragma clang diagnostic push
49
#pragma clang diagnostic ignored "-Wunknown-pragmas"
50
#pragma clang diagnostic ignored "-Wunused-macros"
51
#pragma clang diagnostic ignored "-Wreserved-id-macro"
52
#endif
53
54
// on mingw this is necessary to enable 64-bit time_t, specifically used for
55
// the stat struct. Without this, modification times returned by stat may be
56
// incorrect and consistently fail resume data
57
#ifndef __MINGW_USE_VC2005_COMPAT
58
# define __MINGW_USE_VC2005_COMPAT
59
#endif
60
61
#ifdef __clang__
62
#pragma clang diagnostic pop
63
#endif
64
65
#ifdef __GNUC__
66
#pragma GCC diagnostic pop
67
#endif
68
69
#include "libtorrent/file.hpp"
70
#include "libtorrent/aux_/path.hpp" // for convert_to_native_path_string
71
#include "libtorrent/string_util.hpp"
72
#include <cstring>
73
74
#include "libtorrent/assert.hpp"
75
#include "libtorrent/aux_/throw.hpp"
76
#include "libtorrent/aux_/open_mode.hpp"
77
#include "libtorrent/error_code.hpp"
78
79
#include "libtorrent/aux_/disable_warnings_push.hpp"
80
81
#include <sys/stat.h>
82
83
#ifdef TORRENT_WINDOWS
84
// windows part
85
86
#include "libtorrent/aux_/win_util.hpp"
87
#include "libtorrent/aux_/scope_end.hpp"
88
89
#ifndef WIN32_LEAN_AND_MEAN
90
#define WIN32_LEAN_AND_MEAN
91
#endif
92
#include <windows.h>
93
#include <winioctl.h>
94
#include <sys/types.h>
95
#else
96
// posix part
97
98
#include <unistd.h>
99
#include <sys/types.h>
100
#include <cerrno>
101
#include <dirent.h>
102
103
#include <boost/asio/error.hpp> // for boost::asio::error::eof
104
105
#ifdef TORRENT_LINUX
106
// linux specifics
107
108
#include <linux/fs.h>
109
#include <sys/ioctl.h>
110
#ifdef TORRENT_ANDROID
111
#include <sys/syscall.h>
112
#endif
113
114
#endif
115
116
#endif // posix part
117
118
#include "libtorrent/aux_/disable_warnings_pop.hpp"
119
120
namespace libtorrent {
121
namespace aux {
122
123
#ifdef TORRENT_WINDOWS
124
  int pread_all(handle_type const fd
125
    , span<char> const buf
126
    , std::int64_t const offset
127
    , error_code& ec)
128
  {
129
    OVERLAPPED ol{};
130
    ol.Offset = offset & 0xffffffff;
131
    ol.OffsetHigh = offset >> 32;
132
    DWORD bytes_read = 0;
133
    if (ReadFile(fd, buf.data(), DWORD(buf.size()), &bytes_read, &ol) == FALSE)
134
    {
135
      ec = error_code(::GetLastError(), system_category());
136
      return -1;
137
    }
138
139
    return int(bytes_read);
140
  }
141
142
  int pwrite_all(handle_type const fd
143
    , span<char const> const buf
144
    , std::int64_t const offset
145
    , error_code& ec)
146
  {
147
    OVERLAPPED ol{};
148
    ol.Offset = offset & 0xffffffff;
149
    ol.OffsetHigh = offset >> 32;
150
    DWORD bytes_written = 0;
151
    if (WriteFile(fd, buf.data(), DWORD(buf.size()), &bytes_written, &ol) == FALSE)
152
    {
153
      ec = error_code(::GetLastError(), system_category());
154
      return -1;
155
    }
156
157
    return int(bytes_written);
158
  }
159
#else
160
161
  int pread_all(handle_type const handle
162
    , span<char> buf
163
    , std::int64_t file_offset
164
    , error_code& ec)
165
0
  {
166
0
    int ret = 0;
167
0
    do {
168
0
      auto const r = ::pread(handle, buf.data(), std::size_t(buf.size()), file_offset);
169
0
      if (r == 0)
170
0
      {
171
0
        ec = boost::asio::error::eof;
172
0
        return ret;
173
0
      }
174
0
      if (r < 0)
175
0
      {
176
0
        ec = error_code(errno, system_category());
177
0
        return ret;
178
0
      }
179
0
      ret += r;
180
0
      file_offset += r;
181
0
      buf = buf.subspan(r);
182
0
    } while (buf.size() > 0);
183
0
    return ret;
184
0
  }
185
186
  int pwrite_all(handle_type const handle
187
    , span<char const> buf
188
    , std::int64_t file_offset
189
    , error_code& ec)
190
0
  {
191
0
    int ret = 0;
192
0
    do {
193
0
      auto const r = ::pwrite(handle, buf.data(), std::size_t(buf.size()), file_offset);
194
0
      if (r == 0)
195
0
      {
196
0
        ec = boost::asio::error::eof;
197
0
        return ret;
198
0
      }
199
0
      if (r < 0)
200
0
      {
201
0
        ec = error_code(errno, system_category());
202
0
        return -1;
203
0
      }
204
0
      ret += r;
205
0
      file_offset += r;
206
0
      buf = buf.subspan(r);
207
0
    } while (buf.size() > 0);
208
0
    return ret;
209
0
  }
210
#endif
211
212
namespace {
213
#ifdef TORRENT_WINDOWS
214
  // returns true if the given file has any regions that are
215
  // sparse, i.e. not allocated.
216
  bool is_sparse(HANDLE file)
217
  {
218
    LARGE_INTEGER file_size;
219
    if (!GetFileSizeEx(file, &file_size))
220
      return false;
221
222
#ifndef FSCTL_QUERY_ALLOCATED_RANGES
223
typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
224
  LARGE_INTEGER FileOffset;
225
  LARGE_INTEGER Length;
226
} FILE_ALLOCATED_RANGE_BUFFER;
227
#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3)
228
#endif
229
    FILE_ALLOCATED_RANGE_BUFFER in;
230
    in.FileOffset.QuadPart = 0;
231
    in.Length.QuadPart = file_size.QuadPart;
232
233
    FILE_ALLOCATED_RANGE_BUFFER out[2];
234
235
    DWORD returned_bytes = 0;
236
    BOOL ret = DeviceIoControl(file, FSCTL_QUERY_ALLOCATED_RANGES, static_cast<void*>(&in), sizeof(in)
237
      , out, sizeof(out), &returned_bytes, nullptr);
238
239
    if (ret == FALSE)
240
    {
241
      return true;
242
    }
243
244
    // if we have more than one range in the file, we're sparse
245
    if (returned_bytes != sizeof(FILE_ALLOCATED_RANGE_BUFFER)) {
246
      return true;
247
    }
248
249
    return (in.Length.QuadPart != out[0].Length.QuadPart);
250
  }
251
252
  DWORD file_access(open_mode_t const mode)
253
  {
254
    return (mode & open_mode::write)
255
      ? GENERIC_WRITE | GENERIC_READ
256
      : GENERIC_READ;
257
  }
258
259
  DWORD file_create(open_mode_t const mode)
260
  {
261
    return (mode & open_mode::write) ? OPEN_ALWAYS : OPEN_EXISTING;
262
  }
263
264
265
#ifndef TORRENT_WINRT
266
  std::once_flag g_once_flag;
267
268
  void acquire_manage_volume_privs()
269
  {
270
    using OpenProcessToken_t = BOOL (WINAPI*)(HANDLE, DWORD, PHANDLE);
271
272
    using LookupPrivilegeValue_t = BOOL (WINAPI*)(LPCSTR, LPCSTR, PLUID);
273
274
    using AdjustTokenPrivileges_t = BOOL (WINAPI*)(
275
      HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD);
276
277
    auto OpenProcessToken =
278
      aux::get_library_procedure<aux::advapi32, OpenProcessToken_t>("OpenProcessToken");
279
    auto LookupPrivilegeValue =
280
      aux::get_library_procedure<aux::advapi32, LookupPrivilegeValue_t>("LookupPrivilegeValueA");
281
    auto AdjustTokenPrivileges =
282
      aux::get_library_procedure<aux::advapi32, AdjustTokenPrivileges_t>("AdjustTokenPrivileges");
283
284
    if (OpenProcessToken == nullptr
285
      || LookupPrivilegeValue == nullptr
286
      || AdjustTokenPrivileges == nullptr)
287
    {
288
      return;
289
    }
290
291
    HANDLE token;
292
    if (!OpenProcessToken(GetCurrentProcess()
293
      , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
294
      return;
295
296
    auto close_handle = aux::scope_end([&token] {
297
      CloseHandle(token);
298
    });
299
300
    TOKEN_PRIVILEGES privs{};
301
    if (!LookupPrivilegeValue(nullptr, "SeManageVolumePrivilege"
302
      , &privs.Privileges[0].Luid))
303
    {
304
      return;
305
    }
306
307
    privs.PrivilegeCount = 1;
308
    privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
309
310
    AdjustTokenPrivileges(token, FALSE, &privs, 0, nullptr, nullptr);
311
  }
312
#endif // TORRENT_WINRT
313
314
#ifdef TORRENT_WINRT
315
316
  DWORD file_flags(open_mode_t const mode)
317
  {
318
    return ((mode & open_mode::no_cache) ? FILE_FLAG_WRITE_THROUGH : 0)
319
      | ((mode & open_mode::sequential_access) ? FILE_FLAG_SEQUENTIAL_SCAN : 0)
320
      ;
321
  }
322
323
  DWORD file_attributes(open_mode_t const mode)
324
  {
325
    return (mode & open_mode::hidden) ? FILE_ATTRIBUTE_HIDDEN : FILE_ATTRIBUTE_NORMAL;
326
  }
327
328
  auto create_file(const native_path_string & name, open_mode_t const mode)
329
  {
330
    CREATEFILE2_EXTENDED_PARAMETERS Extended
331
    {
332
      sizeof(CREATEFILE2_EXTENDED_PARAMETERS),
333
      file_attributes(mode),
334
      file_flags(mode)
335
    };
336
337
    return CreateFile2(name.c_str()
338
      , file_access(mode)
339
      , FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE
340
      , file_create(mode)
341
      , &Extended);
342
  }
343
344
#else
345
346
  DWORD file_flags(open_mode_t const mode)
347
  {
348
    // one might think it's a good idea to pass in FILE_FLAG_RANDOM_ACCESS. It
349
    // turns out that it isn't. That flag will break your operating system:
350
    // http://support.microsoft.com/kb/2549369
351
    return ((mode & open_mode::hidden) ? FILE_ATTRIBUTE_HIDDEN : FILE_ATTRIBUTE_NORMAL)
352
      | ((mode & open_mode::no_cache) ? FILE_FLAG_WRITE_THROUGH : 0)
353
      | ((mode & open_mode::sequential_access) ? FILE_FLAG_SEQUENTIAL_SCAN : 0)
354
      ;
355
  }
356
357
  auto create_file(const native_path_string & name, open_mode_t const mode)
358
  {
359
360
    if (mode & aux::open_mode::allow_set_file_valid_data)
361
    {
362
      // Enable privilege required by SetFileValidData()
363
      // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilevaliddata
364
      // This must happen before the file is opened:
365
      // https://devblogs.microsoft.com/oldnewthing/20160603-00/?p=93565
366
      // SetFileValidData() is not available on WinRT, so there is no
367
      // corresponding call in that version of create_file()
368
      std::call_once(g_once_flag, acquire_manage_volume_privs);
369
    }
370
371
    return CreateFileW(name.c_str()
372
      , file_access(mode)
373
      , FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE
374
      , nullptr
375
      , file_create(mode)
376
      , file_flags(mode)
377
      , nullptr);
378
  }
379
380
#endif
381
382
#else
383
  // non-windows
384
385
  int file_flags(open_mode_t const mode)
386
0
  {
387
0
    return ((mode & open_mode::write) ? O_RDWR | O_CREAT : O_RDONLY)
388
0
#ifdef O_CLOEXEC
389
0
      | O_CLOEXEC
390
0
#endif
391
0
#ifdef O_NOATIME
392
0
      | ((mode & open_mode::no_atime) ? O_NOATIME : 0)
393
0
#endif
394
0
#ifdef O_SYNC
395
0
      | ((mode & open_mode::no_cache) ? O_SYNC : 0)
396
0
#endif
397
0
      ;
398
0
  }
399
400
  mode_t file_perms(open_mode_t const mode)
401
0
  {
402
    // rely on default umask to filter x and w permissions
403
    // for group and others
404
0
    mode_t permissions = S_IRUSR | S_IWUSR
405
0
      | S_IRGRP | S_IWGRP
406
0
      | S_IROTH | S_IWOTH;
407
408
0
    if ((mode & aux::open_mode::executable))
409
0
      permissions |= S_IXGRP | S_IXOTH | S_IXUSR;
410
411
0
    return permissions;
412
0
  }
413
414
  int open_file(std::string const filename, open_mode_t const mode)
415
0
  {
416
0
    int ret = ::open(filename.c_str(), file_flags(mode), file_perms(mode));
417
418
0
#ifdef O_NOATIME
419
0
    if (ret < 0 && (mode & open_mode::no_atime))
420
0
    {
421
      // NOATIME may not be allowed for certain files, it's best-effort,
422
      // so just try again without NOATIME
423
0
      ret = ::open(filename.c_str()
424
0
        , file_flags(mode & ~open_mode::no_atime), file_perms(mode));
425
0
    }
426
0
#endif
427
0
    if (ret < 0) throw_ex<storage_error>(error_code(errno, system_category()), operation_t::file_open);
428
0
    return ret;
429
0
  }
430
#endif // TORRENT_WINDOWS
431
432
} // anonymous namespace
433
434
#ifdef TORRENT_WINDOWS
435
file_handle::file_handle(string_view name, std::int64_t const size
436
  , open_mode_t const mode)
437
  : m_fd(create_file(convert_to_native_path_string(name.to_string()), mode))
438
  , m_open_mode(mode)
439
{
440
  if (m_fd == invalid_handle)
441
  {
442
    throw_ex<storage_error>(error_code(GetLastError(), system_category())
443
      , operation_t::file_open);
444
  }
445
446
  // try to make the file sparse if supported
447
  // only set this flag if the file is opened for writing
448
  if ((mode & aux::open_mode::sparse) && (mode & aux::open_mode::write))
449
  {
450
    DWORD temp;
451
    ::DeviceIoControl(m_fd, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &temp, nullptr);
452
  }
453
454
  if ((mode & open_mode::truncate)
455
    && !(mode & aux::open_mode::sparse)
456
    && (mode & aux::open_mode::allow_set_file_valid_data))
457
  {
458
    LARGE_INTEGER sz;
459
    sz.QuadPart = size;
460
    if (SetFilePointerEx(m_fd, sz, nullptr, FILE_BEGIN) == FALSE)
461
      throw_ex<storage_error>(error_code(GetLastError(), system_category()), operation_t::file_seek);
462
463
    if (::SetEndOfFile(m_fd) == FALSE)
464
      throw_ex<storage_error>(error_code(GetLastError(), system_category()), operation_t::file_truncate);
465
466
#ifndef TORRENT_WINRT
467
    // if the user has permissions, avoid filling
468
    // the file with zeroes, but just fill it with
469
    // garbage instead
470
    SetFileValidData(m_fd, size);
471
#endif
472
  }
473
}
474
475
#else
476
477
file_handle::file_handle(string_view name, std::int64_t const size
478
  , open_mode_t const mode)
479
0
  : m_fd(open_file(convert_to_native_path_string(name.to_string()), mode))
480
0
{
481
#ifdef DIRECTIO_ON
482
  // for solaris
483
  if (mode & open_mode::no_cache)
484
    directio(m_fd, DIRECTIO_ON);
485
#endif
486
487
0
  if (mode & open_mode::truncate)
488
0
  {
489
0
#ifdef TORRENT_LINUX
490
    // This flag can only be set on a 0-size file. It's important to make
491
    // this call before ftruncate() below.
492
0
    if (mode & open_mode::no_cow)
493
0
    {
494
0
      int attr;
495
0
      if (ioctl(m_fd, FS_IOC_GETFLAGS, &attr) != -1)
496
0
      {
497
0
        attr |= FS_NOCOW_FL;
498
        // best effort, ignore errors
499
0
        ioctl(m_fd, FS_IOC_SETFLAGS, &attr);
500
0
      }
501
0
    }
502
0
#endif
503
504
0
    static_assert(sizeof(off_t) >= sizeof(size), "There seems to be a large-file issue in truncate()");
505
0
    if (ftruncate(m_fd, static_cast<off_t>(size)) < 0)
506
0
    {
507
0
      int const err = errno;
508
0
      ::close(m_fd);
509
0
      throw_ex<storage_error>(error_code(err, system_category()), operation_t::file_truncate);
510
0
    }
511
512
0
    if (!(mode & open_mode::sparse))
513
0
    {
514
0
#if TORRENT_HAS_FALLOCATE
515
      // if you get a compile error here, you might want to
516
      // define TORRENT_HAS_FALLOCATE to 0.
517
0
      int const ret = ::posix_fallocate(m_fd, 0, size);
518
      // posix_allocate fails with EINVAL in case the underlying
519
      // filesystem does not support this operation
520
0
      if (ret != 0 && ret != EINVAL && ret != ENOTSUP)
521
0
      {
522
0
        ::close(m_fd);
523
0
        throw_ex<storage_error>(error_code(ret, system_category()), operation_t::file_fallocate);
524
0
      }
525
#elif defined F_ALLOCSP64
526
      flock64 fl64;
527
      fl64.l_whence = SEEK_SET;
528
      fl64.l_start = 0;
529
      fl64.l_len = size;
530
      if (fcntl(m_fd, F_ALLOCSP64, &fl64) < 0)
531
      {
532
        int const err = errno;
533
        if (err != ENOTSUP)
534
        {
535
          ::close(m_fd);
536
          throw_ex<storage_error>(error_code(err, system_category()), operation_t::file_fallocate);
537
        }
538
      }
539
#elif defined F_PREALLOCATE
540
      fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, size, 0};
541
      if (fcntl(m_fd, F_PREALLOCATE, &f) < 0)
542
      {
543
        // It appears Apple's new filesystem (APFS) does not
544
        // support this control message and fails with EINVAL
545
        // if so, just skip it
546
        int const err = errno;
547
        if (err != EINVAL && err != ENOTSUP)
548
        {
549
          if (err != ENOSPC)
550
          {
551
            ::close(m_fd);
552
            throw_ex<storage_error>(error_code(err, system_category())
553
              , operation_t::file_fallocate);
554
          }
555
          // ok, let's try to allocate non contiguous space then
556
          f.fst_flags = F_ALLOCATEALL;
557
          if (fcntl(m_fd, F_PREALLOCATE, &f) < 0)
558
          {
559
            int const err2 = errno;
560
            ::close(m_fd);
561
            throw_ex<storage_error>(error_code(err2, system_category())
562
              , operation_t::file_fallocate);
563
          }
564
        }
565
      }
566
#endif // F_PREALLOCATE
567
0
    }
568
0
  }
569
570
#ifdef F_NOCACHE
571
  // for BSD/Mac
572
  if (mode & aux::open_mode::no_cache)
573
  {
574
    int yes = 1;
575
    ::fcntl(m_fd, F_NOCACHE, &yes);
576
577
#ifdef F_NODIRECT
578
    // it's OK to temporarily cache written pages
579
    ::fcntl(m_fd, F_NODIRECT, &yes);
580
#endif
581
  }
582
#endif
583
584
0
#if (TORRENT_HAS_FADVISE && defined POSIX_FADV_SEQUENTIAL)
585
0
  if (mode & aux::open_mode::sequential_access)
586
0
  {
587
    // disable read-ahead
588
0
    ::posix_fadvise(m_fd, 0, 0, POSIX_FADV_SEQUENTIAL);
589
0
  }
590
0
#endif
591
0
}
592
#endif
593
594
void file_handle::close()
595
0
{
596
0
  if (m_fd == invalid_handle) return;
597
598
#ifdef TORRENT_WINDOWS
599
600
  // if this file is open for writing, has the sparse
601
  // flag set, but there are no sparse regions, unset
602
  // the flag
603
  if ((m_open_mode & aux::open_mode::write)
604
    && (m_open_mode & aux::open_mode::sparse)
605
    && !is_sparse(m_fd))
606
  {
607
    // according to MSDN, clearing the sparse flag of a file only
608
    // works on windows vista and later
609
#ifdef TORRENT_MINGW
610
    typedef struct _FILE_SET_SPARSE_BUFFER {
611
      BOOLEAN SetSparse;
612
    } FILE_SET_SPARSE_BUFFER;
613
#endif
614
    DWORD temp;
615
    FILE_SET_SPARSE_BUFFER b;
616
    b.SetSparse = FALSE;
617
    ::DeviceIoControl(m_fd, FSCTL_SET_SPARSE, &b, sizeof(b)
618
      , nullptr, 0, &temp, nullptr);
619
  }
620
#endif
621
622
#ifdef TORRENT_WINDOWS
623
  CloseHandle(m_fd);
624
#else
625
0
  ::close(m_fd);
626
0
#endif
627
0
  m_fd = invalid_handle;
628
0
}
629
630
0
file_handle::~file_handle() { close(); }
631
632
file_handle& file_handle::operator=(file_handle&& rhs) &
633
0
{
634
0
  if (&rhs == this) return *this;
635
0
  close();
636
0
  m_fd = rhs.m_fd;
637
0
  rhs.m_fd = invalid_handle;
638
0
  return *this;
639
0
}
640
641
std::int64_t file_handle::get_size() const
642
0
{
643
#ifdef  TORRENT_WINDOWS
644
  LARGE_INTEGER file_size;
645
  if (GetFileSizeEx(fd(), &file_size) == 0)
646
    throw_ex<storage_error>(error_code(GetLastError(), system_category()), operation_t::file_stat);
647
  return file_size.QuadPart;
648
#else
649
0
  struct ::stat fs;
650
0
  if (::fstat(fd(), &fs) != 0)
651
0
    throw_ex<storage_error>(error_code(errno, system_category()), operation_t::file_stat);
652
0
  return fs.st_size;
653
0
#endif
654
0
}
655
656
} // namespace aux
657
658
}