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/debug.hxx>
29 #include <tools/stream.hxx>
32 #include <osl/mutex.hxx>
33 #include <osl/thread.h> // osl_getThreadTextEncoding
36 #include <osl/file.hxx>
37 #include <osl/detail/file.h>
38 #include <rtl/instance.hxx>
39 #include <rtl/strbuf.hxx>
43 // InternalLock ----------------------------------------------------------------
45 namespace { struct LockMutex
: public rtl::Static
< osl::Mutex
, LockMutex
> {}; }
47 class InternalStreamLock
51 SvFileStream
* m_pStream
;
52 osl::DirectoryItem m_aItem
;
54 InternalStreamLock( sal_Size
, sal_Size
, SvFileStream
* );
55 ~InternalStreamLock();
57 static sal_Bool
LockFile( sal_Size nStart
, sal_Size nEnd
, SvFileStream
* );
58 static void UnlockFile( sal_Size nStart
, sal_Size nEnd
, SvFileStream
* );
61 typedef ::std::vector
< InternalStreamLock
* > InternalStreamLockList
;
63 namespace { struct LockList
: public rtl::Static
< InternalStreamLockList
, LockList
> {}; }
65 InternalStreamLock::InternalStreamLock(
68 SvFileStream
* pStream
) :
69 m_nStartPos( nStart
),
73 osl::DirectoryItem::get( m_pStream
->GetFileName(), m_aItem
);
74 LockList::get().push_back( this );
75 #if OSL_DEBUG_LEVEL > 1
76 OString
aFileName(OUStringToOString(m_pStream
->GetFileName(),
77 osl_getThreadTextEncoding()));
78 fprintf( stderr
, "locked %s", aFileName
.getStr() );
79 if( m_nStartPos
|| m_nEndPos
)
80 fprintf(stderr
, " [ %ld ... %ld ]", m_nStartPos
, m_nEndPos
);
81 fprintf( stderr
, "\n" );
85 InternalStreamLock::~InternalStreamLock()
87 for ( InternalStreamLockList::iterator it
= LockList::get().begin();
88 it
!= LockList::get().end();
92 LockList::get().erase( it
);
96 #if OSL_DEBUG_LEVEL > 1
97 OString
aFileName(OUStringToOString(m_pStream
->GetFileName(),
98 osl_getThreadTextEncoding()));
99 fprintf( stderr
, "unlocked %s", aFileName
.getStr() );
100 if( m_nStartPos
|| m_nEndPos
)
101 fprintf(stderr
, " [ %ld ... %ld ]", m_nStartPos
, m_nEndPos
);
102 fprintf( stderr
, "\n" );
106 sal_Bool
InternalStreamLock::LockFile( sal_Size nStart
, sal_Size nEnd
, SvFileStream
* pStream
)
108 osl::MutexGuard
aGuard( LockMutex::get() );
109 osl::DirectoryItem aItem
;
110 if (osl::DirectoryItem::get( pStream
->GetFileName(), aItem
) != osl::FileBase::E_None
)
112 SAL_INFO("tools.stream", "Failed to lookup stream for locking");
116 osl::FileStatus
aStatus( osl_FileStatus_Mask_Type
);
117 if ( aItem
.getFileStatus( aStatus
) != osl::FileBase::E_None
)
119 SAL_INFO("tools.stream", "Failed to stat stream for locking");
122 if( aStatus
.getFileType() == osl::FileStatus::Directory
)
125 InternalStreamLock
* pLock
= NULL
;
126 InternalStreamLockList
&rLockList
= LockList::get();
127 for( size_t i
= 0; i
< rLockList
.size(); ++i
)
129 pLock
= rLockList
[ i
];
130 if( aItem
.isIdenticalTo( pLock
->m_aItem
) )
132 sal_Bool bDenyByOptions
= sal_False
;
133 StreamMode nLockMode
= pLock
->m_pStream
->GetStreamMode();
134 StreamMode nNewMode
= pStream
->GetStreamMode();
136 if( nLockMode
& STREAM_SHARE_DENYALL
)
137 bDenyByOptions
= sal_True
;
138 else if( ( nLockMode
& STREAM_SHARE_DENYWRITE
) &&
139 ( nNewMode
& STREAM_WRITE
) )
140 bDenyByOptions
= sal_True
;
141 else if( ( nLockMode
& STREAM_SHARE_DENYREAD
) &&
142 ( nNewMode
& STREAM_READ
) )
143 bDenyByOptions
= sal_True
;
147 if( pLock
->m_nStartPos
== 0 && pLock
->m_nEndPos
== 0 ) // whole file is already locked
149 if( nStart
== 0 && nEnd
== 0) // cannot lock whole file
152 if( ( nStart
< pLock
->m_nStartPos
&& nEnd
> pLock
->m_nStartPos
) ||
153 ( nStart
< pLock
->m_nEndPos
&& nEnd
> pLock
->m_nEndPos
) )
158 // hint: new InternalStreamLock() adds the entry to the global list
159 pLock
= new InternalStreamLock( nStart
, nEnd
, pStream
);
163 void InternalStreamLock::UnlockFile( sal_Size nStart
, sal_Size nEnd
, SvFileStream
* pStream
)
165 osl::MutexGuard
aGuard( LockMutex::get() );
166 InternalStreamLock
* pLock
= NULL
;
167 InternalStreamLockList
&rLockList
= LockList::get();
168 if( nStart
== 0 && nEnd
== 0 )
170 // nStart & nEnd = 0, so delete all locks
171 for( size_t i
= 0; i
< rLockList
.size(); ++i
)
173 if( ( pLock
= rLockList
[ i
] )->m_pStream
== pStream
)
175 // hint: delete will remove pLock from the global list
182 for( size_t i
= 0; i
< rLockList
.size(); ++i
)
184 if ( ( pLock
= rLockList
[ i
] )->m_pStream
== pStream
185 && nStart
== pLock
->m_nStartPos
186 && nEnd
== pLock
->m_nEndPos
188 // hint: delete will remove pLock from the global list
195 // StreamData ------------------------------------------------------------------
200 oslFileHandle rHandle
;
202 StreamData() : rHandle( 0 ) { }
205 static sal_uInt32
GetSvError( int nErrno
)
207 static struct { int nErr
; sal_uInt32 sv
; } errArr
[] =
210 { EACCES
, SVSTREAM_ACCESS_DENIED
},
211 { EBADF
, SVSTREAM_INVALID_HANDLE
},
212 #if defined(RS6000) || defined(NETBSD) || \
213 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
214 defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \
216 { EDEADLK
, SVSTREAM_LOCKING_VIOLATION
},
218 { EDEADLOCK
, SVSTREAM_LOCKING_VIOLATION
},
220 { EINVAL
, SVSTREAM_INVALID_PARAMETER
},
221 { EMFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
222 { ENFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
223 { ENOENT
, SVSTREAM_FILE_NOT_FOUND
},
224 { EPERM
, SVSTREAM_ACCESS_DENIED
},
225 { EROFS
, SVSTREAM_ACCESS_DENIED
},
226 { EAGAIN
, SVSTREAM_LOCKING_VIOLATION
},
227 { EISDIR
, SVSTREAM_PATH_NOT_FOUND
},
228 { ELOOP
, SVSTREAM_PATH_NOT_FOUND
},
229 #if !defined(RS6000) && !defined(NETBSD) && !defined (FREEBSD) && \
230 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
232 { EMULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
233 { ENOLINK
, SVSTREAM_PATH_NOT_FOUND
},
235 { ENOTDIR
, SVSTREAM_PATH_NOT_FOUND
},
236 { ETXTBSY
, SVSTREAM_ACCESS_DENIED
},
237 { EEXIST
, SVSTREAM_CANNOT_MAKE
},
238 { ENOSPC
, SVSTREAM_DISK_FULL
},
239 { (int)0xFFFF, SVSTREAM_GENERALERROR
}
242 sal_uInt32 nRetVal
= SVSTREAM_GENERALERROR
; // default error
246 if ( errArr
[i
].nErr
== nErrno
)
248 nRetVal
= errArr
[i
].sv
;
253 while( errArr
[i
].nErr
!= 0xFFFF );
257 static sal_uInt32
GetSvError( oslFileError nErrno
)
259 static struct { oslFileError nErr
; sal_uInt32 sv
; } errArr
[] =
261 { osl_File_E_None
, SVSTREAM_OK
},
262 { osl_File_E_ACCES
, SVSTREAM_ACCESS_DENIED
},
263 { osl_File_E_BADF
, SVSTREAM_INVALID_HANDLE
},
264 { osl_File_E_DEADLK
, SVSTREAM_LOCKING_VIOLATION
},
265 { osl_File_E_INVAL
, SVSTREAM_INVALID_PARAMETER
},
266 { osl_File_E_MFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
267 { osl_File_E_NFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
268 { osl_File_E_NOENT
, SVSTREAM_FILE_NOT_FOUND
},
269 { osl_File_E_PERM
, SVSTREAM_ACCESS_DENIED
},
270 { osl_File_E_ROFS
, SVSTREAM_ACCESS_DENIED
},
271 { osl_File_E_AGAIN
, SVSTREAM_LOCKING_VIOLATION
},
272 { osl_File_E_ISDIR
, SVSTREAM_PATH_NOT_FOUND
},
273 { osl_File_E_LOOP
, SVSTREAM_PATH_NOT_FOUND
},
274 { osl_File_E_MULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
275 { osl_File_E_NOLINK
, SVSTREAM_PATH_NOT_FOUND
},
276 { osl_File_E_NOTDIR
, SVSTREAM_PATH_NOT_FOUND
},
277 { osl_File_E_EXIST
, SVSTREAM_CANNOT_MAKE
},
278 { osl_File_E_NOSPC
, SVSTREAM_DISK_FULL
},
279 { (oslFileError
)0xFFFF, SVSTREAM_GENERALERROR
}
282 sal_uInt32 nRetVal
= SVSTREAM_GENERALERROR
; // default error
286 if ( errArr
[i
].nErr
== nErrno
)
288 nRetVal
= errArr
[i
].sv
;
293 while( errArr
[i
].nErr
!= (oslFileError
)0xFFFF );
297 SvFileStream::SvFileStream( const String
& rFileName
, StreamMode nOpenMode
)
301 bIsWritable
= sal_False
;
302 pInstanceData
= new StreamData
;
304 SetBufferSize( 1024 );
305 // convert URL to SystemPath, if necessary
306 OUString aSystemFileName
;
307 if( FileBase::getSystemPathFromFileURL( rFileName
, aSystemFileName
)
308 != FileBase::E_None
)
310 aSystemFileName
= rFileName
;
312 Open( aSystemFileName
, nOpenMode
);
315 SvFileStream::SvFileStream()
319 bIsWritable
= sal_False
;
320 pInstanceData
= new StreamData
;
321 SetBufferSize( 1024 );
324 SvFileStream::~SvFileStream()
328 InternalStreamLock::UnlockFile( 0, 0, this );
331 delete pInstanceData
;
334 sal_uInt32
SvFileStream::GetFileHandle() const
337 if (osl_getFileOSHandle(pInstanceData
->rHandle
, &handle
) == osl_File_E_None
)
338 return (sal_uInt32
) handle
;
340 return (sal_uInt32
) -1;
343 sal_uInt16
SvFileStream::IsA() const
345 return ID_FILESTREAM
;
348 sal_Size
SvFileStream::GetData( void* pData
, sal_Size nSize
)
351 OStringBuffer
aTraceStr("SvFileStream::GetData(): ");
352 aTraceStr
.append(static_cast<sal_Int64
>(nSize
));
353 aTraceStr
.append(" Bytes from ");
354 aTraceStr
.append(OUStringToOString(aFilename
,
355 osl_getThreadTextEncoding()));
356 OSL_TRACE("%s", aTraceStr
.getStr());
359 sal_uInt64 nRead
= 0;
362 oslFileError rc
= osl_readFile(pInstanceData
->rHandle
,pData
,(sal_uInt64
)nSize
,&nRead
);
363 if ( rc
!= osl_File_E_None
)
365 SetError( ::GetSvError( rc
));
369 return (sal_Size
)nRead
;
372 sal_Size
SvFileStream::PutData( const void* pData
, sal_Size nSize
)
375 OStringBuffer
aTraceStr("SvFileStream::PutData(): ");
376 aTraceStr
.append(static_cast<sal_Int64
>(nSize
));
377 aTraceStr
.append(" Bytes to ");
378 aTraceStr
.append(OUStringToOString(aFilename
,
379 osl_getThreadTextEncoding()));
380 OSL_TRACE("%s", aTraceStr
.getStr());
383 sal_uInt64 nWrite
= 0;
386 oslFileError rc
= osl_writeFile(pInstanceData
->rHandle
,pData
,(sal_uInt64
)nSize
,&nWrite
);
387 if ( rc
!= osl_File_E_None
)
389 SetError( ::GetSvError( rc
) );
393 SetError( SVSTREAM_DISK_FULL
);
395 return (sal_Size
)nWrite
;
398 sal_Size
SvFileStream::SeekPos( sal_Size nPos
)
404 if ( nPos
!= STREAM_SEEK_TO_END
)
405 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_Absolut
, nPos
);
407 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_End
, 0 );
409 if ( rc
!= osl_File_E_None
)
411 SetError( SVSTREAM_SEEK_ERROR
);
414 rc
= osl_getFilePos( pInstanceData
->rHandle
, &nNewPos
);
415 return (sal_Size
) nNewPos
;
417 SetError( SVSTREAM_GENERALERROR
);
421 void SvFileStream::FlushData()
423 // does not exist locally
426 sal_Bool
SvFileStream::LockRange( sal_Size nByteOffset
, sal_Size nBytes
)
433 if ( eStreamMode
& STREAM_SHARE_DENYALL
)
441 if ( eStreamMode
& STREAM_SHARE_DENYREAD
)
447 SetError(SVSTREAM_LOCKING_VIOLATION
);
452 if ( eStreamMode
& STREAM_SHARE_DENYWRITE
)
463 if( ! InternalStreamLock::LockFile( nByteOffset
, nByteOffset
+nBytes
, this ) )
465 #if OSL_DEBUG_LEVEL > 1
466 fprintf( stderr
, "InternalLock on %s [ %ld ... %ld ] failed\n",
467 OUStringToOString(aFilename
, osl_getThreadTextEncoding()).getStr(), nByteOffset
, nByteOffset
+nBytes
);
475 sal_Bool
SvFileStream::UnlockRange( sal_Size nByteOffset
, sal_Size nBytes
)
480 InternalStreamLock::UnlockFile( nByteOffset
, nByteOffset
+nBytes
, this );
485 sal_Bool
SvFileStream::LockFile()
487 return LockRange( 0UL, 0UL );
490 sal_Bool
SvFileStream::UnlockFile()
492 return UnlockRange( 0UL, 0UL );
495 void SvFileStream::Open( const String
& rFilename
, StreamMode nOpenMode
)
498 oslFileHandle nHandleTmp
;
502 eStreamMode
= nOpenMode
;
503 eStreamMode
&= ~STREAM_TRUNC
; // don't truncat on reopen
505 aFilename
= rFilename
;
506 OString
aLocalFilename(OUStringToOString(aFilename
, osl_getThreadTextEncoding()));
509 OStringBuffer
aTraceStr("SvFileStream::Open(): ");
510 aTraceStr
.append(aLocalFilename
);
511 OSL_TRACE( "%s", aTraceStr
.getStr() );
515 osl::DirectoryItem aItem
;
516 osl::FileStatus
aStatus( osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_LinkTargetURL
);
518 // FIXME: we really need to switch to a pure URL model ...
519 if ( osl::File::getFileURLFromSystemPath( aFilename
, aFileURL
) != osl::FileBase::E_None
)
520 aFileURL
= aFilename
;
521 bool bStatValid
= ( osl::DirectoryItem::get( aFileURL
, aItem
) == osl::FileBase::E_None
&&
522 aItem
.getFileStatus( aStatus
) == osl::FileBase::E_None
);
524 // SvFileStream can't open a directory
525 if( bStatValid
&& aStatus
.getFileType() == osl::FileStatus::Directory
)
527 SetError( ::GetSvError( EISDIR
) );
531 if ( !( nOpenMode
& STREAM_WRITE
) )
532 uFlags
= osl_File_OpenFlag_Read
;
533 else if ( !( nOpenMode
& STREAM_READ
) )
534 uFlags
= osl_File_OpenFlag_Write
;
536 uFlags
= osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
;
538 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
539 // Important for Read-Only-Filesystems (e.g, CDROM)
540 if ( (!( nOpenMode
& STREAM_NOCREATE
)) && ( uFlags
!= osl_File_OpenFlag_Read
) )
541 uFlags
|= osl_File_OpenFlag_Create
;
542 if ( nOpenMode
& STREAM_TRUNC
)
543 uFlags
|= osl_File_OpenFlag_Trunc
;
545 uFlags
|= osl_File_OpenFlag_NoExcl
| osl_File_OpenFlag_NoLock
;
547 if ( nOpenMode
& STREAM_WRITE
)
549 if ( nOpenMode
& STREAM_COPY_ON_SYMLINK
)
551 if ( bStatValid
&& aStatus
.getFileType() == osl::FileStatus::Link
&&
552 aStatus
.getLinkTargetURL().getLength() > 0 )
554 // delete the symbolic link, and replace it with the contents of the link
555 if (osl::File::remove( aFileURL
) == osl::FileBase::E_None
)
557 File::copy( aStatus
.getLinkTargetURL(), aFileURL
);
558 #if OSL_DEBUG_LEVEL > 0
560 "Removing link and replacing with file contents (%s) -> (%s).\n",
561 OUStringToOString( aStatus
.getLinkTargetURL(),
562 RTL_TEXTENCODING_UTF8
).getStr(),
563 OUStringToOString( aFileURL
,
564 RTL_TEXTENCODING_UTF8
).getStr() );
571 oslFileError rc
= osl_openFile( aFileURL
.pData
, &nHandleTmp
, uFlags
);
572 if ( rc
!= osl_File_E_None
)
574 if ( uFlags
& osl_File_OpenFlag_Write
)
576 // Change to read-only
577 uFlags
&= ~osl_File_OpenFlag_Write
;
578 rc
= osl_openFile( aFileURL
.pData
, &nHandleTmp
, uFlags
);
581 if ( rc
== osl_File_E_None
)
583 pInstanceData
->rHandle
= nHandleTmp
;
585 if ( uFlags
& osl_File_OpenFlag_Write
)
586 bIsWritable
= sal_True
;
588 if ( !LockFile() ) // whole file
590 rc
= osl_closeFile( nHandleTmp
);
592 bIsWritable
= sal_False
;
593 pInstanceData
->rHandle
= 0;
597 SetError( ::GetSvError( rc
) );
600 void SvFileStream::Close()
607 OStringBuffer
aTraceStr("SvFileStream::Close(): ");
608 aTraceStr
.append(OUStringToOString(aFilename
,
609 osl_getThreadTextEncoding()));
610 OSL_TRACE("%s", aTraceStr
.getStr());
614 osl_closeFile( pInstanceData
->rHandle
);
615 pInstanceData
->rHandle
= 0;
619 bIsWritable
= sal_False
;
620 SvStream::ClearBuffer();
621 SvStream::ClearError();
624 /// set filepointer to beginning of file
625 void SvFileStream::ResetError()
627 SvStream::ClearError();
630 void SvFileStream::SetSize (sal_Size nSize
)
634 oslFileError rc
= osl_setFileSize( pInstanceData
->rHandle
, nSize
);
635 if (rc
!= osl_File_E_None
)
637 SetError ( ::GetSvError( rc
));
642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */