/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <systools/win32/uwinapi.h>
#include <osl/file.hxx>
#include <rtl/alloc.h>
#include <rtl/byteseq.h>
#include <sal/log.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
#include <o3tl/typed_flags_set.hxx>
#include "file-impl.hxx"
#include "file_url.hxx"
#include "file_error.hxx"
#include <atomic>
#include <cassert>
#include <algorithm>
#include <limits>
#include <mutex>
#ifdef max
/* conflict w/ std::numeric_limits<T>::max() */
#undef max
#endif
#ifdef min
#undef min
#endif
namespace {
/** State
*/
enum class StateBits
{
Seekable = 1,
/*< open() sets, iff regular file */
Readable = 2,
/*< open() sets, read() requires */
Writeable = 4,
/*< open() sets, write() requires */
Modified = 8
/* write() sets, flush() resets */
};
}
template<>
struct o3tl::typed_flags<StateBits>: o3tl::is_typed_flags<StateBits, 0xF> {};
namespace {
/** File handle implementation.
*/
struct FileHandle_Impl
{
std::mutex m_mutex;
HANDLE m_hFile;
StateBits m_state;
sal_uInt64 m_size;
/*< file size */
LONGLONG m_offset;
/*< physical offset from begin of file */
// m_filepos is hit hard in some situations, where the overhead of a mutex starts to show up, so use an atomic
std::atomic<LONGLONG> m_filepos;
/*< logical offset from begin of file */
LONGLONG m_bufptr;
/*< buffer offset from begin of file */
SIZE_T m_buflen;
/*< buffer filled [0, m_bufsiz - 1] */
SIZE_T m_bufsiz;
sal_uInt8 * m_buffer;
explicit FileHandle_Impl (HANDLE hFile);
~FileHandle_Impl();
static SIZE_T getpagesize();
sal_uInt64 getPos()
const;
oslFileError setPos (sal_uInt64 uPos);
sal_uInt64 getSize()
const;
oslFileError setSize (sal_uInt64 uPos);
oslFileError readAt(
LONGLONG nOffset,
void * pBuffer,
DWORD nBytesRequested,
sal_uInt64 * pBytesRead);
oslFileError writeAt(
LONGLONG nOffset,
void const * pBuffer,
DWORD nBytesToWrite,
sal_uInt64 * pBytesWritten);
oslFileError readFileAt(
LONGLONG nOffset,
void * pBuffer,
sal_uInt64 uBytesRequested,
sal_uInt64 * pBytesRead);
oslFileError writeFileAt(
LONGLONG nOffset,
void const * pBuffer,
sal_uInt64 uBytesToWrite,
sal_uInt64 * pBytesWritten);
oslFileError readLineAt(
LONGLONG nOffset,
sal_Sequence ** ppSequence,
sal_uInt64 * pBytesRead);
static oslFileError writeSequence_Impl (
sal_Sequence ** ppSequence,
SIZE_T * pnOffset,
const void * pBuffer,
SIZE_T nBytes);
oslFileError syncFile();
};
}
FileHandle_Impl::FileHandle_Impl(HANDLE hFile)
: m_hFile (hFile),
m_state (StateBits::Readable | StateBits::Writeable),
m_size (0),
m_offset (0),
m_filepos (0),
m_bufptr (-1),
m_buflen (0),
m_bufsiz (getpagesize()),
m_buffer (nullptr)
{
m_buffer =
static_cast<sal_uInt8 *>(calloc(m_bufsiz, 1));
}
FileHandle_Impl::~FileHandle_Impl()
{
free(m_buffer);
m_buffer = nullptr;
}
SIZE_T FileHandle_Impl::getpagesize()
{
SYSTEM_INFO info;
::GetSystemInfo(&info);
return sal::static_int_cast< SIZE_T >(info.dwPageSize);
}
sal_uInt64 FileHandle_Impl::getPos()
const
{
return sal::static_int_cast< sal_uInt64 >(m_filepos.load());
}
oslFileError FileHandle_Impl::setPos(sal_uInt64 uPos)
{
m_filepos = sal::static_int_cast< LONGLONG >(uPos);
return osl_File_E_None;
}
sal_uInt64 FileHandle_Impl::getSize()
const
{
LONGLONG bufend = std::max(LONGLONG(0), m_bufptr) + m_buflen;
return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
}
oslFileError FileHandle_Impl::setSize(sal_uInt64 uSize)
{
LARGE_INTEGER nDstPos; nDstPos.QuadPart = sal::static_int_cast< LONGLONG >(uSize);
if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN))
return oslTranslateFileError(GetLastError());
if (!::SetEndOfFile(m_hFile))
return oslTranslateFileError(GetLastError());
m_size = uSize;
nDstPos.QuadPart = m_offset;
if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN))
return oslTranslateFileError(GetLastError());
return osl_File_E_None;
}
oslFileError FileHandle_Impl::readAt(
LONGLONG nOffset,
void * pBuffer,
DWORD nBytesRequested,
sal_uInt64 * pBytesRead)
{
SAL_WARN_IF(!(m_state & StateBits::Seekable),
"sal.osl",
"FileHandle_Impl::readAt(): not seekable");
if (!(m_state & StateBits::Seekable))
return osl_File_E_SPIPE;
SAL_WARN_IF(!(m_state & StateBits::Readable),
"sal.osl",
"FileHandle_Impl::readAt(): not readable");
if (!(m_state & StateBits::Readable))
return osl_File_E_BADF;
if (nOffset != m_offset)
{
LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset;
if (!::SetFilePointerEx(m_hFile, liOffset, nullptr, FILE_BEGIN))
return oslTranslateFileError(GetLastError());
m_offset = nOffset;
}
DWORD dwDone = 0;
if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr))
return oslTranslateFileError(GetLastError());
m_offset += dwDone;
*pBytesRead = dwDone;
return osl_File_E_None;
}
oslFileError FileHandle_Impl::writeAt(
LONGLONG nOffset,
void const * pBuffer,
DWORD nBytesToWrite,
sal_uInt64 * pBytesWritten)
{
SAL_WARN_IF(!(m_state & StateBits::Seekable),
"sal.osl",
"FileHandle_Impl::writeAt(): not seekable");
if (!(m_state & StateBits::Seekable))
return osl_File_E_SPIPE;
SAL_WARN_IF(!(m_state & StateBits::Writeable),
"sal.osl",
"FileHandle_Impl::writeAt(): not writeable");
if (!(m_state & StateBits::Writeable))
return osl_File_E_BADF;
if (nOffset != m_offset)
{
LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset;
if (!::SetFilePointerEx (m_hFile, liOffset, nullptr, FILE_BEGIN))
return oslTranslateFileError(GetLastError());
m_offset = nOffset;
}
DWORD dwDone = 0;
if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr))
return oslTranslateFileError(GetLastError());
m_offset += dwDone;
m_size = std::max(m_size, sal::static_int_cast< sal_uInt64 >(m_offset));
*pBytesWritten = dwDone;
return osl_File_E_None;
}
oslFileError FileHandle_Impl::readFileAt(
LONGLONG nOffset,
void * pBuffer,
sal_uInt64 uBytesRequested,
sal_uInt64 * pBytesRead)
{
static sal_uInt64
const g_limit_dword = std::numeric_limits< DWORD >::max();
if (g_limit_dword < uBytesRequested)
return osl_File_E_OVERFLOW;
DWORD nBytesRequested = sal::static_int_cast< DWORD >(uBytesRequested);
if (!(m_state & StateBits::Seekable))
{
// not seekable (pipe)
DWORD dwDone = 0;
if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr))
return oslTranslateFileError(GetLastError());
*pBytesRead = dwDone;
return osl_File_E_None;
}
else if (!m_buffer)
{
// not buffered
return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead);
}
else
{
sal_uInt8 * buffer =
static_cast< sal_uInt8* >(pBuffer);
for (*pBytesRead = 0; nBytesRequested > 0;)
{
LONGLONG
const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
SIZE_T
const bufpos = nOffset % m_bufsiz;
if (bufptr != m_bufptr)
{
// flush current buffer
oslFileError result = syncFile();
if (result != osl_File_E_None)
return result;
m_bufptr = -1;
m_buflen = 0;
if (nBytesRequested >= m_bufsiz)
{
// buffer too small, read through from file
sal_uInt64 uDone = 0;
result = readAt (nOffset, &(buffer[*pBytesRead]), nBytesRequested, &uDone);
if (result != osl_File_E_None)
return result;
nBytesRequested -= sal::static_int_cast< DWORD >(uDone);
*pBytesRead += uDone;
return osl_File_E_None;
}
// update buffer (pointer)
sal_uInt64 uDone = 0;
result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
if (result != osl_File_E_None)
return result;
m_bufptr = bufptr;
m_buflen = sal::static_int_cast< SIZE_T >(uDone);
}
if (bufpos >= m_buflen)
{
// end of file
return osl_File_E_None;
}
SIZE_T
const bytes = std::min(m_buflen - bufpos,
static_cast<SIZE_T>(nBytesRequested));
memcpy(&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
nBytesRequested -= bytes;
*pBytesRead += bytes;
nOffset += bytes;
}
return osl_File_E_None;
}
}
oslFileError FileHandle_Impl::writeFileAt(
LONGLONG nOffset,
void const * pBuffer,
sal_uInt64 uBytesToWrite,
sal_uInt64 * pBytesWritten)
{
static sal_uInt64
const g_limit_dword = std::numeric_limits< DWORD >::max();
if (g_limit_dword < uBytesToWrite)
return osl_File_E_OVERFLOW;
DWORD nBytesToWrite = sal::static_int_cast< DWORD >(uBytesToWrite);
if (!(m_state & StateBits::Seekable))
{
// not seekable (pipe)
DWORD dwDone = 0;
if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr))
return oslTranslateFileError(GetLastError());
*pBytesWritten = dwDone;
return osl_File_E_None;
}
else if (!m_buffer)
{
// not buffered
return writeAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
}
else
{
sal_uInt8
const * buffer =
static_cast< sal_uInt8
const* >(pBuffer);
for (*pBytesWritten = 0; nBytesToWrite > 0;)
{
LONGLONG
const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
SIZE_T
const bufpos = nOffset % m_bufsiz;
if (bufptr != m_bufptr)
{
// flush current buffer
oslFileError result = syncFile();
if (result != osl_File_E_None)
return result;
m_bufptr = -1;
m_buflen = 0;
if (nBytesToWrite >= m_bufsiz)
{
// buffer too small, write through to file
sal_uInt64 uDone = 0;
result = writeAt(nOffset, &(buffer[*pBytesWritten]), nBytesToWrite, &uDone);
if (result != osl_File_E_None)
return result;
if (uDone != nBytesToWrite)
return osl_File_E_IO;
nBytesToWrite -= sal::static_int_cast< DWORD >(uDone);
*pBytesWritten += uDone;
return osl_File_E_None;
}
// update buffer (pointer)
sal_uInt64 uDone = 0;
result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
if (result != osl_File_E_None)
return result;
m_bufptr = bufptr;
m_buflen = sal::static_int_cast< SIZE_T >(uDone);
}
SIZE_T
const bytes = std::min(m_bufsiz - bufpos,
static_cast<SIZE_T>(nBytesToWrite));
memcpy(&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes);
nBytesToWrite -= bytes;
*pBytesWritten += bytes;
nOffset += bytes;
m_buflen = std::max(m_buflen, bufpos + bytes);
m_state |= StateBits::Modified;
}
return osl_File_E_None;
}
}
oslFileError FileHandle_Impl::readLineAt(
LONGLONG nOffset,
sal_Sequence ** ppSequence,
sal_uInt64 * pBytesRead)
{
oslFileError result = osl_File_E_None;
LONGLONG bufptr = (nOffset / m_bufsiz) * m_bufsiz;
if (bufptr != m_bufptr)
{
/* flush current buffer */
result = syncFile();
if (result != osl_File_E_None)
return result;
/* update buffer (pointer) */
sal_uInt64 uDone = 0;
result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
if (result != osl_File_E_None)
return result;
m_bufptr = bufptr;
m_buflen = sal::static_int_cast< SIZE_T >(uDone);
}
static int const LINE_STATE_BEGIN = 0;
static int const LINE_STATE_CR = 1;
static int const LINE_STATE_LF = 2;
SIZE_T bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr), curpos = bufpos, dstpos = 0
;
int state = (bufpos >= m_buflen) ? LINE_STATE_LF : LINE_STATE_BEGIN;
for (; state != LINE_STATE_LF;)
{
if (curpos >= m_buflen)
{
/* buffer examined */
if (curpos > bufpos) // actually, curpos can't become less than bufpos, so != could do
{
/* flush buffer to sequence */
result = writeSequence_Impl(
ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos);
if (result != osl_File_E_None)
return result;
*pBytesRead += curpos - bufpos;
nOffset += curpos - bufpos;
}
bufptr = nOffset / m_bufsiz * m_bufsiz;
if (bufptr != m_bufptr)
{
/* update buffer (pointer) */
sal_uInt64 uDone = 0;
result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
if (result != osl_File_E_None)
return result;
m_bufptr = bufptr;
m_buflen = sal::static_int_cast< SIZE_T >(uDone);
}
bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr);
curpos = bufpos;
if (bufpos >= m_buflen)
break;
}
switch (state)
{
case LINE_STATE_CR:
state = LINE_STATE_LF;
switch (m_buffer[curpos])
{
case 0x0A: /* CRLF */
/* eat current char */
curpos++;
break;
default: /* single CR */
/* keep current char */
break;
}
break;
default:
/* determine next state */
switch (m_buffer[curpos])
{
case 0x0A: /* single LF */
state = LINE_STATE_LF;
break;
case 0x0D: /* CR */
state = LINE_STATE_CR;
break;
default: /* advance to next char */
curpos++;
break;
}
if (state != LINE_STATE_BEGIN)
{
/* store (and eat) the newline char */
m_buffer[curpos] = 0x0A;
curpos++;
/* flush buffer to sequence */
result = writeSequence_Impl(
ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos - 1);
if (result != osl_File_E_None)
return result;
*pBytesRead += curpos - bufpos;
nOffset += curpos - bufpos;
}
break;
}
}
result = writeSequence_Impl(ppSequence, &dstpos, nullptr, 0);
if (result != osl_File_E_None)
return result;
if (dstpos > 0)
return osl_File_E_None;
if (bufpos >= m_buflen)
return osl_File_E_AGAIN;
return osl_File_E_None;
}
oslFileError FileHandle_Impl::writeSequence_Impl(
sal_Sequence ** ppSequence,
SIZE_T * pnOffset,
const void * pBuffer,
SIZE_T nBytes)
{
sal_Int32 nElements = *pnOffset + nBytes;
if (!*ppSequence)
{
/* construct sequence */
rtl_byte_sequence_constructNoDefault(ppSequence, nElements);
}
else if (nElements != (*ppSequence)->nElements)
{
/* resize sequence */
rtl_byte_sequence_realloc(ppSequence, nElements);
}
if (*ppSequence)
{
/* fill sequence */
memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes);
*pnOffset += nBytes;
}
return (*ppSequence) ? osl_File_E_None : osl_File_E_NOMEM;
}
oslFileError FileHandle_Impl::syncFile()
{
oslFileError result = osl_File_E_None;
if (m_state & StateBits::Modified)
{
sal_uInt64 uDone = 0;
result = writeAt(m_bufptr, m_buffer, m_buflen, &uDone);
if (result != osl_File_E_None)
return result;
if (uDone != m_buflen)
return osl_File_E_IO;
m_state &= ~StateBits::Modified;
}
return result;
}
extern "C" oslFileHandle osl_createFileHandleFromOSHandle(
HANDLE hFile,
sal_uInt32 uFlags)
{
if (!IsValidHandle(hFile))
return nullptr; // EINVAL
FileHandle_Impl * pImpl = new FileHandle_Impl(hFile);
/* check for regular file */
if (GetFileType(hFile) == FILE_TYPE_DISK)
{
/* mark seekable */
pImpl->m_state |= StateBits::Seekable;
/* init current size */
LARGE_INTEGER uSize = { { 0, 0 } };
(void) ::GetFileSizeEx(hFile, &uSize);
pImpl->m_size = (sal::static_int_cast<sal_uInt64>(uSize.HighPart) << 32) + uSize.LowPart;
}
if (!(uFlags & osl_File_OpenFlag_Read))
pImpl->m_state &= ~StateBits::Readable;
if (!(uFlags & osl_File_OpenFlag_Write))
pImpl->m_state &= ~StateBits::Writeable;
SAL_WARN_IF(
!((uFlags & osl_File_OpenFlag_Read) || (uFlags & osl_File_OpenFlag_Write)),
"sal.osl",
"osl_createFileHandleFromOSHandle(): missing read/write access flags");
return static_cast<oslFileHandle>(pImpl);
}
oslFileError SAL_CALL osl_openFile(
rtl_uString * strPath,
oslFileHandle * pHandle,
sal_uInt32 uFlags)
{
OUString strSysPath;
oslFileError result = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
if (result != osl_File_E_None)
return result;
// tdf126742 use FILE_SHARE_WRITE to get closer to non-Windows platform behaviour,
// for details and discussion see task please
DWORD dwAccess = GENERIC_READ, dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE, dwCreation = 0;
if (uFlags & osl_File_OpenFlag_Write)
dwAccess |= GENERIC_WRITE;
if (uFlags & osl_File_OpenFlag_NoLock)
dwShare |= FILE_SHARE_DELETE;
if (uFlags & osl_File_OpenFlag_Create)
dwCreation |= CREATE_NEW;
else
dwCreation |= OPEN_EXISTING;
HANDLE hFile = CreateFileW(
o3tl::toW(strSysPath.getStr()),
dwAccess, dwShare, nullptr, dwCreation, 0, nullptr);
// @@@ ERROR HANDLING @@@
if (!IsValidHandle(hFile))
result = oslTranslateFileError(GetLastError());
*pHandle = osl_createFileHandleFromOSHandle(hFile, uFlags | osl_File_OpenFlag_Read);
return result;
}
oslFileError SAL_CALL osl_syncFile(oslFileHandle Handle)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
return osl_File_E_INVAL;
std::lock_guard lock(pImpl->m_mutex);
oslFileError result = pImpl->syncFile();
if (result != osl_File_E_None)
return result;
if (!FlushFileBuffers(pImpl->m_hFile))
return oslTranslateFileError(GetLastError());
return osl_File_E_None;
}
oslFileError SAL_CALL osl_closeFile(oslFileHandle Handle)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
return osl_File_E_INVAL;
oslFileError result;
{
std::lock_guard lock(pImpl->m_mutex);
result = pImpl->syncFile();
if (result != osl_File_E_None)
{
/* ignore double failure */
(void)::CloseHandle(pImpl->m_hFile);
}
else if (!::CloseHandle(pImpl->m_hFile))
{
/* translate error code */
result = oslTranslateFileError(GetLastError());
}
}
delete pImpl;
return result;
}
namespace {
// coverity[result_independent_of_operands] - crossplatform requirement
template<typename T> bool exceedsMaxSIZE_T(T n)
{ return n > std::numeric_limits< SIZE_T >::max(); }
}
oslFileError SAL_CALL osl_mapFile(
oslFileHandle Handle,
void** ppAddr,
sal_uInt64 uLength,
sal_uInt64 uOffset,
sal_uInt32 uFlags)
{
struct FileMapping
{
HANDLE m_handle;
explicit FileMapping(HANDLE hMap)
: m_handle(hMap)
{}
~FileMapping()
{
(void)::CloseHandle(m_handle);
}
};
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppAddr))
return osl_File_E_INVAL;
*ppAddr = nullptr;
if (exceedsMaxSIZE_T(uLength))
return osl_File_E_OVERFLOW;
SIZE_T const nLength = sal::static_int_cast< SIZE_T >(uLength);
FileMapping aMap(::CreateFileMappingW(pImpl->m_hFile, nullptr, SEC_COMMIT | PAGE_READONLY, 0, 0, nullptr));
if (!IsValidHandle(aMap.m_handle))
return oslTranslateFileError(GetLastError());
DWORD const dwOffsetHi = sal::static_int_cast<DWORD>(uOffset >> 32);
DWORD const dwOffsetLo = sal::static_int_cast<DWORD>(uOffset & 0xFFFFFFFF);
*ppAddr = ::MapViewOfFile(aMap.m_handle, FILE_MAP_READ, dwOffsetHi, dwOffsetLo, nLength);
if (!*ppAddr)
return oslTranslateFileError(GetLastError());
if (uFlags & osl_File_MapFlag_RandomAccess)
{
// Determine memory pagesize.
SYSTEM_INFO info;
::GetSystemInfo(&info);
DWORD const dwPageSize = info.dwPageSize;
/*
* Pagein, touching first byte of each memory page.
* Note: volatile disables optimizing the loop away.
*/
BYTE volatile * pData(static_cast<BYTE*>(*ppAddr));
SIZE_T nSize(nLength);
while (nSize > dwPageSize)
{
pData[0];
pData += dwPageSize;
nSize -= dwPageSize;
}
if (nSize > 0)
{
pData[0];
}
}
return osl_File_E_None;
}
oslFileError SAL_CALL osl_unmapFile(void* pAddr, sal_uInt64 /* uLength */)
{
if (!pAddr)
return osl_File_E_INVAL;
if (!::UnmapViewOfFile(pAddr))
return oslTranslateFileError(GetLastError());
return osl_File_E_None;
}
oslFileError SAL_CALL osl_unmapMappedFile(oslFileHandle /* Handle */, void* pAddr, sal_uInt64 uLength)
{
return osl_unmapFile(pAddr, uLength);
}
oslFileError
SAL_CALL osl_readLine(
oslFileHandle Handle,
sal_Sequence ** ppSequence)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppSequence))
return osl_File_E_INVAL;
sal_uInt64 uBytesRead = 0;
// read at current filepos; filepos += uBytesRead;
std::lock_guard lock(pImpl->m_mutex);
oslFileError result = pImpl->readLineAt(
pImpl->m_filepos, ppSequence, &uBytesRead);
if (result == osl_File_E_None)
pImpl->m_filepos += uBytesRead;
return result;
}
oslFileError SAL_CALL osl_readFile(
oslFileHandle Handle,
void * pBuffer,
sal_uInt64 uBytesRequested,
sal_uInt64 * pBytesRead)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead))
return osl_File_E_INVAL;
// read at current filepos; filepos += *pBytesRead;
std::lock_guard lock(pImpl->m_mutex);
oslFileError result = pImpl->readFileAt(
pImpl->m_filepos, pBuffer, uBytesRequested, pBytesRead);
if (result == osl_File_E_None)
pImpl->m_filepos += *pBytesRead;
return result;
}
oslFileError SAL_CALL osl_writeFile(
oslFileHandle Handle,
const void * pBuffer,
sal_uInt64 uBytesToWrite,
sal_uInt64 * pBytesWritten)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten))
return osl_File_E_INVAL;
// write at current filepos; filepos += *pBytesWritten;
std::lock_guard lock(pImpl->m_mutex);
oslFileError result = pImpl->writeFileAt(
pImpl->m_filepos, pBuffer, uBytesToWrite, pBytesWritten);
if (result == osl_File_E_None)
pImpl->m_filepos += *pBytesWritten;
return result;
}
LONGLONG const g_limit_longlong = std::numeric_limits< LONGLONG >::max();
namespace {
// coverity[result_independent_of_operands] - crossplatform requirement
template<typename T> bool exceedsMaxLONGLONG(T n)
{ return n > g_limit_longlong; }
template<typename T> bool exceedsMinLONGLONG(T n)
{ return n < std::numeric_limits<LONGLONG>::min(); }
}
oslFileError SAL_CALL osl_readFileAt(
oslFileHandle Handle,
sal_uInt64 uOffset,
void* pBuffer,
sal_uInt64 uBytesRequested,
sal_uInt64* pBytesRead)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead))
return osl_File_E_INVAL;
if (!(pImpl->m_state & StateBits::Seekable))
return osl_File_E_SPIPE;
if (exceedsMaxLONGLONG(uOffset))
return osl_File_E_OVERFLOW;
LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset);
// read at specified fileptr
std::lock_guard lock (pImpl->m_mutex);
return pImpl->readFileAt(nOffset, pBuffer, uBytesRequested, pBytesRead);
}
oslFileError SAL_CALL osl_writeFileAt(
oslFileHandle Handle,
sal_uInt64 uOffset,
const void* pBuffer,
sal_uInt64 uBytesToWrite,
sal_uInt64* pBytesWritten)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten))
return osl_File_E_INVAL;
if (!(pImpl->m_state & StateBits::Seekable))
return osl_File_E_SPIPE;
if (exceedsMaxLONGLONG(uOffset))
return osl_File_E_OVERFLOW;
LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset);
// write at specified fileptr
std::lock_guard lock(pImpl->m_mutex);
return pImpl->writeFileAt(nOffset, pBuffer, uBytesToWrite, pBytesWritten);
}
oslFileError SAL_CALL osl_isEndOfFile(oslFileHandle Handle, sal_Bool *pIsEOF)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pIsEOF))
return osl_File_E_INVAL;
std::lock_guard lock(pImpl->m_mutex);
*pIsEOF = (pImpl->getPos() == pImpl->getSize());
return osl_File_E_None;
}
oslFileError SAL_CALL osl_getFilePos(oslFileHandle Handle, sal_uInt64 *pPos)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pPos))
return osl_File_E_INVAL;
// no need to lock because pos is atomic
*pPos = pImpl->getPos();
return osl_File_E_None;
}
oslFileError SAL_CALL osl_setFilePos(oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
return osl_File_E_INVAL;
if (exceedsMaxLONGLONG(uOffset) || exceedsMinLONGLONG(uOffset))
return osl_File_E_OVERFLOW;
LONGLONG nPos = 0, nOffset = sal::static_int_cast< LONGLONG >(uOffset);
std::lock_guard lock(pImpl->m_mutex);
switch (uHow)
{
case osl_Pos_Absolut:
if (nOffset < 0)
return osl_File_E_INVAL;
break;
case osl_Pos_Current:
nPos = sal::static_int_cast< LONGLONG >(pImpl->getPos());
if ((nOffset < 0) && (nPos < -1*nOffset))
return osl_File_E_INVAL;
assert(nPos >= 0);
if (nOffset > g_limit_longlong - nPos)
return osl_File_E_OVERFLOW;
break;
case osl_Pos_End:
nPos = sal::static_int_cast< LONGLONG >(pImpl->getSize());
if ((nOffset < 0) && (nPos < -1*nOffset))
return osl_File_E_INVAL;
assert(nPos >= 0);
if (nOffset > g_limit_longlong - nPos)
return osl_File_E_OVERFLOW;
break;
default:
return osl_File_E_INVAL;
}
return pImpl->setPos(nPos + nOffset);
}
oslFileError SAL_CALL osl_getFileSize(oslFileHandle Handle, sal_uInt64 *pSize)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pSize))
return osl_File_E_INVAL;
std::lock_guard lock(pImpl->m_mutex);
*pSize = pImpl->getSize();
return osl_File_E_None;
}
oslFileError SAL_CALL osl_setFileSize(oslFileHandle Handle, sal_uInt64 uSize)
{
FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
return osl_File_E_INVAL;
if (!(pImpl->m_state & StateBits::Writeable))
return osl_File_E_BADF;
if (exceedsMaxLONGLONG(uSize))
return osl_File_E_OVERFLOW;
std::lock_guard lock(pImpl->m_mutex);
oslFileError result = pImpl->syncFile();
if (result != osl_File_E_None)
return result;
pImpl->m_bufptr = -1;
pImpl->m_buflen = 0;
return pImpl->setSize(uSize);
}
oslFileError SAL_CALL osl_removeFile(rtl_uString* strPath)
{
OUString strSysPath;
oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
if (error == osl_File_E_None)
{
if (DeleteFileW(o3tl::toW(strSysPath.getStr())))
error = osl_File_E_None;
else
error = oslTranslateFileError(GetLastError());
}
return error;
}
oslFileError SAL_CALL osl_copyFile(rtl_uString* strPath, rtl_uString *strDestPath)
{
OUString strSysPath, strSysDestPath;
oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
if (error == osl_File_E_None)
error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false);
if (error == osl_File_E_None)
{
LPCWSTR src = o3tl::toW(strSysPath.getStr());
LPCWSTR dst = o3tl::toW(strSysDestPath.getStr());
if (CopyFileW(src, dst, FALSE))
error = osl_File_E_None;
else
error = oslTranslateFileError(GetLastError());
}
return error;
}
oslFileError SAL_CALL osl_moveFile(rtl_uString* strPath, rtl_uString *strDestPath)
{
OUString strSysPath, strSysDestPath;
oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
if (error == osl_File_E_None)
error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false);
if (error == osl_File_E_None)
{
LPCWSTR src = o3tl::toW(strSysPath.getStr());
LPCWSTR dst = o3tl::toW(strSysDestPath.getStr());
if (MoveFileExW(src, dst, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING))
error = osl_File_E_None;
else
error = oslTranslateFileError(GetLastError());
}
return error;
}
oslFileError SAL_CALL osl_replaceFile(rtl_uString* strPath, rtl_uString* strDestPath)
{
OUString strSysPath, strSysDestPath;
oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
if (error == osl_File_E_None)
error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false);
if (error == osl_File_E_None)
{
LPCWSTR src = o3tl::toW(strSysPath.getStr());
LPCWSTR dst = o3tl::toW(strSysDestPath.getStr());
if (!ReplaceFileW(dst, src, nullptr,
REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS
| REPLACEFILE_IGNORE_ACL_ERRORS,
nullptr, nullptr))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_FILE_NOT_FOUND // no strDestPath file?
|| dwError == ERROR_UNABLE_TO_MOVE_REPLACEMENT // e.g., files on different volumes
|| dwError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2
|| dwError == ERROR_UNABLE_TO_REMOVE_REPLACED)
error = osl_moveFile(strPath, strDestPath);
else
error = oslTranslateFileError(dwError);
}
}
return error;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */