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>
28 #include <osl/thread.h>
29 #include <sal/log.hxx>
31 #include <osl/file.hxx>
32 #include <osl/detail/file.h>
36 // InternalLock ----------------------------------------------------------------
40 std::mutex
& LockMutex()
42 static std::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 std::unique_lock
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 std::unique_lock
aGuard( LockMutex() );
90 gLocks
.erase(pStream
);
95 static ErrCode
GetSvError( int nErrno
)
97 static struct { int nErr
; ErrCode sv
; } const errArr
[] =
100 { EACCES
, SVSTREAM_ACCESS_DENIED
},
101 { EBADF
, SVSTREAM_INVALID_HANDLE
},
102 #if defined(NETBSD) || \
103 defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \
104 defined(__FreeBSD_kernel__) || defined(DRAGONFLY) || \
105 defined(IOS) || defined(HAIKU)
106 { EDEADLK
, SVSTREAM_LOCKING_VIOLATION
},
108 { EDEADLOCK
, SVSTREAM_LOCKING_VIOLATION
},
110 { EINVAL
, SVSTREAM_INVALID_PARAMETER
},
111 { EMFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
112 { ENFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
113 { ENOENT
, SVSTREAM_FILE_NOT_FOUND
},
114 { EPERM
, SVSTREAM_ACCESS_DENIED
},
115 { EROFS
, SVSTREAM_ACCESS_DENIED
},
116 { EAGAIN
, SVSTREAM_LOCKING_VIOLATION
},
117 { EISDIR
, SVSTREAM_PATH_NOT_FOUND
},
118 { ELOOP
, SVSTREAM_PATH_NOT_FOUND
},
119 #if !defined(NETBSD) && !defined (FREEBSD) && \
120 !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \
122 { EMULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
123 { ENOLINK
, SVSTREAM_PATH_NOT_FOUND
},
125 { ENOTDIR
, SVSTREAM_PATH_NOT_FOUND
},
126 { ETXTBSY
, SVSTREAM_ACCESS_DENIED
},
127 { EEXIST
, SVSTREAM_CANNOT_MAKE
},
128 { ENOSPC
, SVSTREAM_DISK_FULL
},
129 { int(0xFFFF), SVSTREAM_GENERALERROR
}
132 ErrCode nRetVal
= SVSTREAM_GENERALERROR
; // default error
136 if ( errArr
[i
].nErr
== nErrno
)
138 nRetVal
= errArr
[i
].sv
;
143 while( errArr
[i
].nErr
!= 0xFFFF );
147 static ErrCode
GetSvError( oslFileError nErrno
)
149 static struct { oslFileError nErr
; ErrCode sv
; } const errArr
[] =
151 { osl_File_E_None
, ERRCODE_NONE
},
152 { osl_File_E_ACCES
, SVSTREAM_ACCESS_DENIED
},
153 { osl_File_E_BADF
, SVSTREAM_INVALID_HANDLE
},
154 { osl_File_E_DEADLK
, SVSTREAM_LOCKING_VIOLATION
},
155 { osl_File_E_INVAL
, SVSTREAM_INVALID_PARAMETER
},
156 { osl_File_E_MFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
157 { osl_File_E_NFILE
, SVSTREAM_TOO_MANY_OPEN_FILES
},
158 { osl_File_E_NOENT
, SVSTREAM_FILE_NOT_FOUND
},
159 { osl_File_E_PERM
, SVSTREAM_ACCESS_DENIED
},
160 { osl_File_E_ROFS
, SVSTREAM_ACCESS_DENIED
},
161 { osl_File_E_AGAIN
, SVSTREAM_LOCKING_VIOLATION
},
162 { osl_File_E_ISDIR
, SVSTREAM_PATH_NOT_FOUND
},
163 { osl_File_E_LOOP
, SVSTREAM_PATH_NOT_FOUND
},
164 { osl_File_E_MULTIHOP
, SVSTREAM_PATH_NOT_FOUND
},
165 { osl_File_E_NOLINK
, SVSTREAM_PATH_NOT_FOUND
},
166 { osl_File_E_NOTDIR
, SVSTREAM_PATH_NOT_FOUND
},
167 { osl_File_E_EXIST
, SVSTREAM_CANNOT_MAKE
},
168 { osl_File_E_NOSPC
, SVSTREAM_DISK_FULL
},
169 { oslFileError(0xFFFF), SVSTREAM_GENERALERROR
}
172 ErrCode nRetVal
= SVSTREAM_GENERALERROR
; // default error
176 if ( errArr
[i
].nErr
== nErrno
)
178 nRetVal
= errArr
[i
].sv
;
183 while( errArr
[i
].nErr
!= oslFileError(0xFFFF) );
187 SvFileStream::SvFileStream( const OUString
& rFileName
, StreamMode nOpenMode
)
190 m_isWritable
= false;
192 SetBufferSize( 1024 );
193 // convert URL to SystemPath, if necessary
194 OUString aSystemFileName
;
195 if( FileBase::getSystemPathFromFileURL( rFileName
, aSystemFileName
)
196 != FileBase::E_None
)
198 aSystemFileName
= rFileName
;
200 Open( aSystemFileName
, nOpenMode
);
203 SvFileStream::SvFileStream()
206 m_isWritable
= false;
207 SetBufferSize( 1024 );
210 SvFileStream::~SvFileStream()
215 std::size_t SvFileStream::GetData( void* pData
, std::size_t nSize
)
217 SAL_INFO("tools", OString::number(static_cast<sal_Int64
>(nSize
)) << " Bytes from " << aFilename
);
219 sal_uInt64 nRead
= 0;
222 oslFileError rc
= osl_readFile(mxFileHandle
,pData
,static_cast<sal_uInt64
>(nSize
),&nRead
);
223 if ( rc
!= osl_File_E_None
)
225 SetError( ::GetSvError( rc
));
229 return static_cast<std::size_t>(nRead
);
232 std::size_t SvFileStream::PutData( const void* pData
, std::size_t nSize
)
234 SAL_INFO("tools", OString::number(static_cast<sal_Int64
>(nSize
)) << " Bytes to " << aFilename
);
236 sal_uInt64 nWrite
= 0;
239 oslFileError rc
= osl_writeFile(mxFileHandle
,pData
,static_cast<sal_uInt64
>(nSize
),&nWrite
);
240 if ( rc
!= osl_File_E_None
)
242 SetError( ::GetSvError( rc
) );
247 SetError( SVSTREAM_DISK_FULL
);
251 return static_cast<std::size_t>(nWrite
);
254 sal_uInt64
SvFileStream::SeekPos(sal_uInt64
const nPos
)
256 // check if a truncated STREAM_SEEK_TO_END was passed
257 assert(nPos
!= sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END
)));
262 if ( nPos
!= STREAM_SEEK_TO_END
)
263 rc
= osl_setFilePos( mxFileHandle
, osl_Pos_Absolut
, nPos
);
265 rc
= osl_setFilePos( mxFileHandle
, osl_Pos_End
, 0 );
267 if ( rc
!= osl_File_E_None
)
269 SetError( SVSTREAM_SEEK_ERROR
);
272 if ( nPos
!= STREAM_SEEK_TO_END
)
274 osl_getFilePos( mxFileHandle
, &nNewPos
);
277 SetError( SVSTREAM_GENERALERROR
);
281 void SvFileStream::FlushData()
283 auto rc
= osl_syncFile(mxFileHandle
);
284 if (rc
!= osl_File_E_None
)
285 SetError( ::GetSvError( rc
));
288 bool SvFileStream::LockFile()
295 if (m_eStreamMode
& StreamMode::SHARE_DENYALL
)
303 if (m_eStreamMode
& StreamMode::SHARE_DENYREAD
)
309 SetError(SVSTREAM_LOCKING_VIOLATION
);
314 if (m_eStreamMode
& StreamMode::SHARE_DENYWRITE
)
325 if( !lockFile( this ) )
327 SAL_WARN("tools.stream", "InternalLock on " << aFilename
<< "failed");
334 void SvFileStream::UnlockFile()
342 void SvFileStream::Open( const OUString
& rFilename
, StreamMode nOpenMode
)
345 oslFileHandle nHandleTmp
;
349 m_eStreamMode
= nOpenMode
;
350 m_eStreamMode
&= ~StreamMode::TRUNC
; // don't truncate on reopen
352 aFilename
= rFilename
;
354 SAL_INFO("tools", aFilename
);
357 osl::DirectoryItem aItem
;
358 osl::FileStatus
aStatus( osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_LinkTargetURL
);
360 // FIXME: we really need to switch to a pure URL model ...
361 if ( osl::File::getFileURLFromSystemPath( aFilename
, aFileURL
) != osl::FileBase::E_None
)
362 aFileURL
= aFilename
;
364 // don't both stat()ing a temporary file, unnecessary
365 bool bStatValid
= true;
366 if (!(nOpenMode
& StreamMode::TEMPORARY
))
368 bStatValid
= ( osl::DirectoryItem::get( aFileURL
, aItem
) == osl::FileBase::E_None
&&
369 aItem
.getFileStatus( aStatus
) == osl::FileBase::E_None
);
371 // SvFileStream can't open a directory
372 if( bStatValid
&& aStatus
.getFileType() == osl::FileStatus::Directory
)
374 SetError( ::GetSvError( EISDIR
) );
379 if ( !( nOpenMode
& StreamMode::WRITE
) )
380 uFlags
= osl_File_OpenFlag_Read
;
381 else if ( !( nOpenMode
& StreamMode::READ
) )
382 uFlags
= osl_File_OpenFlag_Write
;
384 uFlags
= osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
;
386 // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY
387 // Important for Read-Only-Filesystems (e.g, CDROM)
388 if ( (!( nOpenMode
& StreamMode::NOCREATE
)) && ( uFlags
!= osl_File_OpenFlag_Read
) )
389 uFlags
|= osl_File_OpenFlag_Create
;
390 if ( nOpenMode
& StreamMode::TRUNC
)
391 uFlags
|= osl_File_OpenFlag_Trunc
;
393 uFlags
|= osl_File_OpenFlag_NoExcl
| osl_File_OpenFlag_NoLock
;
395 if ( nOpenMode
& StreamMode::WRITE
)
397 if ( nOpenMode
& StreamMode::COPY_ON_SYMLINK
)
399 if ( bStatValid
&& aStatus
.getFileType() == osl::FileStatus::Link
&&
400 aStatus
.getLinkTargetURL().getLength() > 0 )
402 // delete the symbolic link, and replace it with the contents of the link
403 if (osl::File::remove( aFileURL
) == osl::FileBase::E_None
)
405 File::copy( aStatus
.getLinkTargetURL(), aFileURL
);
406 SAL_INFO("tools.stream",
407 "Removing link and replacing with file contents (" <<
408 aStatus
.getLinkTargetURL() << ") -> (" << aFileURL
<< ").");
414 oslFileError rc
= osl_openFile( aFileURL
.pData
, &nHandleTmp
, uFlags
);
415 if ( rc
!= osl_File_E_None
)
417 if ( uFlags
& osl_File_OpenFlag_Write
)
419 // Change to read-only
420 uFlags
&= ~osl_File_OpenFlag_Write
;
421 rc
= osl_openFile( aFileURL
.pData
, &nHandleTmp
, uFlags
);
424 if ( rc
== osl_File_E_None
)
426 mxFileHandle
= nHandleTmp
;
428 if ( uFlags
& osl_File_OpenFlag_Write
)
431 if ( !LockFile() ) // whole file
433 osl_closeFile( nHandleTmp
);
435 m_isWritable
= false;
436 mxFileHandle
= nullptr;
440 SetError( ::GetSvError( rc
) );
443 void SvFileStream::Close()
449 SAL_INFO("tools", "Closing " << aFilename
);
451 osl_closeFile( mxFileHandle
);
452 mxFileHandle
= nullptr;
456 m_isWritable
= false;
457 SvStream::ClearBuffer();
458 SvStream::ClearError();
461 /// set filepointer to beginning of file
462 void SvFileStream::ResetError()
464 SvStream::ClearError();
467 void SvFileStream::SetSize (sal_uInt64
const nSize
)
471 oslFileError rc
= osl_setFileSize( mxFileHandle
, nSize
);
472 if (rc
!= osl_File_E_None
)
474 SetError ( ::GetSvError( rc
));
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */