summaryrefslogtreecommitdiffstats
path: root/tools/source/stream/strmunx.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /tools/source/stream/strmunx.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/source/stream/strmunx.cxx')
-rw-r--r--tools/source/stream/strmunx.cxx496
1 files changed, 496 insertions, 0 deletions
diff --git a/tools/source/stream/strmunx.cxx b/tools/source/stream/strmunx.cxx
new file mode 100644
index 000000000..5964529f3
--- /dev/null
+++ b/tools/source/stream/strmunx.cxx
@@ -0,0 +1,496 @@
+/* -*- 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 <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <tools/stream.hxx>
+#include <map>
+
+#include <mutex>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#include <osl/file.hxx>
+#include <osl/detail/file.h>
+
+using namespace osl;
+
+// InternalLock ----------------------------------------------------------------
+
+namespace {
+
+std::mutex& LockMutex()
+{
+ static std::mutex SINGLETON;
+ return SINGLETON;
+}
+
+std::map<SvFileStream const *, osl::DirectoryItem> gLocks;
+
+bool lockFile( const SvFileStream* pStream )
+{
+ osl::DirectoryItem aItem;
+ if (osl::DirectoryItem::get( pStream->GetFileName(), aItem) != osl::FileBase::E_None )
+ {
+ SAL_INFO("tools.stream", "Failed to lookup stream for locking");
+ return true;
+ }
+
+ osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
+ if ( aItem.getFileStatus( aStatus ) != osl::FileBase::E_None )
+ {
+ SAL_INFO("tools.stream", "Failed to stat stream for locking");
+ return true;
+ }
+ if( aStatus.getFileType() == osl::FileStatus::Directory )
+ return true;
+
+ std::unique_lock aGuard( LockMutex() );
+ for( const auto& [rLockStream, rLockItem] : gLocks )
+ {
+ if( aItem.isIdenticalTo( rLockItem ) )
+ {
+ StreamMode nLockMode = rLockStream->GetStreamMode();
+ StreamMode nNewMode = pStream->GetStreamMode();
+ bool bDenyByOptions = (nLockMode & StreamMode::SHARE_DENYALL) ||
+ ( (nLockMode & StreamMode::SHARE_DENYWRITE) && (nNewMode & StreamMode::WRITE) ) ||
+ ( (nLockMode & StreamMode::SHARE_DENYREAD) && (nNewMode & StreamMode::READ) );
+
+ if( bDenyByOptions )
+ {
+ return false; // file is already locked
+ }
+ }
+ }
+ gLocks[pStream] = aItem;
+ return true;
+}
+
+void unlockFile( SvFileStream const * pStream )
+{
+ std::unique_lock aGuard( LockMutex() );
+ gLocks.erase(pStream);
+}
+
+}
+
+// StreamData ------------------------------------------------------------------
+
+class StreamData
+{
+public:
+ oslFileHandle rHandle;
+
+ StreamData() : rHandle( nullptr ) { }
+};
+
+static ErrCode GetSvError( int nErrno )
+{
+ static struct { int nErr; ErrCode sv; } const errArr[] =
+ {
+ { 0, ERRCODE_NONE },
+ { EACCES, SVSTREAM_ACCESS_DENIED },
+ { EBADF, SVSTREAM_INVALID_HANDLE },
+#if defined(NETBSD) || \
+ defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
+ defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \
+ defined(IOS) || defined(HAIKU)
+ { EDEADLK, SVSTREAM_LOCKING_VIOLATION },
+#else
+ { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION },
+#endif
+ { EINVAL, SVSTREAM_INVALID_PARAMETER },
+ { EMFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
+ { ENFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
+ { ENOENT, SVSTREAM_FILE_NOT_FOUND },
+ { EPERM, SVSTREAM_ACCESS_DENIED },
+ { EROFS, SVSTREAM_ACCESS_DENIED },
+ { EAGAIN, SVSTREAM_LOCKING_VIOLATION },
+ { EISDIR, SVSTREAM_PATH_NOT_FOUND },
+ { ELOOP, SVSTREAM_PATH_NOT_FOUND },
+#if !defined(NETBSD) && !defined (FREEBSD) && \
+ !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
+ !defined(DRAGONFLY)
+ { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND },
+ { ENOLINK, SVSTREAM_PATH_NOT_FOUND },
+#endif
+ { ENOTDIR, SVSTREAM_PATH_NOT_FOUND },
+ { ETXTBSY, SVSTREAM_ACCESS_DENIED },
+ { EEXIST, SVSTREAM_CANNOT_MAKE },
+ { ENOSPC, SVSTREAM_DISK_FULL },
+ { int(0xFFFF), SVSTREAM_GENERALERROR }
+ };
+
+ ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
+ int i=0;
+ do
+ {
+ if ( errArr[i].nErr == nErrno )
+ {
+ nRetVal = errArr[i].sv;
+ break;
+ }
+ ++i;
+ }
+ while( errArr[i].nErr != 0xFFFF );
+ return nRetVal;
+}
+
+static ErrCode GetSvError( oslFileError nErrno )
+{
+ static struct { oslFileError nErr; ErrCode sv; } const errArr[] =
+ {
+ { osl_File_E_None, ERRCODE_NONE },
+ { osl_File_E_ACCES, SVSTREAM_ACCESS_DENIED },
+ { osl_File_E_BADF, SVSTREAM_INVALID_HANDLE },
+ { osl_File_E_DEADLK, SVSTREAM_LOCKING_VIOLATION },
+ { osl_File_E_INVAL, SVSTREAM_INVALID_PARAMETER },
+ { osl_File_E_MFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
+ { osl_File_E_NFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
+ { osl_File_E_NOENT, SVSTREAM_FILE_NOT_FOUND },
+ { osl_File_E_PERM, SVSTREAM_ACCESS_DENIED },
+ { osl_File_E_ROFS, SVSTREAM_ACCESS_DENIED },
+ { osl_File_E_AGAIN, SVSTREAM_LOCKING_VIOLATION },
+ { osl_File_E_ISDIR, SVSTREAM_PATH_NOT_FOUND },
+ { osl_File_E_LOOP, SVSTREAM_PATH_NOT_FOUND },
+ { osl_File_E_MULTIHOP, SVSTREAM_PATH_NOT_FOUND },
+ { osl_File_E_NOLINK, SVSTREAM_PATH_NOT_FOUND },
+ { osl_File_E_NOTDIR, SVSTREAM_PATH_NOT_FOUND },
+ { osl_File_E_EXIST, SVSTREAM_CANNOT_MAKE },
+ { osl_File_E_NOSPC, SVSTREAM_DISK_FULL },
+ { oslFileError(0xFFFF), SVSTREAM_GENERALERROR }
+ };
+
+ ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
+ int i=0;
+ do
+ {
+ if ( errArr[i].nErr == nErrno )
+ {
+ nRetVal = errArr[i].sv;
+ break;
+ }
+ ++i;
+ }
+ while( errArr[i].nErr != oslFileError(0xFFFF) );
+ return nRetVal;
+}
+
+SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode )
+{
+ bIsOpen = false;
+ m_isWritable = false;
+ pInstanceData.reset(new StreamData);
+
+ SetBufferSize( 1024 );
+ // convert URL to SystemPath, if necessary
+ OUString aSystemFileName;
+ if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName )
+ != FileBase::E_None )
+ {
+ aSystemFileName = rFileName;
+ }
+ Open( aSystemFileName, nOpenMode );
+}
+
+SvFileStream::SvFileStream()
+{
+ bIsOpen = false;
+ m_isWritable = false;
+ pInstanceData.reset(new StreamData);
+ SetBufferSize( 1024 );
+}
+
+SvFileStream::~SvFileStream()
+{
+ Close();
+}
+
+std::size_t SvFileStream::GetData( void* pData, std::size_t nSize )
+{
+ SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename);
+
+ sal_uInt64 nRead = 0;
+ if ( IsOpen() )
+ {
+ oslFileError rc = osl_readFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nRead);
+ if ( rc != osl_File_E_None )
+ {
+ SetError( ::GetSvError( rc ));
+ return -1;
+ }
+ }
+ return static_cast<std::size_t>(nRead);
+}
+
+std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize )
+{
+ SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename);
+
+ sal_uInt64 nWrite = 0;
+ if ( IsOpen() )
+ {
+ oslFileError rc = osl_writeFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite);
+ if ( rc != osl_File_E_None )
+ {
+ SetError( ::GetSvError( rc ) );
+ return -1;
+ }
+ else if( !nWrite )
+ SetError( SVSTREAM_DISK_FULL );
+ }
+ return static_cast<std::size_t>(nWrite);
+}
+
+sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
+{
+ // check if a truncated STREAM_SEEK_TO_END was passed
+ assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END)));
+ if ( IsOpen() )
+ {
+ oslFileError rc;
+ sal_uInt64 nNewPos;
+ if ( nPos != STREAM_SEEK_TO_END )
+ rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_Absolut, nPos );
+ else
+ rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_End, 0 );
+
+ if ( rc != osl_File_E_None )
+ {
+ SetError( SVSTREAM_SEEK_ERROR );
+ return 0;
+ }
+ if ( nPos != STREAM_SEEK_TO_END )
+ return nPos;
+ osl_getFilePos( pInstanceData->rHandle, &nNewPos );
+ return nNewPos;
+ }
+ SetError( SVSTREAM_GENERALERROR );
+ return 0;
+}
+
+void SvFileStream::FlushData()
+{
+ auto rc = osl_syncFile(pInstanceData->rHandle);
+ if (rc != osl_File_E_None)
+ SetError( ::GetSvError( rc ));
+ }
+
+bool SvFileStream::LockFile()
+{
+ int nLockMode = 0;
+
+ if ( ! IsOpen() )
+ return false;
+
+ if (m_eStreamMode & StreamMode::SHARE_DENYALL)
+ {
+ if (m_isWritable)
+ nLockMode = F_WRLCK;
+ else
+ nLockMode = F_RDLCK;
+ }
+
+ if (m_eStreamMode & StreamMode::SHARE_DENYREAD)
+ {
+ if (m_isWritable)
+ nLockMode = F_WRLCK;
+ else
+ {
+ SetError(SVSTREAM_LOCKING_VIOLATION);
+ return false;
+ }
+ }
+
+ if (m_eStreamMode & StreamMode::SHARE_DENYWRITE)
+ {
+ if (m_isWritable)
+ nLockMode = F_WRLCK;
+ else
+ nLockMode = F_RDLCK;
+ }
+
+ if (!nLockMode)
+ return true;
+
+ if( !lockFile( this ) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "InternalLock on %s failed\n",
+ OUStringToOString(aFilename, osl_getThreadTextEncoding()).getStr() );
+#endif
+ return false;
+ }
+
+ return true;
+}
+
+void SvFileStream::UnlockFile()
+{
+ if ( ! IsOpen() )
+ return;
+
+ unlockFile( this );
+}
+
+void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode )
+{
+ sal_uInt32 uFlags;
+ oslFileHandle nHandleTmp;
+
+ Close();
+ errno = 0;
+ m_eStreamMode = nOpenMode;
+ m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen
+
+ aFilename = rFilename;
+
+ SAL_INFO("tools", aFilename);
+
+ OUString aFileURL;
+ osl::DirectoryItem aItem;
+ osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL );
+
+ // FIXME: we really need to switch to a pure URL model ...
+ if ( osl::File::getFileURLFromSystemPath( aFilename, aFileURL ) != osl::FileBase::E_None )
+ aFileURL = aFilename;
+
+ // don't both stat()ing a temporary file, unnecessary
+ bool bStatValid = true;
+ if (!(nOpenMode & StreamMode::TEMPORARY))
+ {
+ bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None &&
+ aItem.getFileStatus( aStatus ) == osl::FileBase::E_None );
+
+ // SvFileStream can't open a directory
+ if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory )
+ {
+ SetError( ::GetSvError( EISDIR ) );
+ return;
+ }
+ }
+
+ if ( !( nOpenMode & StreamMode::WRITE ) )
+ uFlags = osl_File_OpenFlag_Read;
+ else if ( !( nOpenMode & StreamMode::READ ) )
+ uFlags = osl_File_OpenFlag_Write;
+ else
+ uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
+
+ // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
+ // Important for Read-Only-Filesystems (e.g, CDROM)
+ if ( (!( nOpenMode & StreamMode::NOCREATE )) && ( uFlags != osl_File_OpenFlag_Read ) )
+ uFlags |= osl_File_OpenFlag_Create;
+ if ( nOpenMode & StreamMode::TRUNC )
+ uFlags |= osl_File_OpenFlag_Trunc;
+
+ uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock;
+
+ if ( nOpenMode & StreamMode::WRITE)
+ {
+ if ( nOpenMode & StreamMode::COPY_ON_SYMLINK )
+ {
+ if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link &&
+ aStatus.getLinkTargetURL().getLength() > 0 )
+ {
+ // delete the symbolic link, and replace it with the contents of the link
+ if (osl::File::remove( aFileURL ) == osl::FileBase::E_None )
+ {
+ File::copy( aStatus.getLinkTargetURL(), aFileURL );
+#if OSL_DEBUG_LEVEL > 0
+ fprintf( stderr,
+ "Removing link and replacing with file contents (%s) -> (%s).\n",
+ OUStringToOString( aStatus.getLinkTargetURL(),
+ RTL_TEXTENCODING_UTF8).getStr(),
+ OUStringToOString( aFileURL,
+ RTL_TEXTENCODING_UTF8).getStr() );
+#endif
+ }
+ }
+ }
+ }
+
+ oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
+ if ( rc != osl_File_E_None )
+ {
+ if ( uFlags & osl_File_OpenFlag_Write )
+ {
+ // Change to read-only
+ uFlags &= ~osl_File_OpenFlag_Write;
+ rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
+ }
+ }
+ if ( rc == osl_File_E_None )
+ {
+ pInstanceData->rHandle = nHandleTmp;
+ bIsOpen = true;
+ if ( uFlags & osl_File_OpenFlag_Write )
+ m_isWritable = true;
+
+ if ( !LockFile() ) // whole file
+ {
+ osl_closeFile( nHandleTmp );
+ bIsOpen = false;
+ m_isWritable = false;
+ pInstanceData->rHandle = nullptr;
+ }
+ }
+ else
+ SetError( ::GetSvError( rc ) );
+}
+
+void SvFileStream::Close()
+{
+ UnlockFile();
+
+ if ( IsOpen() )
+ {
+ SAL_INFO("tools", "Closing " << aFilename);
+ FlushBuffer();
+ osl_closeFile( pInstanceData->rHandle );
+ pInstanceData->rHandle = nullptr;
+ }
+
+ bIsOpen = false;
+ m_isWritable = false;
+ SvStream::ClearBuffer();
+ SvStream::ClearError();
+}
+
+/// set filepointer to beginning of file
+void SvFileStream::ResetError()
+{
+ SvStream::ClearError();
+}
+
+void SvFileStream::SetSize (sal_uInt64 const nSize)
+{
+ if (IsOpen())
+ {
+ oslFileError rc = osl_setFileSize( pInstanceData->rHandle, nSize );
+ if (rc != osl_File_E_None )
+ {
+ SetError ( ::GetSvError( rc ));
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */