2004-01-16 03:57:45 +01:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2003-2016, Arvid Norberg
|
2004-01-16 03:57:45 +01:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2016-09-14 17:28:50 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
|
|
|
|
2015-08-16 18:17:23 +02:00
|
|
|
#ifdef __GNUC__
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-macros"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __clang__
|
|
|
|
#pragma clang diagnostic push
|
2016-11-19 20:19:35 +01:00
|
|
|
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
2015-08-16 18:17:23 +02:00
|
|
|
#pragma clang diagnostic ignored "-Wunused-macros"
|
2016-11-19 20:19:35 +01:00
|
|
|
#pragma clang diagnostic ignored "-Wreserved-id-macro"
|
2015-08-16 18:17:23 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// these defines are just in case the system we're on needs them for 64 bit file
|
|
|
|
// support
|
2013-11-08 09:10:22 +01:00
|
|
|
#define _FILE_OFFSET_BITS 64
|
|
|
|
#define _LARGE_FILES 1
|
|
|
|
|
2014-02-15 03:45:04 +01:00
|
|
|
// on mingw this is necessary to enable 64-bit time_t, specifically used for
|
|
|
|
// the stat struct. Without this, modification times returned by stat may be
|
|
|
|
// incorrect and consistently fail resume data
|
|
|
|
#ifndef __MINGW_USE_VC2005_COMPAT
|
|
|
|
# define __MINGW_USE_VC2005_COMPAT
|
|
|
|
#endif
|
|
|
|
|
2015-08-16 18:17:23 +02:00
|
|
|
#ifdef __clang__
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#endif
|
|
|
|
|
2016-09-14 17:28:50 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
2009-01-11 03:02:34 +01:00
|
|
|
#include "libtorrent/alloca.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/file.hpp"
|
2016-08-18 23:08:40 +02:00
|
|
|
#include "libtorrent/string_util.hpp"
|
2016-05-17 15:24:06 +02:00
|
|
|
#include "libtorrent/aux_/max_path.hpp" // for TORRENT_MAX_PATH
|
2014-07-06 21:18:00 +02:00
|
|
|
#include <cstring>
|
2015-03-15 00:10:20 +01:00
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#ifdef TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
#include <set>
|
2016-05-23 14:15:39 +02:00
|
|
|
#include <cstdio>
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// for convert_to_wstring and convert_to_native
|
2015-03-15 00:10:20 +01:00
|
|
|
#include "libtorrent/aux_/escape_string.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/assert.hpp"
|
2007-03-17 18:15:16 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
#include "libtorrent/io.hpp"
|
|
|
|
#endif
|
|
|
|
|
2016-09-14 17:28:50 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
|
|
|
|
2012-08-21 23:54:07 +02:00
|
|
|
#include <sys/stat.h>
|
2016-07-10 05:17:55 +02:00
|
|
|
#include <climits> // for IOV_MAX
|
2012-08-21 23:54:07 +02:00
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2004-09-24 12:50:03 +02:00
|
|
|
// windows part
|
2005-08-18 13:20:17 +02:00
|
|
|
|
2011-02-02 08:41:32 +01:00
|
|
|
#ifndef PtrToPtr64
|
|
|
|
#define PtrToPtr64(x) (x)
|
|
|
|
#endif
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
#include "libtorrent/utf8.hpp"
|
2009-11-23 00:55:54 +01:00
|
|
|
|
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#endif
|
2008-07-20 13:14:54 +02:00
|
|
|
#include <windows.h>
|
|
|
|
#include <winioctl.h>
|
2012-08-21 23:54:07 +02:00
|
|
|
#ifndef TORRENT_MINGW
|
2009-10-26 02:29:39 +01:00
|
|
|
#include <direct.h> // for _getcwd, _mkdir
|
2012-08-21 23:54:07 +02:00
|
|
|
#else
|
|
|
|
#include <dirent.h>
|
|
|
|
#endif
|
2009-10-26 02:29:39 +01:00
|
|
|
#include <sys/types.h>
|
2004-03-31 01:55:52 +02:00
|
|
|
#else
|
2008-07-20 13:14:54 +02:00
|
|
|
// posix part
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2004-03-31 01:55:52 +02:00
|
|
|
#include <unistd.h>
|
2004-04-02 12:43:37 +02:00
|
|
|
#include <sys/types.h>
|
2016-07-10 05:17:55 +02:00
|
|
|
#include <cerrno>
|
2009-10-26 02:29:39 +01:00
|
|
|
#include <dirent.h>
|
|
|
|
|
2009-09-05 17:02:49 +02:00
|
|
|
#ifdef TORRENT_LINUX
|
2009-10-26 02:29:39 +01:00
|
|
|
// linux specifics
|
|
|
|
|
2009-09-05 17:02:49 +02:00
|
|
|
#include <sys/ioctl.h>
|
2013-11-27 17:58:02 +01:00
|
|
|
#ifdef TORRENT_ANDROID
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#define lseek lseek64
|
2016-10-19 19:00:03 +02:00
|
|
|
#define pread pread64
|
|
|
|
#define pwrite pwrite64
|
|
|
|
#define ftruncate ftruncate64
|
2013-11-27 17:58:02 +01:00
|
|
|
#endif
|
|
|
|
|
2011-02-14 05:12:26 +01:00
|
|
|
#elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
2009-10-26 02:29:39 +01:00
|
|
|
// mac specifics
|
|
|
|
|
|
|
|
#include <copyfile.h>
|
|
|
|
|
|
|
|
#endif
|
2004-03-31 01:55:52 +02:00
|
|
|
|
2005-08-23 11:59:56 +02:00
|
|
|
// make sure the _FILE_OFFSET_BITS define worked
|
2009-06-09 07:46:51 +02:00
|
|
|
// on this platform. It's supposed to make file
|
|
|
|
// related functions support 64-bit offsets.
|
2009-09-05 17:02:49 +02:00
|
|
|
// this test makes sure lseek() returns a type
|
2009-06-09 07:46:51 +02:00
|
|
|
// at least 64 bits wide
|
2016-05-23 14:15:39 +02:00
|
|
|
static_assert(sizeof(lseek(0, 0, 0)) >= 8, "64 bit file operations are required");
|
2005-08-23 11:59:56 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif // posix part
|
2004-03-31 01:55:52 +02:00
|
|
|
|
2016-09-14 17:28:50 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_PREADV
|
|
|
|
# if defined TORRENT_WINDOWS
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
// wrap the windows function in something that looks
|
|
|
|
// like preadv() and pwritev()
|
2005-08-23 11:59:56 +02:00
|
|
|
|
2016-01-20 00:47:21 +01:00
|
|
|
// windows only lets us wait for 64 handles at a time, so this function makes
|
|
|
|
// sure we wait for all of them, partially in sequence
|
2016-12-09 14:23:54 +01:00
|
|
|
DWORD wait_for_multiple_objects(int num_handles, HANDLE* h)
|
2016-01-20 00:47:21 +01:00
|
|
|
{
|
2016-01-20 03:21:00 +01:00
|
|
|
int batch_size = (std::min)(num_handles, MAXIMUM_WAIT_OBJECTS);
|
2016-01-20 00:47:21 +01:00
|
|
|
while (WaitForMultipleObjects(batch_size, h, TRUE, INFINITE) != WAIT_FAILED)
|
|
|
|
{
|
|
|
|
h += batch_size;
|
|
|
|
num_handles -= batch_size;
|
2016-01-20 03:21:00 +01:00
|
|
|
batch_size = (std::min)(num_handles, MAXIMUM_WAIT_OBJECTS);
|
2016-01-20 00:47:21 +01:00
|
|
|
if (batch_size <= 0) return WAIT_OBJECT_0;
|
|
|
|
}
|
|
|
|
return WAIT_FAILED;
|
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
int preadv(HANDLE fd, libtorrent::file::iovec_t const* bufs, int num_bufs, std::int64_t file_offset)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-22 20:43:40 +02:00
|
|
|
TORRENT_ALLOCA(ol, OVERLAPPED, num_bufs);
|
|
|
|
std::memset(ol.data(), 0, sizeof(OVERLAPPED) * num_bufs);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-10-22 20:43:40 +02:00
|
|
|
TORRENT_ALLOCA(h, HANDLE, num_bufs);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
|
|
|
{
|
|
|
|
ol[i].OffsetHigh = file_offset >> 32;
|
|
|
|
ol[i].Offset = file_offset & 0xffffffff;
|
2016-06-20 17:32:06 +02:00
|
|
|
ol[i].hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
2014-07-06 21:18:00 +02:00
|
|
|
h[i] = ol[i].hEvent;
|
2016-06-20 17:32:06 +02:00
|
|
|
if (h[i] == nullptr)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// we failed to create the event, roll-back and return an error
|
|
|
|
for (int j = 0; j < i; ++j) CloseHandle(h[i]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
file_offset += bufs[i].iov_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
|
|
|
{
|
|
|
|
DWORD num_read;
|
2016-04-25 23:22:09 +02:00
|
|
|
if (ReadFile(fd, bufs[i].iov_base, DWORD(bufs[i].iov_len), &num_read, &ol[i]) == FALSE
|
2014-07-06 21:18:00 +02:00
|
|
|
&& GetLastError() != ERROR_IO_PENDING
|
|
|
|
#ifdef ERROR_CANT_WAIT
|
|
|
|
&& GetLastError() != ERROR_CANT_WAIT
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
if (wait_for_multiple_objects(int(h.size()), h.data()) == WAIT_FAILED)
|
2016-01-13 06:49:17 +01:00
|
|
|
{
|
|
|
|
ret = -1;
|
2016-01-13 07:07:27 +01:00
|
|
|
goto done;
|
2016-01-13 06:49:17 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto& o : ol)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
if (WaitForSingleObject(o.hEvent, INFINITE) == WAIT_FAILED)
|
2016-01-13 06:49:17 +01:00
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
DWORD num_read;
|
2016-10-27 02:40:56 +02:00
|
|
|
if (GetOverlappedResult(fd, &o, &num_read, FALSE) == FALSE)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
#ifdef ERROR_CANT_WAIT
|
|
|
|
TORRENT_ASSERT(GetLastError() != ERROR_CANT_WAIT);
|
|
|
|
#endif
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret += num_read;
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto hnd : h)
|
|
|
|
CloseHandle(hnd);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
int pwritev(HANDLE fd, libtorrent::file::iovec_t const* bufs, int num_bufs, std::int64_t file_offset)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-22 20:43:40 +02:00
|
|
|
TORRENT_ALLOCA(ol, OVERLAPPED, num_bufs);
|
|
|
|
std::memset(ol.data(), 0, sizeof(OVERLAPPED) * num_bufs);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-10-22 20:43:40 +02:00
|
|
|
TORRENT_ALLOCA(h, HANDLE, num_bufs);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
|
|
|
{
|
|
|
|
ol[i].OffsetHigh = file_offset >> 32;
|
|
|
|
ol[i].Offset = file_offset & 0xffffffff;
|
2016-06-20 17:32:06 +02:00
|
|
|
ol[i].hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
2014-07-06 21:18:00 +02:00
|
|
|
h[i] = ol[i].hEvent;
|
2016-06-20 17:32:06 +02:00
|
|
|
if (h[i] == nullptr)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// we failed to create the event, roll-back and return an error
|
|
|
|
for (int j = 0; j < i; ++j) CloseHandle(h[i]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
file_offset += bufs[i].iov_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < num_bufs; ++i)
|
|
|
|
{
|
|
|
|
DWORD num_written;
|
2016-04-25 23:22:09 +02:00
|
|
|
if (WriteFile(fd, bufs[i].iov_base, DWORD(bufs[i].iov_len), &num_written, &ol[i]) == FALSE
|
2014-07-06 21:18:00 +02:00
|
|
|
&& GetLastError() != ERROR_IO_PENDING
|
|
|
|
#ifdef ERROR_CANT_WAIT
|
|
|
|
&& GetLastError() != ERROR_CANT_WAIT
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
if (wait_for_multiple_objects(int(h.size()), h.data()) == WAIT_FAILED)
|
2016-01-13 06:49:17 +01:00
|
|
|
{
|
|
|
|
ret = -1;
|
2016-01-13 07:07:27 +01:00
|
|
|
goto done;
|
2016-01-13 06:49:17 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto& o : ol)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
if (WaitForSingleObject(o.hEvent, INFINITE) == WAIT_FAILED)
|
2016-01-13 06:49:17 +01:00
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
DWORD num_written;
|
2016-10-27 02:40:56 +02:00
|
|
|
if (GetOverlappedResult(fd, &o, &num_written, FALSE) == FALSE)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
#ifdef ERROR_CANT_WAIT
|
|
|
|
TORRENT_ASSERT(GetLastError() != ERROR_CANT_WAIT);
|
|
|
|
#endif
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret += num_written;
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto hnd : h)
|
|
|
|
CloseHandle(hnd);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# else
|
2015-07-14 23:50:08 +02:00
|
|
|
# undef _BSD_SOURCE
|
|
|
|
# define _BSD_SOURCE // deprecated since glibc 2.20
|
|
|
|
# undef _DEFAULT_SOURCE
|
|
|
|
# define _DEFAULT_SOURCE
|
2014-07-06 21:18:00 +02:00
|
|
|
# include <sys/uio.h>
|
|
|
|
# endif
|
|
|
|
#endif
|
2004-04-05 00:15:31 +02:00
|
|
|
|
2016-05-23 14:15:39 +02:00
|
|
|
static_assert((libtorrent::file::rw_mask & libtorrent::file::sparse) == 0, "internal flags error");
|
|
|
|
static_assert((libtorrent::file::rw_mask & libtorrent::file::attribute_mask) == 0, "internal flags error");
|
|
|
|
static_assert((libtorrent::file::sparse & libtorrent::file::attribute_mask) == 0, "internal flags error");
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2014-11-17 04:41:01 +01:00
|
|
|
#if defined TORRENT_WINDOWS && defined UNICODE && !TORRENT_USE_WSTRING
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma message ( "wide character support not available. Files will be saved using narrow string names" )
|
|
|
|
#else
|
|
|
|
#warning "wide character support not available. Files will be saved using narrow string names"
|
2010-06-06 03:18:58 +02:00
|
|
|
#endif
|
2014-11-17 04:41:01 +01:00
|
|
|
|
2010-06-06 03:18:58 +02:00
|
|
|
#endif // TORRENT_WINDOWS
|
|
|
|
|
2004-01-16 03:57:45 +01:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
int bufs_size(span<file::iovec_t const> bufs)
|
2015-11-14 06:21:03 +01:00
|
|
|
{
|
2015-11-19 01:51:17 +01:00
|
|
|
std::size_t size = 0;
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto buf : bufs)
|
|
|
|
size += buf.iov_len;
|
2015-11-19 01:51:17 +01:00
|
|
|
return int(size);
|
2015-11-14 06:21:03 +01:00
|
|
|
}
|
|
|
|
|
2012-03-17 20:06:11 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
std::string convert_separators(std::string p)
|
|
|
|
{
|
2014-05-10 05:23:05 +02:00
|
|
|
for (int i = 0; i < int(p.size()); ++i)
|
2012-03-17 20:06:11 +01:00
|
|
|
if (p[i] == '/') p[i] = '\\';
|
|
|
|
return p;
|
|
|
|
}
|
2014-04-04 09:30:54 +02:00
|
|
|
|
|
|
|
time_t file_time_to_posix(FILETIME f)
|
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
const std::uint64_t posix_time_offset = 11644473600LL;
|
|
|
|
std::uint64_t ft = (std::uint64_t(f.dwHighDateTime) << 32)
|
2014-04-04 09:30:54 +02:00
|
|
|
| f.dwLowDateTime;
|
|
|
|
|
|
|
|
// windows filetime is specified in 100 nanoseconds resolution.
|
|
|
|
// convert to seconds
|
|
|
|
return time_t(ft / 10000000 - posix_time_offset);
|
|
|
|
}
|
2012-03-17 20:06:11 +01:00
|
|
|
#endif
|
|
|
|
|
2015-02-18 04:32:49 +01:00
|
|
|
void stat_file(std::string const& inf, file_status* s
|
2016-09-23 22:51:20 +02:00
|
|
|
, error_code& ec, int const flags)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
ec.clear();
|
2011-01-25 09:21:01 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2015-11-19 01:51:17 +01:00
|
|
|
TORRENT_UNUSED(flags);
|
|
|
|
|
2016-01-16 03:21:23 +01:00
|
|
|
std::string p = convert_separators(inf);
|
|
|
|
#if TORRENT_USE_UNC_PATHS
|
|
|
|
// UNC paths must be absolute
|
|
|
|
// network paths are already UNC paths
|
|
|
|
if (inf.substr(0,2) == "\\\\") p = inf;
|
|
|
|
else p = "\\\\?\\" + (is_complete(p) ? p : combine_path(current_working_directory(), p));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if TORRENT_USE_WSTRING
|
|
|
|
#define CreateFile_ CreateFileW
|
|
|
|
std::wstring f = convert_to_wstring(p);
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
2016-01-16 03:21:23 +01:00
|
|
|
#define CreateFile_ CreateFileA
|
|
|
|
std::string f = convert_to_native(p);
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
2016-01-16 03:21:23 +01:00
|
|
|
|
|
|
|
// in order to open a directory, we need the FILE_FLAG_BACKUP_SEMANTICS
|
|
|
|
HANDLE h = CreateFile_(f.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ
|
2016-06-20 17:32:06 +02:00
|
|
|
| FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
2016-01-16 03:21:23 +01:00
|
|
|
|
|
|
|
if (h == INVALID_HANDLE_VALUE)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2015-04-11 01:19:47 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2016-01-16 03:21:23 +01:00
|
|
|
TORRENT_ASSERT(ec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BY_HANDLE_FILE_INFORMATION data;
|
|
|
|
if (!GetFileInformationByHandle(h, &data))
|
|
|
|
{
|
|
|
|
ec.assign(GetLastError(), system_category());
|
|
|
|
TORRENT_ASSERT(ec);
|
|
|
|
CloseHandle(h);
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-04-04 09:30:54 +02:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
s->file_size = (std::uint64_t(data.nFileSizeHigh) << 32) | data.nFileSizeLow;
|
2014-04-04 09:30:54 +02:00
|
|
|
s->ctime = file_time_to_posix(data.ftCreationTime);
|
|
|
|
s->atime = file_time_to_posix(data.ftLastAccessTime);
|
|
|
|
s->mtime = file_time_to_posix(data.ftLastWriteTime);
|
|
|
|
|
2014-04-18 08:27:54 +02:00
|
|
|
s->mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
|
|
? file_status::directory
|
|
|
|
: (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE)
|
|
|
|
? file_status::character_special : file_status::regular_file;
|
2016-01-16 03:21:23 +01:00
|
|
|
CloseHandle(h);
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
2016-01-16 03:21:23 +01:00
|
|
|
|
2014-04-04 09:30:54 +02:00
|
|
|
// posix version
|
|
|
|
|
2015-02-18 04:32:49 +01:00
|
|
|
std::string const& f = convert_to_native(inf);
|
2014-04-04 09:30:54 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
struct stat ret;
|
2010-04-01 02:44:29 +02:00
|
|
|
int retval;
|
|
|
|
if (flags & dont_follow_links)
|
|
|
|
retval = ::lstat(f.c_str(), &ret);
|
|
|
|
else
|
|
|
|
retval = ::stat(f.c_str(), &ret);
|
|
|
|
if (retval < 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->file_size = ret.st_size;
|
|
|
|
s->atime = ret.st_atime;
|
|
|
|
s->mtime = ret.st_mtime;
|
|
|
|
s->ctime = ret.st_ctime;
|
2014-04-04 09:30:54 +02:00
|
|
|
|
|
|
|
s->mode = (S_ISREG(ret.st_mode) ? file_status::regular_file : 0)
|
|
|
|
| (S_ISDIR(ret.st_mode) ? file_status::directory : 0)
|
|
|
|
| (S_ISLNK(ret.st_mode) ? file_status::link : 0)
|
|
|
|
| (S_ISFIFO(ret.st_mode) ? file_status::fifo : 0)
|
|
|
|
| (S_ISCHR(ret.st_mode) ? file_status::character_special : 0)
|
|
|
|
| (S_ISBLK(ret.st_mode) ? file_status::block_special : 0)
|
|
|
|
| (S_ISSOCK(ret.st_mode) ? file_status::socket : 0);
|
|
|
|
|
|
|
|
#endif // TORRENT_WINDOWS
|
2009-10-26 02:29:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void rename(std::string const& inf, std::string const& newf, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
|
2010-02-15 06:49:10 +01:00
|
|
|
#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS
|
2009-10-26 02:29:39 +01:00
|
|
|
std::wstring f1 = convert_to_wstring(inf);
|
|
|
|
std::wstring f2 = convert_to_wstring(newf);
|
|
|
|
if (_wrename(f1.c_str(), f2.c_str()) < 0)
|
|
|
|
#else
|
2015-02-18 04:32:49 +01:00
|
|
|
std::string const& f1 = convert_to_native(inf);
|
|
|
|
std::string const& f2 = convert_to_native(newf);
|
2009-10-27 02:47:54 +01:00
|
|
|
if (::rename(f1.c_str(), f2.c_str()) < 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
|
|
|
{
|
2016-03-16 01:41:12 +01:00
|
|
|
ec.assign(errno, generic_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_directories(std::string const& f, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
2011-09-05 01:29:47 +02:00
|
|
|
if (is_directory(f, ec)) return;
|
|
|
|
if (ec != boost::system::errc::no_such_file_or_directory)
|
|
|
|
return;
|
|
|
|
ec.clear();
|
2009-10-26 02:29:39 +01:00
|
|
|
if (is_root_path(f)) return;
|
|
|
|
if (has_parent_path(f))
|
|
|
|
{
|
|
|
|
create_directories(parent_path(f), ec);
|
|
|
|
if (ec) return;
|
|
|
|
}
|
|
|
|
create_directory(f, ec);
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_directory(std::string const& f, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
#if TORRENT_USE_WSTRING
|
2010-02-15 06:49:10 +01:00
|
|
|
#define CreateDirectory_ CreateDirectoryW
|
2009-10-26 02:29:39 +01:00
|
|
|
std::wstring n = convert_to_wstring(f);
|
|
|
|
#else
|
2010-02-15 06:49:10 +01:00
|
|
|
#define CreateDirectory_ CreateDirectoryA
|
2015-02-18 04:32:49 +01:00
|
|
|
std::string const& n = convert_to_native(f);
|
2015-03-21 01:12:40 +01:00
|
|
|
#endif // TORRENT_USE_WSTRING
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2010-02-15 06:49:10 +01:00
|
|
|
if (CreateDirectory_(n.c_str(), 0) == 0
|
2009-10-26 02:29:39 +01:00
|
|
|
&& GetLastError() != ERROR_ALREADY_EXISTS)
|
2015-04-11 01:19:47 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
2015-03-21 01:12:40 +01:00
|
|
|
std::string n = convert_to_native(f);
|
2016-08-13 01:24:03 +02:00
|
|
|
int ret = ::mkdir(n.c_str(), 0777);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (ret < 0 && errno != EEXIST)
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
void hard_link(std::string const& file, std::string const& link
|
|
|
|
, error_code& ec)
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
|
|
|
|
#if TORRENT_USE_WSTRING
|
|
|
|
#define CreateHardLink_ CreateHardLinkW
|
|
|
|
std::wstring n_exist = convert_to_wstring(file);
|
|
|
|
std::wstring n_link = convert_to_wstring(link);
|
|
|
|
#else
|
|
|
|
#define CreateHardLink_ CreateHardLinkA
|
|
|
|
std::string n_exist = convert_to_native(file);
|
|
|
|
std::string n_link = convert_to_native(link);
|
|
|
|
#endif
|
2016-06-20 17:32:06 +02:00
|
|
|
BOOL ret = CreateHardLink_(n_link.c_str(), n_exist.c_str(), nullptr);
|
2015-03-21 01:12:40 +01:00
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// something failed. Does the filesystem not support hard links?
|
|
|
|
// TODO: 3 find out what error code is reported when the filesystem
|
|
|
|
// does not support hard links.
|
2016-10-02 21:27:50 +02:00
|
|
|
DWORD const error = GetLastError();
|
2016-03-20 21:14:29 +01:00
|
|
|
if (error != ERROR_NOT_SUPPORTED && error != ERROR_ACCESS_DENIED)
|
2015-08-31 02:14:55 +02:00
|
|
|
{
|
|
|
|
// it's possible CreateHardLink will copy the file internally too,
|
|
|
|
// if the filesystem does not support it.
|
|
|
|
ec.assign(GetLastError(), system_category());
|
|
|
|
return;
|
|
|
|
}
|
2015-03-21 01:12:40 +01:00
|
|
|
|
2015-08-31 02:14:55 +02:00
|
|
|
// fall back to making a copy
|
2015-03-21 01:12:40 +01:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
std::string n_exist = convert_to_native(file);
|
|
|
|
std::string n_link = convert_to_native(link);
|
|
|
|
|
|
|
|
// assume posix's link() function exists
|
|
|
|
int ret = ::link(n_exist.c_str(), n_link.c_str());
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// most errors are passed through, except for the ones that indicate that
|
|
|
|
// hard links are not supported and require a copy.
|
|
|
|
// TODO: 2 test this on a FAT volume to see what error we get!
|
|
|
|
if (errno != EMLINK || errno != EXDEV)
|
|
|
|
{
|
|
|
|
// some error happened, report up to the caller
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2015-03-21 01:12:40 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-08-31 02:14:55 +02:00
|
|
|
|
|
|
|
// fall back to making a copy
|
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// if we get here, we should copy the file
|
|
|
|
copy_file(file, link, ec);
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
bool is_directory(std::string const& f, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
error_code e;
|
|
|
|
file_status s;
|
|
|
|
stat_file(f, &s, e);
|
|
|
|
if (!e && s.mode & file_status::directory) return true;
|
|
|
|
ec = e;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-30 01:35:11 +02:00
|
|
|
void recursive_copy(std::string const& old_path, std::string const& new_path, error_code& ec)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(!ec);
|
|
|
|
if (is_directory(old_path, ec))
|
|
|
|
{
|
|
|
|
create_directory(new_path, ec);
|
|
|
|
if (ec) return;
|
|
|
|
for (directory i(old_path, ec); !i.done(); i.next(ec))
|
|
|
|
{
|
|
|
|
std::string f = i.file();
|
|
|
|
if (f == ".." || f == ".") continue;
|
|
|
|
recursive_copy(combine_path(old_path, f), combine_path(new_path, f), ec);
|
|
|
|
if (ec) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!ec)
|
|
|
|
{
|
|
|
|
copy_file(old_path, new_path, ec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
void copy_file(std::string const& inf, std::string const& newf, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
#if TORRENT_USE_WSTRING
|
2010-02-15 06:49:10 +01:00
|
|
|
#define CopyFile_ CopyFileW
|
2009-10-26 02:29:39 +01:00
|
|
|
std::wstring f1 = convert_to_wstring(inf);
|
|
|
|
std::wstring f2 = convert_to_wstring(newf);
|
|
|
|
#else
|
2010-02-15 06:49:10 +01:00
|
|
|
#define CopyFile_ CopyFileA
|
2015-02-18 04:32:49 +01:00
|
|
|
std::string const& f1 = convert_to_native(inf);
|
|
|
|
std::string const& f2 = convert_to_native(newf);
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
|
|
|
|
2010-02-15 06:49:10 +01:00
|
|
|
if (CopyFile_(f1.c_str(), f2.c_str(), false) == 0)
|
2015-04-11 01:19:47 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
#elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
2015-03-21 01:12:40 +01:00
|
|
|
std::string f1 = convert_to_native(inf);
|
|
|
|
std::string f2 = convert_to_native(newf);
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
// this only works on 10.5
|
|
|
|
copyfile_state_t state = copyfile_state_alloc();
|
|
|
|
if (copyfile(f1.c_str(), f2.c_str(), state, COPYFILE_ALL) < 0)
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
copyfile_state_free(state);
|
|
|
|
#else
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string const f1 = convert_to_native(inf);
|
|
|
|
std::string const f2 = convert_to_native(newf);
|
2015-03-21 01:12:40 +01:00
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
int const infd = ::open(f1.c_str(), O_RDONLY);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (infd < 0)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
2009-10-28 20:52:58 +01:00
|
|
|
|
|
|
|
// rely on default umask to filter x and w permissions
|
|
|
|
// for group and others
|
2016-08-13 13:04:53 +02:00
|
|
|
int const permissions = S_IRUSR | S_IWUSR
|
2009-10-28 20:52:58 +01:00
|
|
|
| S_IRGRP | S_IWGRP
|
|
|
|
| S_IROTH | S_IWOTH;
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
int const outfd = ::open(f2.c_str(), O_WRONLY | O_CREAT, permissions);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (outfd < 0)
|
|
|
|
{
|
|
|
|
close(infd);
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
char buffer[4096];
|
|
|
|
for (;;)
|
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
int const num_read = read(infd, buffer, sizeof(buffer));
|
2009-10-26 02:29:39 +01:00
|
|
|
if (num_read == 0) break;
|
2011-02-21 06:24:41 +01:00
|
|
|
if (num_read < 0)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2011-02-21 06:24:41 +01:00
|
|
|
break;
|
|
|
|
}
|
2016-08-13 13:04:53 +02:00
|
|
|
int const num_written = write(outfd, buffer, num_read);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (num_written < num_read)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
break;
|
|
|
|
}
|
2011-02-14 02:59:01 +01:00
|
|
|
if (num_read < int(sizeof(buffer))) break;
|
2009-10-26 02:29:39 +01:00
|
|
|
}
|
|
|
|
close(infd);
|
|
|
|
close(outfd);
|
|
|
|
#endif // TORRENT_WINDOWS
|
|
|
|
}
|
|
|
|
|
2016-05-01 04:18:38 +02:00
|
|
|
void move_file(std::string const& inf, std::string const& newf, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
|
|
|
|
file_status s;
|
|
|
|
stat_file(inf, &s, ec);
|
|
|
|
if (ec) return;
|
|
|
|
|
|
|
|
if (has_parent_path(newf))
|
|
|
|
{
|
|
|
|
create_directories(parent_path(newf), ec);
|
|
|
|
if (ec) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rename(inf, newf, ec);
|
|
|
|
|
|
|
|
// on OSX, the error when trying to rename a file across different
|
|
|
|
// volumes is EXDEV, which will make it fall back to copying.
|
|
|
|
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (ec != boost::system::errc::no_such_file_or_directory
|
|
|
|
&& ec != boost::system::errc::invalid_argument
|
|
|
|
&& ec != boost::system::errc::permission_denied)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
copy_file(inf, newf, ec);
|
|
|
|
|
|
|
|
if (!ec)
|
|
|
|
{
|
|
|
|
// ignore errors when removing
|
|
|
|
error_code ignore;
|
|
|
|
remove(inf, ignore);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string split_path(std::string const& f)
|
|
|
|
{
|
|
|
|
if (f.empty()) return f;
|
|
|
|
|
|
|
|
std::string ret;
|
|
|
|
char const* start = f.c_str();
|
|
|
|
char const* p = start;
|
|
|
|
while (*start != 0)
|
|
|
|
{
|
|
|
|
while (*p != '/'
|
|
|
|
&& *p != '\0'
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
2009-10-26 02:29:39 +01:00
|
|
|
&& *p != '\\'
|
|
|
|
#endif
|
|
|
|
) ++p;
|
|
|
|
if (p - start > 0)
|
|
|
|
{
|
|
|
|
ret.append(start, p - start);
|
|
|
|
ret.append(1, '\0');
|
|
|
|
}
|
|
|
|
if (*p != 0) ++p;
|
|
|
|
start = p;
|
|
|
|
}
|
|
|
|
ret.append(1, '\0');
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
char const* next_path_element(char const* p)
|
|
|
|
{
|
|
|
|
p += strlen(p) + 1;
|
2016-07-09 22:26:26 +02:00
|
|
|
if (*p == 0) return nullptr;
|
2009-10-26 02:29:39 +01:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string extension(std::string const& f)
|
|
|
|
{
|
2016-04-25 23:22:09 +02:00
|
|
|
for (int i = int(f.size()) - 1; i >= 0; --i)
|
2013-10-24 09:22:44 +02:00
|
|
|
{
|
|
|
|
if (f[i] == '/') break;
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
if (f[i] == '\\') break;
|
|
|
|
#endif
|
|
|
|
if (f[i] != '.') continue;
|
|
|
|
return f.substr(i);
|
|
|
|
}
|
|
|
|
return "";
|
2009-10-26 02:29:39 +01:00
|
|
|
}
|
|
|
|
|
2013-11-18 07:59:47 +01:00
|
|
|
std::string remove_extension(std::string const& f)
|
|
|
|
{
|
2016-08-13 01:24:03 +02:00
|
|
|
char const* slash = std::strrchr(f.c_str(), '/');
|
2013-11-18 07:59:47 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2016-08-13 01:24:03 +02:00
|
|
|
slash = (std::max)((char const*)std::strrchr(f.c_str(), '\\'), slash);
|
2013-11-18 07:59:47 +01:00
|
|
|
#endif
|
2016-08-13 01:24:03 +02:00
|
|
|
char const* ext = std::strrchr(f.c_str(), '.');
|
2013-11-18 07:59:47 +01:00
|
|
|
// if we don't have an extension, just return f
|
2016-07-09 22:26:26 +02:00
|
|
|
if (ext == nullptr || ext == &f[0] || (slash != nullptr && ext < slash)) return f;
|
2013-11-18 07:59:47 +01:00
|
|
|
return f.substr(0, ext - &f[0]);
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
void replace_extension(std::string& f, std::string const& ext)
|
|
|
|
{
|
2016-04-25 23:22:09 +02:00
|
|
|
for (int i = int(f.size()) - 1; i >= 0; --i)
|
2013-10-24 09:22:44 +02:00
|
|
|
{
|
|
|
|
if (f[i] == '/') break;
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
if (f[i] == '\\') break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (f[i] != '.') continue;
|
|
|
|
|
|
|
|
f.resize(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
f += '.';
|
2009-10-26 02:29:39 +01:00
|
|
|
f += ext;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_root_path(std::string const& f)
|
|
|
|
{
|
|
|
|
if (f.empty()) return false;
|
|
|
|
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
2011-11-27 11:23:50 +01:00
|
|
|
// match \\ form
|
2009-10-26 02:29:39 +01:00
|
|
|
if (f == "\\\\") return true;
|
|
|
|
int i = 0;
|
|
|
|
// match the xx:\ or xx:/ form
|
|
|
|
while (f[i] && is_alpha(f[i])) ++i;
|
|
|
|
if (i == int(f.size()-2) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/'))
|
|
|
|
return true;
|
2011-11-27 11:23:50 +01:00
|
|
|
// match network paths \\computer_name\ form
|
|
|
|
if (f.size() > 2 && f[0] == '\\' && f[1] == '\\')
|
|
|
|
{
|
|
|
|
// we don't care about the last character, since it's OK for it
|
|
|
|
// to be a slash or a back slash
|
|
|
|
bool found = false;
|
2016-04-17 22:56:07 +02:00
|
|
|
for (int j = 2; j < int(f.size()) - 1; ++j)
|
2011-11-27 11:23:50 +01:00
|
|
|
{
|
2016-04-17 22:56:07 +02:00
|
|
|
if (f[j] != '\\' && f[j] != '/') continue;
|
2011-11-27 11:23:50 +01:00
|
|
|
// there is a directory separator in here,
|
|
|
|
// i.e. this is not the root
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!found) return true;
|
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
|
|
|
// as well as parent_path("/") should be "/".
|
|
|
|
if (f == "/") return true;
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_parent_path(std::string const& f)
|
|
|
|
{
|
|
|
|
if (f.empty()) return false;
|
2011-11-27 11:23:50 +01:00
|
|
|
if (is_root_path(f)) return false;
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2016-04-25 23:22:09 +02:00
|
|
|
int len = int(f.size()) - 1;
|
2009-10-26 02:29:39 +01:00
|
|
|
// if the last character is / or \ ignore it
|
|
|
|
if (f[len] == '/' || f[len] == '\\') --len;
|
|
|
|
while (len >= 0)
|
|
|
|
{
|
|
|
|
if (f[len] == '/' || f[len] == '\\')
|
|
|
|
break;
|
|
|
|
--len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string parent_path(std::string const& f)
|
|
|
|
{
|
|
|
|
if (f.empty()) return f;
|
|
|
|
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
if (f == "\\\\") return "";
|
|
|
|
#endif
|
|
|
|
if (f == "/") return "";
|
|
|
|
|
2016-04-25 23:22:09 +02:00
|
|
|
int len = int(f.size());
|
2009-10-26 02:29:39 +01:00
|
|
|
// if the last character is / or \ ignore it
|
|
|
|
if (f[len-1] == '/' || f[len-1] == '\\') --len;
|
|
|
|
while (len > 0)
|
|
|
|
{
|
|
|
|
--len;
|
|
|
|
if (f[len] == '/' || f[len] == '\\')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f[len] == '/' || f[len] == '\\') ++len;
|
|
|
|
return std::string(f.c_str(), len);
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
char const* filename_cstr(char const* f)
|
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
if (f == nullptr) return f;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-08-13 01:24:03 +02:00
|
|
|
char const* sep = std::strrchr(f, '/');
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2016-08-13 01:24:03 +02:00
|
|
|
char const* altsep = std::strrchr(f, '\\');
|
2014-07-06 21:18:00 +02:00
|
|
|
if (sep == 0 || altsep > sep) sep = altsep;
|
|
|
|
#endif
|
2016-07-09 22:26:26 +02:00
|
|
|
if (sep == nullptr) return f;
|
2014-07-06 21:18:00 +02:00
|
|
|
return sep+1;
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string filename(std::string const& f)
|
|
|
|
{
|
2009-11-02 05:08:37 +01:00
|
|
|
if (f.empty()) return "";
|
|
|
|
char const* first = f.c_str();
|
2016-08-13 01:24:03 +02:00
|
|
|
char const* sep = std::strrchr(first, '/');
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
2016-08-13 01:24:03 +02:00
|
|
|
char const* altsep = std::strrchr(first, '\\');
|
2009-10-26 02:29:39 +01:00
|
|
|
if (sep == 0 || altsep > sep) sep = altsep;
|
|
|
|
#endif
|
2016-07-09 22:26:26 +02:00
|
|
|
if (sep == nullptr) return f;
|
2009-11-02 05:08:37 +01:00
|
|
|
|
2011-02-14 02:59:01 +01:00
|
|
|
if (sep - first == int(f.size()) - 1)
|
2009-11-02 05:08:37 +01:00
|
|
|
{
|
|
|
|
// if the last character is a / (or \)
|
|
|
|
// ignore it
|
|
|
|
int len = 0;
|
|
|
|
while (sep > first)
|
|
|
|
{
|
|
|
|
--sep;
|
|
|
|
if (*sep == '/'
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
2009-11-02 05:08:37 +01:00
|
|
|
|| *sep == '\\'
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
return std::string(sep + 1, len);
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
return std::string(first, len);
|
2016-05-01 04:18:38 +02:00
|
|
|
|
2009-11-02 05:08:37 +01:00
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
return std::string(sep + 1);
|
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
void append_path(std::string& branch, string_view leaf)
|
2015-02-18 04:32:49 +01:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
TORRENT_ASSERT(!is_complete(leaf));
|
2015-02-18 04:32:49 +01:00
|
|
|
if (branch.empty() || branch == ".")
|
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
branch.assign(leaf.data(), leaf.size());
|
2015-02-18 04:32:49 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-08-13 13:04:53 +02:00
|
|
|
if (leaf.size() == 0) return;
|
2015-02-18 04:32:49 +01:00
|
|
|
|
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
|
|
|
#define TORRENT_SEPARATOR_CHAR '\\'
|
2016-06-04 16:03:13 +02:00
|
|
|
bool const need_sep = branch[branch.size()-1] != '\\'
|
2015-02-18 04:32:49 +01:00
|
|
|
&& branch[branch.size()-1] != '/';
|
|
|
|
#else
|
|
|
|
#define TORRENT_SEPARATOR_CHAR '/'
|
2016-06-04 16:03:13 +02:00
|
|
|
bool const need_sep = branch[branch.size()-1] != '/';
|
2015-02-18 04:32:49 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (need_sep) branch += TORRENT_SEPARATOR_CHAR;
|
2016-08-13 13:04:53 +02:00
|
|
|
branch.append(leaf.data(), leaf.size());
|
2015-02-18 04:32:49 +01:00
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string combine_path(string_view lhs, string_view rhs)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(!is_complete(rhs));
|
2016-08-13 13:04:53 +02:00
|
|
|
if (lhs.empty() || lhs == ".") return rhs.to_string();
|
|
|
|
if (rhs.empty() || rhs == ".") return lhs.to_string();
|
2009-10-26 02:29:39 +01:00
|
|
|
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
2010-03-07 22:59:02 +01:00
|
|
|
#define TORRENT_SEPARATOR "\\"
|
2016-09-07 00:34:03 +02:00
|
|
|
bool const need_sep = lhs[lhs.size() - 1] != '\\' && lhs[lhs.size() - 1] != '/';
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
2010-03-07 22:59:02 +01:00
|
|
|
#define TORRENT_SEPARATOR "/"
|
2016-09-07 00:34:03 +02:00
|
|
|
bool const need_sep = lhs[lhs.size() - 1] != '/';
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
2010-03-07 22:59:02 +01:00
|
|
|
std::string ret;
|
2016-04-25 23:22:09 +02:00
|
|
|
int target_size = int(lhs.size() + rhs.size() + 2);
|
2010-03-07 22:59:02 +01:00
|
|
|
ret.resize(target_size);
|
2016-08-13 13:04:53 +02:00
|
|
|
target_size = std::snprintf(&ret[0], target_size, "%*s%s%*s"
|
|
|
|
, int(lhs.size()), lhs.data()
|
2016-09-07 00:34:03 +02:00
|
|
|
, (need_sep ? TORRENT_SEPARATOR : "")
|
2016-08-13 13:04:53 +02:00
|
|
|
, int(rhs.size()), rhs.data());
|
2010-03-08 03:55:16 +01:00
|
|
|
ret.resize(target_size);
|
2010-03-07 22:59:02 +01:00
|
|
|
return ret;
|
2009-10-26 02:29:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string current_working_directory()
|
|
|
|
{
|
2012-08-21 23:54:07 +02:00
|
|
|
#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW
|
2010-02-15 06:49:10 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
2009-10-26 02:29:39 +01:00
|
|
|
wchar_t cwd[TORRENT_MAX_PATH];
|
|
|
|
_wgetcwd(cwd, sizeof(cwd) / sizeof(wchar_t));
|
|
|
|
#else
|
|
|
|
char cwd[TORRENT_MAX_PATH];
|
|
|
|
_getcwd(cwd, sizeof(cwd));
|
2010-02-15 06:49:10 +01:00
|
|
|
#endif // TORRENT_USE_WSTRING
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
|
|
|
char cwd[TORRENT_MAX_PATH];
|
2016-07-09 22:26:26 +02:00
|
|
|
if (getcwd(cwd, sizeof(cwd)) == nullptr) return "/";
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
2012-09-08 21:21:31 +02:00
|
|
|
#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW && TORRENT_USE_WSTRING
|
2009-10-26 02:29:39 +01:00
|
|
|
return convert_from_wstring(cwd);
|
|
|
|
#else
|
|
|
|
return convert_from_native(cwd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-04-28 23:13:55 +02:00
|
|
|
#if TORRENT_USE_UNC_PATHS
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string canonicalize_path(string_view f)
|
2012-04-28 23:13:55 +02:00
|
|
|
{
|
|
|
|
std::string ret;
|
|
|
|
ret.resize(f.size());
|
|
|
|
char* write_cur = &ret[0];
|
|
|
|
char* last_write_sep = write_cur;
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
char const* read_cur = f.data();
|
2012-04-28 23:13:55 +02:00
|
|
|
char const* last_read_sep = read_cur;
|
|
|
|
|
|
|
|
// the last_*_sep pointers point to one past
|
|
|
|
// the last path separator encountered and is
|
2016-08-13 13:04:53 +02:00
|
|
|
// initialized to the first character in the path
|
|
|
|
for (int i = 0; i < int(f.size()); ++i)
|
2012-04-28 23:13:55 +02:00
|
|
|
{
|
|
|
|
if (*read_cur != '\\')
|
|
|
|
{
|
|
|
|
*write_cur++ = *read_cur++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int element_len = read_cur - last_read_sep;
|
2016-08-13 01:24:03 +02:00
|
|
|
if (element_len == 1 && std::memcmp(last_read_sep, ".", 1) == 0)
|
2012-04-28 23:13:55 +02:00
|
|
|
{
|
|
|
|
--write_cur;
|
|
|
|
++read_cur;
|
|
|
|
last_read_sep = read_cur;
|
|
|
|
continue;
|
|
|
|
}
|
2016-08-13 01:24:03 +02:00
|
|
|
if (element_len == 2 && std::memcmp(last_read_sep, "..", 2) == 0)
|
2012-04-28 23:13:55 +02:00
|
|
|
{
|
|
|
|
// find the previous path separator
|
|
|
|
if (last_write_sep > &ret[0])
|
|
|
|
{
|
|
|
|
--last_write_sep;
|
|
|
|
while (last_write_sep > &ret[0]
|
|
|
|
&& last_write_sep[-1] != '\\')
|
|
|
|
--last_write_sep;
|
|
|
|
}
|
|
|
|
write_cur = last_write_sep;
|
|
|
|
// find the previous path separator
|
|
|
|
if (last_write_sep > &ret[0])
|
|
|
|
{
|
|
|
|
--last_write_sep;
|
|
|
|
while (last_write_sep > &ret[0]
|
|
|
|
&& last_write_sep[-1] != '\\')
|
|
|
|
--last_write_sep;
|
|
|
|
}
|
|
|
|
++read_cur;
|
|
|
|
last_read_sep = read_cur;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*write_cur++ = *read_cur++;
|
|
|
|
last_write_sep = write_cur;
|
|
|
|
last_read_sep = read_cur;
|
|
|
|
}
|
|
|
|
// terminate destination string
|
|
|
|
*write_cur = 0;
|
|
|
|
ret.resize(write_cur - &ret[0]);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-05-01 04:18:38 +02:00
|
|
|
#endif
|
2012-04-28 23:13:55 +02:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t file_size(std::string const& f)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
file_status s;
|
|
|
|
stat_file(f, &s, ec);
|
|
|
|
if (ec) return 0;
|
|
|
|
return s.file_size;
|
|
|
|
}
|
|
|
|
|
2014-12-26 22:25:37 +01:00
|
|
|
bool exists(std::string const& f, error_code& ec)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
file_status s;
|
|
|
|
stat_file(f, &s, ec);
|
2015-01-15 03:31:38 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (ec == boost::system::errc::no_such_file_or_directory)
|
|
|
|
ec.clear();
|
|
|
|
return false;
|
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-26 22:25:37 +01:00
|
|
|
bool exists(std::string const& f)
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
return exists(f, ec);
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
void remove(std::string const& inf, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
|
|
|
|
#ifdef TORRENT_WINDOWS
|
2009-11-28 09:58:07 +01:00
|
|
|
// windows does not allow trailing / or \ in
|
|
|
|
// the path when removing files
|
|
|
|
std::string pruned;
|
|
|
|
if (inf[inf.size() - 1] == '/'
|
|
|
|
|| inf[inf.size() - 1] == '\\')
|
|
|
|
pruned = inf.substr(0, inf.size() - 1);
|
|
|
|
else
|
|
|
|
pruned = inf;
|
2010-02-15 06:49:10 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
|
|
|
#define DeleteFile_ DeleteFileW
|
|
|
|
#define RemoveDirectory_ RemoveDirectoryW
|
2009-11-28 09:58:07 +01:00
|
|
|
std::wstring f = convert_to_wstring(pruned);
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
2010-02-15 06:49:10 +01:00
|
|
|
#define DeleteFile_ DeleteFileA
|
|
|
|
#define RemoveDirectory_ RemoveDirectoryA
|
2009-11-28 09:58:07 +01:00
|
|
|
std::string f = convert_to_native(pruned);
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
2010-02-15 06:49:10 +01:00
|
|
|
if (DeleteFile_(f.c_str()) == 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
if (GetLastError() == ERROR_ACCESS_DENIED)
|
|
|
|
{
|
2010-02-15 06:49:10 +01:00
|
|
|
if (RemoveDirectory_(f.c_str()) != 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-04-11 01:19:47 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else // TORRENT_WINDOWS
|
2015-02-18 04:32:49 +01:00
|
|
|
std::string const& f = convert_to_native(inf);
|
2009-10-27 02:47:54 +01:00
|
|
|
if (::remove(f.c_str()) < 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif // TORRENT_WINDOWS
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_all(std::string const& f, error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
|
|
|
|
file_status s;
|
|
|
|
stat_file(f, &s, ec);
|
|
|
|
if (ec) return;
|
|
|
|
|
|
|
|
if (s.mode & file_status::directory)
|
|
|
|
{
|
|
|
|
for (directory i(f, ec); !i.done(); i.next(ec))
|
|
|
|
{
|
|
|
|
if (ec) return;
|
|
|
|
std::string p = i.file();
|
|
|
|
if (p == "." || p == "..") continue;
|
|
|
|
remove_all(combine_path(f, p), ec);
|
|
|
|
if (ec) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
remove(f, ec);
|
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string complete(string_view f)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
if (is_complete(f)) return f.to_string();
|
2014-12-17 15:33:52 +01:00
|
|
|
if (f == ".") return current_working_directory();
|
2009-10-26 02:29:39 +01:00
|
|
|
return combine_path(current_working_directory(), f);
|
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
bool is_complete(string_view f)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
if (f.empty()) return false;
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
2009-10-26 02:29:39 +01:00
|
|
|
int i = 0;
|
|
|
|
// match the xx:\ or xx:/ form
|
|
|
|
while (f[i] && is_alpha(f[i])) ++i;
|
2010-03-21 18:40:15 +01:00
|
|
|
if (i < int(f.size()-1) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/'))
|
2009-10-26 02:29:39 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// match the \\ form
|
|
|
|
if (int(f.size()) >= 2 && f[0] == '\\' && f[1] == '\\')
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
if (f[0] == '/') return true;
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
directory::directory(std::string const& path, error_code& ec)
|
|
|
|
: m_done(false)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
#ifdef TORRENT_WINDOWS
|
2014-01-19 05:11:49 +01:00
|
|
|
m_inode = 0;
|
2009-10-26 02:29:39 +01:00
|
|
|
// the path passed to FindFirstFile() must be
|
|
|
|
// a pattern
|
2012-03-17 20:06:11 +01:00
|
|
|
std::string f = convert_separators(path);
|
|
|
|
if (!f.empty() && f[f.size()-1] != '\\') f += "\\*";
|
2009-10-26 02:29:39 +01:00
|
|
|
else f += "*";
|
2010-02-15 06:49:10 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
|
|
|
#define FindFirstFile_ FindFirstFileW
|
2009-10-26 02:29:39 +01:00
|
|
|
std::wstring p = convert_to_wstring(f);
|
|
|
|
#else
|
2010-02-15 06:49:10 +01:00
|
|
|
#define FindFirstFile_ FindFirstFileA
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string p = convert_to_native(f);
|
|
|
|
#endif
|
2010-02-15 06:49:10 +01:00
|
|
|
m_handle = FindFirstFile_(p.c_str(), &m_fd);
|
2009-10-26 02:29:39 +01:00
|
|
|
if (m_handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2015-04-11 01:19:47 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
m_done = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
2011-02-28 01:35:58 +01:00
|
|
|
|
2016-08-13 01:24:03 +02:00
|
|
|
std::memset(&m_dirent, 0, sizeof(dirent));
|
2011-02-28 01:35:58 +01:00
|
|
|
m_name[0] = 0;
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
// the path passed to opendir() may not
|
|
|
|
// end with a /
|
|
|
|
std::string p = path;
|
|
|
|
if (!path.empty() && path[path.size()-1] == '/')
|
|
|
|
p.resize(path.size()-1);
|
|
|
|
|
|
|
|
p = convert_to_native(p);
|
2016-08-13 01:24:03 +02:00
|
|
|
m_handle = ::opendir(p.c_str());
|
2016-07-09 22:26:26 +02:00
|
|
|
if (m_handle == nullptr)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
m_done = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// read the first entry
|
|
|
|
next(ec);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
directory::~directory()
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
if (m_handle != INVALID_HANDLE_VALUE)
|
|
|
|
FindClose(m_handle);
|
|
|
|
#else
|
|
|
|
if (m_handle) closedir(m_handle);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint64_t directory::inode() const
|
2014-01-19 05:11:49 +01:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
return m_inode;
|
|
|
|
#else
|
|
|
|
return m_dirent.d_ino;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string directory::file() const
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_WINDOWS
|
2010-02-15 06:49:10 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
2009-10-26 02:29:39 +01:00
|
|
|
return convert_from_wstring(m_fd.cFileName);
|
|
|
|
#else
|
|
|
|
return convert_from_native(m_fd.cFileName);
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
return convert_from_native(m_dirent.d_name);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void directory::next(error_code& ec)
|
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
#ifdef TORRENT_WINDOWS
|
2010-02-15 06:49:10 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
|
|
|
#define FindNextFile_ FindNextFileW
|
|
|
|
#else
|
|
|
|
#define FindNextFile_ FindNextFileA
|
|
|
|
#endif
|
|
|
|
if (FindNextFile_(m_handle, &m_fd) == 0)
|
2009-10-26 02:29:39 +01:00
|
|
|
{
|
|
|
|
m_done = true;
|
|
|
|
int err = GetLastError();
|
|
|
|
if (err != ERROR_NO_MORE_FILES)
|
2015-04-11 01:19:47 +02:00
|
|
|
ec.assign(err, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
}
|
2014-01-19 05:11:49 +01:00
|
|
|
++m_inode;
|
2009-10-26 02:29:39 +01:00
|
|
|
#else
|
|
|
|
dirent* dummy;
|
|
|
|
if (readdir_r(m_handle, &m_dirent, &dummy) != 0)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-10-26 02:29:39 +01:00
|
|
|
m_done = true;
|
|
|
|
}
|
2016-07-09 22:26:26 +02:00
|
|
|
if (dummy == nullptr) m_done = true;
|
2009-10-26 02:29:39 +01:00
|
|
|
#endif
|
|
|
|
}
|
2007-06-10 22:46:09 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifndef INVALID_HANDLE_VALUE
|
2016-07-10 13:34:45 +02:00
|
|
|
#define INVALID_HANDLE_VALUE (-1)
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
2013-02-16 09:26:55 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
struct overlapped_t
|
|
|
|
{
|
|
|
|
overlapped_t()
|
|
|
|
{
|
2016-08-13 01:24:03 +02:00
|
|
|
std::memset(&ol, 0, sizeof(ol));
|
2013-02-16 09:26:55 +01:00
|
|
|
ol.hEvent = CreateEvent(0, true, false, 0);
|
|
|
|
}
|
|
|
|
~overlapped_t()
|
|
|
|
{
|
|
|
|
if (ol.hEvent != INVALID_HANDLE_VALUE)
|
|
|
|
CloseHandle(ol.hEvent);
|
|
|
|
}
|
|
|
|
int wait(HANDLE file, error_code& ec)
|
|
|
|
{
|
2013-04-11 01:37:22 +02:00
|
|
|
if (ol.hEvent != INVALID_HANDLE_VALUE
|
|
|
|
&& WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED)
|
2013-02-16 09:26:55 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2013-02-16 09:26:55 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD ret = -1;
|
|
|
|
if (GetOverlappedResult(file, &ol, &ret, false) == 0)
|
|
|
|
{
|
|
|
|
DWORD last_error = GetLastError();
|
|
|
|
if (last_error != ERROR_HANDLE_EOF)
|
|
|
|
{
|
2014-03-24 08:42:55 +01:00
|
|
|
#ifdef ERROR_CANT_WAIT
|
2013-02-16 09:26:55 +01:00
|
|
|
TORRENT_ASSERT(last_error != ERROR_CANT_WAIT);
|
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(last_error, system_category());
|
2013-02-16 09:26:55 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
OVERLAPPED ol;
|
|
|
|
};
|
|
|
|
#endif // TORRENT_WINDOWS
|
|
|
|
|
2014-02-23 20:13:53 +01:00
|
|
|
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
bool get_manage_volume_privs();
|
|
|
|
|
|
|
|
// this needs to be run before CreateFile
|
|
|
|
bool file::has_manage_volume_privs = get_manage_volume_privs();
|
|
|
|
#endif
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
file::file()
|
|
|
|
: m_file_handle(INVALID_HANDLE_VALUE)
|
|
|
|
, m_open_mode(0)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
m_file_id = 0;
|
|
|
|
#endif
|
|
|
|
}
|
2008-07-18 01:41:46 +02:00
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
file::file(std::string const& path, int mode, error_code& ec)
|
2008-07-20 13:14:54 +02:00
|
|
|
: m_file_handle(INVALID_HANDLE_VALUE)
|
|
|
|
, m_open_mode(0)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
m_file_id = 0;
|
|
|
|
#endif
|
2011-02-21 06:24:41 +01:00
|
|
|
// the return value is not important, since the
|
|
|
|
// error code contains the same information
|
2008-07-20 13:14:54 +02:00
|
|
|
open(path, mode, ec);
|
|
|
|
}
|
2008-07-18 01:41:46 +02:00
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
file::~file()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
2004-01-16 03:57:45 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
2015-09-02 07:30:40 +02:00
|
|
|
namespace
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint32_t silly_hash(std::string const& str)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint32_t ret = 1;
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto const ch : str)
|
2015-09-02 07:30:40 +02:00
|
|
|
{
|
2016-11-27 14:46:53 +01:00
|
|
|
if (ch == 0) continue;
|
|
|
|
ret *= std::uint32_t(ch);
|
2015-09-02 07:30:40 +02:00
|
|
|
}
|
|
|
|
return ret;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
bool file::open(std::string const& path, int mode, error_code& ec)
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
|
|
|
close();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#ifdef TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
m_file_path = path;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
m_file_id = silly_hash(path);
|
|
|
|
#endif
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2004-04-03 00:21:20 +02:00
|
|
|
|
2009-01-21 08:31:49 +01:00
|
|
|
struct open_mode_t
|
|
|
|
{
|
|
|
|
DWORD rw_mode;
|
|
|
|
DWORD create_mode;
|
|
|
|
};
|
|
|
|
|
2015-03-14 01:42:27 +01:00
|
|
|
static const open_mode_t mode_array[] =
|
2009-01-21 08:31:49 +01:00
|
|
|
{
|
|
|
|
// read_only
|
2011-06-09 08:08:24 +02:00
|
|
|
{GENERIC_READ, OPEN_EXISTING},
|
2009-01-21 08:31:49 +01:00
|
|
|
// write_only
|
2011-06-09 08:08:24 +02:00
|
|
|
{GENERIC_WRITE, OPEN_ALWAYS},
|
2009-01-21 08:31:49 +01:00
|
|
|
// read_write
|
2011-06-09 08:08:24 +02:00
|
|
|
{GENERIC_WRITE | GENERIC_READ, OPEN_ALWAYS},
|
2009-01-21 08:31:49 +01:00
|
|
|
};
|
|
|
|
|
2015-03-14 01:42:27 +01:00
|
|
|
static const DWORD attrib_array[] =
|
2009-01-21 08:31:49 +01:00
|
|
|
{
|
|
|
|
FILE_ATTRIBUTE_NORMAL, // no attrib
|
|
|
|
FILE_ATTRIBUTE_HIDDEN, // hidden
|
|
|
|
FILE_ATTRIBUTE_NORMAL, // executable
|
|
|
|
FILE_ATTRIBUTE_HIDDEN, // hidden + executable
|
|
|
|
};
|
|
|
|
|
2012-04-28 23:13:55 +02:00
|
|
|
std::string p = convert_separators(path);
|
2012-03-03 22:51:15 +01:00
|
|
|
#if TORRENT_USE_UNC_PATHS
|
|
|
|
// UNC paths must be absolute
|
2012-04-28 23:13:55 +02:00
|
|
|
// network paths are already UNC paths
|
2012-03-04 11:54:10 +01:00
|
|
|
if (path.substr(0,2) == "\\\\") p = path;
|
2012-03-17 20:06:11 +01:00
|
|
|
else p = "\\\\?\\" + (is_complete(p) ? p : combine_path(current_working_directory(), p));
|
2006-11-14 01:08:16 +01:00
|
|
|
#endif
|
2004-01-16 03:57:45 +01:00
|
|
|
|
2012-02-23 10:13:18 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
2012-03-03 22:51:15 +01:00
|
|
|
#define CreateFile_ CreateFileW
|
2016-01-18 23:59:30 +01:00
|
|
|
std::wstring file_path = convert_to_wstring(p);
|
2012-02-23 10:13:18 +01:00
|
|
|
#else
|
2012-03-03 22:51:15 +01:00
|
|
|
#define CreateFile_ CreateFileA
|
2016-01-18 23:59:30 +01:00
|
|
|
std::string file_path = convert_to_native(p);
|
2012-02-23 07:46:23 +01:00
|
|
|
#endif
|
|
|
|
|
2011-06-17 08:55:40 +02:00
|
|
|
TORRENT_ASSERT((mode & rw_mask) < sizeof(mode_array)/sizeof(mode_array[0]));
|
2011-06-09 08:08:24 +02:00
|
|
|
open_mode_t const& m = mode_array[mode & rw_mask];
|
2009-01-21 08:31:49 +01:00
|
|
|
DWORD a = attrib_array[(mode & attribute_mask) >> 12];
|
|
|
|
|
2012-05-27 20:17:51 +02:00
|
|
|
// one might think it's a good idea to pass in FILE_FLAG_RANDOM_ACCESS. It
|
|
|
|
// turns out that it isn't. That flag will break your operating system:
|
|
|
|
// http://support.microsoft.com/kb/2549369
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
DWORD flags = ((mode & random_access) ? 0 : FILE_FLAG_SEQUENTIAL_SCAN)
|
2011-06-09 08:08:24 +02:00
|
|
|
| (a ? a : FILE_ATTRIBUTE_NORMAL)
|
2014-07-06 21:18:00 +02:00
|
|
|
| FILE_FLAG_OVERLAPPED
|
|
|
|
| ((mode & no_cache) ? FILE_FLAG_WRITE_THROUGH : 0);
|
2011-04-25 04:15:18 +02:00
|
|
|
|
2016-01-18 23:59:30 +01:00
|
|
|
handle_type handle = CreateFile_(file_path.c_str(), m.rw_mode
|
2015-03-09 07:09:50 +01:00
|
|
|
, (mode & lock_file) ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE
|
2011-06-09 08:08:24 +02:00
|
|
|
, 0, m.create_mode, flags, 0);
|
2008-07-20 13:14:54 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
2004-01-16 03:57:45 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2011-02-21 06:24:41 +01:00
|
|
|
TORRENT_ASSERT(ec);
|
2008-07-20 13:14:54 +02:00
|
|
|
return false;
|
|
|
|
}
|
2004-03-31 01:55:52 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_file_handle = handle;
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
// try to make the file sparse if supported
|
2010-01-09 19:40:05 +01:00
|
|
|
// only set this flag if the file is opened for writing
|
|
|
|
if ((mode & file::sparse) && (mode & rw_mask) != read_only)
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
|
|
|
DWORD temp;
|
2013-02-16 09:26:55 +01:00
|
|
|
overlapped_t ol;
|
2014-07-06 21:18:00 +02:00
|
|
|
BOOL ret = ::DeviceIoControl(native_handle(), FSCTL_SET_SPARSE, 0, 0
|
|
|
|
, 0, 0, &temp, &ol.ol);
|
2013-02-16 09:26:55 +01:00
|
|
|
error_code error;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ret == FALSE && GetLastError() == ERROR_IO_PENDING)
|
|
|
|
ol.wait(native_handle(), error);
|
2004-01-16 03:57:45 +01:00
|
|
|
}
|
2009-10-26 02:29:39 +01:00
|
|
|
#else // TORRENT_WINDOWS
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
// rely on default umask to filter x and w permissions
|
|
|
|
// for group and others
|
2008-07-22 15:02:06 +02:00
|
|
|
int permissions = S_IRUSR | S_IWUSR
|
|
|
|
| S_IRGRP | S_IWGRP
|
|
|
|
| S_IROTH | S_IWOTH;
|
|
|
|
|
2009-01-11 23:27:43 +01:00
|
|
|
if (mode & attribute_executable)
|
|
|
|
permissions |= S_IXGRP | S_IXOTH | S_IXUSR;
|
2012-09-24 18:13:57 +02:00
|
|
|
#ifdef O_BINARY
|
|
|
|
static const int mode_array[] = {O_RDONLY | O_BINARY, O_WRONLY | O_CREAT | O_BINARY, O_RDWR | O_CREAT | O_BINARY};
|
|
|
|
#else
|
2009-01-21 08:31:49 +01:00
|
|
|
static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT};
|
2012-09-24 18:13:57 +02:00
|
|
|
#endif
|
2009-03-01 01:02:33 +01:00
|
|
|
|
2015-03-09 07:09:50 +01:00
|
|
|
int open_mode = 0
|
2010-02-02 20:44:52 +01:00
|
|
|
#ifdef O_NOATIME
|
2014-07-06 21:18:00 +02:00
|
|
|
| ((mode & no_atime) ? O_NOATIME : 0)
|
2010-02-02 20:44:52 +01:00
|
|
|
#endif
|
2015-01-08 00:21:54 +01:00
|
|
|
#ifdef O_SYNC
|
2016-10-26 02:46:23 +02:00
|
|
|
| ((mode & no_cache) ? O_SYNC : 0)
|
2012-07-13 06:04:56 +02:00
|
|
|
#endif
|
2015-03-09 07:09:50 +01:00
|
|
|
;
|
|
|
|
|
2016-03-20 05:55:31 +01:00
|
|
|
handle_type handle = ::open(convert_to_native(path).c_str()
|
|
|
|
, mode_array[mode & rw_mask] | open_mode
|
2014-07-06 21:18:00 +02:00
|
|
|
, permissions);
|
2009-01-17 10:37:40 +01:00
|
|
|
|
2012-07-13 06:04:56 +02:00
|
|
|
#ifdef O_NOATIME
|
|
|
|
// O_NOATIME is not allowed for files we don't own
|
|
|
|
// so, if we get EPERM when we try to open with it
|
|
|
|
// try again without O_NOATIME
|
2014-07-06 21:18:00 +02:00
|
|
|
if (handle == -1 && (mode & no_atime) && errno == EPERM)
|
2012-07-13 06:04:56 +02:00
|
|
|
{
|
|
|
|
mode &= ~no_atime;
|
2015-03-09 07:09:50 +01:00
|
|
|
open_mode &= ~O_NOATIME;
|
2016-10-26 02:46:23 +02:00
|
|
|
handle = ::open(convert_to_native(path).c_str(), mode_array[mode & rw_mask] | open_mode
|
2015-03-09 07:09:50 +01:00
|
|
|
, permissions);
|
2012-07-13 06:04:56 +02:00
|
|
|
}
|
2009-01-17 10:37:40 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (handle == -1)
|
2004-01-16 03:57:45 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-01-19 10:26:11 +01:00
|
|
|
TORRENT_ASSERT(ec);
|
2008-07-20 13:14:54 +02:00
|
|
|
return false;
|
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_file_handle = handle;
|
|
|
|
|
2014-04-16 09:43:36 +02:00
|
|
|
// The purpose of the lock_file flag is primarily to prevent other
|
|
|
|
// processes from corrupting files that are being used by libtorrent.
|
|
|
|
// the posix file locking mechanism does not prevent others from
|
|
|
|
// accessing files, unless they also attempt to lock the file. That's
|
|
|
|
// why the SETLK mechanism is not used here.
|
2011-06-09 08:08:24 +02:00
|
|
|
|
2010-03-21 18:26:21 +01:00
|
|
|
#ifdef DIRECTIO_ON
|
|
|
|
// for solaris
|
2014-07-06 21:18:00 +02:00
|
|
|
if (mode & no_cache)
|
2010-03-21 18:26:21 +01:00
|
|
|
{
|
|
|
|
int yes = 1;
|
2014-07-06 21:18:00 +02:00
|
|
|
directio(native_handle(), DIRECTIO_ON);
|
2010-03-21 18:26:21 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
#ifdef F_NOCACHE
|
2010-03-21 18:26:21 +01:00
|
|
|
// for BSD/Mac
|
2014-07-06 21:18:00 +02:00
|
|
|
if (mode & no_cache)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
|
|
|
int yes = 1;
|
2014-07-06 21:18:00 +02:00
|
|
|
fcntl(native_handle(), F_NOCACHE, &yes);
|
|
|
|
|
|
|
|
#ifdef F_NODIRECT
|
|
|
|
// it's OK to temporarily cache written pages
|
|
|
|
fcntl(native_handle(), F_NODIRECT, &yes);
|
|
|
|
#endif
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
2008-07-20 13:14:54 +02:00
|
|
|
#endif
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
#ifdef POSIX_FADV_RANDOM
|
2011-04-25 04:15:18 +02:00
|
|
|
if (mode & random_access)
|
|
|
|
{
|
|
|
|
// disable read-ahead
|
2014-07-06 21:18:00 +02:00
|
|
|
posix_fadvise(native_handle(), 0, 0, POSIX_FADV_RANDOM);
|
2011-04-25 04:15:18 +02:00
|
|
|
}
|
2008-07-20 13:14:54 +02:00
|
|
|
#endif
|
2009-01-11 03:02:34 +01:00
|
|
|
|
|
|
|
#endif
|
|
|
|
m_open_mode = mode;
|
|
|
|
|
2008-07-20 18:34:01 +02:00
|
|
|
TORRENT_ASSERT(is_open());
|
2008-07-20 13:14:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
2004-03-31 01:55:52 +02:00
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#ifdef TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
void file::print_info(FILE* out) const
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!is_open()) return;
|
2016-05-17 15:24:06 +02:00
|
|
|
std::fprintf(out, "\n===> FILE: %s\n", m_file_path.c_str());
|
2009-01-17 09:35:48 +01:00
|
|
|
}
|
2009-01-19 10:21:21 +01:00
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool file::is_open() const
|
2009-01-21 08:31:49 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
return m_file_handle != INVALID_HANDLE_VALUE;
|
2009-01-21 08:31:49 +01:00
|
|
|
}
|
|
|
|
|
2013-02-16 09:26:55 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2014-07-06 21:18:00 +02:00
|
|
|
// returns true if the given file has any regions that are
|
|
|
|
// sparse, i.e. not allocated.
|
|
|
|
bool is_sparse(HANDLE file)
|
2013-02-16 09:26:55 +01:00
|
|
|
{
|
|
|
|
LARGE_INTEGER file_size;
|
|
|
|
if (!GetFileSizeEx(file, &file_size))
|
2014-05-10 05:23:05 +02:00
|
|
|
return false;
|
2013-02-16 09:26:55 +01:00
|
|
|
|
|
|
|
overlapped_t ol;
|
2016-06-20 17:32:06 +02:00
|
|
|
if (ol.ol.hEvent == nullptr) return false;
|
2013-02-16 09:26:55 +01:00
|
|
|
|
2016-05-15 19:28:22 +02:00
|
|
|
#ifndef FSCTL_QUERY_ALLOCATED_RANGES
|
2013-02-16 09:26:55 +01:00
|
|
|
typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
|
|
|
|
LARGE_INTEGER FileOffset;
|
|
|
|
LARGE_INTEGER Length;
|
2016-05-15 19:28:22 +02:00
|
|
|
} FILE_ALLOCATED_RANGE_BUFFER;
|
2013-02-16 09:26:55 +01:00
|
|
|
#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3)
|
|
|
|
#endif
|
|
|
|
FILE_ALLOCATED_RANGE_BUFFER in;
|
|
|
|
in.FileOffset.QuadPart = 0;
|
|
|
|
in.Length.QuadPart = file_size.QuadPart;
|
|
|
|
|
|
|
|
FILE_ALLOCATED_RANGE_BUFFER out[2];
|
|
|
|
|
|
|
|
DWORD returned_bytes = 0;
|
|
|
|
BOOL ret = DeviceIoControl(file, FSCTL_QUERY_ALLOCATED_RANGES, (void*)&in, sizeof(in)
|
2014-07-06 21:18:00 +02:00
|
|
|
, out, sizeof(out), &returned_bytes, &ol.ol);
|
2013-02-16 09:26:55 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ret == FALSE && GetLastError() == ERROR_IO_PENDING)
|
2013-02-16 09:26:55 +01:00
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
returned_bytes = ol.wait(file, ec);
|
|
|
|
if (ec) return true;
|
|
|
|
}
|
|
|
|
else if (ret == FALSE)
|
|
|
|
{
|
2015-11-19 01:51:17 +01:00
|
|
|
// int error = GetLastError();
|
2013-02-16 09:26:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-11 01:38:54 +02:00
|
|
|
// if we have more than one range in the file, we're sparse
|
|
|
|
if (returned_bytes != sizeof(FILE_ALLOCATED_RANGE_BUFFER)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (in.Length.QuadPart != out[0].Length.QuadPart);
|
2013-02-16 09:26:55 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
void file::close()
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_DISK_STATS
|
|
|
|
m_file_id = 0;
|
|
|
|
#endif
|
2009-01-17 09:35:48 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!is_open()) return;
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2013-02-16 09:26:55 +01:00
|
|
|
|
|
|
|
// if this file is open for writing, has the sparse
|
|
|
|
// flag set, but there are no sparse regions, unset
|
|
|
|
// the flag
|
|
|
|
int rw_mode = m_open_mode & rw_mask;
|
2014-02-19 18:26:58 +01:00
|
|
|
if ((rw_mode != read_only)
|
2013-02-16 09:26:55 +01:00
|
|
|
&& (m_open_mode & sparse)
|
2014-07-06 21:18:00 +02:00
|
|
|
&& !is_sparse(native_handle()))
|
2013-02-16 09:26:55 +01:00
|
|
|
{
|
|
|
|
overlapped_t ol;
|
|
|
|
// according to MSDN, clearing the sparse flag of a file only
|
|
|
|
// works on windows vista and later
|
|
|
|
#ifdef TORRENT_MINGW
|
2014-07-06 21:18:00 +02:00
|
|
|
typedef struct _FILE_SET_SPARSE_BUFFER {
|
|
|
|
BOOLEAN SetSparse;
|
2016-05-15 19:28:22 +02:00
|
|
|
} FILE_SET_SPARSE_BUFFER;
|
2013-02-16 09:26:55 +01:00
|
|
|
#endif
|
|
|
|
DWORD temp;
|
|
|
|
FILE_SET_SPARSE_BUFFER b;
|
|
|
|
b.SetSparse = FALSE;
|
2014-07-06 21:18:00 +02:00
|
|
|
BOOL ret = ::DeviceIoControl(native_handle(), FSCTL_SET_SPARSE, &b, sizeof(b)
|
|
|
|
, 0, 0, &temp, &ol.ol);
|
2013-02-16 09:26:55 +01:00
|
|
|
error_code ec;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ret == FALSE && GetLastError() == ERROR_IO_PENDING)
|
2013-02-16 09:26:55 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ol.wait(native_handle(), ec);
|
2013-02-16 09:26:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
CloseHandle(native_handle());
|
2006-11-14 01:08:16 +01:00
|
|
|
#else
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_file_handle != INVALID_HANDLE_VALUE)
|
|
|
|
::close(m_file_handle);
|
2006-11-14 01:08:16 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
m_file_handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
m_open_mode = 0;
|
2009-01-11 03:02:34 +01:00
|
|
|
}
|
|
|
|
|
2015-04-19 00:00:27 +02:00
|
|
|
namespace {
|
|
|
|
|
2016-05-15 19:28:22 +02:00
|
|
|
#if !TORRENT_USE_PREADV
|
2016-10-27 02:40:56 +02:00
|
|
|
void gather_copy(span<file::iovec_t const> bufs, char* dst)
|
2011-04-26 09:03:05 +02:00
|
|
|
{
|
2015-11-19 01:51:17 +01:00
|
|
|
std::size_t offset = 0;
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto buf : bufs)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
std::memcpy(dst + offset, buf.iov_base, buf.iov_len);
|
|
|
|
offset += buf.iov_len;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2011-04-26 09:03:05 +02:00
|
|
|
}
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
void scatter_copy(span<file::iovec_t const> bufs, char const* src)
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
2015-11-19 01:51:17 +01:00
|
|
|
std::size_t offset = 0;
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto buf : bufs)
|
2013-01-06 19:38:33 +01:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
std::memcpy(buf.iov_base, src + offset, buf.iov_len);
|
|
|
|
offset += buf.iov_len;
|
2013-01-06 19:38:33 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2008-07-20 13:14:54 +02:00
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
bool coalesce_read_buffers(span<file::iovec_t const>& bufs
|
|
|
|
, file::iovec_t& tmp)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
int const buf_size = bufs_size(bufs);
|
2016-08-13 01:24:03 +02:00
|
|
|
char* buf = static_cast<char*>(std::malloc(buf_size));
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!buf) return false;
|
2016-10-27 02:40:56 +02:00
|
|
|
tmp.iov_base = buf;
|
|
|
|
tmp.iov_len = buf_size;
|
|
|
|
bufs = span<file::iovec_t const>(tmp);
|
2014-07-06 21:18:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
void coalesce_read_buffers_end(span<file::iovec_t const> bufs
|
2016-03-20 05:55:31 +01:00
|
|
|
, char* const buf, bool const copy)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
if (copy) scatter_copy(bufs, buf);
|
2016-08-13 01:24:03 +02:00
|
|
|
std::free(buf);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
bool coalesce_write_buffers(span<file::iovec_t const>& bufs
|
|
|
|
, file::iovec_t& tmp)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
int const buf_size = bufs_size(bufs);
|
2016-08-13 01:24:03 +02:00
|
|
|
char* buf = static_cast<char*>(std::malloc(buf_size));
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!buf) return false;
|
2016-10-27 02:40:56 +02:00
|
|
|
gather_copy(bufs, buf);
|
|
|
|
tmp.iov_base = buf;
|
|
|
|
tmp.iov_len = buf_size;
|
|
|
|
bufs = span<file::iovec_t const>(tmp);
|
2014-07-06 21:18:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
2015-12-12 06:15:24 +01:00
|
|
|
#endif // TORRENT_USE_PREADV
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
template <class Fun>
|
2016-10-27 02:40:56 +02:00
|
|
|
std::int64_t iov(Fun f, handle_type fd, std::int64_t file_offset
|
|
|
|
, span<file::iovec_t const> bufs, error_code& ec)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
#if TORRENT_USE_PREADV
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int ret = 0;
|
2016-10-27 02:40:56 +02:00
|
|
|
while (!bufs.empty())
|
2007-04-19 05:06:15 +02:00
|
|
|
{
|
2016-05-17 15:24:06 +02:00
|
|
|
#ifdef IOV_MAX
|
2016-10-27 02:40:56 +02:00
|
|
|
auto const nbufs = bufs.first((std::min)(int(bufs.size()), IOV_MAX));
|
2016-05-17 15:24:06 +02:00
|
|
|
#else
|
2016-10-27 02:40:56 +02:00
|
|
|
auto const nbufs = bufs;
|
2016-05-17 15:24:06 +02:00
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int tmp_ret = 0;
|
2016-10-27 02:40:56 +02:00
|
|
|
tmp_ret = f(fd, nbufs.data(), int(nbufs.size()), file_offset);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (tmp_ret < 0)
|
2007-08-11 17:22:06 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
ec.assign(GetLastError(), system_category());
|
|
|
|
#else
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2008-07-20 13:14:54 +02:00
|
|
|
return -1;
|
2007-08-11 17:22:06 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
file_offset += tmp_ret;
|
|
|
|
ret += tmp_ret;
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2015-11-14 06:21:03 +01:00
|
|
|
// we got a short read/write. It's either 0, and we're at EOF, or we
|
|
|
|
// just need to issue the read/write operation again. In either case,
|
|
|
|
// punt that to the upper layer, as reissuing the operations is
|
|
|
|
// complicated here
|
2016-10-27 02:40:56 +02:00
|
|
|
const int expected_len = bufs_size(nbufs);
|
2015-11-14 06:21:03 +01:00
|
|
|
if (tmp_ret < expected_len) break;
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
bufs = bufs.subspan(nbufs.size());
|
2007-04-19 05:06:15 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
return ret;
|
2004-04-05 00:15:31 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#elif TORRENT_USE_PREAD
|
2009-01-01 02:47:57 +01:00
|
|
|
|
2016-12-09 14:23:54 +01:00
|
|
|
std::int64_t ret = 0;
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto i : bufs)
|
2009-01-01 02:47:57 +01:00
|
|
|
{
|
2016-12-10 20:15:25 +01:00
|
|
|
std::int64_t const tmp_ret = f(fd, i.iov_base, i.iov_len, file_offset);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (tmp_ret < 0)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2014-07-20 10:59:02 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
#else
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2013-01-11 05:49:08 +01:00
|
|
|
#endif
|
2013-02-13 19:03:59 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
file_offset += tmp_ret;
|
|
|
|
ret += tmp_ret;
|
2016-10-27 02:40:56 +02:00
|
|
|
if (tmp_ret < int(i.iov_len)) break;
|
2009-01-01 02:47:57 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
return ret;
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2014-07-20 10:59:02 +02:00
|
|
|
#else // not PREADV nor PREAD
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
int ret = 0;
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
if (SetFilePointerEx(fd, offs, &offs, FILE_BEGIN) == FALSE)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2009-01-11 03:02:34 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#else
|
|
|
|
if (lseek(fd, file_offset, SEEK_SET) < 0)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
return -1;
|
2011-09-05 07:47:50 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2011-09-05 07:47:50 +02:00
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
for (auto i : bufs)
|
2009-04-04 10:23:53 +02:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
int tmp_ret = f(fd, i.iov_base, i.iov_len);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (tmp_ret < 0)
|
2009-04-04 10:23:53 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
ec.assign(GetLastError(), system_category());
|
|
|
|
#else
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2009-04-04 10:23:53 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
file_offset += tmp_ret;
|
|
|
|
ret += tmp_ret;
|
2016-10-27 02:40:56 +02:00
|
|
|
if (tmp_ret < int(i.iov_len)) break;
|
2009-04-04 10:23:53 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
return ret;
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2016-05-15 19:28:22 +02:00
|
|
|
#endif // USE_PREADV
|
2009-01-01 02:47:57 +01:00
|
|
|
}
|
|
|
|
|
2015-04-19 00:00:27 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// this has to be thread safe and atomic. i.e. on posix systems it has to be
|
|
|
|
// turned into a series of pread() calls
|
2016-10-27 02:40:56 +02:00
|
|
|
std::int64_t file::readv(std::int64_t file_offset, span<iovec_t const> bufs
|
2014-07-06 21:18:00 +02:00
|
|
|
, error_code& ec, int flags)
|
2009-01-01 02:47:57 +01:00
|
|
|
{
|
2013-01-06 19:38:33 +01:00
|
|
|
if (m_file_handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
ec = error_code(ERROR_INVALID_HANDLE, system_category());
|
2013-01-06 19:38:33 +01:00
|
|
|
#else
|
2015-11-24 06:50:51 +01:00
|
|
|
ec = error_code(boost::system::errc::bad_file_descriptor, generic_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2013-01-06 19:38:33 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write);
|
2016-10-27 02:40:56 +02:00
|
|
|
TORRENT_ASSERT(!bufs.empty());
|
2009-01-01 02:47:57 +01:00
|
|
|
TORRENT_ASSERT(is_open());
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_PREADV
|
2015-08-21 10:05:51 +02:00
|
|
|
TORRENT_UNUSED(flags);
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
int ret = iov(&::preadv, native_handle(), file_offset, bufs, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
#else
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2016-03-20 16:38:55 +01:00
|
|
|
// there's no point in coalescing single buffer writes
|
2016-10-27 02:40:56 +02:00
|
|
|
if (bufs.size() == 1)
|
2016-03-20 16:38:55 +01:00
|
|
|
{
|
|
|
|
flags &= ~file::coalesce_buffers;
|
|
|
|
}
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
iovec_t tmp;
|
|
|
|
span<iovec_t const> tmp_bufs = bufs;
|
2016-03-20 16:38:55 +01:00
|
|
|
if ((flags & file::coalesce_buffers))
|
2009-01-01 02:47:57 +01:00
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
if (!coalesce_read_buffers(tmp_bufs, tmp))
|
2014-07-06 21:18:00 +02:00
|
|
|
// ok, that failed, don't coalesce this read
|
|
|
|
flags &= ~file::coalesce_buffers;
|
2009-01-01 02:47:57 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_PREAD
|
2016-12-09 14:23:54 +01:00
|
|
|
std::int64_t ret = iov(&::pread, native_handle(), file_offset, tmp_bufs, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
#else
|
2016-12-09 14:23:54 +01:00
|
|
|
std::int64_t ret = iov(&::read, native_handle(), file_offset, tmp_bufs, ec);
|
2014-05-23 04:09:27 +02:00
|
|
|
#endif
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2016-03-20 16:38:55 +01:00
|
|
|
if ((flags & file::coalesce_buffers))
|
2016-10-27 02:40:56 +02:00
|
|
|
coalesce_read_buffers_end(bufs
|
2015-08-08 08:33:54 +02:00
|
|
|
, static_cast<char*>(tmp.iov_base), !ec);
|
2009-01-11 03:02:34 +01:00
|
|
|
|
2013-01-11 05:49:08 +01:00
|
|
|
#endif
|
2009-01-11 03:02:34 +01:00
|
|
|
return ret;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// This has to be thread safe, i.e. atomic.
|
|
|
|
// that means, on posix this has to be turned into a series of
|
|
|
|
// pwrite() calls
|
2016-10-27 02:40:56 +02:00
|
|
|
std::int64_t file::writev(std::int64_t file_offset, span<iovec_t const> bufs
|
2014-07-06 21:18:00 +02:00
|
|
|
, error_code& ec, int flags)
|
|
|
|
{
|
|
|
|
if (m_file_handle == INVALID_HANDLE_VALUE)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
ec = error_code(ERROR_INVALID_HANDLE, system_category());
|
|
|
|
#else
|
2015-11-24 06:50:51 +01:00
|
|
|
ec = error_code(boost::system::errc::bad_file_descriptor, generic_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2009-01-11 03:02:34 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write);
|
2016-10-27 02:40:56 +02:00
|
|
|
TORRENT_ASSERT(!bufs.empty());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(is_open());
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.clear();
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_PREADV
|
2015-08-21 10:05:51 +02:00
|
|
|
TORRENT_UNUSED(flags);
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
int ret = iov(&::pwritev, native_handle(), file_offset, bufs, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
#else
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2016-03-20 16:38:55 +01:00
|
|
|
// there's no point in coalescing single buffer writes
|
2016-10-27 02:40:56 +02:00
|
|
|
if (bufs.size() == 1)
|
2016-03-20 16:38:55 +01:00
|
|
|
{
|
|
|
|
flags &= ~file::coalesce_buffers;
|
|
|
|
}
|
|
|
|
|
2016-10-27 02:40:56 +02:00
|
|
|
iovec_t tmp;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (flags & file::coalesce_buffers)
|
|
|
|
{
|
2016-10-27 02:40:56 +02:00
|
|
|
if (!coalesce_write_buffers(bufs, tmp))
|
2014-07-06 21:18:00 +02:00
|
|
|
// ok, that failed, don't coalesce writes
|
|
|
|
flags &= ~file::coalesce_buffers;
|
2011-09-05 07:47:50 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_PREAD
|
2016-12-09 14:23:54 +01:00
|
|
|
std::int64_t ret = iov(&::pwrite, native_handle(), file_offset, bufs, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
#else
|
2016-12-09 14:23:54 +01:00
|
|
|
std::int64_t ret = iov(&::write, native_handle(), file_offset, bufs, ec);
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2011-09-05 07:47:50 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (flags & file::coalesce_buffers)
|
2016-08-13 01:24:03 +02:00
|
|
|
std::free(tmp.iov_base);
|
2009-04-04 10:23:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2015-08-09 04:53:11 +02:00
|
|
|
#if TORRENT_USE_FDATASYNC \
|
2014-07-06 21:18:00 +02:00
|
|
|
&& !defined F_NOCACHE && \
|
|
|
|
!defined DIRECTIO_ON
|
|
|
|
if (m_open_mode & no_cache)
|
2009-04-04 10:23:53 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (fdatasync(native_handle()) != 0
|
|
|
|
&& errno != EINVAL
|
|
|
|
&& errno != ENOSYS)
|
2009-04-04 10:23:53 +02:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2009-04-04 10:23:53 +02:00
|
|
|
}
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2009-04-04 10:23:53 +02:00
|
|
|
return ret;
|
2009-09-05 09:21:10 +02:00
|
|
|
}
|
|
|
|
|
2014-02-08 10:18:09 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
bool get_manage_volume_privs()
|
|
|
|
{
|
|
|
|
typedef BOOL (WINAPI *OpenProcessToken_t)(
|
|
|
|
HANDLE ProcessHandle,
|
|
|
|
DWORD DesiredAccess,
|
|
|
|
PHANDLE TokenHandle);
|
|
|
|
|
|
|
|
typedef BOOL (WINAPI *LookupPrivilegeValue_t)(
|
|
|
|
LPCSTR lpSystemName,
|
|
|
|
LPCSTR lpName,
|
|
|
|
PLUID lpLuid);
|
|
|
|
|
|
|
|
typedef BOOL (WINAPI *AdjustTokenPrivileges_t)(
|
|
|
|
HANDLE TokenHandle,
|
|
|
|
BOOL DisableAllPrivileges,
|
|
|
|
PTOKEN_PRIVILEGES NewState,
|
|
|
|
DWORD BufferLength,
|
|
|
|
PTOKEN_PRIVILEGES PreviousState,
|
|
|
|
PDWORD ReturnLength);
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
static OpenProcessToken_t pOpenProcessToken = nullptr;
|
|
|
|
static LookupPrivilegeValue_t pLookupPrivilegeValue = nullptr;
|
|
|
|
static AdjustTokenPrivileges_t pAdjustTokenPrivileges = nullptr;
|
2014-02-08 10:18:09 +01:00
|
|
|
static bool failed_advapi = false;
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
if (pOpenProcessToken == nullptr && !failed_advapi)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
HMODULE advapi = LoadLibraryA("advapi32");
|
2016-06-20 17:32:06 +02:00
|
|
|
if (advapi == nullptr)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
failed_advapi = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pOpenProcessToken = (OpenProcessToken_t)GetProcAddress(advapi, "OpenProcessToken");
|
|
|
|
pLookupPrivilegeValue = (LookupPrivilegeValue_t)GetProcAddress(advapi, "LookupPrivilegeValueA");
|
|
|
|
pAdjustTokenPrivileges = (AdjustTokenPrivileges_t)GetProcAddress(advapi, "AdjustTokenPrivileges");
|
2016-06-20 17:32:06 +02:00
|
|
|
if (pOpenProcessToken == nullptr
|
|
|
|
|| pLookupPrivilegeValue == nullptr
|
|
|
|
|| pAdjustTokenPrivileges == nullptr)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
failed_advapi = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE token;
|
|
|
|
if (!pOpenProcessToken(GetCurrentProcess()
|
|
|
|
, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TOKEN_PRIVILEGES privs;
|
2016-06-20 17:32:06 +02:00
|
|
|
if (!pLookupPrivilegeValue(nullptr, "SeManageVolumePrivilege"
|
2014-02-08 10:18:09 +01:00
|
|
|
, &privs.Privileges[0].Luid))
|
|
|
|
{
|
|
|
|
CloseHandle(token);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
privs.PrivilegeCount = 1;
|
|
|
|
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
bool ret = pAdjustTokenPrivileges(token, FALSE, &privs, 0, nullptr, nullptr)
|
2014-02-08 10:18:09 +01:00
|
|
|
&& GetLastError() == ERROR_SUCCESS;
|
|
|
|
|
|
|
|
CloseHandle(token);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
void set_file_valid_data(HANDLE f, std::int64_t size)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
typedef BOOL (WINAPI *SetFileValidData_t)(HANDLE, LONGLONG);
|
2016-06-20 17:32:06 +02:00
|
|
|
static SetFileValidData_t pSetFileValidData = nullptr;
|
2014-02-08 10:18:09 +01:00
|
|
|
static bool failed_kernel32 = false;
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
if (pSetFileValidData == nullptr && !failed_kernel32)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
HMODULE k32 = LoadLibraryA("kernel32");
|
2016-06-20 17:32:06 +02:00
|
|
|
if (k32 == nullptr)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
failed_kernel32 = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pSetFileValidData = (SetFileValidData_t)GetProcAddress(k32, "SetFileValidData");
|
2016-06-20 17:32:06 +02:00
|
|
|
if (pSetFileValidData == nullptr)
|
2014-02-08 10:18:09 +01:00
|
|
|
{
|
|
|
|
failed_kernel32 = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TORRENT_ASSERT(pSetFileValidData);
|
|
|
|
|
|
|
|
// we don't necessarily expect to have enough
|
|
|
|
// privilege to do this, so ignore errors.
|
|
|
|
pSetFileValidData(f, size);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
bool file::set_size(std::int64_t s, error_code& ec)
|
2015-06-21 18:41:50 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_open());
|
|
|
|
TORRENT_ASSERT(s >= 0);
|
2004-01-16 03:57:45 +01:00
|
|
|
|
2008-07-20 13:14:54 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2011-11-13 05:12:56 +01:00
|
|
|
|
2009-01-11 03:02:34 +01:00
|
|
|
LARGE_INTEGER offs;
|
2010-03-06 21:47:44 +01:00
|
|
|
LARGE_INTEGER cur_size;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (GetFileSizeEx(native_handle(), &cur_size) == FALSE)
|
2009-01-11 03:02:34 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2009-01-11 03:02:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
2010-03-06 21:47:44 +01:00
|
|
|
offs.QuadPart = s;
|
|
|
|
// only set the file size if it's not already at
|
|
|
|
// the right size. We don't want to update the
|
|
|
|
// modification time if we don't have to
|
|
|
|
if (cur_size.QuadPart != s)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (SetFilePointerEx(native_handle(), offs, &offs, FILE_BEGIN) == FALSE)
|
2010-03-06 21:47:44 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2010-03-06 21:47:44 +01:00
|
|
|
return false;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
if (::SetEndOfFile(native_handle()) == FALSE)
|
2010-03-06 21:47:44 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2010-03-06 21:47:44 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2013-01-07 02:56:40 +01:00
|
|
|
|
2016-01-25 18:22:33 +01:00
|
|
|
#if _WIN32_WINNT >= 0x0600 // only if Windows Vista or newer
|
2009-02-16 12:04:54 +01:00
|
|
|
if ((m_open_mode & sparse) == 0)
|
2009-02-14 05:18:17 +01:00
|
|
|
{
|
2016-01-18 23:59:30 +01:00
|
|
|
typedef DWORD (WINAPI *GetFileInformationByHandleEx_t)(HANDLE hFile
|
|
|
|
, FILE_INFO_BY_HANDLE_CLASS FileInformationClass
|
|
|
|
, LPVOID lpFileInformation
|
|
|
|
, DWORD dwBufferSize);
|
2013-01-07 02:56:40 +01:00
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
static GetFileInformationByHandleEx_t GetFileInformationByHandleEx_ = nullptr;
|
2013-01-07 02:56:40 +01:00
|
|
|
|
|
|
|
static bool failed_kernel32 = false;
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
if ((GetFileInformationByHandleEx_ == nullptr) && !failed_kernel32)
|
2010-03-20 09:18:49 +01:00
|
|
|
{
|
2013-01-07 02:56:40 +01:00
|
|
|
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
|
|
|
|
if (kernel32)
|
|
|
|
{
|
2016-01-18 23:59:30 +01:00
|
|
|
GetFileInformationByHandleEx_ = (GetFileInformationByHandleEx_t)GetProcAddress(kernel32, "GetFileInformationByHandleEx");
|
2013-01-07 02:56:40 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
failed_kernel32 = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-08 10:18:09 +01:00
|
|
|
offs.QuadPart = 0;
|
2016-01-18 23:59:30 +01:00
|
|
|
if (GetFileInformationByHandleEx_)
|
2013-01-07 02:56:40 +01:00
|
|
|
{
|
|
|
|
// only allocate the space if the file
|
|
|
|
// is not fully allocated
|
2016-01-18 23:59:30 +01:00
|
|
|
FILE_STANDARD_INFO inf;
|
|
|
|
if (GetFileInformationByHandleEx_(native_handle()
|
|
|
|
, FileStandardInfo, &inf, sizeof(inf)) == FALSE)
|
2013-11-20 09:06:33 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2013-11-20 09:06:33 +01:00
|
|
|
if (ec) return false;
|
|
|
|
}
|
2016-01-18 23:59:30 +01:00
|
|
|
offs = inf.AllocationSize;
|
2014-02-08 10:18:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (offs.QuadPart != s)
|
|
|
|
{
|
|
|
|
// if the user has permissions, avoid filling
|
|
|
|
// the file with zeroes, but just fill it with
|
|
|
|
// garbage instead
|
|
|
|
set_file_valid_data(m_file_handle, s);
|
2010-03-20 09:18:49 +01:00
|
|
|
}
|
2009-02-14 05:18:17 +01:00
|
|
|
}
|
2016-01-25 18:22:33 +01:00
|
|
|
#endif // if Windows Vista
|
2010-10-18 09:15:57 +02:00
|
|
|
#else // NON-WINDOWS
|
2010-03-06 21:09:18 +01:00
|
|
|
struct stat st;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (fstat(native_handle(), &st) != 0)
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2008-07-20 13:14:54 +02:00
|
|
|
return false;
|
|
|
|
}
|
2010-03-06 21:09:18 +01:00
|
|
|
|
|
|
|
// only truncate the file if it doesn't already
|
|
|
|
// have the right size. We don't want to update
|
2014-07-06 21:18:00 +02:00
|
|
|
if (st.st_size != s && ftruncate(native_handle(), s) < 0)
|
2010-03-06 21:09:18 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2010-03-06 21:09:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we're not in sparse mode, allocate the storage
|
|
|
|
// but only if the number of allocated blocks for the file
|
|
|
|
// is less than the file size. Otherwise we would just
|
|
|
|
// update the modification time of the file for no good
|
|
|
|
// reason.
|
|
|
|
if ((m_open_mode & sparse) == 0
|
2016-12-12 03:37:07 +01:00
|
|
|
&& std::int64_t(st.st_blocks) < (s + st.st_blksize - 1) / st.st_blksize)
|
2009-02-16 01:42:44 +01:00
|
|
|
{
|
2010-03-06 21:09:18 +01:00
|
|
|
// How do we know that the file is already allocated?
|
|
|
|
// if we always try to allocate the space, we'll update
|
|
|
|
// the modification time without actually changing the file
|
|
|
|
// but if we don't do anything if the file size is
|
2009-02-16 01:42:44 +01:00
|
|
|
#ifdef F_PREALLOCATE
|
|
|
|
fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, s, 0};
|
2014-07-06 21:18:00 +02:00
|
|
|
if (fcntl(native_handle(), F_PREALLOCATE, &f) < 0)
|
2009-02-16 01:42:44 +01:00
|
|
|
{
|
2011-04-13 08:54:56 +02:00
|
|
|
if (errno != ENOSPC)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2011-04-13 08:54:56 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// ok, let's try to allocate non contiguous space then
|
2015-08-09 04:53:11 +02:00
|
|
|
f.fst_flags = F_ALLOCATEALL;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (fcntl(native_handle(), F_PREALLOCATE, &f) < 0)
|
2011-04-13 08:54:56 +02:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2011-04-13 08:54:56 +02:00
|
|
|
return false;
|
|
|
|
}
|
2009-02-16 01:42:44 +01:00
|
|
|
}
|
2010-04-08 07:09:11 +02:00
|
|
|
#endif // F_PREALLOCATE
|
|
|
|
|
2011-09-09 01:08:37 +02:00
|
|
|
#ifdef F_ALLOCSP64
|
|
|
|
flock64 fl64;
|
|
|
|
fl64.l_whence = SEEK_SET;
|
|
|
|
fl64.l_start = 0;
|
|
|
|
fl64.l_len = s;
|
|
|
|
if (fcntl(native_handle(), F_ALLOCSP64, &fl64) < 0)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2011-09-09 01:08:37 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // F_ALLOCSP64
|
|
|
|
|
2010-04-08 07:09:11 +02:00
|
|
|
#if TORRENT_HAS_FALLOCATE
|
2010-03-06 21:09:18 +01:00
|
|
|
// if fallocate failed, we have to use posix_fallocate
|
|
|
|
// which can be painfully slow
|
2010-04-08 07:31:00 +02:00
|
|
|
// if you get a compile error here, you might want to
|
|
|
|
// define TORRENT_HAS_FALLOCATE to 0.
|
2016-04-30 06:24:45 +02:00
|
|
|
int const ret = posix_fallocate(native_handle(), 0, s);
|
2010-10-18 09:15:57 +02:00
|
|
|
// posix_allocate fails with EINVAL in case the underlying
|
2015-09-21 23:58:20 +02:00
|
|
|
// filesystem does not support this operation
|
2010-10-18 09:15:57 +02:00
|
|
|
if (ret != 0 && ret != EINVAL)
|
2010-01-18 01:51:40 +01:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(ret, system_category());
|
2010-01-18 01:51:40 +01:00
|
|
|
return false;
|
|
|
|
}
|
2010-04-08 07:09:11 +02:00
|
|
|
#endif // TORRENT_HAS_FALLOCATE
|
2009-02-16 01:42:44 +01:00
|
|
|
}
|
2010-04-08 07:09:11 +02:00
|
|
|
#endif // TORRENT_WINDOWS
|
2008-07-20 13:14:54 +02:00
|
|
|
return true;
|
2004-01-16 03:57:45 +01:00
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t file::get_size(error_code& ec) const
|
2004-01-16 03:57:45 +01:00
|
|
|
{
|
2008-07-20 13:14:54 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2009-01-11 03:02:34 +01:00
|
|
|
LARGE_INTEGER file_size;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!GetFileSizeEx(native_handle(), &file_size))
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
ec.assign(GetLastError(), system_category());
|
2008-07-20 13:14:54 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
return file_size.QuadPart;
|
2008-07-20 13:14:54 +02:00
|
|
|
#else
|
2009-01-11 03:02:34 +01:00
|
|
|
struct stat fs;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (fstat(native_handle(), &fs) != 0)
|
2008-07-20 13:14:54 +02:00
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, system_category());
|
2008-07-20 13:14:54 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2009-01-11 03:02:34 +01:00
|
|
|
return fs.st_size;
|
2008-07-20 13:14:54 +02:00
|
|
|
#endif
|
2004-01-16 03:57:45 +01:00
|
|
|
}
|
2009-02-17 01:11:38 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t file::sparse_end(std::int64_t start) const
|
2009-02-17 01:11:38 +01:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_WINDOWS
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-05-15 19:28:22 +02:00
|
|
|
#ifndef FSCTL_QUERY_ALLOCATED_RANGES
|
2010-02-14 05:05:18 +01:00
|
|
|
typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
|
|
|
|
LARGE_INTEGER FileOffset;
|
|
|
|
LARGE_INTEGER Length;
|
2016-05-15 19:28:22 +02:00
|
|
|
} FILE_ALLOCATED_RANGE_BUFFER;
|
2010-02-14 05:05:18 +01:00
|
|
|
#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3)
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif // TORRENT_MINGW
|
|
|
|
|
2009-02-17 01:11:38 +01:00
|
|
|
FILE_ALLOCATED_RANGE_BUFFER buffer;
|
|
|
|
DWORD bytes_returned = 0;
|
|
|
|
FILE_ALLOCATED_RANGE_BUFFER in;
|
|
|
|
error_code ec;
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t file_size = get_size(ec);
|
2009-02-17 01:11:38 +01:00
|
|
|
if (ec) return start;
|
2012-01-22 00:24:24 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
in.FileOffset.QuadPart = start;
|
|
|
|
in.Length.QuadPart = file_size - start;
|
2012-01-22 00:24:24 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!DeviceIoControl(native_handle(), FSCTL_QUERY_ALLOCATED_RANGES
|
2009-02-17 01:11:38 +01:00
|
|
|
, &in, sizeof(FILE_ALLOCATED_RANGE_BUFFER)
|
|
|
|
, &buffer, sizeof(FILE_ALLOCATED_RANGE_BUFFER), &bytes_returned, 0))
|
|
|
|
{
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there are no allocated regions within the rest
|
|
|
|
// of the file, return the end of the file
|
|
|
|
if (bytes_returned == 0) return file_size;
|
|
|
|
|
|
|
|
// assume that this range overlaps the start of the
|
|
|
|
// region we were interested in, and that start actually
|
|
|
|
// resides in an allocated region.
|
|
|
|
if (buffer.FileOffset.QuadPart < start) return start;
|
|
|
|
|
|
|
|
// return the offset to the next allocated region
|
|
|
|
return buffer.FileOffset.QuadPart;
|
2016-03-20 05:55:31 +01:00
|
|
|
|
2009-02-17 01:11:38 +01:00
|
|
|
#elif defined SEEK_DATA
|
|
|
|
// this is supported on solaris
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t ret = lseek(native_handle(), start, SEEK_DATA);
|
2009-02-17 01:11:38 +01:00
|
|
|
if (ret < 0) return start;
|
2011-09-05 01:29:47 +02:00
|
|
|
return start;
|
2009-02-17 01:11:38 +01:00
|
|
|
#else
|
|
|
|
return start;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#ifdef TORRENT_DEBUG_FILE_LEAKS
|
2014-07-06 21:18:00 +02:00
|
|
|
std::set<file_handle*> global_file_handles;
|
2016-05-01 00:54:23 +02:00
|
|
|
std::mutex file_handle_mutex;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
file_handle::file_handle()
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(file_handle_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
global_file_handles.insert(this);
|
|
|
|
stack[0] = 0;
|
|
|
|
}
|
|
|
|
file_handle::file_handle(file* f): m_file(f)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(file_handle_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
global_file_handles.insert(this);
|
|
|
|
if (f) print_backtrace(stack, sizeof(stack), 10);
|
|
|
|
else stack[0] = 0;
|
|
|
|
}
|
|
|
|
file_handle::file_handle(file_handle const& fh)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(file_handle_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
global_file_handles.insert(this);
|
|
|
|
m_file = fh.m_file;
|
|
|
|
if (m_file) print_backtrace(stack, sizeof(stack), 10);
|
|
|
|
else stack[0] = 0;
|
|
|
|
}
|
|
|
|
file_handle::~file_handle()
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(file_handle_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
global_file_handles.erase(this);
|
|
|
|
stack[0] = 0;
|
|
|
|
}
|
|
|
|
file* file_handle::operator->() { return m_file.get(); }
|
|
|
|
file const* file_handle::operator->() const { return m_file.get(); }
|
|
|
|
file& file_handle::operator*() { return *m_file.get(); }
|
|
|
|
file const& file_handle::operator*() const { return *m_file.get(); }
|
|
|
|
file* file_handle::get() { return m_file.get(); }
|
|
|
|
file const* file_handle::get() const { return m_file.get(); }
|
|
|
|
file_handle::operator bool() const { return m_file.get(); }
|
|
|
|
file_handle& file_handle::reset(file* f)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(file_handle_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (f) print_backtrace(stack, sizeof(stack), 10);
|
|
|
|
else stack[0] = 0;
|
|
|
|
l.unlock();
|
|
|
|
m_file.reset(f);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_open_files(char const* event, char const* name)
|
|
|
|
{
|
2016-05-23 14:15:39 +02:00
|
|
|
FILE* out = std::fopen("open_files.log", "a+");
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(file_handle_mutex);
|
2016-05-17 15:24:06 +02:00
|
|
|
std::fprintf(out, "\n\nEVENT: %s TORRENT: %s\n\n", event, name);
|
2014-07-06 21:18:00 +02:00
|
|
|
for (std::set<file_handle*>::iterator i = global_file_handles.begin()
|
|
|
|
, end(global_file_handles.end()); i != end; ++i)
|
|
|
|
{
|
2016-06-20 17:32:06 +02:00
|
|
|
TORRENT_ASSERT(*i != nullptr);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!*i) continue;
|
|
|
|
file_handle const& h = **i;
|
|
|
|
if (!h) continue;
|
|
|
|
|
|
|
|
if (!h->is_open()) continue;
|
|
|
|
h->print_info(out);
|
2016-05-17 15:24:06 +02:00
|
|
|
std::fprintf(out, "\n%s\n\n", h.stack);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2016-05-23 14:15:39 +02:00
|
|
|
std::fclose(out);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#endif
|
2004-01-16 03:57:45 +01:00
|
|
|
}
|