Bump version to 21.06.18.1
[LibreOffice.git] / tools / source / stream / strmunx.cxx
blob249af86fc90f41f905994df251c9bf24de6260cb
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>
33 #include <rtl/instance.hxx>
35 using namespace osl;
37 // InternalLock ----------------------------------------------------------------
39 namespace {
41 struct LockMutex : public rtl::Static< osl::Mutex, LockMutex > {};
43 struct Locks : public rtl::Static< std::map<SvFileStream const *, osl::DirectoryItem>, Locks > {};
45 bool lockFile( const SvFileStream* pStream )
47 osl::DirectoryItem aItem;
48 if (osl::DirectoryItem::get( pStream->GetFileName(), aItem) != osl::FileBase::E_None )
50 SAL_INFO("tools.stream", "Failed to lookup stream for locking");
51 return true;
54 osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
55 if ( aItem.getFileStatus( aStatus ) != osl::FileBase::E_None )
57 SAL_INFO("tools.stream", "Failed to stat stream for locking");
58 return true;
60 if( aStatus.getFileType() == osl::FileStatus::Directory )
61 return true;
63 osl::MutexGuard aGuard( LockMutex::get() );
64 auto &rLocks = Locks::get();
65 for( const auto& [rLockStream, rLockItem] : rLocks )
67 if( aItem.isIdenticalTo( rLockItem ) )
69 StreamMode nLockMode = rLockStream->GetStreamMode();
70 StreamMode nNewMode = pStream->GetStreamMode();
71 bool bDenyByOptions = (nLockMode & StreamMode::SHARE_DENYALL) ||
72 ( (nLockMode & StreamMode::SHARE_DENYWRITE) && (nNewMode & StreamMode::WRITE) ) ||
73 ( (nLockMode & StreamMode::SHARE_DENYREAD) && (nNewMode & StreamMode::READ) );
75 if( bDenyByOptions )
77 return false; // file is already locked
81 rLocks[pStream] = aItem;
82 return true;
85 void unlockFile( SvFileStream const * pStream )
87 osl::MutexGuard aGuard( LockMutex::get() );
88 auto &rLocks = Locks::get();
89 rLocks.erase(pStream);
94 // StreamData ------------------------------------------------------------------
96 class StreamData
98 public:
99 oslFileHandle rHandle;
101 StreamData() : rHandle( nullptr ) { }
104 static ErrCode GetSvError( int nErrno )
106 static struct { int nErr; ErrCode sv; } const errArr[] =
108 { 0, ERRCODE_NONE },
109 { EACCES, SVSTREAM_ACCESS_DENIED },
110 { EBADF, SVSTREAM_INVALID_HANDLE },
111 #if defined(NETBSD) || \
112 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
113 defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \
114 defined(IOS) || defined(HAIKU)
115 { EDEADLK, SVSTREAM_LOCKING_VIOLATION },
116 #else
117 { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION },
118 #endif
119 { EINVAL, SVSTREAM_INVALID_PARAMETER },
120 { EMFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
121 { ENFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
122 { ENOENT, SVSTREAM_FILE_NOT_FOUND },
123 { EPERM, SVSTREAM_ACCESS_DENIED },
124 { EROFS, SVSTREAM_ACCESS_DENIED },
125 { EAGAIN, SVSTREAM_LOCKING_VIOLATION },
126 { EISDIR, SVSTREAM_PATH_NOT_FOUND },
127 { ELOOP, SVSTREAM_PATH_NOT_FOUND },
128 #if !defined(NETBSD) && !defined (FREEBSD) && \
129 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
130 !defined(DRAGONFLY)
131 { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND },
132 { ENOLINK, SVSTREAM_PATH_NOT_FOUND },
133 #endif
134 { ENOTDIR, SVSTREAM_PATH_NOT_FOUND },
135 { ETXTBSY, SVSTREAM_ACCESS_DENIED },
136 { EEXIST, SVSTREAM_CANNOT_MAKE },
137 { ENOSPC, SVSTREAM_DISK_FULL },
138 { int(0xFFFF), SVSTREAM_GENERALERROR }
141 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
142 int i=0;
145 if ( errArr[i].nErr == nErrno )
147 nRetVal = errArr[i].sv;
148 break;
150 ++i;
152 while( errArr[i].nErr != 0xFFFF );
153 return nRetVal;
156 static ErrCode GetSvError( oslFileError nErrno )
158 static struct { oslFileError nErr; ErrCode sv; } const errArr[] =
160 { osl_File_E_None, ERRCODE_NONE },
161 { osl_File_E_ACCES, SVSTREAM_ACCESS_DENIED },
162 { osl_File_E_BADF, SVSTREAM_INVALID_HANDLE },
163 { osl_File_E_DEADLK, SVSTREAM_LOCKING_VIOLATION },
164 { osl_File_E_INVAL, SVSTREAM_INVALID_PARAMETER },
165 { osl_File_E_MFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
166 { osl_File_E_NFILE, SVSTREAM_TOO_MANY_OPEN_FILES },
167 { osl_File_E_NOENT, SVSTREAM_FILE_NOT_FOUND },
168 { osl_File_E_PERM, SVSTREAM_ACCESS_DENIED },
169 { osl_File_E_ROFS, SVSTREAM_ACCESS_DENIED },
170 { osl_File_E_AGAIN, SVSTREAM_LOCKING_VIOLATION },
171 { osl_File_E_ISDIR, SVSTREAM_PATH_NOT_FOUND },
172 { osl_File_E_LOOP, SVSTREAM_PATH_NOT_FOUND },
173 { osl_File_E_MULTIHOP, SVSTREAM_PATH_NOT_FOUND },
174 { osl_File_E_NOLINK, SVSTREAM_PATH_NOT_FOUND },
175 { osl_File_E_NOTDIR, SVSTREAM_PATH_NOT_FOUND },
176 { osl_File_E_EXIST, SVSTREAM_CANNOT_MAKE },
177 { osl_File_E_NOSPC, SVSTREAM_DISK_FULL },
178 { oslFileError(0xFFFF), SVSTREAM_GENERALERROR }
181 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
182 int i=0;
185 if ( errArr[i].nErr == nErrno )
187 nRetVal = errArr[i].sv;
188 break;
190 ++i;
192 while( errArr[i].nErr != oslFileError(0xFFFF) );
193 return nRetVal;
196 SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode )
198 bIsOpen = false;
199 m_isWritable = false;
200 pInstanceData.reset(new StreamData);
202 SetBufferSize( 1024 );
203 // convert URL to SystemPath, if necessary
204 OUString aSystemFileName;
205 if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName )
206 != FileBase::E_None )
208 aSystemFileName = rFileName;
210 Open( aSystemFileName, nOpenMode );
213 SvFileStream::SvFileStream()
215 bIsOpen = false;
216 m_isWritable = false;
217 pInstanceData.reset(new StreamData);
218 SetBufferSize( 1024 );
221 SvFileStream::~SvFileStream()
223 Close();
226 std::size_t SvFileStream::GetData( void* pData, std::size_t nSize )
228 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename);
230 sal_uInt64 nRead = 0;
231 if ( IsOpen() )
233 oslFileError rc = osl_readFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nRead);
234 if ( rc != osl_File_E_None )
236 SetError( ::GetSvError( rc ));
237 return -1;
240 return static_cast<std::size_t>(nRead);
243 std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize )
245 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename);
247 sal_uInt64 nWrite = 0;
248 if ( IsOpen() )
250 oslFileError rc = osl_writeFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite);
251 if ( rc != osl_File_E_None )
253 SetError( ::GetSvError( rc ) );
254 return -1;
256 else if( !nWrite )
257 SetError( SVSTREAM_DISK_FULL );
259 return static_cast<std::size_t>(nWrite);
262 sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
264 // check if a truncated STREAM_SEEK_TO_END was passed
265 assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END)));
266 if ( IsOpen() )
268 oslFileError rc;
269 sal_uInt64 nNewPos;
270 if ( nPos != STREAM_SEEK_TO_END )
271 rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_Absolut, nPos );
272 else
273 rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_End, 0 );
275 if ( rc != osl_File_E_None )
277 SetError( SVSTREAM_SEEK_ERROR );
278 return 0L;
280 if ( nPos != STREAM_SEEK_TO_END )
281 return nPos;
282 osl_getFilePos( pInstanceData->rHandle, &nNewPos );
283 return nNewPos;
285 SetError( SVSTREAM_GENERALERROR );
286 return 0L;
289 void SvFileStream::FlushData()
291 // does not exist locally
294 bool SvFileStream::LockFile()
296 int nLockMode = 0;
298 if ( ! IsOpen() )
299 return false;
301 if (m_eStreamMode & StreamMode::SHARE_DENYALL)
303 if (m_isWritable)
304 nLockMode = F_WRLCK;
305 else
306 nLockMode = F_RDLCK;
309 if (m_eStreamMode & StreamMode::SHARE_DENYREAD)
311 if (m_isWritable)
312 nLockMode = F_WRLCK;
313 else
315 SetError(SVSTREAM_LOCKING_VIOLATION);
316 return false;
320 if (m_eStreamMode & StreamMode::SHARE_DENYWRITE)
322 if (m_isWritable)
323 nLockMode = F_WRLCK;
324 else
325 nLockMode = F_RDLCK;
328 if (!nLockMode)
329 return true;
331 if( !lockFile( this ) )
333 #if OSL_DEBUG_LEVEL > 1
334 fprintf( stderr, "InternalLock on %s failed\n",
335 OUStringToOString(aFilename, osl_getThreadTextEncoding()).getStr() );
336 #endif
337 return false;
340 return true;
343 void SvFileStream::UnlockFile()
345 if ( ! IsOpen() )
346 return;
348 unlockFile( this );
351 void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode )
353 sal_uInt32 uFlags;
354 oslFileHandle nHandleTmp;
356 Close();
357 errno = 0;
358 m_eStreamMode = nOpenMode;
359 m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen
361 aFilename = rFilename;
363 SAL_INFO("tools", aFilename);
365 OUString aFileURL;
366 osl::DirectoryItem aItem;
367 osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL );
369 // FIXME: we really need to switch to a pure URL model ...
370 if ( osl::File::getFileURLFromSystemPath( aFilename, aFileURL ) != osl::FileBase::E_None )
371 aFileURL = aFilename;
372 bool bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None &&
373 aItem.getFileStatus( aStatus ) == osl::FileBase::E_None );
375 // SvFileStream can't open a directory
376 if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory )
378 SetError( ::GetSvError( EISDIR ) );
379 return;
382 if ( !( nOpenMode & StreamMode::WRITE ) )
383 uFlags = osl_File_OpenFlag_Read;
384 else if ( !( nOpenMode & StreamMode::READ ) )
385 uFlags = osl_File_OpenFlag_Write;
386 else
387 uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
389 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
390 // Important for Read-Only-Filesystems (e.g, CDROM)
391 if ( (!( nOpenMode & StreamMode::NOCREATE )) && ( uFlags != osl_File_OpenFlag_Read ) )
392 uFlags |= osl_File_OpenFlag_Create;
393 if ( nOpenMode & StreamMode::TRUNC )
394 uFlags |= osl_File_OpenFlag_Trunc;
396 uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock;
398 if ( nOpenMode & StreamMode::WRITE)
400 if ( nOpenMode & StreamMode::COPY_ON_SYMLINK )
402 if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link &&
403 aStatus.getLinkTargetURL().getLength() > 0 )
405 // delete the symbolic link, and replace it with the contents of the link
406 if (osl::File::remove( aFileURL ) == osl::FileBase::E_None )
408 File::copy( aStatus.getLinkTargetURL(), aFileURL );
409 #if OSL_DEBUG_LEVEL > 0
410 fprintf( stderr,
411 "Removing link and replacing with file contents (%s) -> (%s).\n",
412 OUStringToOString( aStatus.getLinkTargetURL(),
413 RTL_TEXTENCODING_UTF8).getStr(),
414 OUStringToOString( aFileURL,
415 RTL_TEXTENCODING_UTF8).getStr() );
416 #endif
422 oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
423 if ( rc != osl_File_E_None )
425 if ( uFlags & osl_File_OpenFlag_Write )
427 // Change to read-only
428 uFlags &= ~osl_File_OpenFlag_Write;
429 rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
432 if ( rc == osl_File_E_None )
434 pInstanceData->rHandle = nHandleTmp;
435 bIsOpen = true;
436 if ( uFlags & osl_File_OpenFlag_Write )
437 m_isWritable = true;
439 if ( !LockFile() ) // whole file
441 osl_closeFile( nHandleTmp );
442 bIsOpen = false;
443 m_isWritable = false;
444 pInstanceData->rHandle = nullptr;
447 else
448 SetError( ::GetSvError( rc ) );
451 void SvFileStream::Close()
453 UnlockFile();
455 if ( IsOpen() )
457 SAL_INFO("tools", "Closing " << aFilename);
458 Flush();
459 osl_closeFile( pInstanceData->rHandle );
460 pInstanceData->rHandle = nullptr;
463 bIsOpen = false;
464 m_isWritable = false;
465 SvStream::ClearBuffer();
466 SvStream::ClearError();
469 /// set filepointer to beginning of file
470 void SvFileStream::ResetError()
472 SvStream::ClearError();
475 void SvFileStream::SetSize (sal_uInt64 const nSize)
477 if (IsOpen())
479 oslFileError rc = osl_setFileSize( pInstanceData->rHandle, nSize );
480 if (rc != osl_File_E_None )
482 SetError ( ::GetSvError( rc ));
487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */