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 .
25 #include <tools/stream.hxx>
28 #include <osl/mutex.hxx>
29 #include <osl/thread.h>
30 #include <sal/log.hxx>
33 #include <osl/file.hxx>
34 #include <osl/detail/file.h>
35 #include <rtl/instance.hxx>
39 // InternalLock ----------------------------------------------------------------
43 struct LockMutex
: public rtl::Static
< osl::Mutex
, LockMutex
> {};
45 struct InternalStreamLock
47 sal_uInt64 m_nStartPos
;
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
),
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" );
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" );
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");
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");
104 if( aStatus
.getFileType() == osl::FileStatus::Directory
)
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
) );
121 if( rLock
.m_nStartPos
== 0 && rLock
.m_nEndPos
== 0 ) // whole file is already locked
123 if( nStart
== 0 && nEnd
== 0) // cannot lock whole file
126 if( ( nStart
< rLock
.m_nStartPos
&& nEnd
> rLock
.m_nStartPos
) ||
127 ( nStart
< rLock
.m_nEndPos
&& nEnd
> rLock
.m_nEndPos
) )
132 rLockList
.push_back( InternalStreamLock( nStart
, nEnd
, pStream
) );
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 ------------------------------------------------------------------
155 oslFileHandle rHandle
;
157 StreamData() : rHandle( nullptr ) { }
160 static ErrCode
GetSvError( int nErrno
)
162 static struct { int nErr
; ErrCode sv
; } const errArr
[] =
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
},
173 { EDEADLOCK
, SVSTREAM_LOCKING_VIOLATION
},
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__) && \
187 { EMULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
188 { ENOLINK
, SVSTREAM_PATH_NOT_FOUND
},
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
201 if ( errArr
[i
].nErr
== nErrno
)
203 nRetVal
= errArr
[i
].sv
;
208 while( errArr
[i
].nErr
!= 0xFFFF );
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
241 if ( errArr
[i
].nErr
== nErrno
)
243 nRetVal
= errArr
[i
].sv
;
248 while( errArr
[i
].nErr
!= oslFileError(0xFFFF) );
252 SvFileStream::SvFileStream( const OUString
& rFileName
, StreamMode nOpenMode
)
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()
272 m_isWritable
= false;
273 pInstanceData
.reset(new StreamData
);
274 SetBufferSize( 1024 );
277 SvFileStream::~SvFileStream()
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;
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
));
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;
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
) );
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
)));
326 if ( nPos
!= STREAM_SEEK_TO_END
)
327 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_Absolut
, nPos
);
329 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_End
, 0 );
331 if ( rc
!= osl_File_E_None
)
333 SetError( SVSTREAM_SEEK_ERROR
);
336 if ( nPos
!= STREAM_SEEK_TO_END
)
338 rc
= osl_getFilePos( pInstanceData
->rHandle
, &nNewPos
);
341 SetError( SVSTREAM_GENERALERROR
);
345 void SvFileStream::FlushData()
347 // does not exist locally
350 bool SvFileStream::LockRange(sal_uInt64
const nByteOffset
, std::size_t nBytes
)
357 if (m_eStreamMode
& StreamMode::SHARE_DENYALL
)
365 if (m_eStreamMode
& StreamMode::SHARE_DENYREAD
)
371 SetError(SVSTREAM_LOCKING_VIOLATION
);
376 if (m_eStreamMode
& StreamMode::SHARE_DENYWRITE
)
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
);
399 bool SvFileStream::UnlockRange(sal_uInt64
const nByteOffset
, std::size_t nBytes
)
404 unlockFile( nByteOffset
, nByteOffset
+nBytes
, this );
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
)
422 oslFileHandle nHandleTmp
;
426 m_eStreamMode
= nOpenMode
;
427 m_eStreamMode
&= ~StreamMode::TRUNC
; // don't truncate on reopen
429 aFilename
= rFilename
;
431 SAL_INFO("tools", aFilename
);
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
) );
450 if ( !( nOpenMode
& StreamMode::WRITE
) )
451 uFlags
= osl_File_OpenFlag_Read
;
452 else if ( !( nOpenMode
& StreamMode::READ
) )
453 uFlags
= osl_File_OpenFlag_Write
;
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
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() );
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
;
504 if ( uFlags
& osl_File_OpenFlag_Write
)
507 if ( !LockFile() ) // whole file
509 rc
= osl_closeFile( nHandleTmp
);
511 m_isWritable
= false;
512 pInstanceData
->rHandle
= nullptr;
516 SetError( ::GetSvError( rc
) );
519 void SvFileStream::Close()
525 SAL_INFO("tools", "Closing " << aFilename
);
527 osl_closeFile( pInstanceData
->rHandle
);
528 pInstanceData
->rHandle
= nullptr;
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
)
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: */