Version 7.3.5.2, tag libreoffice-7.3.5.2
[LibreOffice.git] / tools / source / stream / strmunx.cxx
blob853389266ac3de6c8a5dc845100c23659fe2ecbb
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 <stdio.h>
21 #include <fcntl.h>
22 #include <errno.h>
24 #include <tools/stream.hxx>
25 #include <map>
27 #include <osl/mutex.hxx>
28 #include <osl/thread.h>
29 #include <sal/log.hxx>
31 #include <osl/file.hxx>
32 #include <osl/detail/file.h>
34 using namespace osl;
36 // InternalLock ----------------------------------------------------------------
38 namespace {
40 osl::Mutex& LockMutex()
42 static osl::Mutex SINGLETON;
43 return SINGLETON;
46 std::map<SvFileStream const *, osl::DirectoryItem> gLocks;
48 bool lockFile( const SvFileStream* pStream )
50 osl::DirectoryItem aItem;
51 if (osl::DirectoryItem::get( pStream->GetFileName(), aItem) != osl::FileBase::E_None )
53 SAL_INFO("tools.stream", "Failed to lookup stream for locking");
54 return true;
57 osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
58 if ( aItem.getFileStatus( aStatus ) != osl::FileBase::E_None )
60 SAL_INFO("tools.stream", "Failed to stat stream for locking");
61 return true;
63 if( aStatus.getFileType() == osl::FileStatus::Directory )
64 return true;
66 osl::MutexGuard aGuard( LockMutex() );
67 for( const auto& [rLockStream, rLockItem] : gLocks )
69 if( aItem.isIdenticalTo( rLockItem ) )
71 StreamMode nLockMode = rLockStream->GetStreamMode();
72 StreamMode nNewMode = pStream->GetStreamMode();
73 bool bDenyByOptions = (nLockMode & StreamMode::SHARE_DENYALL) ||
74 ( (nLockMode & StreamMode::SHARE_DENYWRITE) && (nNewMode & StreamMode::WRITE) ) ||
75 ( (nLockMode & StreamMode::SHARE_DENYREAD) && (nNewMode & StreamMode::READ) );
77 if( bDenyByOptions )
79 return false; // file is already locked
83 gLocks[pStream] = aItem;
84 return true;
87 void unlockFile( SvFileStream const * pStream )
89 osl::MutexGuard aGuard( LockMutex() );
90 gLocks.erase(pStream);
95 // StreamData ------------------------------------------------------------------
97 class StreamData
99 public:
100 oslFileHandle rHandle;
102 StreamData() : rHandle( nullptr ) { }
105 static ErrCode GetSvError( int nErrno )
107 static struct { int nErr; ErrCode sv; } const errArr[] =
109 { 0, ERRCODE_NONE },
110 { EACCES, SVSTREAM_ACCESS_DENIED },
111 { EBADF, SVSTREAM_INVALID_HANDLE },
112 #if defined(NETBSD) || \
113 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
114 defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \
115 defined(IOS) || defined(HAIKU)
116 { EDEADLK, SVSTREAM_LOCKING_VIOLATION },
117 #else
118 { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION },
119 #endif
120 { EINVAL, SVSTREAM_INVALID_PARAMETER },
121 { EMFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
122 { ENFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
123 { ENOENT, SVSTREAM_FILE_NOT_FOUND },
124 { EPERM, SVSTREAM_ACCESS_DENIED },
125 { EROFS, SVSTREAM_ACCESS_DENIED },
126 { EAGAIN, SVSTREAM_LOCKING_VIOLATION },
127 { EISDIR, SVSTREAM_PATH_NOT_FOUND },
128 { ELOOP, SVSTREAM_PATH_NOT_FOUND },
129 #if !defined(NETBSD) && !defined (FREEBSD) && \
130 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
131 !defined(DRAGONFLY)
132 { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND },
133 { ENOLINK, SVSTREAM_PATH_NOT_FOUND },
134 #endif
135 { ENOTDIR, SVSTREAM_PATH_NOT_FOUND },
136 { ETXTBSY, SVSTREAM_ACCESS_DENIED },
137 { EEXIST, SVSTREAM_CANNOT_MAKE },
138 { ENOSPC, SVSTREAM_DISK_FULL },
139 { int(0xFFFF), SVSTREAM_GENERALERROR }
142 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
143 int i=0;
146 if ( errArr[i].nErr == nErrno )
148 nRetVal = errArr[i].sv;
149 break;
151 ++i;
153 while( errArr[i].nErr != 0xFFFF );
154 return nRetVal;
157 static ErrCode GetSvError( oslFileError nErrno )
159 static struct { oslFileError nErr; ErrCode sv; } const errArr[] =
161 { osl_File_E_None, ERRCODE_NONE },
162 { osl_File_E_ACCES, SVSTREAM_ACCESS_DENIED },
163 { osl_File_E_BADF, SVSTREAM_INVALID_HANDLE },
164 { osl_File_E_DEADLK, SVSTREAM_LOCKING_VIOLATION },
165 { osl_File_E_INVAL, SVSTREAM_INVALID_PARAMETER },
166 { osl_File_E_MFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
167 { osl_File_E_NFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
168 { osl_File_E_NOENT, SVSTREAM_FILE_NOT_FOUND },
169 { osl_File_E_PERM, SVSTREAM_ACCESS_DENIED },
170 { osl_File_E_ROFS, SVSTREAM_ACCESS_DENIED },
171 { osl_File_E_AGAIN, SVSTREAM_LOCKING_VIOLATION },
172 { osl_File_E_ISDIR, SVSTREAM_PATH_NOT_FOUND },
173 { osl_File_E_LOOP, SVSTREAM_PATH_NOT_FOUND },
174 { osl_File_E_MULTIHOP, SVSTREAM_PATH_NOT_FOUND },
175 { osl_File_E_NOLINK, SVSTREAM_PATH_NOT_FOUND },
176 { osl_File_E_NOTDIR, SVSTREAM_PATH_NOT_FOUND },
177 { osl_File_E_EXIST, SVSTREAM_CANNOT_MAKE },
178 { osl_File_E_NOSPC, SVSTREAM_DISK_FULL },
179 { oslFileError(0xFFFF), SVSTREAM_GENERALERROR }
182 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
183 int i=0;
186 if ( errArr[i].nErr == nErrno )
188 nRetVal = errArr[i].sv;
189 break;
191 ++i;
193 while( errArr[i].nErr != oslFileError(0xFFFF) );
194 return nRetVal;
197 SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode )
199 bIsOpen = false;
200 m_isWritable = false;
201 mbDontFlushOnClose = false;
202 pInstanceData.reset(new StreamData);
204 SetBufferSize( 1024 );
205 // convert URL to SystemPath, if necessary
206 OUString aSystemFileName;
207 if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName )
208 != FileBase::E_None )
210 aSystemFileName = rFileName;
212 Open( aSystemFileName, nOpenMode );
215 SvFileStream::SvFileStream()
217 bIsOpen = false;
218 m_isWritable = false;
219 mbDontFlushOnClose = false;
220 pInstanceData.reset(new StreamData);
221 SetBufferSize( 1024 );
224 SvFileStream::~SvFileStream()
226 Close();
229 std::size_t SvFileStream::GetData( void* pData, std::size_t nSize )
231 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename);
233 sal_uInt64 nRead = 0;
234 if ( IsOpen() )
236 oslFileError rc = osl_readFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nRead);
237 if ( rc != osl_File_E_None )
239 SetError( ::GetSvError( rc ));
240 return -1;
243 return static_cast<std::size_t>(nRead);
246 std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize )
248 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename);
250 sal_uInt64 nWrite = 0;
251 if ( IsOpen() )
253 oslFileError rc = osl_writeFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite);
254 if ( rc != osl_File_E_None )
256 SetError( ::GetSvError( rc ) );
257 return -1;
259 else if( !nWrite )
260 SetError( SVSTREAM_DISK_FULL );
262 return static_cast<std::size_t>(nWrite);
265 sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
267 // check if a truncated STREAM_SEEK_TO_END was passed
268 assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END)));
269 if ( IsOpen() )
271 oslFileError rc;
272 sal_uInt64 nNewPos;
273 if ( nPos != STREAM_SEEK_TO_END )
274 rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_Absolut, nPos );
275 else
276 rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_End, 0 );
278 if ( rc != osl_File_E_None )
280 SetError( SVSTREAM_SEEK_ERROR );
281 return 0;
283 if ( nPos != STREAM_SEEK_TO_END )
284 return nPos;
285 osl_getFilePos( pInstanceData->rHandle, &nNewPos );
286 return nNewPos;
288 SetError( SVSTREAM_GENERALERROR );
289 return 0;
292 void SvFileStream::FlushData()
294 auto rc = osl_syncFile(pInstanceData->rHandle);
295 if (rc != osl_File_E_None)
296 SetError( ::GetSvError( rc ));
299 bool SvFileStream::LockFile()
301 int nLockMode = 0;
303 if ( ! IsOpen() )
304 return false;
306 if (m_eStreamMode & StreamMode::SHARE_DENYALL)
308 if (m_isWritable)
309 nLockMode = F_WRLCK;
310 else
311 nLockMode = F_RDLCK;
314 if (m_eStreamMode & StreamMode::SHARE_DENYREAD)
316 if (m_isWritable)
317 nLockMode = F_WRLCK;
318 else
320 SetError(SVSTREAM_LOCKING_VIOLATION);
321 return false;
325 if (m_eStreamMode & StreamMode::SHARE_DENYWRITE)
327 if (m_isWritable)
328 nLockMode = F_WRLCK;
329 else
330 nLockMode = F_RDLCK;
333 if (!nLockMode)
334 return true;
336 if( !lockFile( this ) )
338 #if OSL_DEBUG_LEVEL > 1
339 fprintf( stderr, "InternalLock on %s failed\n",
340 OUStringToOString(aFilename, osl_getThreadTextEncoding()).getStr() );
341 #endif
342 return false;
345 return true;
348 void SvFileStream::UnlockFile()
350 if ( ! IsOpen() )
351 return;
353 unlockFile( this );
356 void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode )
358 sal_uInt32 uFlags;
359 oslFileHandle nHandleTmp;
361 Close();
362 errno = 0;
363 m_eStreamMode = nOpenMode;
364 m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen
366 aFilename = rFilename;
368 SAL_INFO("tools", aFilename);
370 OUString aFileURL;
371 osl::DirectoryItem aItem;
372 osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL );
374 // FIXME: we really need to switch to a pure URL model ...
375 if ( osl::File::getFileURLFromSystemPath( aFilename, aFileURL ) != osl::FileBase::E_None )
376 aFileURL = aFilename;
377 bool bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None &&
378 aItem.getFileStatus( aStatus ) == osl::FileBase::E_None );
380 // SvFileStream can't open a directory
381 if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory )
383 SetError( ::GetSvError( EISDIR ) );
384 return;
387 if ( !( nOpenMode & StreamMode::WRITE ) )
388 uFlags = osl_File_OpenFlag_Read;
389 else if ( !( nOpenMode & StreamMode::READ ) )
390 uFlags = osl_File_OpenFlag_Write;
391 else
392 uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
394 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
395 // Important for Read-Only-Filesystems (e.g, CDROM)
396 if ( (!( nOpenMode & StreamMode::NOCREATE )) && ( uFlags != osl_File_OpenFlag_Read ) )
397 uFlags |= osl_File_OpenFlag_Create;
398 if ( nOpenMode & StreamMode::TRUNC )
399 uFlags |= osl_File_OpenFlag_Trunc;
401 uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock;
403 if ( nOpenMode & StreamMode::WRITE)
405 if ( nOpenMode & StreamMode::COPY_ON_SYMLINK )
407 if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link &&
408 aStatus.getLinkTargetURL().getLength() > 0 )
410 // delete the symbolic link, and replace it with the contents of the link
411 if (osl::File::remove( aFileURL ) == osl::FileBase::E_None )
413 File::copy( aStatus.getLinkTargetURL(), aFileURL );
414 #if OSL_DEBUG_LEVEL > 0
415 fprintf( stderr,
416 "Removing link and replacing with file contents (%s) -> (%s).\n",
417 OUStringToOString( aStatus.getLinkTargetURL(),
418 RTL_TEXTENCODING_UTF8).getStr(),
419 OUStringToOString( aFileURL,
420 RTL_TEXTENCODING_UTF8).getStr() );
421 #endif
427 oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
428 if ( rc != osl_File_E_None )
430 if ( uFlags & osl_File_OpenFlag_Write )
432 // Change to read-only
433 uFlags &= ~osl_File_OpenFlag_Write;
434 rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
437 if ( rc == osl_File_E_None )
439 pInstanceData->rHandle = nHandleTmp;
440 bIsOpen = true;
441 if ( uFlags & osl_File_OpenFlag_Write )
442 m_isWritable = true;
444 if ( !LockFile() ) // whole file
446 osl_closeFile( nHandleTmp );
447 bIsOpen = false;
448 m_isWritable = false;
449 pInstanceData->rHandle = nullptr;
452 else
453 SetError( ::GetSvError( rc ) );
456 void SvFileStream::Close()
458 UnlockFile();
460 if ( IsOpen() )
462 SAL_INFO("tools", "Closing " << aFilename);
463 if ( !mbDontFlushOnClose )
464 Flush();
465 osl_closeFile( pInstanceData->rHandle );
466 pInstanceData->rHandle = nullptr;
469 bIsOpen = false;
470 m_isWritable = false;
471 SvStream::ClearBuffer();
472 SvStream::ClearError();
475 /// set filepointer to beginning of file
476 void SvFileStream::ResetError()
478 SvStream::ClearError();
481 void SvFileStream::SetSize (sal_uInt64 const nSize)
483 if (IsOpen())
485 oslFileError rc = osl_setFileSize( pInstanceData->rHandle, nSize );
486 if (rc != osl_File_E_None )
488 SetError ( ::GetSvError( rc ));
493 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */