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 .
28 #include <tools/stream.hxx>
31 #include <osl/mutex.hxx>
32 #include <osl/thread.h>
33 #include <sal/log.hxx>
36 #include <osl/file.hxx>
37 #include <osl/detail/file.h>
38 #include <rtl/instance.hxx>
39 #include <rtl/strbuf.hxx>
43 // InternalLock ----------------------------------------------------------------
47 struct LockMutex
: public rtl::Static
< osl::Mutex
, LockMutex
> {};
49 struct InternalStreamLock
53 SvFileStream
* m_pStream
;
54 osl::DirectoryItem m_aItem
;
56 InternalStreamLock( sal_Size
, sal_Size
, SvFileStream
* );
57 ~InternalStreamLock();
60 struct LockList
: public rtl::Static
< std::vector
<InternalStreamLock
>, LockList
> {};
62 InternalStreamLock::InternalStreamLock(
65 SvFileStream
* pStream
) :
66 m_nStartPos( nStart
),
70 osl::DirectoryItem::get( m_pStream
->GetFileName(), m_aItem
);
71 #if OSL_DEBUG_LEVEL > 1
72 OString
aFileName(OUStringToOString(m_pStream
->GetFileName(),
73 osl_getThreadTextEncoding()));
74 fprintf( stderr
, "locked %s", aFileName
.getStr() );
75 if( m_nStartPos
|| m_nEndPos
)
76 fprintf(stderr
, " [ %ld ... %ld ]", m_nStartPos
, m_nEndPos
);
77 fprintf( stderr
, "\n" );
81 InternalStreamLock::~InternalStreamLock()
83 #if OSL_DEBUG_LEVEL > 1
84 OString
aFileName(OUStringToOString(m_pStream
->GetFileName(),
85 osl_getThreadTextEncoding()));
86 fprintf( stderr
, "unlocked %s", aFileName
.getStr() );
87 if( m_nStartPos
|| m_nEndPos
)
88 fprintf(stderr
, " [ %ld ... %ld ]", m_nStartPos
, m_nEndPos
);
89 fprintf( stderr
, "\n" );
93 bool lockFile( sal_Size nStart
, sal_Size nEnd
, SvFileStream
* pStream
)
95 osl::DirectoryItem aItem
;
96 if (osl::DirectoryItem::get( pStream
->GetFileName(), aItem
) != osl::FileBase::E_None
)
98 SAL_INFO("tools.stream", "Failed to lookup stream for locking");
102 osl::FileStatus
aStatus( osl_FileStatus_Mask_Type
);
103 if ( aItem
.getFileStatus( aStatus
) != osl::FileBase::E_None
)
105 SAL_INFO("tools.stream", "Failed to stat stream for locking");
108 if( aStatus
.getFileType() == osl::FileStatus::Directory
)
111 osl::MutexGuard
aGuard( LockMutex::get() );
112 std::vector
<InternalStreamLock
> &rLockList
= LockList::get();
113 for( std::vector
<InternalStreamLock
>::const_iterator i
= rLockList
.begin();
114 i
!= rLockList
.end(); )
116 if( aItem
.isIdenticalTo( i
->m_aItem
) )
118 bool bDenyByOptions
= false;
119 StreamMode nLockMode
= i
->m_pStream
->GetStreamMode();
120 StreamMode nNewMode
= pStream
->GetStreamMode();
122 if( nLockMode
& StreamMode::SHARE_DENYALL
)
123 bDenyByOptions
= true;
124 else if( ( nLockMode
& StreamMode::SHARE_DENYWRITE
) &&
125 ( nNewMode
& StreamMode::WRITE
) )
126 bDenyByOptions
= true;
127 else if( ( nLockMode
&StreamMode::SHARE_DENYREAD
) &&
128 ( nNewMode
& StreamMode::READ
) )
129 bDenyByOptions
= true;
133 if( i
->m_nStartPos
== 0 && i
->m_nEndPos
== 0 ) // whole file is already locked
135 if( nStart
== 0 && nEnd
== 0) // cannot lock whole file
138 if( ( nStart
< i
->m_nStartPos
&& nEnd
> i
->m_nStartPos
) ||
139 ( nStart
< i
->m_nEndPos
&& nEnd
> i
->m_nEndPos
) )
144 rLockList
.push_back( InternalStreamLock( nStart
, nEnd
, pStream
) );
148 void unlockFile( sal_Size nStart
, sal_Size nEnd
, SvFileStream
* pStream
)
150 osl::MutexGuard
aGuard( LockMutex::get() );
151 std::vector
<InternalStreamLock
> &rLockList
= LockList::get();
152 for( std::vector
<InternalStreamLock
>::iterator i
= rLockList
.begin();
153 i
!= rLockList
.end(); )
155 if ( i
->m_pStream
== pStream
156 && ( ( nStart
== 0 && nEnd
== 0 )
157 || ( i
->m_nStartPos
== nStart
&& i
->m_nEndPos
== nEnd
) ) )
159 i
= rLockList
.erase(i
);
170 // StreamData ------------------------------------------------------------------
175 oslFileHandle rHandle
;
177 StreamData() : rHandle( 0 ) { }
180 static sal_uInt32
GetSvError( int nErrno
)
182 static struct { int nErr
; sal_uInt32 sv
; } errArr
[] =
185 { EACCES
, SVSTREAM_ACCESS_DENIED
},
186 { EBADF
, SVSTREAM_INVALID_HANDLE
},
187 #if defined(NETBSD) || \
188 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
189 defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \
191 { EDEADLK
, SVSTREAM_LOCKING_VIOLATION
},
193 { EDEADLOCK
, SVSTREAM_LOCKING_VIOLATION
},
195 { EINVAL
, SVSTREAM_INVALID_PARAMETER
},
196 { EMFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
197 { ENFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
198 { ENOENT
, SVSTREAM_FILE_NOT_FOUND
},
199 { EPERM
, SVSTREAM_ACCESS_DENIED
},
200 { EROFS
, SVSTREAM_ACCESS_DENIED
},
201 { EAGAIN
, SVSTREAM_LOCKING_VIOLATION
},
202 { EISDIR
, SVSTREAM_PATH_NOT_FOUND
},
203 { ELOOP
, SVSTREAM_PATH_NOT_FOUND
},
204 #if !defined(NETBSD) && !defined (FREEBSD) && \
205 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
207 { EMULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
208 { ENOLINK
, SVSTREAM_PATH_NOT_FOUND
},
210 { ENOTDIR
, SVSTREAM_PATH_NOT_FOUND
},
211 { ETXTBSY
, SVSTREAM_ACCESS_DENIED
},
212 { EEXIST
, SVSTREAM_CANNOT_MAKE
},
213 { ENOSPC
, SVSTREAM_DISK_FULL
},
214 { (int)0xFFFF, SVSTREAM_GENERALERROR
}
217 sal_uInt32 nRetVal
= SVSTREAM_GENERALERROR
; // default error
221 if ( errArr
[i
].nErr
== nErrno
)
223 nRetVal
= errArr
[i
].sv
;
228 while( errArr
[i
].nErr
!= 0xFFFF );
232 static sal_uInt32
GetSvError( oslFileError nErrno
)
234 static struct { oslFileError nErr
; sal_uInt32 sv
; } errArr
[] =
236 { osl_File_E_None
, SVSTREAM_OK
},
237 { osl_File_E_ACCES
, SVSTREAM_ACCESS_DENIED
},
238 { osl_File_E_BADF
, SVSTREAM_INVALID_HANDLE
},
239 { osl_File_E_DEADLK
, SVSTREAM_LOCKING_VIOLATION
},
240 { osl_File_E_INVAL
, SVSTREAM_INVALID_PARAMETER
},
241 { osl_File_E_MFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
242 { osl_File_E_NFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
243 { osl_File_E_NOENT
, SVSTREAM_FILE_NOT_FOUND
},
244 { osl_File_E_PERM
, SVSTREAM_ACCESS_DENIED
},
245 { osl_File_E_ROFS
, SVSTREAM_ACCESS_DENIED
},
246 { osl_File_E_AGAIN
, SVSTREAM_LOCKING_VIOLATION
},
247 { osl_File_E_ISDIR
, SVSTREAM_PATH_NOT_FOUND
},
248 { osl_File_E_LOOP
, SVSTREAM_PATH_NOT_FOUND
},
249 { osl_File_E_MULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
250 { osl_File_E_NOLINK
, SVSTREAM_PATH_NOT_FOUND
},
251 { osl_File_E_NOTDIR
, SVSTREAM_PATH_NOT_FOUND
},
252 { osl_File_E_EXIST
, SVSTREAM_CANNOT_MAKE
},
253 { osl_File_E_NOSPC
, SVSTREAM_DISK_FULL
},
254 { (oslFileError
)0xFFFF, SVSTREAM_GENERALERROR
}
257 sal_uInt32 nRetVal
= SVSTREAM_GENERALERROR
; // default error
261 if ( errArr
[i
].nErr
== nErrno
)
263 nRetVal
= errArr
[i
].sv
;
268 while( errArr
[i
].nErr
!= (oslFileError
)0xFFFF );
272 SvFileStream::SvFileStream( const OUString
& rFileName
, StreamMode nOpenMode
)
277 pInstanceData
= new StreamData
;
279 SetBufferSize( 1024 );
280 // convert URL to SystemPath, if necessary
281 OUString aSystemFileName
;
282 if( FileBase::getSystemPathFromFileURL( rFileName
, aSystemFileName
)
283 != FileBase::E_None
)
285 aSystemFileName
= rFileName
;
287 Open( aSystemFileName
, nOpenMode
);
290 SvFileStream::SvFileStream()
295 pInstanceData
= new StreamData
;
296 SetBufferSize( 1024 );
299 SvFileStream::~SvFileStream()
303 unlockFile( 0, 0, this );
306 delete pInstanceData
;
309 sal_Size
SvFileStream::GetData( void* pData
, sal_Size nSize
)
312 OStringBuffer
aTraceStr("SvFileStream::GetData(): ");
313 aTraceStr
.append(static_cast<sal_Int64
>(nSize
));
314 aTraceStr
.append(" Bytes from ");
315 aTraceStr
.append(OUStringToOString(aFilename
,
316 osl_getThreadTextEncoding()));
317 OSL_TRACE("%s", aTraceStr
.getStr());
320 sal_uInt64 nRead
= 0;
323 oslFileError rc
= osl_readFile(pInstanceData
->rHandle
,pData
,(sal_uInt64
)nSize
,&nRead
);
324 if ( rc
!= osl_File_E_None
)
326 SetError( ::GetSvError( rc
));
330 return (sal_Size
)nRead
;
333 sal_Size
SvFileStream::PutData( const void* pData
, sal_Size nSize
)
336 OStringBuffer
aTraceStr("SvFileStream::PutData(): ");
337 aTraceStr
.append(static_cast<sal_Int64
>(nSize
));
338 aTraceStr
.append(" Bytes to ");
339 aTraceStr
.append(OUStringToOString(aFilename
,
340 osl_getThreadTextEncoding()));
341 OSL_TRACE("%s", aTraceStr
.getStr());
344 sal_uInt64 nWrite
= 0;
347 oslFileError rc
= osl_writeFile(pInstanceData
->rHandle
,pData
,(sal_uInt64
)nSize
,&nWrite
);
348 if ( rc
!= osl_File_E_None
)
350 SetError( ::GetSvError( rc
) );
354 SetError( SVSTREAM_DISK_FULL
);
356 return (sal_Size
)nWrite
;
359 sal_uInt64
SvFileStream::SeekPos(sal_uInt64
const nPos
)
361 // check if a truncated STREAM_SEEK_TO_END was passed
362 assert(nPos
!= (sal_uInt64
)(sal_uInt32
)STREAM_SEEK_TO_END
);
367 if ( nPos
!= STREAM_SEEK_TO_END
)
368 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_Absolut
, nPos
);
370 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_End
, 0 );
372 if ( rc
!= osl_File_E_None
)
374 SetError( SVSTREAM_SEEK_ERROR
);
377 if ( nPos
!= STREAM_SEEK_TO_END
)
379 rc
= osl_getFilePos( pInstanceData
->rHandle
, &nNewPos
);
380 return (sal_Size
) nNewPos
;
382 SetError( SVSTREAM_GENERALERROR
);
386 void SvFileStream::FlushData()
388 // does not exist locally
391 bool SvFileStream::LockRange( sal_Size nByteOffset
, sal_Size nBytes
)
398 if ( eStreamMode
& StreamMode::SHARE_DENYALL
)
406 if ( eStreamMode
& StreamMode::SHARE_DENYREAD
)
412 SetError(SVSTREAM_LOCKING_VIOLATION
);
417 if ( eStreamMode
& StreamMode::SHARE_DENYWRITE
)
428 if( !lockFile( nByteOffset
, nByteOffset
+nBytes
, this ) )
430 #if OSL_DEBUG_LEVEL > 1
431 fprintf( stderr
, "InternalLock on %s [ %ld ... %ld ] failed\n",
432 OUStringToOString(aFilename
, osl_getThreadTextEncoding()).getStr(), nByteOffset
, nByteOffset
+nBytes
);
440 bool SvFileStream::UnlockRange( sal_Size nByteOffset
, sal_Size nBytes
)
445 unlockFile( nByteOffset
, nByteOffset
+nBytes
, this );
450 bool SvFileStream::LockFile()
452 return LockRange( 0UL, 0UL );
455 bool SvFileStream::UnlockFile()
457 return UnlockRange( 0UL, 0UL );
460 void SvFileStream::Open( const OUString
& rFilename
, StreamMode nOpenMode
)
463 oslFileHandle nHandleTmp
;
467 eStreamMode
= nOpenMode
;
468 eStreamMode
&= ~StreamMode::TRUNC
; // don't truncat on reopen
470 aFilename
= rFilename
;
473 OString
aLocalFilename(OUStringToOString(aFilename
, osl_getThreadTextEncoding()));
474 OStringBuffer
aTraceStr("SvFileStream::Open(): ");
475 aTraceStr
.append(aLocalFilename
);
476 OSL_TRACE( "%s", aTraceStr
.getStr() );
480 osl::DirectoryItem aItem
;
481 osl::FileStatus
aStatus( osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_LinkTargetURL
);
483 // FIXME: we really need to switch to a pure URL model ...
484 if ( osl::File::getFileURLFromSystemPath( aFilename
, aFileURL
) != osl::FileBase::E_None
)
485 aFileURL
= aFilename
;
486 bool bStatValid
= ( osl::DirectoryItem::get( aFileURL
, aItem
) == osl::FileBase::E_None
&&
487 aItem
.getFileStatus( aStatus
) == osl::FileBase::E_None
);
489 // SvFileStream can't open a directory
490 if( bStatValid
&& aStatus
.getFileType() == osl::FileStatus::Directory
)
492 SetError( ::GetSvError( EISDIR
) );
496 if ( !( nOpenMode
& StreamMode::WRITE
) )
497 uFlags
= osl_File_OpenFlag_Read
;
498 else if ( !( nOpenMode
& StreamMode::READ
) )
499 uFlags
= osl_File_OpenFlag_Write
;
501 uFlags
= osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
;
503 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
504 // Important for Read-Only-Filesystems (e.g, CDROM)
505 if ( (!( nOpenMode
& StreamMode::NOCREATE
)) && ( uFlags
!= osl_File_OpenFlag_Read
) )
506 uFlags
|= osl_File_OpenFlag_Create
;
507 if ( nOpenMode
& StreamMode::TRUNC
)
508 uFlags
|= osl_File_OpenFlag_Trunc
;
510 uFlags
|= osl_File_OpenFlag_NoExcl
| osl_File_OpenFlag_NoLock
;
512 if ( nOpenMode
& StreamMode::WRITE
)
514 if ( nOpenMode
& StreamMode::COPY_ON_SYMLINK
)
516 if ( bStatValid
&& aStatus
.getFileType() == osl::FileStatus::Link
&&
517 aStatus
.getLinkTargetURL().getLength() > 0 )
519 // delete the symbolic link, and replace it with the contents of the link
520 if (osl::File::remove( aFileURL
) == osl::FileBase::E_None
)
522 File::copy( aStatus
.getLinkTargetURL(), aFileURL
);
523 #if OSL_DEBUG_LEVEL > 0
525 "Removing link and replacing with file contents (%s) -> (%s).\n",
526 OUStringToOString( aStatus
.getLinkTargetURL(),
527 RTL_TEXTENCODING_UTF8
).getStr(),
528 OUStringToOString( aFileURL
,
529 RTL_TEXTENCODING_UTF8
).getStr() );
536 oslFileError rc
= osl_openFile( aFileURL
.pData
, &nHandleTmp
, uFlags
);
537 if ( rc
!= osl_File_E_None
)
539 if ( uFlags
& osl_File_OpenFlag_Write
)
541 // Change to read-only
542 uFlags
&= ~osl_File_OpenFlag_Write
;
543 rc
= osl_openFile( aFileURL
.pData
, &nHandleTmp
, uFlags
);
546 if ( rc
== osl_File_E_None
)
548 pInstanceData
->rHandle
= nHandleTmp
;
550 if ( uFlags
& osl_File_OpenFlag_Write
)
553 if ( !LockFile() ) // whole file
555 rc
= osl_closeFile( nHandleTmp
);
558 pInstanceData
->rHandle
= 0;
562 SetError( ::GetSvError( rc
) );
565 void SvFileStream::Close()
572 OStringBuffer
aTraceStr("SvFileStream::Close(): ");
573 aTraceStr
.append(OUStringToOString(aFilename
,
574 osl_getThreadTextEncoding()));
575 OSL_TRACE("%s", aTraceStr
.getStr());
579 osl_closeFile( pInstanceData
->rHandle
);
580 pInstanceData
->rHandle
= 0;
585 SvStream::ClearBuffer();
586 SvStream::ClearError();
589 /// set filepointer to beginning of file
590 void SvFileStream::ResetError()
592 SvStream::ClearError();
595 void SvFileStream::SetSize (sal_uInt64
const nSize
)
599 oslFileError rc
= osl_setFileSize( pInstanceData
->rHandle
, nSize
);
600 if (rc
!= osl_File_E_None
)
602 SetError ( ::GetSvError( rc
));
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */