Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / tools / source / stream / strmunx.cxx
blob152ed9b10274fc0c02a8de50749318b3250ecaf9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
21 #include <stdio.h>
22 #include <fcntl.h>
23 #include <errno.h>
25 #include <tools/stream.hxx>
26 #include <vector>
28 #include <osl/mutex.hxx>
29 #include <osl/thread.h>
30 #include <sal/log.hxx>
32 // class FileBase
33 #include <osl/file.hxx>
34 #include <osl/detail/file.h>
35 #include <rtl/instance.hxx>
37 using namespace osl;
39 // InternalLock ----------------------------------------------------------------
41 namespace {
43 struct LockMutex : public rtl::Static< osl::Mutex, LockMutex > {};
45 struct InternalStreamLock
47 sal_uInt64 m_nStartPos;
48 sal_uInt64 m_nEndPos;
49 SvFileStream* m_pStream;
50 osl::DirectoryItem m_aItem;
52 InternalStreamLock( sal_uInt64, sal_uInt64, SvFileStream* );
53 ~InternalStreamLock();
56 struct LockList : public rtl::Static< std::vector<InternalStreamLock>, LockList > {};
58 InternalStreamLock::InternalStreamLock(
59 sal_uInt64 const nStart,
60 sal_uInt64 const nEnd,
61 SvFileStream* pStream ) :
62 m_nStartPos( nStart ),
63 m_nEndPos( nEnd ),
64 m_pStream( pStream )
66 (void)osl::DirectoryItem::get( m_pStream->GetFileName(), m_aItem );
67 #if OSL_DEBUG_LEVEL > 1
68 OString aFileName(OUStringToOString(m_pStream->GetFileName(),
69 osl_getThreadTextEncoding()));
70 fprintf( stderr, "locked %s", aFileName.getStr() );
71 if( m_nStartPos || m_nEndPos )
72 fprintf(stderr, " [ %ld ... %ld ]", m_nStartPos, m_nEndPos );
73 fprintf( stderr, "\n" );
74 #endif
77 InternalStreamLock::~InternalStreamLock()
79 #if OSL_DEBUG_LEVEL > 1
80 OString aFileName(OUStringToOString(m_pStream->GetFileName(),
81 osl_getThreadTextEncoding()));
82 fprintf( stderr, "unlocked %s", aFileName.getStr() );
83 if( m_nStartPos || m_nEndPos )
84 fprintf(stderr, " [ %ld ... %ld ]", m_nStartPos, m_nEndPos );
85 fprintf( stderr, "\n" );
86 #endif
89 bool lockFile( sal_uInt64 const nStart, sal_uInt64 const nEnd, SvFileStream* pStream )
91 osl::DirectoryItem aItem;
92 if (osl::DirectoryItem::get( pStream->GetFileName(), aItem) != osl::FileBase::E_None )
94 SAL_INFO("tools.stream", "Failed to lookup stream for locking");
95 return true;
98 osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
99 if ( aItem.getFileStatus( aStatus ) != osl::FileBase::E_None )
101 SAL_INFO("tools.stream", "Failed to stat stream for locking");
102 return true;
104 if( aStatus.getFileType() == osl::FileStatus::Directory )
105 return true;
107 osl::MutexGuard aGuard( LockMutex::get() );
108 std::vector<InternalStreamLock> &rLockList = LockList::get();
109 for( const auto& rLock : rLockList )
111 if( aItem.isIdenticalTo( rLock.m_aItem ) )
113 StreamMode nLockMode = rLock.m_pStream->GetStreamMode();
114 StreamMode nNewMode = pStream->GetStreamMode();
115 bool bDenyByOptions = (nLockMode & StreamMode::SHARE_DENYALL) ||
116 ( (nLockMode & StreamMode::SHARE_DENYWRITE) && (nNewMode & StreamMode::WRITE) ) ||
117 ( (nLockMode & StreamMode::SHARE_DENYREAD) && (nNewMode & StreamMode::READ) );
119 if( bDenyByOptions )
121 if( rLock.m_nStartPos == 0 && rLock.m_nEndPos == 0 ) // whole file is already locked
122 return false;
123 if( nStart == 0 && nEnd == 0) // cannot lock whole file
124 return false;
126 if( ( nStart < rLock.m_nStartPos && nEnd > rLock.m_nStartPos ) ||
127 ( nStart < rLock.m_nEndPos && nEnd > rLock.m_nEndPos ) )
128 return false;
132 rLockList.push_back( InternalStreamLock( nStart, nEnd, pStream ) );
133 return true;
136 void unlockFile( sal_uInt64 const nStart, sal_uInt64 const nEnd, SvFileStream const * pStream )
138 osl::MutexGuard aGuard( LockMutex::get() );
139 std::vector<InternalStreamLock> &rLockList = LockList::get();
140 rLockList.erase(std::remove_if(rLockList.begin(), rLockList.end(),
141 [&pStream, &nStart, &nEnd](const InternalStreamLock& rLock) {
142 return rLock.m_pStream == pStream
143 && ((nStart == 0 && nEnd == 0)
144 || (rLock.m_nStartPos == nStart && rLock.m_nEndPos == nEnd));
145 }), rLockList.end());
150 // StreamData ------------------------------------------------------------------
152 class StreamData
154 public:
155 oslFileHandle rHandle;
157 StreamData() : rHandle( nullptr ) { }
160 static ErrCode GetSvError( int nErrno )
162 static struct { int nErr; ErrCode sv; } const errArr[] =
164 { 0, ERRCODE_NONE },
165 { EACCES, SVSTREAM_ACCESS_DENIED },
166 { EBADF, SVSTREAM_INVALID_HANDLE },
167 #if defined(NETBSD) || \
168 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
169 defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \
170 defined(IOS) || defined(HAIKU)
171 { EDEADLK, SVSTREAM_LOCKING_VIOLATION },
172 #else
173 { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION },
174 #endif
175 { EINVAL, SVSTREAM_INVALID_PARAMETER },
176 { EMFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
177 { ENFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
178 { ENOENT, SVSTREAM_FILE_NOT_FOUND },
179 { EPERM, SVSTREAM_ACCESS_DENIED },
180 { EROFS, SVSTREAM_ACCESS_DENIED },
181 { EAGAIN, SVSTREAM_LOCKING_VIOLATION },
182 { EISDIR, SVSTREAM_PATH_NOT_FOUND },
183 { ELOOP, SVSTREAM_PATH_NOT_FOUND },
184 #if !defined(NETBSD) && !defined (FREEBSD) && \
185 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
186 !defined(DRAGONFLY)
187 { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND },
188 { ENOLINK, SVSTREAM_PATH_NOT_FOUND },
189 #endif
190 { ENOTDIR, SVSTREAM_PATH_NOT_FOUND },
191 { ETXTBSY, SVSTREAM_ACCESS_DENIED },
192 { EEXIST, SVSTREAM_CANNOT_MAKE },
193 { ENOSPC, SVSTREAM_DISK_FULL },
194 { int(0xFFFF), SVSTREAM_GENERALERROR }
197 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
198 int i=0;
201 if ( errArr[i].nErr == nErrno )
203 nRetVal = errArr[i].sv;
204 break;
206 ++i;
208 while( errArr[i].nErr != 0xFFFF );
209 return nRetVal;
212 static ErrCode GetSvError( oslFileError nErrno )
214 static struct { oslFileError nErr; ErrCode sv; } const errArr[] =
216 { osl_File_E_None, ERRCODE_NONE },
217 { osl_File_E_ACCES, SVSTREAM_ACCESS_DENIED },
218 { osl_File_E_BADF, SVSTREAM_INVALID_HANDLE },
219 { osl_File_E_DEADLK, SVSTREAM_LOCKING_VIOLATION },
220 { osl_File_E_INVAL, SVSTREAM_INVALID_PARAMETER },
221 { osl_File_E_MFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
222 { osl_File_E_NFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
223 { osl_File_E_NOENT, SVSTREAM_FILE_NOT_FOUND },
224 { osl_File_E_PERM, SVSTREAM_ACCESS_DENIED },
225 { osl_File_E_ROFS, SVSTREAM_ACCESS_DENIED },
226 { osl_File_E_AGAIN, SVSTREAM_LOCKING_VIOLATION },
227 { osl_File_E_ISDIR, SVSTREAM_PATH_NOT_FOUND },
228 { osl_File_E_LOOP, SVSTREAM_PATH_NOT_FOUND },
229 { osl_File_E_MULTIHOP, SVSTREAM_PATH_NOT_FOUND },
230 { osl_File_E_NOLINK, SVSTREAM_PATH_NOT_FOUND },
231 { osl_File_E_NOTDIR, SVSTREAM_PATH_NOT_FOUND },
232 { osl_File_E_EXIST, SVSTREAM_CANNOT_MAKE },
233 { osl_File_E_NOSPC, SVSTREAM_DISK_FULL },
234 { oslFileError(0xFFFF), SVSTREAM_GENERALERROR }
237 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
238 int i=0;
241 if ( errArr[i].nErr == nErrno )
243 nRetVal = errArr[i].sv;
244 break;
246 ++i;
248 while( errArr[i].nErr != oslFileError(0xFFFF) );
249 return nRetVal;
252 SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode )
254 bIsOpen = false;
255 m_isWritable = false;
256 pInstanceData.reset(new StreamData);
258 SetBufferSize( 1024 );
259 // convert URL to SystemPath, if necessary
260 OUString aSystemFileName;
261 if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName )
262 != FileBase::E_None )
264 aSystemFileName = rFileName;
266 Open( aSystemFileName, nOpenMode );
269 SvFileStream::SvFileStream()
271 bIsOpen = false;
272 m_isWritable = false;
273 pInstanceData.reset(new StreamData);
274 SetBufferSize( 1024 );
277 SvFileStream::~SvFileStream()
279 Close();
282 std::size_t SvFileStream::GetData( void* pData, std::size_t nSize )
284 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename);
286 sal_uInt64 nRead = 0;
287 if ( IsOpen() )
289 oslFileError rc = osl_readFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nRead);
290 if ( rc != osl_File_E_None )
292 SetError( ::GetSvError( rc ));
293 return -1;
296 return static_cast<std::size_t>(nRead);
299 std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize )
301 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename);
303 sal_uInt64 nWrite = 0;
304 if ( IsOpen() )
306 oslFileError rc = osl_writeFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite);
307 if ( rc != osl_File_E_None )
309 SetError( ::GetSvError( rc ) );
310 return -1;
312 else if( !nWrite )
313 SetError( SVSTREAM_DISK_FULL );
315 return static_cast<std::size_t>(nWrite);
318 sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
320 // check if a truncated STREAM_SEEK_TO_END was passed
321 assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END)));
322 if ( IsOpen() )
324 oslFileError rc;
325 sal_uInt64 nNewPos;
326 if ( nPos != STREAM_SEEK_TO_END )
327 rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_Absolut, nPos );
328 else
329 rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_End, 0 );
331 if ( rc != osl_File_E_None )
333 SetError( SVSTREAM_SEEK_ERROR );
334 return 0L;
336 if ( nPos != STREAM_SEEK_TO_END )
337 return nPos;
338 rc = osl_getFilePos( pInstanceData->rHandle, &nNewPos );
339 return nNewPos;
341 SetError( SVSTREAM_GENERALERROR );
342 return 0L;
345 void SvFileStream::FlushData()
347 // does not exist locally
350 bool SvFileStream::LockRange(sal_uInt64 const nByteOffset, std::size_t nBytes)
352 int nLockMode = 0;
354 if ( ! IsOpen() )
355 return false;
357 if (m_eStreamMode & StreamMode::SHARE_DENYALL)
359 if (m_isWritable)
360 nLockMode = F_WRLCK;
361 else
362 nLockMode = F_RDLCK;
365 if (m_eStreamMode & StreamMode::SHARE_DENYREAD)
367 if (m_isWritable)
368 nLockMode = F_WRLCK;
369 else
371 SetError(SVSTREAM_LOCKING_VIOLATION);
372 return false;
376 if (m_eStreamMode & StreamMode::SHARE_DENYWRITE)
378 if (m_isWritable)
379 nLockMode = F_WRLCK;
380 else
381 nLockMode = F_RDLCK;
384 if (!nLockMode)
385 return true;
387 if( !lockFile( nByteOffset, nByteOffset+nBytes, this ) )
389 #if OSL_DEBUG_LEVEL > 1
390 fprintf( stderr, "InternalLock on %s [ %ld ... %ld ] failed\n",
391 OUStringToOString(aFilename, osl_getThreadTextEncoding()).getStr(), nByteOffset, nByteOffset+nBytes );
392 #endif
393 return false;
396 return true;
399 bool SvFileStream::UnlockRange(sal_uInt64 const nByteOffset, std::size_t nBytes)
401 if ( ! IsOpen() )
402 return false;
404 unlockFile( nByteOffset, nByteOffset+nBytes, this );
406 return true;
409 bool SvFileStream::LockFile()
411 return LockRange( 0UL, 0UL );
414 void SvFileStream::UnlockFile()
416 UnlockRange( 0UL, 0UL );
419 void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode )
421 sal_uInt32 uFlags;
422 oslFileHandle nHandleTmp;
424 Close();
425 errno = 0;
426 m_eStreamMode = nOpenMode;
427 m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen
429 aFilename = rFilename;
431 SAL_INFO("tools", aFilename);
433 OUString aFileURL;
434 osl::DirectoryItem aItem;
435 osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL );
437 // FIXME: we really need to switch to a pure URL model ...
438 if ( osl::File::getFileURLFromSystemPath( aFilename, aFileURL ) != osl::FileBase::E_None )
439 aFileURL = aFilename;
440 bool bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None &&
441 aItem.getFileStatus( aStatus ) == osl::FileBase::E_None );
443 // SvFileStream can't open a directory
444 if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory )
446 SetError( ::GetSvError( EISDIR ) );
447 return;
450 if ( !( nOpenMode & StreamMode::WRITE ) )
451 uFlags = osl_File_OpenFlag_Read;
452 else if ( !( nOpenMode & StreamMode::READ ) )
453 uFlags = osl_File_OpenFlag_Write;
454 else
455 uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
457 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
458 // Important for Read-Only-Filesystems (e.g, CDROM)
459 if ( (!( nOpenMode & StreamMode::NOCREATE )) && ( uFlags != osl_File_OpenFlag_Read ) )
460 uFlags |= osl_File_OpenFlag_Create;
461 if ( nOpenMode & StreamMode::TRUNC )
462 uFlags |= osl_File_OpenFlag_Trunc;
464 uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock;
466 if ( nOpenMode & StreamMode::WRITE)
468 if ( nOpenMode & StreamMode::COPY_ON_SYMLINK )
470 if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link &&
471 aStatus.getLinkTargetURL().getLength() > 0 )
473 // delete the symbolic link, and replace it with the contents of the link
474 if (osl::File::remove( aFileURL ) == osl::FileBase::E_None )
476 File::copy( aStatus.getLinkTargetURL(), aFileURL );
477 #if OSL_DEBUG_LEVEL > 0
478 fprintf( stderr,
479 "Removing link and replacing with file contents (%s) -> (%s).\n",
480 OUStringToOString( aStatus.getLinkTargetURL(),
481 RTL_TEXTENCODING_UTF8).getStr(),
482 OUStringToOString( aFileURL,
483 RTL_TEXTENCODING_UTF8).getStr() );
484 #endif
490 oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
491 if ( rc != osl_File_E_None )
493 if ( uFlags & osl_File_OpenFlag_Write )
495 // Change to read-only
496 uFlags &= ~osl_File_OpenFlag_Write;
497 rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
500 if ( rc == osl_File_E_None )
502 pInstanceData->rHandle = nHandleTmp;
503 bIsOpen = true;
504 if ( uFlags & osl_File_OpenFlag_Write )
505 m_isWritable = true;
507 if ( !LockFile() ) // whole file
509 rc = osl_closeFile( nHandleTmp );
510 bIsOpen = false;
511 m_isWritable = false;
512 pInstanceData->rHandle = nullptr;
515 else
516 SetError( ::GetSvError( rc ) );
519 void SvFileStream::Close()
521 UnlockFile();
523 if ( IsOpen() )
525 SAL_INFO("tools", "Closing " << aFilename);
526 Flush();
527 osl_closeFile( pInstanceData->rHandle );
528 pInstanceData->rHandle = nullptr;
531 bIsOpen = false;
532 m_isWritable = false;
533 SvStream::ClearBuffer();
534 SvStream::ClearError();
537 /// set filepointer to beginning of file
538 void SvFileStream::ResetError()
540 SvStream::ClearError();
543 void SvFileStream::SetSize (sal_uInt64 const nSize)
545 if (IsOpen())
547 oslFileError rc = osl_setFileSize( pInstanceData->rHandle, nSize );
548 if (rc != osl_File_E_None )
550 SetError ( ::GetSvError( rc ));
555 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */