use insert function instead of for loop
[LibreOffice.git] / tools / source / stream / strmunx.cxx
blob28c548540aebfd5c06030c25148e829a1a3dc69c
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 <mutex>
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 std::mutex& LockMutex()
42 static std::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 std::unique_lock 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 std::unique_lock aGuard( LockMutex() );
90 gLocks.erase(pStream);
95 static ErrCode GetSvError( int nErrno )
97 static struct { int nErr; ErrCode sv; } const errArr[] =
99 { 0, ERRCODE_NONE },
100 { EACCES, SVSTREAM_ACCESS_DENIED },
101 { EBADF, SVSTREAM_INVALID_HANDLE },
102 #if defined(NETBSD) || \
103 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
104 defined(__FreeBSD_kernel__) || defined(DRAGONFLY) || \
105 defined(IOS) || defined(HAIKU)
106 { EDEADLK, SVSTREAM_LOCKING_VIOLATION },
107 #else
108 { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION },
109 #endif
110 { EINVAL, SVSTREAM_INVALID_PARAMETER },
111 { EMFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
112 { ENFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
113 { ENOENT, SVSTREAM_FILE_NOT_FOUND },
114 { EPERM, SVSTREAM_ACCESS_DENIED },
115 { EROFS, SVSTREAM_ACCESS_DENIED },
116 { EAGAIN, SVSTREAM_LOCKING_VIOLATION },
117 { EISDIR, SVSTREAM_PATH_NOT_FOUND },
118 { ELOOP, SVSTREAM_PATH_NOT_FOUND },
119 #if !defined(NETBSD) && !defined (FREEBSD) && \
120 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
121 !defined(DRAGONFLY)
122 { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND },
123 { ENOLINK, SVSTREAM_PATH_NOT_FOUND },
124 #endif
125 { ENOTDIR, SVSTREAM_PATH_NOT_FOUND },
126 { ETXTBSY, SVSTREAM_ACCESS_DENIED },
127 { EEXIST, SVSTREAM_CANNOT_MAKE },
128 { ENOSPC, SVSTREAM_DISK_FULL },
129 { int(0xFFFF), SVSTREAM_GENERALERROR }
132 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
133 int i=0;
136 if ( errArr[i].nErr == nErrno )
138 nRetVal = errArr[i].sv;
139 break;
141 ++i;
143 while( errArr[i].nErr != 0xFFFF );
144 return nRetVal;
147 static ErrCode GetSvError( oslFileError nErrno )
149 static struct { oslFileError nErr; ErrCode sv; } const errArr[] =
151 { osl_File_E_None, ERRCODE_NONE },
152 { osl_File_E_ACCES, SVSTREAM_ACCESS_DENIED },
153 { osl_File_E_BADF, SVSTREAM_INVALID_HANDLE },
154 { osl_File_E_DEADLK, SVSTREAM_LOCKING_VIOLATION },
155 { osl_File_E_INVAL, SVSTREAM_INVALID_PARAMETER },
156 { osl_File_E_MFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
157 { osl_File_E_NFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
158 { osl_File_E_NOENT, SVSTREAM_FILE_NOT_FOUND },
159 { osl_File_E_PERM, SVSTREAM_ACCESS_DENIED },
160 { osl_File_E_ROFS, SVSTREAM_ACCESS_DENIED },
161 { osl_File_E_AGAIN, SVSTREAM_LOCKING_VIOLATION },
162 { osl_File_E_ISDIR, SVSTREAM_PATH_NOT_FOUND },
163 { osl_File_E_LOOP, SVSTREAM_PATH_NOT_FOUND },
164 { osl_File_E_MULTIHOP, SVSTREAM_PATH_NOT_FOUND },
165 { osl_File_E_NOLINK, SVSTREAM_PATH_NOT_FOUND },
166 { osl_File_E_NOTDIR, SVSTREAM_PATH_NOT_FOUND },
167 { osl_File_E_EXIST, SVSTREAM_CANNOT_MAKE },
168 { osl_File_E_NOSPC, SVSTREAM_DISK_FULL },
169 { oslFileError(0xFFFF), SVSTREAM_GENERALERROR }
172 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
173 int i=0;
176 if ( errArr[i].nErr == nErrno )
178 nRetVal = errArr[i].sv;
179 break;
181 ++i;
183 while( errArr[i].nErr != oslFileError(0xFFFF) );
184 return nRetVal;
187 SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode )
189 bIsOpen = false;
190 m_isWritable = false;
192 SetBufferSize( 1024 );
193 // convert URL to SystemPath, if necessary
194 OUString aSystemFileName;
195 if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName )
196 != FileBase::E_None )
198 aSystemFileName = rFileName;
200 Open( aSystemFileName, nOpenMode );
203 SvFileStream::SvFileStream()
205 bIsOpen = false;
206 m_isWritable = false;
207 SetBufferSize( 1024 );
210 SvFileStream::~SvFileStream()
212 Close();
215 std::size_t SvFileStream::GetData( void* pData, std::size_t nSize )
217 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename);
219 sal_uInt64 nRead = 0;
220 if ( IsOpen() )
222 oslFileError rc = osl_readFile(mxFileHandle,pData,static_cast<sal_uInt64>(nSize),&nRead);
223 if ( rc != osl_File_E_None )
225 SetError( ::GetSvError( rc ));
226 return -1;
229 return static_cast<std::size_t>(nRead);
232 std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize )
234 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename);
236 sal_uInt64 nWrite = 0;
237 if ( IsOpen() )
239 oslFileError rc = osl_writeFile(mxFileHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite);
240 if ( rc != osl_File_E_None )
242 SetError( ::GetSvError( rc ) );
243 return -1;
245 else if( !nWrite )
247 SetError( SVSTREAM_DISK_FULL );
248 return -1;
251 return static_cast<std::size_t>(nWrite);
254 sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
256 // check if a truncated STREAM_SEEK_TO_END was passed
257 assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END)));
258 if ( IsOpen() )
260 oslFileError rc;
261 sal_uInt64 nNewPos;
262 if ( nPos != STREAM_SEEK_TO_END )
263 rc = osl_setFilePos( mxFileHandle, osl_Pos_Absolut, nPos );
264 else
265 rc = osl_setFilePos( mxFileHandle, osl_Pos_End, 0 );
267 if ( rc != osl_File_E_None )
269 SetError( SVSTREAM_SEEK_ERROR );
270 return 0;
272 if ( nPos != STREAM_SEEK_TO_END )
273 return nPos;
274 osl_getFilePos( mxFileHandle, &nNewPos );
275 return nNewPos;
277 SetError( SVSTREAM_GENERALERROR );
278 return 0;
281 void SvFileStream::FlushData()
283 auto rc = osl_syncFile(mxFileHandle);
284 if (rc != osl_File_E_None)
285 SetError( ::GetSvError( rc ));
288 bool SvFileStream::LockFile()
290 int nLockMode = 0;
292 if ( ! IsOpen() )
293 return false;
295 if (m_eStreamMode & StreamMode::SHARE_DENYALL)
297 if (m_isWritable)
298 nLockMode = F_WRLCK;
299 else
300 nLockMode = F_RDLCK;
303 if (m_eStreamMode & StreamMode::SHARE_DENYREAD)
305 if (m_isWritable)
306 nLockMode = F_WRLCK;
307 else
309 SetError(SVSTREAM_LOCKING_VIOLATION);
310 return false;
314 if (m_eStreamMode & StreamMode::SHARE_DENYWRITE)
316 if (m_isWritable)
317 nLockMode = F_WRLCK;
318 else
319 nLockMode = F_RDLCK;
322 if (!nLockMode)
323 return true;
325 if( !lockFile( this ) )
327 SAL_WARN("tools.stream", "InternalLock on " << aFilename << "failed");
328 return false;
331 return true;
334 void SvFileStream::UnlockFile()
336 if ( ! IsOpen() )
337 return;
339 unlockFile( this );
342 void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode )
344 sal_uInt32 uFlags;
345 oslFileHandle nHandleTmp;
347 Close();
348 errno = 0;
349 m_eStreamMode = nOpenMode;
350 m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen
352 aFilename = rFilename;
354 SAL_INFO("tools", aFilename);
356 OUString aFileURL;
357 osl::DirectoryItem aItem;
358 osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL );
360 // FIXME: we really need to switch to a pure URL model ...
361 if ( osl::File::getFileURLFromSystemPath( aFilename, aFileURL ) != osl::FileBase::E_None )
362 aFileURL = aFilename;
364 // don't both stat()ing a temporary file, unnecessary
365 bool bStatValid = true;
366 if (!(nOpenMode & StreamMode::TEMPORARY))
368 bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None &&
369 aItem.getFileStatus( aStatus ) == osl::FileBase::E_None );
371 // SvFileStream can't open a directory
372 if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory )
374 SetError( ::GetSvError( EISDIR ) );
375 return;
379 if ( !( nOpenMode & StreamMode::WRITE ) )
380 uFlags = osl_File_OpenFlag_Read;
381 else if ( !( nOpenMode & StreamMode::READ ) )
382 uFlags = osl_File_OpenFlag_Write;
383 else
384 uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
386 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
387 // Important for Read-Only-Filesystems (e.g, CDROM)
388 if ( (!( nOpenMode & StreamMode::NOCREATE )) && ( uFlags != osl_File_OpenFlag_Read ) )
389 uFlags |= osl_File_OpenFlag_Create;
390 if ( nOpenMode & StreamMode::TRUNC )
391 uFlags |= osl_File_OpenFlag_Trunc;
393 uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock;
395 if ( nOpenMode & StreamMode::WRITE)
397 if ( nOpenMode & StreamMode::COPY_ON_SYMLINK )
399 if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link &&
400 aStatus.getLinkTargetURL().getLength() > 0 )
402 // delete the symbolic link, and replace it with the contents of the link
403 if (osl::File::remove( aFileURL ) == osl::FileBase::E_None )
405 File::copy( aStatus.getLinkTargetURL(), aFileURL );
406 SAL_INFO("tools.stream",
407 "Removing link and replacing with file contents (" <<
408 aStatus.getLinkTargetURL() << ") -> (" << aFileURL << ").");
414 oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
415 if ( rc != osl_File_E_None )
417 if ( uFlags & osl_File_OpenFlag_Write )
419 // Change to read-only
420 uFlags &= ~osl_File_OpenFlag_Write;
421 rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
424 if ( rc == osl_File_E_None )
426 mxFileHandle = nHandleTmp;
427 bIsOpen = true;
428 if ( uFlags & osl_File_OpenFlag_Write )
429 m_isWritable = true;
431 if ( !LockFile() ) // whole file
433 osl_closeFile( nHandleTmp );
434 bIsOpen = false;
435 m_isWritable = false;
436 mxFileHandle = nullptr;
439 else
440 SetError( ::GetSvError( rc ) );
443 void SvFileStream::Close()
445 UnlockFile();
447 if ( IsOpen() )
449 SAL_INFO("tools", "Closing " << aFilename);
450 FlushBuffer();
451 osl_closeFile( mxFileHandle );
452 mxFileHandle = nullptr;
455 bIsOpen = false;
456 m_isWritable = false;
457 SvStream::ClearBuffer();
458 SvStream::ClearError();
461 /// set filepointer to beginning of file
462 void SvFileStream::ResetError()
464 SvStream::ClearError();
467 void SvFileStream::SetSize (sal_uInt64 const nSize)
469 if (IsOpen())
471 oslFileError rc = osl_setFileSize( mxFileHandle, nSize );
472 if (rc != osl_File_E_None )
474 SetError ( ::GetSvError( rc ));
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */