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>
33 #include <rtl/instance.hxx>
37 // InternalLock ----------------------------------------------------------------
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");
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");
60 if( aStatus
.getFileType() == osl::FileStatus::Directory
)
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
) );
77 return false; // file is already locked
81 rLocks
[pStream
] = aItem
;
85 void unlockFile( SvFileStream
const * pStream
)
87 osl::MutexGuard
aGuard( LockMutex::get() );
88 auto &rLocks
= Locks::get();
89 rLocks
.erase(pStream
);
94 // StreamData ------------------------------------------------------------------
99 oslFileHandle rHandle
;
101 StreamData() : rHandle( nullptr ) { }
104 static ErrCode
GetSvError( int nErrno
)
106 static struct { int nErr
; ErrCode sv
; } const errArr
[] =
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
},
117 { EDEADLOCK
, SVSTREAM_LOCKING_VIOLATION
},
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__) && \
131 { EMULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
132 { ENOLINK
, SVSTREAM_PATH_NOT_FOUND
},
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
145 if ( errArr
[i
].nErr
== nErrno
)
147 nRetVal
= errArr
[i
].sv
;
152 while( errArr
[i
].nErr
!= 0xFFFF );
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
185 if ( errArr
[i
].nErr
== nErrno
)
187 nRetVal
= errArr
[i
].sv
;
192 while( errArr
[i
].nErr
!= oslFileError(0xFFFF) );
196 SvFileStream::SvFileStream( const OUString
& rFileName
, StreamMode nOpenMode
)
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()
216 m_isWritable
= false;
217 pInstanceData
.reset(new StreamData
);
218 SetBufferSize( 1024 );
221 SvFileStream::~SvFileStream()
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;
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
));
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;
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
) );
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
)));
270 if ( nPos
!= STREAM_SEEK_TO_END
)
271 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_Absolut
, nPos
);
273 rc
= osl_setFilePos( pInstanceData
->rHandle
, osl_Pos_End
, 0 );
275 if ( rc
!= osl_File_E_None
)
277 SetError( SVSTREAM_SEEK_ERROR
);
280 if ( nPos
!= STREAM_SEEK_TO_END
)
282 osl_getFilePos( pInstanceData
->rHandle
, &nNewPos
);
285 SetError( SVSTREAM_GENERALERROR
);
289 void SvFileStream::FlushData()
291 // does not exist locally
294 bool SvFileStream::LockFile()
301 if (m_eStreamMode
& StreamMode::SHARE_DENYALL
)
309 if (m_eStreamMode
& StreamMode::SHARE_DENYREAD
)
315 SetError(SVSTREAM_LOCKING_VIOLATION
);
320 if (m_eStreamMode
& StreamMode::SHARE_DENYWRITE
)
331 if( !lockFile( this ) )
333 #if OSL_DEBUG_LEVEL > 1
334 fprintf( stderr
, "InternalLock on %s failed\n",
335 OUStringToOString(aFilename
, osl_getThreadTextEncoding()).getStr() );
343 void SvFileStream::UnlockFile()
351 void SvFileStream::Open( const OUString
& rFilename
, StreamMode nOpenMode
)
354 oslFileHandle nHandleTmp
;
358 m_eStreamMode
= nOpenMode
;
359 m_eStreamMode
&= ~StreamMode::TRUNC
; // don't truncate on reopen
361 aFilename
= rFilename
;
363 SAL_INFO("tools", aFilename
);
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
) );
382 if ( !( nOpenMode
& StreamMode::WRITE
) )
383 uFlags
= osl_File_OpenFlag_Read
;
384 else if ( !( nOpenMode
& StreamMode::READ
) )
385 uFlags
= osl_File_OpenFlag_Write
;
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
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() );
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
;
436 if ( uFlags
& osl_File_OpenFlag_Write
)
439 if ( !LockFile() ) // whole file
441 osl_closeFile( nHandleTmp
);
443 m_isWritable
= false;
444 pInstanceData
->rHandle
= nullptr;
448 SetError( ::GetSvError( rc
) );
451 void SvFileStream::Close()
457 SAL_INFO("tools", "Closing " << aFilename
);
459 osl_closeFile( pInstanceData
->rHandle
);
460 pInstanceData
->rHandle
= nullptr;
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
)
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: */