1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #include <tools/stream.hxx>
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>
36 // InternalLock ----------------------------------------------------------------
40 osl::Mutex
& LockMutex()
42 static osl::Mutex 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");
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");
63 if( aStatus
.getFileType() == osl::FileStatus::Directory
)
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
) );
79 return false; // file is already locked
83 gLocks
[pStream
] = aItem
;
87 void unlockFile( SvFileStream
const * pStream
)
89 osl::MutexGuard
aGuard( LockMutex() );
90 gLocks
.erase(pStream
);
95 // StreamData ------------------------------------------------------------------
100 oslFileHandle rHandle
;
102 StreamData() : rHandle( nullptr ) { }
105 static ErrCode
GetSvError( int nErrno
)
107 static struct { int nErr
; ErrCode sv
; } const errArr
[] =
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
},
118 { EDEADLOCK
, SVSTREAM_LOCKING_VIOLATION
},
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__) && \
132 { EMULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
133 { ENOLINK
, SVSTREAM_PATH_NOT_FOUND
},
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
146 if ( errArr
[i
].nErr
== nErrno
)
148 nRetVal
= errArr
[i
].sv
;
153 while( errArr
[i
].nErr
!= 0xFFFF );
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
186 if ( errArr
[i
].nErr
== nErrno
)
188 nRetVal
= errArr
[i
].sv
;
193 while( errArr
[i
].nErr
!= oslFileError(0xFFFF) );
197 SvFileStream::SvFileStream( const OUString
& rFileName
, StreamMode nOpenMode
)
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()
218 m_isWritable
= false;
219 mbDontFlushOnClose
= false;
220 pInstanceData
.reset(new StreamData
);
221 SetBufferSize( 1024 );
224 SvFileStream::~SvFileStream()
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;
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
));
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;
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
) );
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
)));
273 if ( nPos
!= STREAM_SEEK_TO_END
)
274 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_Absolut
, nPos
);
276 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_End
, 0 );
278 if ( rc
!= osl_File_E_None
)
280 SetError( SVSTREAM_SEEK_ERROR
);
283 if ( nPos
!= STREAM_SEEK_TO_END
)
285 osl_getFilePos( pInstanceData
->rHandle
, &nNewPos
);
288 SetError( SVSTREAM_GENERALERROR
);
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()
306 if (m_eStreamMode
& StreamMode::SHARE_DENYALL
)
314 if (m_eStreamMode
& StreamMode::SHARE_DENYREAD
)
320 SetError(SVSTREAM_LOCKING_VIOLATION
);
325 if (m_eStreamMode
& StreamMode::SHARE_DENYWRITE
)
336 if( !lockFile( this ) )
338 #if OSL_DEBUG_LEVEL > 1
339 fprintf( stderr
, "InternalLock on %s failed\n",
340 OUStringToOString(aFilename
, osl_getThreadTextEncoding()).getStr() );
348 void SvFileStream::UnlockFile()
356 void SvFileStream::Open( const OUString
& rFilename
, StreamMode nOpenMode
)
359 oslFileHandle nHandleTmp
;
363 m_eStreamMode
= nOpenMode
;
364 m_eStreamMode
&= ~StreamMode::TRUNC
; // don't truncate on reopen
366 aFilename
= rFilename
;
368 SAL_INFO("tools", aFilename
);
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
) );
387 if ( !( nOpenMode
& StreamMode::WRITE
) )
388 uFlags
= osl_File_OpenFlag_Read
;
389 else if ( !( nOpenMode
& StreamMode::READ
) )
390 uFlags
= osl_File_OpenFlag_Write
;
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
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() );
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
;
441 if ( uFlags
& osl_File_OpenFlag_Write
)
444 if ( !LockFile() ) // whole file
446 osl_closeFile( nHandleTmp
);
448 m_isWritable
= false;
449 pInstanceData
->rHandle
= nullptr;
453 SetError( ::GetSvError( rc
) );
456 void SvFileStream::Close()
462 SAL_INFO("tools", "Closing " << aFilename
);
463 if ( !mbDontFlushOnClose
)
465 osl_closeFile( pInstanceData
->rHandle
);
466 pInstanceData
->rHandle
= nullptr;
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
)
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: */