diff options
Diffstat (limited to 'sal/osl/unx/file.cxx')
-rw-r--r-- | sal/osl/unx/file.cxx | 1397 |
1 files changed, 1397 insertions, 0 deletions
diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx new file mode 100644 index 000000000000..cc0c041bc328 --- /dev/null +++ b/sal/osl/unx/file.cxx @@ -0,0 +1,1397 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sal.hxx" + +#include "osl/file.hxx" + +#include "osl/diagnose.h" +#include "rtl/alloc.h" + +#include "system.h" +#include "file_error_transl.h" +#include "file_url.h" + +#include <algorithm> +#include <limits> + +#include <string.h> +#include <pthread.h> +#include <sys/mman.h> + +#if defined(MACOSX) + +#include <sys/param.h> +#include <sys/mount.h> +#define HAVE_O_EXLOCK + +// add MACOSX Time Value +#define TimeValue CFTimeValue +#include <CoreFoundation/CoreFoundation.h> +#undef TimeValue + +#endif /* MACOSX */ + +#ifdef DEBUG_OSL_FILE +# define OSL_FILE_TRACE 0 ? (void)(0) : osl_trace +# define PERROR( a, b ) perror( a ); fprintf( stderr, b ) +#else +# define OSL_FILE_TRACE 1 ? (void)(0) : osl_trace +# define PERROR( a, b ) +#endif + +/******************************************************************* + * + * FileHandle_Impl interface + * + ******************************************************************/ +struct FileHandle_Impl +{ + pthread_mutex_t m_mutex; + rtl_String * m_strFilePath; /* holds native file path */ + int m_fd; + + /** State + */ + enum StateBits + { + STATE_SEEKABLE = 1, /* default */ + STATE_READABLE = 2, /* default */ + STATE_WRITEABLE = 4, /* open() sets, write() requires, else osl_File_E_BADF */ + STATE_MODIFIED = 8 /* write() sets, flush() resets */ + }; + int m_state; + + sal_uInt64 m_size; /* file size */ + off_t m_offset; /* physical offset from begin of file */ + off_t m_fileptr; /* logical offset from begin of file */ + + off_t 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 (int fd, char const * path = "<anon>"); + ~FileHandle_Impl(); + + static void* operator new (size_t n); + static void operator delete (void * p, size_t); + + static size_t getpagesize(); + + sal_uInt64 getPos() const; + oslFileError setPos (sal_uInt64 uPos); + + sal_uInt64 getSize() const; + oslFileError setSize (sal_uInt64 uSize); + + oslFileError readAt ( + off_t nOffset, + void * pBuffer, + size_t nBytesRequested, + sal_uInt64 * pBytesRead); + + oslFileError writeAt ( + off_t nOffset, + void const * pBuffer, + size_t nBytesToWrite, + sal_uInt64 * pBytesWritten); + + oslFileError readFileAt ( + off_t nOffset, + void * pBuffer, + size_t nBytesRequested, + sal_uInt64 * pBytesRead); + + oslFileError writeFileAt ( + off_t nOffset, + void const * pBuffer, + size_t nBytesToWrite, + sal_uInt64 * pBytesWritten); + + oslFileError readLineAt ( + off_t nOffset, + sal_Sequence ** ppSequence, + sal_uInt64 * pBytesRead); + + oslFileError writeSequence_Impl ( + sal_Sequence ** ppSequence, + size_t * pnOffset, + const void * pBuffer, + size_t nBytes); + + oslFileError syncFile(); + + /** Buffer cache / allocator. + */ + class Allocator + { + rtl_cache_type * m_cache; + size_t m_bufsiz; + + Allocator (Allocator const &); + Allocator & operator= (Allocator const &); + + public: + static Allocator & get(); + + void allocate (sal_uInt8 ** ppBuffer, size_t * pnSize); + void deallocate (sal_uInt8 * pBuffer); + + protected: + Allocator(); + ~Allocator(); + }; + + /** Guard. + */ + class Guard + { + pthread_mutex_t * m_mutex; + + public: + explicit Guard(pthread_mutex_t * pMutex); + ~Guard(); + }; +}; + +/******************************************************************* + * + * FileHandle_Impl implementation + * + ******************************************************************/ + +FileHandle_Impl::Allocator & +FileHandle_Impl::Allocator::get() +{ + static Allocator g_aBufferAllocator; + return g_aBufferAllocator; +} + +FileHandle_Impl::Allocator::Allocator() + : m_cache (0), + m_bufsiz (0) +{ + size_t const pagesize = FileHandle_Impl::getpagesize(); + if (size_t(-1) != pagesize) + { + m_cache = rtl_cache_create ( + "osl_file_buffer_cache", pagesize, 0, 0, 0, 0, 0, 0, 0); + if (0 != m_cache) + m_bufsiz = pagesize; + } +} +FileHandle_Impl::Allocator::~Allocator() +{ + rtl_cache_destroy (m_cache), m_cache = 0; +} + +void FileHandle_Impl::Allocator::allocate (sal_uInt8 ** ppBuffer, size_t * pnSize) +{ + OSL_PRECOND((0 != ppBuffer) && (0 != pnSize), "FileHandle_Impl::Allocator::allocate(): contract violation"); + *ppBuffer = static_cast< sal_uInt8* >(rtl_cache_alloc(m_cache)), *pnSize = m_bufsiz; +} +void FileHandle_Impl::Allocator::deallocate (sal_uInt8 * pBuffer) +{ + if (0 != pBuffer) + rtl_cache_free (m_cache, pBuffer); +} + +FileHandle_Impl::Guard::Guard(pthread_mutex_t * pMutex) + : m_mutex (pMutex) +{ + OSL_PRECOND (m_mutex != 0, "FileHandle_Impl::Guard::Guard(): null pointer."); + (void) pthread_mutex_lock (m_mutex); // ignoring EINVAL ... +} +FileHandle_Impl::Guard::~Guard() +{ + OSL_PRECOND (m_mutex != 0, "FileHandle_Impl::Guard::~Guard(): null pointer."); + (void) pthread_mutex_unlock (m_mutex); +} + +FileHandle_Impl::FileHandle_Impl (int fd, char const * path) + : m_strFilePath (0), + m_fd (fd), + m_state (STATE_SEEKABLE | STATE_READABLE), + m_size (0), + m_offset (0), + m_fileptr (0), + m_bufptr (-1), + m_buflen (0), + m_bufsiz (0), + m_buffer (0) +{ + (void) pthread_mutex_init(&m_mutex, 0); + rtl_string_newFromStr (&m_strFilePath, path); + Allocator::get().allocate (&m_buffer, &m_bufsiz); + if (0 != m_buffer) + memset (m_buffer, 0, m_bufsiz); +} +FileHandle_Impl::~FileHandle_Impl() +{ + Allocator::get().deallocate (m_buffer), m_buffer = 0; + rtl_string_release (m_strFilePath), m_strFilePath = 0; + (void) pthread_mutex_destroy(&m_mutex); // ignoring EBUSY ... +} + +void* FileHandle_Impl::operator new (size_t n) +{ + return rtl_allocateMemory(n); +} +void FileHandle_Impl::operator delete (void * p, size_t) +{ + rtl_freeMemory(p); +} + +size_t FileHandle_Impl::getpagesize() +{ +#if defined(FREEBSD) || defined(NETBSD) || defined(MACOSX) + return sal::static_int_cast< size_t >(::getpagesize()); +#else /* POSIX */ + return sal::static_int_cast< size_t >(::sysconf(_SC_PAGESIZE)); +#endif /* xBSD || POSIX */ +} + +sal_uInt64 FileHandle_Impl::getPos() const +{ + return sal::static_int_cast< sal_uInt64 >(m_fileptr); +} + +oslFileError FileHandle_Impl::setPos (sal_uInt64 uPos) +{ + OSL_FILE_TRACE("FileHandle_Impl::setPos(%d, %lld) => %lld", m_fd, getPos(), uPos); + m_fileptr = sal::static_int_cast< off_t >(uPos); + return osl_File_E_None; +} + +sal_uInt64 FileHandle_Impl::getSize() const +{ + off_t const bufend = std::max((off_t)(0), m_bufptr) + m_buflen; + return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend)); +} + +oslFileError FileHandle_Impl::setSize (sal_uInt64 uSize) +{ + off_t const nSize = sal::static_int_cast< off_t >(uSize); + if (-1 == ftruncate (m_fd, nSize)) + { + /* Failure. Save original result. Try fallback algorithm */ + oslFileError result = oslTranslateFileError (OSL_FET_ERROR, errno); + + /* Check against current size. Fail upon 'shrink' */ + if (uSize <= getSize()) + { + /* Failure upon 'shrink'. Return original result */ + return (result); + } + + /* Save current position */ + off_t const nCurPos = (off_t)lseek (m_fd, (off_t)0, SEEK_CUR); + if (nCurPos == (off_t)(-1)) + return (result); + + /* Try 'expand' via 'lseek()' and 'write()' */ + if (-1 == lseek (m_fd, (off_t)(nSize - 1), SEEK_SET)) + return (result); + + if (-1 == write (m_fd, (char*)"", (size_t)1)) + { + /* Failure. Restore saved position */ + (void) lseek (m_fd, (off_t)(nCurPos), SEEK_SET); + return (result); + } + + /* Success. Restore saved position */ + if (-1 == lseek (m_fd, (off_t)nCurPos, SEEK_SET)) + return (result); + } + + OSL_FILE_TRACE("osl_setFileSize(%d, %lld) => %ld", m_fd, getSize(), nSize); + m_size = sal::static_int_cast< sal_uInt64 >(nSize); + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::readAt ( + off_t nOffset, + void * pBuffer, + size_t nBytesRequested, + sal_uInt64 * pBytesRead) +{ + OSL_PRECOND((m_state & STATE_SEEKABLE), "FileHandle_Impl::readAt(): not seekable"); + if (!(m_state & STATE_SEEKABLE)) + return osl_File_E_SPIPE; + + OSL_PRECOND((m_state & STATE_READABLE), "FileHandle_Impl::readAt(): not readable"); + if (!(m_state & STATE_READABLE)) + return osl_File_E_BADF; + +#if defined(LINUX) || defined(SOLARIS) + + ssize_t nBytes = ::pread (m_fd, pBuffer, nBytesRequested, nOffset); + if ((-1 == nBytes) && (EOVERFLOW == errno)) + { + /* Some 'pread()'s fail with EOVERFLOW when reading at (or past) + * end-of-file, different from 'lseek() + read()' behaviour. + * Returning '0 bytes read' and 'osl_File_E_None' instead. + */ + nBytes = 0; + } + if (-1 == nBytes) + return oslTranslateFileError (OSL_FET_ERROR, errno); + +#else /* !(LINUX || SOLARIS) */ + + if (nOffset != m_offset) + { + if (-1 == ::lseek (m_fd, nOffset, SEEK_SET)) + return oslTranslateFileError (OSL_FET_ERROR, errno); + m_offset = nOffset; + } + + ssize_t nBytes = ::read (m_fd, pBuffer, nBytesRequested); + if (-1 == nBytes) + return oslTranslateFileError (OSL_FET_ERROR, errno); + m_offset += nBytes; + +#endif /* !(LINUX || SOLARIS) */ + + OSL_FILE_TRACE("FileHandle_Impl::readAt(%d, %lld, %ld)", m_fd, nOffset, nBytes); + *pBytesRead = nBytes; + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::writeAt ( + off_t nOffset, + void const * pBuffer, + size_t nBytesToWrite, + sal_uInt64 * pBytesWritten) +{ + OSL_PRECOND((m_state & STATE_SEEKABLE), "FileHandle_Impl::writeAt(): not seekable"); + if (!(m_state & STATE_SEEKABLE)) + return osl_File_E_SPIPE; + + OSL_PRECOND((m_state & STATE_WRITEABLE), "FileHandle_Impl::writeAt(): not writeable"); + if (!(m_state & STATE_WRITEABLE)) + return osl_File_E_BADF; + +#if defined(LINUX) || defined(SOLARIS) + + ssize_t nBytes = ::pwrite (m_fd, pBuffer, nBytesToWrite, nOffset); + if (-1 == nBytes) + return oslTranslateFileError (OSL_FET_ERROR, errno); + +#else /* !(LINUX || SOLARIS) */ + + if (nOffset != m_offset) + { + if (-1 == ::lseek (m_fd, nOffset, SEEK_SET)) + return oslTranslateFileError (OSL_FET_ERROR, errno); + m_offset = nOffset; + } + + ssize_t nBytes = ::write (m_fd, pBuffer, nBytesToWrite); + if (-1 == nBytes) + return oslTranslateFileError (OSL_FET_ERROR, errno); + m_offset += nBytes; + +#endif /* !(LINUX || SOLARIS) */ + + OSL_FILE_TRACE("FileHandle_Impl::writeAt(%d, %lld, %ld)", m_fd, nOffset, nBytes); + m_size = std::max (m_size, sal::static_int_cast< sal_uInt64 >(nOffset + nBytes)); + + *pBytesWritten = nBytes; + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::readFileAt ( + off_t nOffset, + void * pBuffer, + size_t nBytesRequested, + sal_uInt64 * pBytesRead) +{ + if (0 == (m_state & STATE_SEEKABLE)) + { + // not seekable (pipe) + ssize_t nBytes = ::read (m_fd, pBuffer, nBytesRequested); + if (-1 == nBytes) + return oslTranslateFileError (OSL_FET_ERROR, errno); + *pBytesRead = nBytes; + return osl_File_E_None; + } + else if (0 == m_buffer) + { + // not buffered + return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead); + } + else + { + sal_uInt8 * buffer = static_cast<sal_uInt8*>(pBuffer); + for (*pBytesRead = 0; nBytesRequested > 0; ) + { + off_t 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 -= 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 = uDone; + } + if (bufpos >= m_buflen) + { + // end of file + return osl_File_E_None; + } + + size_t const bytes = std::min (m_buflen - bufpos, nBytesRequested); + OSL_FILE_TRACE("FileHandle_Impl::readFileAt(%d, %lld, %ld)", m_fd, nOffset, bytes); + + memcpy (&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes); + nBytesRequested -= bytes, *pBytesRead += bytes, nOffset += bytes; + } + return osl_File_E_None; + } +} + +oslFileError FileHandle_Impl::writeFileAt ( + off_t nOffset, + void const * pBuffer, + size_t nBytesToWrite, + sal_uInt64 * pBytesWritten) +{ + if (0 == (m_state & STATE_SEEKABLE)) + { + // not seekable (pipe) + ssize_t nBytes = ::write (m_fd, pBuffer, nBytesToWrite); + if (-1 == nBytes) + return oslTranslateFileError (OSL_FET_ERROR, errno); + *pBytesWritten = nBytes; + return osl_File_E_None; + } + else if (0 == 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; ) + { + off_t 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 to 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 -= 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 = uDone; + } + + size_t const bytes = std::min (m_bufsiz - bufpos, nBytesToWrite); + OSL_FILE_TRACE("FileHandle_Impl::writeFileAt(%d, %lld, %ld)", m_fd, nOffset, bytes); + + memcpy (&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes); + nBytesToWrite -= bytes, *pBytesWritten += bytes, nOffset += bytes; + + m_buflen = std::max(m_buflen, bufpos + bytes); + m_state |= STATE_MODIFIED; + } + return osl_File_E_None; + } +} + +oslFileError FileHandle_Impl::readLineAt ( + off_t nOffset, + sal_Sequence ** ppSequence, + sal_uInt64 * pBytesRead) +{ + oslFileError result = osl_File_E_None; + + off_t 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 = 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 = 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 (0 < (curpos - bufpos)) + { + /* 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 = uDone; + } + + bufpos = 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, 0, 0); + if (result != osl_File_E_None) + return (result); + if (0 < dstpos) + 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 != 0) + { + /* fill sequence */ + memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes), *pnOffset += nBytes; + } + return (*ppSequence != 0) ? osl_File_E_None : osl_File_E_NOMEM; +} + +oslFileError FileHandle_Impl::syncFile() +{ + oslFileError result = osl_File_E_None; + if (m_state & STATE_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 &= ~STATE_MODIFIED; + } + return (result); +} + +/**************************************************************************** + * osl_createFileHandleFromFD + ***************************************************************************/ +extern "C" oslFileHandle osl_createFileHandleFromFD( int fd ) +{ + if (-1 == fd) + return 0; // EINVAL + + struct stat aFileStat; + if (-1 == fstat (fd, &aFileStat)) + return 0; // EBADF + + FileHandle_Impl * pImpl = new FileHandle_Impl (fd); + if (0 == pImpl) + return 0; // ENOMEM + + // assume writeable + pImpl->m_state |= FileHandle_Impl::STATE_WRITEABLE; + if (!S_ISREG(aFileStat.st_mode)) + { + /* not a regular file, mark not seekable */ + pImpl->m_state &= ~FileHandle_Impl::STATE_SEEKABLE; + } + else + { + /* regular file, init current size */ + pImpl->m_size = sal::static_int_cast< sal_uInt64 >(aFileStat.st_size); + } + + OSL_FILE_TRACE("osl_createFileHandleFromFD(%d, writeable) => %s", + pImpl->m_fd, rtl_string_getStr(pImpl->m_strFilePath)); + return (oslFileHandle)(pImpl); +} + +/******************************************************************* + * osl_file_adjustLockFlags + ******************************************************************/ +static int osl_file_adjustLockFlags (const char * path, int flags) +{ +#ifdef MACOSX + /* + * The AFP implementation of MacOS X 10.4 treats O_EXLOCK in a way + * that makes it impossible for OOo to create a backup copy of the + * file it keeps opened. OTOH O_SHLOCK for AFP behaves as desired by + * the OOo file handling, so we need to check the path of the file + * for the filesystem name. + */ + struct statfs s; + if( 0 <= statfs( path, &s ) ) + { + if( 0 == strncmp("afpfs", s.f_fstypename, 5) ) + { + flags &= ~O_EXLOCK; + flags |= O_SHLOCK; + } + else + { + /* Needed flags to allow opening a webdav file */ + flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK); + } + } +#endif /* MACOSX */ + + (void) path; + return flags; +} + +/**************************************************************************** + * osl_file_queryLocking + ***************************************************************************/ +struct Locking_Impl +{ + int m_enabled; + Locking_Impl() : m_enabled(0) + { +#ifndef HAVE_O_EXLOCK + m_enabled = ((getenv("SAL_ENABLE_FILE_LOCKING") != 0) || (getenv("STAR_ENABLE_FILE_LOCKING") != 0)); +#endif /* HAVE_O_EXLOCK */ + } +}; +static int osl_file_queryLocking (sal_uInt32 uFlags) +{ + if (!(uFlags & osl_File_OpenFlag_NoLock)) + { + if ((uFlags & osl_File_OpenFlag_Write) || (uFlags & osl_File_OpenFlag_Create)) + { + static Locking_Impl g_locking; + return (g_locking.m_enabled != 0); + } + } + return 0; +} + +/**************************************************************************** + * osl_openFile + ***************************************************************************/ +#ifdef HAVE_O_EXLOCK +#define OPEN_WRITE_FLAGS ( O_RDWR | O_EXLOCK | O_NONBLOCK ) +#define OPEN_CREATE_FLAGS ( O_CREAT | O_EXCL | O_RDWR | O_EXLOCK | O_NONBLOCK ) +#else +#define OPEN_WRITE_FLAGS ( O_RDWR ) +#define OPEN_CREATE_FLAGS ( O_CREAT | O_EXCL | O_RDWR ) +#endif + +oslFileError +SAL_CALL osl_openFile( rtl_uString* ustrFileURL, oslFileHandle* pHandle, sal_uInt32 uFlags ) +{ + oslFileError eRet; + + if ((ustrFileURL == 0) || (ustrFileURL->length == 0) || (pHandle == 0)) + return osl_File_E_INVAL; + + /* convert file URL to system path */ + char buffer[PATH_MAX]; + eRet = FileURLToPath (buffer, sizeof(buffer), ustrFileURL); + if (eRet != osl_File_E_None) + return eRet; +#ifdef MACOSX + if (macxp_resolveAlias (buffer, sizeof(buffer)) != 0) + return oslTranslateFileError (OSL_FET_ERROR, errno); +#endif /* MACOSX */ + + /* set mode and flags */ + int mode = S_IRUSR | S_IRGRP | S_IROTH; + int flags = O_RDONLY; + if (uFlags & osl_File_OpenFlag_Write) + { + mode |= S_IWUSR | S_IWGRP | S_IWOTH; + flags = OPEN_WRITE_FLAGS; + } + if (uFlags & osl_File_OpenFlag_Create) + { + mode |= S_IWUSR | S_IWGRP | S_IWOTH; + flags = OPEN_CREATE_FLAGS; + } + if (uFlags & osl_File_OpenFlag_NoLock) + { +#ifdef HAVE_O_EXLOCK + flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK); +#endif /* HAVE_O_EXLOCK */ + } + else + { + flags = osl_file_adjustLockFlags (buffer, flags); + } + + /* open the file */ + int fd = open( buffer, flags, mode ); + if (-1 == fd) + return oslTranslateFileError (OSL_FET_ERROR, errno); + + /* reset O_NONBLOCK flag */ + if (flags & O_NONBLOCK) + { + int f = fcntl (fd, F_GETFL, 0); + if (-1 == f) + { + eRet = oslTranslateFileError (OSL_FET_ERROR, errno); + (void) close(fd); + return eRet; + } + if (-1 == fcntl (fd, F_SETFL, (f & ~O_NONBLOCK))) + { + eRet = oslTranslateFileError (OSL_FET_ERROR, errno); + (void) close(fd); + return eRet; + } + } + + /* get file status (mode, size) */ + struct stat aFileStat; + if (-1 == fstat (fd, &aFileStat)) + { + eRet = oslTranslateFileError (OSL_FET_ERROR, errno); + (void) close(fd); + return eRet; + } + if (!S_ISREG(aFileStat.st_mode)) + { + /* we only open regular files here */ + (void) close(fd); + return osl_File_E_INVAL; + } + + if (osl_file_queryLocking (uFlags)) + { +#ifdef MACOSX + if (-1 == flock (fd, LOCK_EX | LOCK_NB)) + { + /* Mac OSX returns ENOTSUP for webdav drives. We should try read lock */ + if ((errno != ENOTSUP) || ((-1 == flock (fd, LOCK_SH | LOCK_NB)) && (errno != ENOTSUP))) + { + eRet = oslTranslateFileError (OSL_FET_ERROR, errno); + (void) close(fd); + return eRet; + } + } +#else /* F_SETLK */ + { + struct flock aflock; + + aflock.l_type = F_WRLCK; + aflock.l_whence = SEEK_SET; + aflock.l_start = 0; + aflock.l_len = 0; + + if (-1 == fcntl (fd, F_SETLK, &aflock)) + { + eRet = oslTranslateFileError (OSL_FET_ERROR, errno); + (void) close(fd); + return eRet; + } + } +#endif /* F_SETLK */ + } + + /* allocate memory for impl structure */ + FileHandle_Impl * pImpl = new FileHandle_Impl (fd, buffer); + if (!pImpl) + { + eRet = oslTranslateFileError (OSL_FET_ERROR, ENOMEM); + (void) close(fd); + return eRet; + } + if (flags & O_RDWR) + pImpl->m_state |= FileHandle_Impl::STATE_WRITEABLE; + pImpl->m_size = sal::static_int_cast< sal_uInt64 >(aFileStat.st_size); + + OSL_TRACE("osl_openFile(%d, %s) => %s", pImpl->m_fd, + flags & O_RDWR ? "writeable":"readonly", + rtl_string_getStr(pImpl->m_strFilePath)); + + *pHandle = (oslFileHandle)(pImpl); + return osl_File_E_None; +} + +/****************************************************************************/ +/* osl_closeFile */ +/****************************************************************************/ +oslFileError +SAL_CALL osl_closeFile( oslFileHandle Handle ) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((pImpl == 0) || (pImpl->m_fd < 0)) + return osl_File_E_INVAL; + + (void) pthread_mutex_lock (&(pImpl->m_mutex)); + + /* close(2) implicitly (and unconditionally) unlocks */ + OSL_TRACE("osl_closeFile(%d) => %s", pImpl->m_fd, rtl_string_getStr(pImpl->m_strFilePath)); + oslFileError result = pImpl->syncFile(); + if (result != osl_File_E_None) + { + /* close, ignoring double failure */ + (void) close (pImpl->m_fd); + } + else if (-1 == close (pImpl->m_fd)) + { + /* translate error code */ + result = oslTranslateFileError (OSL_FET_ERROR, errno); + } + + (void) pthread_mutex_unlock (&(pImpl->m_mutex)); + delete pImpl; + return (result); +} + +/************************************************ + * osl_syncFile + ***********************************************/ +oslFileError +SAL_CALL osl_syncFile(oslFileHandle Handle) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + + OSL_TRACE("osl_syncFile(%d)", pImpl->m_fd); + oslFileError result = pImpl->syncFile(); + if (result != osl_File_E_None) + return (result); + if (-1 == fsync (pImpl->m_fd)) + return oslTranslateFileError (OSL_FET_ERROR, errno); + + return osl_File_E_None; +} + +/******************************************* + osl_mapFile +********************************************/ +oslFileError +SAL_CALL osl_mapFile ( + oslFileHandle Handle, + void** ppAddr, + sal_uInt64 uLength, + sal_uInt64 uOffset, + sal_uInt32 uFlags +) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == ppAddr)) + return osl_File_E_INVAL; + *ppAddr = 0; + + static sal_uInt64 const g_limit_size_t = std::numeric_limits< size_t >::max(); + if (g_limit_size_t < uLength) + return osl_File_E_OVERFLOW; + size_t const nLength = sal::static_int_cast< size_t >(uLength); + + static sal_uInt64 const g_limit_off_t = std::numeric_limits< off_t >::max(); + if (g_limit_off_t < uOffset) + return osl_File_E_OVERFLOW; + off_t const nOffset = sal::static_int_cast< off_t >(uOffset); + + void* p = mmap(NULL, nLength, PROT_READ, MAP_SHARED, pImpl->m_fd, nOffset); + if (MAP_FAILED == p) + return oslTranslateFileError(OSL_FET_ERROR, errno); + *ppAddr = p; + + if (uFlags & osl_File_MapFlag_RandomAccess) + { + // Determine memory pagesize. + size_t const nPageSize = FileHandle_Impl::getpagesize(); + if (size_t(-1) != nPageSize) + { + /* + * Pagein, touching first byte of every memory page. + * Note: volatile disables optimizing the loop away. + */ + sal_uInt8 * pData (reinterpret_cast<sal_uInt8*>(*ppAddr)); + size_t nSize (nLength); + + volatile sal_uInt8 c = 0; + while (nSize > nPageSize) + { + c ^= pData[0]; + pData += nPageSize; + nSize -= nPageSize; + } + if (nSize > 0) + { + c^= pData[0]; + pData += nSize; + nSize -= nSize; + } + } + } + if (uFlags & osl_File_MapFlag_WillNeed) + { + // On Linux, madvise(..., MADV_WILLNEED) appears to have the undesirable + // effect of not returning until the data has actually been paged in, so + // that its net effect would typically be to slow down the process + // (which could start processing at the beginning of the data while the + // OS simultaneously pages in the rest); on other platforms, it remains + // to be evaluated whether madvise or equivalent is available and + // actually useful: +#if defined MACOSX + int e = posix_madvise(p, nLength, POSIX_MADV_WILLNEED); + if (e != 0) + { + OSL_TRACE( + "posix_madvise(..., POSIX_MADV_WILLNEED) failed with %d", e); + } +#elif defined SOLARIS + if (madvise(static_cast< caddr_t >(p), nLength, MADV_WILLNEED) != 0) + { + OSL_TRACE("madvise(..., MADV_WILLNEED) failed with %d", errno); + } +#endif + } + return osl_File_E_None; +} + +/******************************************* + osl_unmapFile +********************************************/ +oslFileError +SAL_CALL osl_unmapFile (void* pAddr, sal_uInt64 uLength) +{ + if (0 == pAddr) + return osl_File_E_INVAL; + + static sal_uInt64 const g_limit_size_t = std::numeric_limits< size_t >::max(); + if (g_limit_size_t < uLength) + return osl_File_E_OVERFLOW; + size_t const nLength = sal::static_int_cast< size_t >(uLength); + + if (-1 == munmap(static_cast<char*>(pAddr), nLength)) + return oslTranslateFileError(OSL_FET_ERROR, errno); + + return osl_File_E_None; +} + +/******************************************* + osl_readLine +********************************************/ +oslFileError +SAL_CALL osl_readLine ( + oslFileHandle Handle, + sal_Sequence ** ppSequence) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == ppSequence)) + return osl_File_E_INVAL; + sal_uInt64 uBytesRead = 0; + + // read at current fileptr; fileptr += uBytesRead; + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + oslFileError result = pImpl->readLineAt ( + pImpl->m_fileptr, ppSequence, &uBytesRead); + if (result == osl_File_E_None) + pImpl->m_fileptr += uBytesRead; + return (result); +} + +/******************************************* + osl_readFile +********************************************/ +oslFileError +SAL_CALL osl_readFile ( + oslFileHandle Handle, + void * pBuffer, + sal_uInt64 uBytesRequested, + sal_uInt64 * pBytesRead) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pBuffer) || (0 == pBytesRead)) + return osl_File_E_INVAL; + + static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max(); + if (g_limit_ssize_t < uBytesRequested) + return osl_File_E_OVERFLOW; + size_t const nBytesRequested = sal::static_int_cast< size_t >(uBytesRequested); + + // read at current fileptr; fileptr += *pBytesRead; + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + oslFileError result = pImpl->readFileAt ( + pImpl->m_fileptr, pBuffer, nBytesRequested, pBytesRead); + if (result == osl_File_E_None) + pImpl->m_fileptr += *pBytesRead; + return (result); +} + +/******************************************* + osl_writeFile +********************************************/ +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 ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pBuffer) || (0 == pBytesWritten)) + return osl_File_E_INVAL; + if (0 == (pImpl->m_state & FileHandle_Impl::STATE_WRITEABLE)) + return osl_File_E_BADF; + + static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max(); + if (g_limit_ssize_t < uBytesToWrite) + return osl_File_E_OVERFLOW; + size_t const nBytesToWrite = sal::static_int_cast< size_t >(uBytesToWrite); + + // write at current fileptr; fileptr += *pBytesWritten; + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + oslFileError result = pImpl->writeFileAt ( + pImpl->m_fileptr, pBuffer, nBytesToWrite, pBytesWritten); + if (result == osl_File_E_None) + pImpl->m_fileptr += *pBytesWritten; + return (result); +} + +/******************************************* + osl_readFileAt +********************************************/ +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 ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pBuffer) || (0 == pBytesRead)) + return osl_File_E_INVAL; + if (0 == (pImpl->m_state & FileHandle_Impl::STATE_SEEKABLE)) + return osl_File_E_SPIPE; + + static sal_uInt64 const g_limit_off_t = std::numeric_limits< off_t >::max(); + if (g_limit_off_t < uOffset) + return osl_File_E_OVERFLOW; + off_t const nOffset = sal::static_int_cast< off_t >(uOffset); + + static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max(); + if (g_limit_ssize_t < uBytesRequested) + return osl_File_E_OVERFLOW; + size_t const nBytesRequested = sal::static_int_cast< size_t >(uBytesRequested); + + // read at specified fileptr + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + return pImpl->readFileAt (nOffset, pBuffer, nBytesRequested, pBytesRead); +} + +/******************************************* + osl_writeFileAt +********************************************/ +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 ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pBuffer) || (0 == pBytesWritten)) + return osl_File_E_INVAL; + if (0 == (pImpl->m_state & FileHandle_Impl::STATE_SEEKABLE)) + return osl_File_E_SPIPE; + if (0 == (pImpl->m_state & FileHandle_Impl::STATE_WRITEABLE)) + return osl_File_E_BADF; + + static sal_uInt64 const g_limit_off_t = std::numeric_limits< off_t >::max(); + if (g_limit_off_t < uOffset) + return osl_File_E_OVERFLOW; + off_t const nOffset = sal::static_int_cast< off_t >(uOffset); + + static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max(); + if (g_limit_ssize_t < uBytesToWrite) + return osl_File_E_OVERFLOW; + size_t const nBytesToWrite = sal::static_int_cast< size_t >(uBytesToWrite); + + // write at specified fileptr + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + return pImpl->writeFileAt (nOffset, pBuffer, nBytesToWrite, pBytesWritten); +} + +/****************************************************************************/ +/* osl_isEndOfFile */ +/****************************************************************************/ +oslFileError +SAL_CALL osl_isEndOfFile( oslFileHandle Handle, sal_Bool *pIsEOF ) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pIsEOF)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + *pIsEOF = (pImpl->getPos() == pImpl->getSize()); + return osl_File_E_None; +} + +/************************************************ + * osl_getFilePos + ***********************************************/ +oslFileError +SAL_CALL osl_getFilePos( oslFileHandle Handle, sal_uInt64* pPos ) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pPos)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + *pPos = pImpl->getPos(); + return osl_File_E_None; +} + +/******************************************* + osl_setFilePos +********************************************/ +oslFileError +SAL_CALL osl_setFilePos (oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd)) + return osl_File_E_INVAL; + + static sal_Int64 const g_limit_off_t = std::numeric_limits< off_t >::max(); + if (g_limit_off_t < uOffset) + return osl_File_E_OVERFLOW; + off_t nPos = 0, nOffset = sal::static_int_cast< off_t >(uOffset); + + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + switch(uHow) + { + case osl_Pos_Absolut: + if (0 > nOffset) + return osl_File_E_INVAL; + break; + + case osl_Pos_Current: + nPos = sal::static_int_cast< off_t >(pImpl->getPos()); + if ((0 > nOffset) && (-1*nOffset > nPos)) + return osl_File_E_INVAL; + if (g_limit_off_t < nPos + nOffset) + return osl_File_E_OVERFLOW; + break; + + case osl_Pos_End: + nPos = sal::static_int_cast< off_t >(pImpl->getSize()); + if ((0 > nOffset) && (-1*nOffset > nPos)) + return osl_File_E_INVAL; + if (g_limit_off_t < nPos + nOffset) + return osl_File_E_OVERFLOW; + break; + + default: + return osl_File_E_INVAL; + } + + return pImpl->setPos (nPos + nOffset); +} + +/**************************************************************************** + * osl_getFileSize + ****************************************************************************/ +oslFileError +SAL_CALL osl_getFileSize( oslFileHandle Handle, sal_uInt64* pSize ) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd) || (0 == pSize)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + *pSize = pImpl->getSize(); + return osl_File_E_None; +} + +/************************************************ + * osl_setFileSize + ***********************************************/ +oslFileError +SAL_CALL osl_setFileSize( oslFileHandle Handle, sal_uInt64 uSize ) +{ + FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((0 == pImpl) || (-1 == pImpl->m_fd)) + return osl_File_E_INVAL; + if (0 == (pImpl->m_state & FileHandle_Impl::STATE_WRITEABLE)) + return osl_File_E_BADF; + + static sal_uInt64 const g_limit_off_t = std::numeric_limits< off_t >::max(); + if (g_limit_off_t < uSize) + return osl_File_E_OVERFLOW; + + oslFileError result = pImpl->syncFile(); + if (result != osl_File_E_None) + return (result); + pImpl->m_bufptr = -1, pImpl->m_buflen = 0; + + return pImpl->setSize (uSize); +} |