LibreOffice Module tools (master) 1
strmunx.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <stdio.h>
21#include <fcntl.h>
22#include <errno.h>
23
24#include <tools/stream.hxx>
25#include <map>
26
27#include <mutex>
28#include <osl/thread.h>
29#include <sal/log.hxx>
30
31#include <osl/file.hxx>
32#include <osl/detail/file.h>
33
34using namespace osl;
35
36// InternalLock ----------------------------------------------------------------
37
38namespace {
39
40std::mutex& LockMutex()
41{
42 static std::mutex SINGLETON;
43 return SINGLETON;
44}
45
46std::map<SvFileStream const *, osl::DirectoryItem> gLocks;
47
48bool lockFile( const SvFileStream* pStream )
49{
50 osl::DirectoryItem aItem;
51 if (osl::DirectoryItem::get( pStream->GetFileName(), aItem) != osl::FileBase::E_None )
52 {
53 SAL_INFO("tools.stream", "Failed to lookup stream for locking");
54 return true;
55 }
56
57 osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
58 if ( aItem.getFileStatus( aStatus ) != osl::FileBase::E_None )
59 {
60 SAL_INFO("tools.stream", "Failed to stat stream for locking");
61 return true;
62 }
63 if( aStatus.getFileType() == osl::FileStatus::Directory )
64 return true;
65
66 std::unique_lock aGuard( LockMutex() );
67 for( const auto& [rLockStream, rLockItem] : gLocks )
68 {
69 if( aItem.isIdenticalTo( rLockItem ) )
70 {
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) );
76
77 if( bDenyByOptions )
78 {
79 return false; // file is already locked
80 }
81 }
82 }
83 gLocks[pStream] = aItem;
84 return true;
85}
86
87void unlockFile( SvFileStream const * pStream )
88{
89 std::unique_lock aGuard( LockMutex() );
90 gLocks.erase(pStream);
91}
92
93}
94
95static ErrCode GetSvError( int nErrno )
96{
97 static struct { int nErr; ErrCode sv; } const errArr[] =
98 {
99 { 0, ERRCODE_NONE },
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 },
107#else
108 { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION },
109#endif
110 { EINVAL, SVSTREAM_INVALID_PARAMETER },
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__) && \
121 !defined(DRAGONFLY)
122 { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND },
123 { ENOLINK, SVSTREAM_PATH_NOT_FOUND },
124#endif
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 }
130 };
131
132 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
133 int i=0;
134 do
135 {
136 if ( errArr[i].nErr == nErrno )
137 {
138 nRetVal = errArr[i].sv;
139 break;
140 }
141 ++i;
142 }
143 while( errArr[i].nErr != 0xFFFF );
144 return nRetVal;
145}
146
147static ErrCode GetSvError( oslFileError nErrno )
148{
149 static struct { oslFileError nErr; ErrCode sv; } const errArr[] =
150 {
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 }
170 };
171
172 ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error
173 int i=0;
174 do
175 {
176 if ( errArr[i].nErr == nErrno )
177 {
178 nRetVal = errArr[i].sv;
179 break;
180 }
181 ++i;
182 }
183 while( errArr[i].nErr != oslFileError(0xFFFF) );
184 return nRetVal;
185}
186
187SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode )
188{
189 bIsOpen = false;
190 m_isWritable = false;
191
192 SetBufferSize( 1024 );
193 // convert URL to SystemPath, if necessary
194 OUString aSystemFileName;
195 if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName )
196 != FileBase::E_None )
197 {
198 aSystemFileName = rFileName;
199 }
200 Open( aSystemFileName, nOpenMode );
201}
202
204{
205 bIsOpen = false;
206 m_isWritable = false;
207 SetBufferSize( 1024 );
208}
209
211{
212 Close();
213}
214
215std::size_t SvFileStream::GetData( void* pData, std::size_t nSize )
216{
217 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename);
218
219 sal_uInt64 nRead = 0;
220 if ( IsOpen() )
221 {
222 oslFileError rc = osl_readFile(mxFileHandle,pData,static_cast<sal_uInt64>(nSize),&nRead);
223 if ( rc != osl_File_E_None )
224 {
225 SetError( ::GetSvError( rc ));
226 return -1;
227 }
228 }
229 return static_cast<std::size_t>(nRead);
230}
231
232std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize )
233{
234 SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename);
235
236 sal_uInt64 nWrite = 0;
237 if ( IsOpen() )
238 {
239 oslFileError rc = osl_writeFile(mxFileHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite);
240 if ( rc != osl_File_E_None )
241 {
242 SetError( ::GetSvError( rc ) );
243 return -1;
244 }
245 else if( !nWrite )
247 }
248 return static_cast<std::size_t>(nWrite);
249}
250
251sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
252{
253 // check if a truncated STREAM_SEEK_TO_END was passed
254 assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END)));
255 if ( IsOpen() )
256 {
257 oslFileError rc;
258 sal_uInt64 nNewPos;
259 if ( nPos != STREAM_SEEK_TO_END )
260 rc = osl_setFilePos( mxFileHandle, osl_Pos_Absolut, nPos );
261 else
262 rc = osl_setFilePos( mxFileHandle, osl_Pos_End, 0 );
263
264 if ( rc != osl_File_E_None )
265 {
267 return 0;
268 }
269 if ( nPos != STREAM_SEEK_TO_END )
270 return nPos;
271 osl_getFilePos( mxFileHandle, &nNewPos );
272 return nNewPos;
273 }
275 return 0;
276}
277
279{
280 auto rc = osl_syncFile(mxFileHandle);
281 if (rc != osl_File_E_None)
282 SetError( ::GetSvError( rc ));
283 }
284
286{
287 int nLockMode = 0;
288
289 if ( ! IsOpen() )
290 return false;
291
293 {
294 if (m_isWritable)
295 nLockMode = F_WRLCK;
296 else
297 nLockMode = F_RDLCK;
298 }
299
301 {
302 if (m_isWritable)
303 nLockMode = F_WRLCK;
304 else
305 {
307 return false;
308 }
309 }
310
312 {
313 if (m_isWritable)
314 nLockMode = F_WRLCK;
315 else
316 nLockMode = F_RDLCK;
317 }
318
319 if (!nLockMode)
320 return true;
321
322 if( !lockFile( this ) )
323 {
324#if OSL_DEBUG_LEVEL > 1
325 fprintf( stderr, "InternalLock on %s failed\n",
326 OUStringToOString(aFilename, osl_getThreadTextEncoding()).getStr() );
327#endif
328 return false;
329 }
330
331 return true;
332}
333
335{
336 if ( ! IsOpen() )
337 return;
338
339 unlockFile( this );
340}
341
342void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode )
343{
344 sal_uInt32 uFlags;
345 oslFileHandle nHandleTmp;
346
347 Close();
348 errno = 0;
349 m_eStreamMode = nOpenMode;
350 m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen
351
352 aFilename = rFilename;
353
354 SAL_INFO("tools", aFilename);
355
356 OUString aFileURL;
357 osl::DirectoryItem aItem;
358 osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL );
359
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;
363
364 // don't both stat()ing a temporary file, unnecessary
365 bool bStatValid = true;
366 if (!(nOpenMode & StreamMode::TEMPORARY))
367 {
368 bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None &&
369 aItem.getFileStatus( aStatus ) == osl::FileBase::E_None );
370
371 // SvFileStream can't open a directory
372 if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory )
373 {
374 SetError( ::GetSvError( EISDIR ) );
375 return;
376 }
377 }
378
379 if ( !( nOpenMode & StreamMode::WRITE ) )
380 uFlags = osl_File_OpenFlag_Read;
381 else if ( !( nOpenMode & StreamMode::READ ) )
382 uFlags = osl_File_OpenFlag_Write;
383 else
384 uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
385
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;
392
393 uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock;
394
395 if ( nOpenMode & StreamMode::WRITE)
396 {
397 if ( nOpenMode & StreamMode::COPY_ON_SYMLINK )
398 {
399 if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link &&
400 aStatus.getLinkTargetURL().getLength() > 0 )
401 {
402 // delete the symbolic link, and replace it with the contents of the link
403 if (osl::File::remove( aFileURL ) == osl::FileBase::E_None )
404 {
405 File::copy( aStatus.getLinkTargetURL(), aFileURL );
406#if OSL_DEBUG_LEVEL > 0
407 fprintf( stderr,
408 "Removing link and replacing with file contents (%s) -> (%s).\n",
409 OUStringToOString( aStatus.getLinkTargetURL(),
410 RTL_TEXTENCODING_UTF8).getStr(),
411 OUStringToOString( aFileURL,
412 RTL_TEXTENCODING_UTF8).getStr() );
413#endif
414 }
415 }
416 }
417 }
418
419 oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
420 if ( rc != osl_File_E_None )
421 {
422 if ( uFlags & osl_File_OpenFlag_Write )
423 {
424 // Change to read-only
425 uFlags &= ~osl_File_OpenFlag_Write;
426 rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags );
427 }
428 }
429 if ( rc == osl_File_E_None )
430 {
431 mxFileHandle = nHandleTmp;
432 bIsOpen = true;
433 if ( uFlags & osl_File_OpenFlag_Write )
434 m_isWritable = true;
435
436 if ( !LockFile() ) // whole file
437 {
438 osl_closeFile( nHandleTmp );
439 bIsOpen = false;
440 m_isWritable = false;
441 mxFileHandle = nullptr;
442 }
443 }
444 else
445 SetError( ::GetSvError( rc ) );
446}
447
449{
450 UnlockFile();
451
452 if ( IsOpen() )
453 {
454 SAL_INFO("tools", "Closing " << aFilename);
455 FlushBuffer();
456 osl_closeFile( mxFileHandle );
457 mxFileHandle = nullptr;
458 }
459
460 bIsOpen = false;
461 m_isWritable = false;
464}
465
468{
470}
471
472void SvFileStream::SetSize (sal_uInt64 const nSize)
473{
474 if (IsOpen())
475 {
476 oslFileError rc = osl_setFileSize( mxFileHandle, nSize );
477 if (rc != osl_File_E_None )
478 {
479 SetError ( ::GetSvError( rc ));
480 }
481 }
482}
483
484/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsOpen() const
Definition: stream.hxx:618
bool LockFile()
Definition: strmunx.cxx:285
OUString aFilename
Definition: stream.hxx:593
const OUString & GetFileName() const
Definition: stream.hxx:620
virtual std::size_t GetData(void *pData, std::size_t nSize) override
Does not check for EOF, makes isEof callable.
Definition: strmunx.cxx:215
virtual void ResetError() override
set filepointer to beginning of file
Definition: strmunx.cxx:467
virtual void FlushData() override
Definition: strmunx.cxx:278
void Open(const OUString &rFileName, StreamMode eOpenMode)
Definition: strmunx.cxx:342
void * mxFileHandle
Definition: stream.hxx:589
virtual ~SvFileStream() override
Definition: strmunx.cxx:210
void Close()
Definition: strmunx.cxx:448
void UnlockFile()
Definition: strmunx.cxx:334
bool bIsOpen
Definition: stream.hxx:594
virtual void SetSize(sal_uInt64 nSize) override
Definition: strmunx.cxx:472
virtual sal_uInt64 SeekPos(sal_uInt64 nPos) override
Definition: strmunx.cxx:251
virtual std::size_t PutData(const void *pData, std::size_t nSize) override
Definition: strmunx.cxx:232
bool m_isWritable
Definition: stream.hxx:182
SAL_DLLPRIVATE void ClearBuffer()
Definition: stream.cxx:406
void SetBufferSize(sal_uInt16 m_nBufSize)
Definition: stream.cxx:380
SAL_DLLPRIVATE void ClearError()
Definition: stream.cxx:350
StreamMode GetStreamMode() const
Definition: stream.hxx:387
StreamMode m_eStreamMode
Definition: stream.hxx:181
void SetError(ErrCode nErrorCode)
Definition: stream.cxx:356
void FlushBuffer()
If we have data in our internal buffers, write them out.
Definition: stream.cxx:1101
#define SVSTREAM_TOO_MANY_OPEN_FILES
#define SVSTREAM_ACCESS_DENIED
#define SVSTREAM_INVALID_PARAMETER
#define SVSTREAM_PATH_NOT_FOUND
#define SVSTREAM_LOCKING_VIOLATION
#define SVSTREAM_SEEK_ERROR
#define SVSTREAM_DISK_FULL
#define SVSTREAM_INVALID_HANDLE
#define SVSTREAM_GENERALERROR
#define ERRCODE_NONE
#define SVSTREAM_CANNOT_MAKE
#define SVSTREAM_FILE_NOT_FOUND
sal_uInt16 nPos
#define SAL_INFO(area, stream)
std::unique_ptr< sal_Int32[]> pData
int i
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
#define STREAM_SEEK_TO_END
Definition: stream.hxx:73
StreamMode
Definition: stream.hxx:46
@ READ
allow read accesses
@ COPY_ON_SYMLINK
copy-on-write for symlinks (Unix-only)
@ TEMPORARY
temporary file attribute (Windows-only)
@ TRUNC
Truncate existing file to zero length.
@ NOCREATE
1 == Don't create file
@ WRITE
allow write accesses
static ErrCode GetSvError(int nErrno)
Definition: strmunx.cxx:95