2004-01-19 20:36:55 +01:00
|
|
|
/*
|
|
|
|
|
2004-01-19 22:23:48 +01:00
|
|
|
Copyright (c) 2003, Magnus Jonsson & Arvid Norberg
|
2004-01-19 20:36:55 +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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2004-01-16 03:35:39 +01:00
|
|
|
#include "libtorrent/file.hpp"
|
2004-01-19 20:36:55 +01:00
|
|
|
#include <sstream>
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
#include <windows.h>
|
2004-01-16 03:35:39 +01:00
|
|
|
|
|
|
|
#define DWORD_MAX 0xffffffffu
|
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
namespace
|
|
|
|
{
|
2004-01-23 00:49:48 +01:00
|
|
|
// must be used to not leak memory in case something would throw
|
|
|
|
class auto_LocalFree
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
auto_LocalFree(HLOCAL memory)
|
|
|
|
: m_memory(memory)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
~auto_LocalFree()
|
|
|
|
{
|
2004-10-16 03:10:42 +02:00
|
|
|
if (m_memory)
|
2004-01-23 00:49:48 +01:00
|
|
|
LocalFree(m_memory);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
HLOCAL m_memory;
|
|
|
|
};
|
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
void throw_exception(const char* thrower)
|
|
|
|
{
|
2004-10-16 03:10:42 +02:00
|
|
|
char *buffer = 0;
|
2004-01-19 20:36:55 +01:00
|
|
|
int err = GetLastError();
|
2004-10-13 14:55:13 +02:00
|
|
|
|
|
|
|
#ifdef _UNICODE
|
2005-01-14 15:05:25 +01:00
|
|
|
FormatMessage(
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM
|
|
|
|
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
|
|
, 0, err, 0, (LPWSTR)(LPCSTR)&buffer, 0, 0);
|
2004-10-13 14:55:13 +02:00
|
|
|
#else
|
2005-01-14 15:05:25 +01:00
|
|
|
FormatMessage(
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM
|
|
|
|
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
|
|
, 0, err, 0, (LPSTR)&buffer, 0, 0);
|
2004-10-13 14:55:13 +02:00
|
|
|
#endif
|
2004-01-23 00:49:48 +01:00
|
|
|
|
|
|
|
auto_LocalFree auto_free(buffer); // needed for exception safety
|
2004-01-19 20:36:55 +01:00
|
|
|
std::stringstream s;
|
2004-01-23 00:49:48 +01:00
|
|
|
s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");
|
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
throw libtorrent::file_error(s.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
2004-01-16 03:35:39 +01:00
|
|
|
|
|
|
|
struct file::impl : boost::noncopyable
|
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
enum open_flags
|
|
|
|
{
|
|
|
|
read_flag = 1,
|
|
|
|
write_flag = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
enum seek_mode
|
|
|
|
{
|
|
|
|
seek_begin = FILE_BEGIN,
|
|
|
|
seek_from_here = FILE_CURRENT,
|
|
|
|
seek_end = FILE_END
|
|
|
|
};
|
2004-01-16 03:35:39 +01:00
|
|
|
|
|
|
|
impl()
|
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
m_file_handle = INVALID_HANDLE_VALUE;
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void open(const char *file_name, open_flags flags)
|
|
|
|
{
|
|
|
|
assert(file_name);
|
2004-01-19 20:36:55 +01:00
|
|
|
assert(flags & (read_flag | write_flag));
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
DWORD access_mask = 0;
|
|
|
|
if (flags & read_flag)
|
|
|
|
access_mask |= GENERIC_READ;
|
|
|
|
if (flags & write_flag)
|
|
|
|
access_mask |= GENERIC_WRITE;
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
assert(access_mask & (GENERIC_READ | GENERIC_WRITE));
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-10-13 14:55:13 +02:00
|
|
|
#ifdef _UNICODE
|
2004-01-19 20:36:55 +01:00
|
|
|
HANDLE new_handle = CreateFile(
|
2004-10-13 14:37:58 +02:00
|
|
|
(LPCWSTR)file_name
|
2004-01-19 20:36:55 +01:00
|
|
|
, access_mask
|
|
|
|
, FILE_SHARE_READ
|
2004-06-28 22:23:42 +02:00
|
|
|
, 0
|
2005-01-14 15:05:25 +01:00
|
|
|
, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
|
2004-01-19 20:36:55 +01:00
|
|
|
, FILE_ATTRIBUTE_NORMAL
|
2004-06-28 22:23:42 +02:00
|
|
|
, 0);
|
2004-10-13 14:55:13 +02:00
|
|
|
#else
|
|
|
|
HANDLE new_handle = CreateFile(
|
|
|
|
file_name
|
|
|
|
, access_mask
|
|
|
|
, FILE_SHARE_READ
|
|
|
|
, 0
|
2005-01-14 15:05:25 +01:00
|
|
|
, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
|
2004-10-13 14:55:13 +02:00
|
|
|
, FILE_ATTRIBUTE_NORMAL
|
|
|
|
, 0);
|
|
|
|
#endif
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
if (new_handle == INVALID_HANDLE_VALUE)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
std::stringstream s;
|
2005-01-14 15:05:25 +01:00
|
|
|
throw_exception(file_name);
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
// will only close old file if the open succeeded
|
|
|
|
close();
|
2004-01-19 20:36:55 +01:00
|
|
|
m_file_handle = new_handle;
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void close()
|
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
if (m_file_handle != INVALID_HANDLE_VALUE)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
|
|
|
CloseHandle(m_file_handle);
|
2004-01-19 20:36:55 +01:00
|
|
|
m_file_handle = INVALID_HANDLE_VALUE;
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~impl()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_type write(const char* buffer, size_type num_bytes)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
2004-01-19 20:36:55 +01:00
|
|
|
assert(num_bytes > 0);
|
|
|
|
assert((DWORD)num_bytes == num_bytes);
|
|
|
|
DWORD bytes_written = 0;
|
|
|
|
if (num_bytes != 0)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
if (FALSE == WriteFile(
|
|
|
|
m_file_handle
|
|
|
|
, buffer
|
|
|
|
, (DWORD)num_bytes
|
|
|
|
, &bytes_written
|
2004-06-28 22:23:42 +02:00
|
|
|
, 0))
|
2004-01-19 20:36:55 +01:00
|
|
|
{
|
|
|
|
throw_exception("file::write");
|
|
|
|
}
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
return bytes_written;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_type read(char* buffer, size_type num_bytes)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
2004-01-19 20:36:55 +01:00
|
|
|
assert(num_bytes > 0);
|
|
|
|
assert((DWORD)num_bytes == num_bytes);
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
DWORD bytes_read = 0;
|
|
|
|
if (num_bytes != 0)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
if (FALSE == ReadFile(
|
|
|
|
m_file_handle
|
|
|
|
, buffer
|
|
|
|
, (DWORD)num_bytes
|
|
|
|
, &bytes_read
|
2004-06-28 22:23:42 +02:00
|
|
|
, 0))
|
2004-01-19 20:36:55 +01:00
|
|
|
{
|
|
|
|
throw_exception("file::read");
|
|
|
|
}
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
return bytes_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
void seek(size_type pos, seek_mode from_where)
|
|
|
|
{
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(pos >= 0 || from_where != seek_begin);
|
|
|
|
assert(pos <= 0 || from_where != seek_end);
|
2004-01-16 03:35:39 +01:00
|
|
|
LARGE_INTEGER offs;
|
2004-01-19 20:36:55 +01:00
|
|
|
offs.QuadPart = pos;
|
|
|
|
if (FALSE == SetFilePointerEx(
|
|
|
|
m_file_handle
|
|
|
|
, offs
|
|
|
|
, &offs
|
|
|
|
, from_where))
|
|
|
|
{
|
2004-10-16 03:10:42 +02:00
|
|
|
throw_exception("file::seek");
|
2004-01-19 20:36:55 +01:00
|
|
|
}
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
size_type tell()
|
|
|
|
{
|
|
|
|
LARGE_INTEGER offs;
|
2004-01-19 20:36:55 +01:00
|
|
|
offs.QuadPart = 0;
|
2004-01-16 03:35:39 +01:00
|
|
|
|
|
|
|
// is there any other way to get offset?
|
2004-01-19 20:36:55 +01:00
|
|
|
if (FALSE == SetFilePointerEx(
|
|
|
|
m_file_handle
|
|
|
|
, offs
|
|
|
|
, &offs
|
|
|
|
, FILE_CURRENT))
|
|
|
|
{
|
|
|
|
throw_exception("file::tell");
|
|
|
|
}
|
2004-01-16 03:35:39 +01:00
|
|
|
|
|
|
|
size_type pos=offs.QuadPart;
|
|
|
|
assert(pos>=0);
|
|
|
|
return pos;
|
|
|
|
}
|
2004-01-19 20:36:55 +01:00
|
|
|
/*
|
2004-01-16 03:35:39 +01:00
|
|
|
size_type size()
|
|
|
|
{
|
|
|
|
LARGE_INTEGER s;
|
2004-01-19 20:36:55 +01:00
|
|
|
if (FALSE == GetFileSizeEx(m_file_handle, &s))
|
|
|
|
{
|
|
|
|
throw_exception("file::size");
|
|
|
|
}
|
2004-01-16 03:35:39 +01:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
size_type size = s.QuadPart;
|
|
|
|
assert(size >= 0);
|
2004-01-16 03:35:39 +01:00
|
|
|
return size;
|
|
|
|
}
|
2004-01-19 20:36:55 +01:00
|
|
|
*/
|
2004-01-16 03:35:39 +01:00
|
|
|
private:
|
2004-01-19 20:36:55 +01:00
|
|
|
|
2004-01-16 03:35:39 +01:00
|
|
|
HANDLE m_file_handle;
|
2004-01-19 20:36:55 +01:00
|
|
|
|
2004-01-16 03:35:39 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
2004-01-16 03:35:39 +01:00
|
|
|
|
|
|
|
const file::seek_mode file::begin(file::impl::seek_begin);
|
|
|
|
const file::seek_mode file::end(file::impl::seek_end);
|
|
|
|
|
|
|
|
const file::open_mode file::in(file::impl::read_flag);
|
|
|
|
const file::open_mode file::out(file::impl::write_flag);
|
|
|
|
|
|
|
|
file::file()
|
|
|
|
: m_impl(new libtorrent::file::impl())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
file::file(boost::filesystem::path const& p, open_mode m)
|
|
|
|
: m_impl(new libtorrent::file::impl())
|
|
|
|
{
|
|
|
|
open(p,m);
|
|
|
|
}
|
|
|
|
|
|
|
|
file::~file()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void file::open(boost::filesystem::path const& p, open_mode m)
|
|
|
|
{
|
2004-10-16 03:10:42 +02:00
|
|
|
assert(p.is_complete());
|
2004-01-19 20:36:55 +01:00
|
|
|
m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void file::close()
|
|
|
|
{
|
|
|
|
m_impl->close();
|
|
|
|
}
|
|
|
|
|
2004-01-19 19:20:47 +01:00
|
|
|
size_type file::write(const char* buffer, size_type num_bytes)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
return m_impl->write(buffer, num_bytes);
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
|
2004-01-19 19:20:47 +01:00
|
|
|
size_type file::read(char* buffer, size_type num_bytes)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
2004-01-19 20:36:55 +01:00
|
|
|
return m_impl->read(buffer, num_bytes);
|
2004-01-16 03:35:39 +01:00
|
|
|
}
|
|
|
|
|
2004-01-19 19:20:47 +01:00
|
|
|
void file::seek(size_type pos, seek_mode m)
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
|
|
|
m_impl->seek(pos,impl::seek_mode(m.m_val));
|
|
|
|
}
|
|
|
|
|
2004-01-19 19:20:47 +01:00
|
|
|
size_type file::tell()
|
2004-01-16 03:35:39 +01:00
|
|
|
{
|
|
|
|
return m_impl->tell();
|
|
|
|
}
|
2004-01-19 20:36:55 +01:00
|
|
|
}
|