/* -*- 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 #include #include #include #include #include #include #include "file-impl.hxx" #include "file_url.hxx" #include "file_error.hxx" #include #include #include #include #include #ifdef max /* conflict w/ std::numeric_limits::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: o3tl::is_typed_flags {}; 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 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(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(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(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(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(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(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(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 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(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(uOffset >> 32); DWORD const dwOffsetLo = sal::static_int_cast(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(*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(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(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(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 bool exceedsMaxLONGLONG(T n) { return n > g_limit_longlong; } template bool exceedsMinLONGLONG(T n) { return n < std::numeric_limits::min(); } } oslFileError SAL_CALL osl_readFileAt( oslFileHandle Handle, sal_uInt64 uOffset, void* pBuffer, sal_uInt64 uBytesRequested, sal_uInt64* pBytesRead) { FileHandle_Impl * pImpl = static_cast(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(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(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(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(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(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(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: */