2 * Copyright (c) 2002 Frodo
3 * Portions Copyright (c) by the authors of ffmpeg and xvid
4 * Copyright (C) 2002-2018 Team Kodi
5 * This file is part of Kodi - https://kodi.tv
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 * See LICENSES/README.md for more information.
13 #include "Directory.h"
14 #include "DirectoryCache.h"
15 #include "FileCache.h"
16 #include "FileFactory.h"
18 #include "PasswordManager.h"
19 #include "ServiceBroker.h"
20 #include "application/ApplicationComponents.h"
21 #include "application/ApplicationPowerHandling.h"
22 #include "commons/Exception.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "utils/BitstreamStats.h"
26 #include "utils/StringUtils.h"
27 #include "utils/URIUtils.h"
28 #include "utils/log.h"
30 using namespace XFILE
;
32 //////////////////////////////////////////////////////////////////////
33 // Construction/Destruction
34 //////////////////////////////////////////////////////////////////////
36 #pragma warning (disable:4244)
39 //*********************************************************************************************
40 CFile::CFile() = default;
42 //*********************************************************************************************
48 //*********************************************************************************************
50 bool CFile::Copy(const std::string
& strFileName
, const std::string
& strDest
, XFILE::IFileCallback
* pCallback
, void* pContext
)
52 const CURL
pathToUrl(strFileName
);
53 const CURL
pathToUrlDest(strDest
);
54 return Copy(pathToUrl
, pathToUrlDest
, pCallback
, pContext
);
57 bool CFile::Copy(const CURL
& url2
, const CURL
& dest
, XFILE::IFileCallback
* pCallback
, void* pContext
)
61 const std::string
pathToUrl(dest
.Get());
62 if (pathToUrl
.empty())
65 // special case for zips - ignore caching
67 if (StringUtils::StartsWith(url
.Get(), "zip://") || URIUtils::IsInAPK(url
.Get()))
68 url
.SetOptions("?cache=no");
69 if (file
.Open(url
.Get(), READ_TRUNCATED
| READ_CHUNKED
))
73 if (URIUtils::IsHD(pathToUrl
)) // create possible missing dirs
75 std::vector
<std::string
> tokens
;
76 std::string strDirectory
= URIUtils::GetDirectory(pathToUrl
);
77 URIUtils::RemoveSlashAtEnd(strDirectory
); // for the test below
78 if (!(strDirectory
.size() == 2 && strDirectory
[1] == ':'))
80 CURL
url(strDirectory
);
87 // Try to use the recursive creation first, if it fails
88 // it might not be implemented for that subsystem so let's
89 // fall back to the old method in that case
90 if (!CDirectory::Create(url
))
92 StringUtils::Tokenize(url
.GetFileName(), tokens
, pathsep
);
93 std::string strCurrPath
;
95 if (!url
.GetProtocol().empty())
98 strCurrPath
+= url
.GetProtocol() + "://";
99 } // If the directory has a / at the beginning, don't forget it
100 else if (strDirectory
[0] == pathsep
[0])
101 strCurrPath
+= pathsep
;
103 for (const std::string
& iter
: tokens
)
105 strCurrPath
+= iter
+ pathsep
;
106 CDirectory::Create(strCurrPath
);
111 if (CFile::Exists(dest
))
113 if (!newFile
.OpenForWrite(dest
, true)) // overwrite always
119 int iBufferSize
= DetermineChunkSize(file
.GetChunkSize(), 128 * 1024);
121 std::vector
<char> buffer(iBufferSize
);
122 ssize_t iRead
, iWrite
;
124 unsigned long long llFileSize
= file
.GetLength();
125 unsigned long long llPos
= 0;
130 auto& components
= CServiceBroker::GetAppComponents();
131 const auto appPower
= components
.GetComponent
<CApplicationPowerHandling
>();
134 appPower
->ResetScreenSaver();
136 iRead
= file
.Read(buffer
.data(), buffer
.size());
137 if (iRead
== 0) break;
140 CLog::Log(LOGERROR
, "{} - Failed read from file {}", __FUNCTION__
, url
.GetRedacted());
141 llFileSize
= (uint64_t)-1;
145 /* write data and make sure we managed to write it all */
147 while(iWrite
< iRead
)
149 ssize_t iWrite2
= newFile
.Write(buffer
.data() + iWrite
, iRead
- iWrite
);
157 CLog::Log(LOGERROR
, "{} - Failed write to file {}", __FUNCTION__
, dest
.GetRedacted());
158 llFileSize
= (uint64_t)-1;
164 // calculate the current and average speeds
165 float end
= timer
.GetElapsedSeconds();
167 if (pCallback
&& end
- start
> 0.5f
&& end
)
171 float averageSpeed
= llPos
/ end
;
174 ipercent
= 100 * llPos
/ llFileSize
;
176 if(!pCallback
->OnFileCallback(pContext
, ipercent
, averageSpeed
))
178 CLog::Log(LOGERROR
, "{} - User aborted copy", __FUNCTION__
);
179 llFileSize
= (uint64_t)-1;
185 /* close both files */
189 /* verify that we managed to completed the file */
190 if (llFileSize
&& llPos
!= llFileSize
)
200 //*********************************************************************************************
202 bool CFile::CURLCreate(const std::string
&url
)
208 bool CFile::CURLAddOption(XFILE::CURLOPTIONTYPE type
, const char* name
, const char * value
)
211 case XFILE::CURL_OPTION_CREDENTIALS
:
213 m_curl
.SetUserName(name
);
214 m_curl
.SetPassword(value
);
217 case XFILE::CURL_OPTION_PROTOCOL
:
218 case XFILE::CURL_OPTION_HEADER
:
220 m_curl
.SetProtocolOption(name
, value
);
223 case XFILE::CURL_OPTION_OPTION
:
225 m_curl
.SetOption(name
, value
);
234 bool CFile::CURLOpen(unsigned int flags
)
236 return Open(m_curl
, flags
);
239 bool CFile::Open(const std::string
& strFileName
, const unsigned int flags
)
241 const CURL
pathToUrl(strFileName
);
242 return Open(pathToUrl
, flags
);
245 bool CFile::Open(const CURL
& file
, const unsigned int flags
)
249 if ((flags
& READ_REOPEN
) == 0)
251 CLog::Log(LOGERROR
, "File::Open - already open: {}", file
.GetRedacted());
256 return m_pFile
->ReOpen(URIUtils::SubstitutePath(file
));
265 CURL
url(URIUtils::SubstitutePath(file
)), url2(url
);
267 if (url2
.IsProtocol("apk") || url2
.IsProtocol("zip") )
270 if (!g_directoryCache
.FileExists(url2
.Get(), bPathInCache
) )
277 * There are 5 buffer modes available (configurable in as.xml)
278 * 0) Buffer all internet filesystems (like 2 but additionally also ftp, webdav, etc.)
279 * 1) Buffer all filesystems (including local)
280 * 2) Only buffer true internet filesystems (streams) (http, etc.)
282 * 4) Buffer all remote (non-local) filesystems
284 if (!(m_flags
& READ_NO_CACHE
))
286 const std::string
pathToUrl(url
.Get());
287 if (URIUtils::IsDVD(pathToUrl
) || URIUtils::IsBluray(pathToUrl
) ||
288 (m_flags
& READ_AUDIO_VIDEO
))
290 const auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
292 const int cacheBufferMode
= (settings
)
293 ? settings
->GetInt(CSettings::SETTING_FILECACHE_BUFFERMODE
)
294 : CACHE_BUFFER_MODE_NETWORK
;
296 if ((cacheBufferMode
== CACHE_BUFFER_MODE_INTERNET
&&
297 URIUtils::IsInternetStream(pathToUrl
, true)) ||
298 (cacheBufferMode
== CACHE_BUFFER_MODE_TRUE_INTERNET
&&
299 URIUtils::IsInternetStream(pathToUrl
, false)) ||
300 (cacheBufferMode
== CACHE_BUFFER_MODE_NETWORK
&&
301 URIUtils::IsNetworkFilesystem(pathToUrl
)) ||
302 (cacheBufferMode
== CACHE_BUFFER_MODE_ALL
&&
303 (URIUtils::IsNetworkFilesystem(pathToUrl
) || URIUtils::IsHD(pathToUrl
))))
305 m_flags
|= READ_CACHED
;
309 if (m_flags
& READ_CACHED
)
311 m_pFile
= std::make_unique
<CFileCache
>(m_flags
);
316 return m_pFile
->Open(url
);
320 m_pFile
.reset(CFileFactory::CreateLoader(url
));
326 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
327 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
331 if (!m_pFile
->Open(authUrl
))
334 catch (CRedirectException
*pRedirectEx
)
336 // the file implementation decided this item should use a different implementation.
337 // the exception will contain the new implementation.
338 CLog::Log(LOGDEBUG
, "File::Open - redirecting implementation for {}", file
.GetRedacted());
339 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
341 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
342 m_pFile
.reset(pRedirectEx
->m_pNewFileImp
);
347 CURL
newAuthUrl(*pNewUrl
);
348 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
349 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
351 if (!m_pFile
->Open(newAuthUrl
))
356 if (!m_pFile
->Open(authUrl
))
363 CLog::Log(LOGERROR
, "File::Open - unknown exception when opening {}", file
.GetRedacted());
367 if (ShouldUseStreamBuffer(url
))
369 m_pBuffer
= std::make_unique
<CFileStreamBuffer
>(0);
370 m_pBuffer
->Attach(m_pFile
.get());
373 if (m_flags
& READ_BITRATE
)
375 m_bitStreamStats
= std::make_unique
<BitstreamStats
>();
376 m_bitStreamStats
->Start();
381 XBMCCOMMONS_HANDLE_UNCHECKED
382 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
383 CLog::Log(LOGERROR
, "{} - Error opening {}", __FUNCTION__
, file
.GetRedacted());
387 bool CFile::ShouldUseStreamBuffer(const CURL
& url
)
389 if (m_flags
& READ_NO_BUFFER
)
392 if (m_flags
& READ_CHUNKED
|| m_pFile
->GetChunkSize() > 0)
395 // file size > 200 MB but not in optical disk
396 if (m_pFile
->GetLength() > 200 * 1024 * 1024 && !URIUtils::IsDVD(url
.GetShareName()))
402 bool CFile::OpenForWrite(const std::string
& strFileName
, bool bOverWrite
)
404 const CURL
pathToUrl(strFileName
);
405 return OpenForWrite(pathToUrl
, bOverWrite
);
408 bool CFile::OpenForWrite(const CURL
& file
, bool bOverWrite
)
412 CURL url
= URIUtils::SubstitutePath(file
);
414 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
415 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
417 m_pFile
.reset(CFileFactory::CreateLoader(url
));
419 if (m_pFile
&& m_pFile
->OpenForWrite(authUrl
, bOverWrite
))
421 // add this file to our directory cache (if it's stored)
422 g_directoryCache
.AddFile(url
.Get());
427 XBMCCOMMONS_HANDLE_UNCHECKED
430 CLog::Log(LOGERROR
, "{} - Unhandled exception opening {}", __FUNCTION__
, file
.GetRedacted());
432 CLog::Log(LOGERROR
, "{} - Error opening {}", __FUNCTION__
, file
.GetRedacted());
436 int CFile::DetermineChunkSize(const int srcChunkSize
, const int reqChunkSize
)
438 // Determine cache chunk size: if source chunk size is bigger than 1
439 // use source chunk size else use requested chunk size
440 return (srcChunkSize
> 1 ? srcChunkSize
: reqChunkSize
);
443 bool CFile::Exists(const std::string
& strFileName
, bool bUseCache
/* = true */)
445 const CURL
pathToUrl(strFileName
);
446 return Exists(pathToUrl
, bUseCache
);
449 bool CFile::Exists(const CURL
& file
, bool bUseCache
/* = true */)
451 CURL
url(URIUtils::SubstitutePath(file
));
453 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
454 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
461 if (g_directoryCache
.FileExists(url
.Get(), bPathInCache
))
467 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
471 return pFile
->Exists(authUrl
);
473 XBMCCOMMONS_HANDLE_UNCHECKED
474 catch (CRedirectException
*pRedirectEx
)
476 // the file implementation decided this item should use a different implementation.
477 // the exception will contain the new implementation and optional a redirected URL.
478 CLog::Log(LOGDEBUG
, "File::Exists - redirecting implementation for {}", file
.GetRedacted());
479 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
481 std::unique_ptr
<IFile
> pImp(pRedirectEx
->m_pNewFileImp
);
482 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
492 if (g_directoryCache
.FileExists(pNewUrl
->Get(), bPathInCache
))
497 CURL newAuthUrl
= *pNewUrl
;
498 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
499 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
501 return pImp
->Exists(newAuthUrl
);
505 return pImp
->Exists(authUrl
);
510 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
511 CLog::Log(LOGERROR
, "{} - Error checking for {}", __FUNCTION__
, file
.GetRedacted());
515 int CFile::Stat(struct __stat64
*buffer
)
527 return m_pFile
->Stat(buffer
);
530 int CFile::Stat(const std::string
& strFileName
, struct __stat64
* buffer
)
532 const CURL
pathToUrl(strFileName
);
533 return Stat(pathToUrl
, buffer
);
536 int CFile::Stat(const CURL
& file
, struct __stat64
* buffer
)
541 CURL
url(URIUtils::SubstitutePath(file
));
543 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
544 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
548 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
551 return pFile
->Stat(authUrl
, buffer
);
553 XBMCCOMMONS_HANDLE_UNCHECKED
554 catch (CRedirectException
*pRedirectEx
)
556 // the file implementation decided this item should use a different implementation.
557 // the exception will contain the new implementation and optional a redirected URL.
558 CLog::Log(LOGDEBUG
, "File::Stat - redirecting implementation for {}", file
.GetRedacted());
559 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
561 std::unique_ptr
<IFile
> pImp(pRedirectEx
->m_pNewFileImp
);
562 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
569 CURL newAuthUrl
= *pNewUrl
;
570 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
571 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
573 if (!pImp
->Stat(newAuthUrl
, buffer
))
581 if (pImp
.get() && !pImp
->Stat(authUrl
, buffer
))
588 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
589 CLog::Log(LOGERROR
, "{} - Error statting {}", __FUNCTION__
, file
.GetRedacted());
593 ssize_t
CFile::Read(void *lpBuf
, size_t uiBufSize
)
597 if (lpBuf
== NULL
&& uiBufSize
!= 0)
600 if (uiBufSize
> SSIZE_MAX
)
601 uiBufSize
= SSIZE_MAX
;
605 // "test" read with zero size
606 // some VFSs don't handle correctly null buffer pointer
607 // provide valid buffer pointer for them
609 return m_pFile
->Read(&dummy
, 0);
614 if(m_flags
& READ_TRUNCATED
)
616 const ssize_t nBytes
= m_pBuffer
->sgetn(
617 (char *)lpBuf
, std::min
<std::streamsize
>((std::streamsize
)uiBufSize
,
618 m_pBuffer
->in_avail()));
619 if (m_bitStreamStats
&& nBytes
>0)
620 m_bitStreamStats
->AddSampleBytes(nBytes
);
625 const ssize_t nBytes
= m_pBuffer
->sgetn((char*)lpBuf
, uiBufSize
);
626 if (m_bitStreamStats
&& nBytes
>0)
627 m_bitStreamStats
->AddSampleBytes(nBytes
);
634 if(m_flags
& READ_TRUNCATED
)
636 const ssize_t nBytes
= m_pFile
->Read(lpBuf
, uiBufSize
);
637 if (m_bitStreamStats
&& nBytes
>0)
638 m_bitStreamStats
->AddSampleBytes(nBytes
);
644 while((uiBufSize
-done
) > 0)
646 const ssize_t curr
= m_pFile
->Read((char*)lpBuf
+done
, uiBufSize
-done
);
649 if (curr
< 0 && done
== 0)
656 if (m_bitStreamStats
&& done
> 0)
657 m_bitStreamStats
->AddSampleBytes(done
);
661 XBMCCOMMONS_HANDLE_UNCHECKED
664 CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
);
670 //*********************************************************************************************
681 XBMCCOMMONS_HANDLE_UNCHECKED
682 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
692 XBMCCOMMONS_HANDLE_UNCHECKED
693 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
696 //*********************************************************************************************
697 int64_t CFile::Seek(int64_t iFilePosition
, int iWhence
)
704 if(iWhence
== SEEK_CUR
)
705 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::cur
);
706 else if(iWhence
== SEEK_END
)
707 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::end
);
708 else if(iWhence
== SEEK_SET
)
709 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::beg
);
714 return m_pFile
->Seek(iFilePosition
, iWhence
);
716 XBMCCOMMONS_HANDLE_UNCHECKED
717 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
721 //*********************************************************************************************
722 int CFile::Truncate(int64_t iSize
)
729 return m_pFile
->Truncate(iSize
);
731 XBMCCOMMONS_HANDLE_UNCHECKED
732 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
736 //*********************************************************************************************
737 int64_t CFile::GetLength()
742 return m_pFile
->GetLength();
745 XBMCCOMMONS_HANDLE_UNCHECKED
746 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
750 //*********************************************************************************************
751 int64_t CFile::GetPosition() const
757 return m_pBuffer
->pubseekoff(0, std::ios_base::cur
);
761 return m_pFile
->GetPosition();
763 XBMCCOMMONS_HANDLE_UNCHECKED
764 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
769 //*********************************************************************************************
770 bool CFile::ReadString(char *szLine
, int iLineLength
)
772 if (!m_pFile
|| !szLine
)
777 typedef CFileStreamBuffer::traits_type traits
;
778 CFileStreamBuffer::int_type aByte
= m_pBuffer
->sgetc();
780 if(aByte
== traits::eof())
785 aByte
= m_pBuffer
->sbumpc();
787 if(aByte
== traits::eof())
790 if(aByte
== traits::to_int_type('\n'))
792 if(m_pBuffer
->sgetc() == traits::to_int_type('\r'))
797 if(aByte
== traits::to_int_type('\r'))
799 if(m_pBuffer
->sgetc() == traits::to_int_type('\n'))
804 *szLine
= traits::to_char_type(aByte
);
809 // if we have no space for terminating character we failed
820 return m_pFile
->ReadString(szLine
, iLineLength
);
822 XBMCCOMMONS_HANDLE_UNCHECKED
823 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
827 ssize_t
CFile::Write(const void* lpBuf
, size_t uiBufSize
)
831 if (lpBuf
== NULL
&& uiBufSize
!= 0)
836 if (uiBufSize
== 0 && lpBuf
== NULL
)
837 { // "test" write with zero size
838 // some VFSs don't handle correctly null buffer pointer
839 // provide valid buffer pointer for them
840 const char dummyBuf
= 0;
841 return m_pFile
->Write(&dummyBuf
, 0);
844 return m_pFile
->Write(lpBuf
, uiBufSize
);
846 XBMCCOMMONS_HANDLE_UNCHECKED
847 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
851 bool CFile::Delete(const std::string
& strFileName
)
853 const CURL
pathToUrl(strFileName
);
854 return Delete(pathToUrl
);
857 bool CFile::Delete(const CURL
& file
)
861 CURL
url(URIUtils::SubstitutePath(file
));
863 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
864 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
866 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
870 if(pFile
->Delete(authUrl
))
872 g_directoryCache
.ClearFile(url
.Get());
876 XBMCCOMMONS_HANDLE_UNCHECKED
877 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
879 CLog::Log(LOGERROR
, "{} - Error deleting file {}", __FUNCTION__
, file
.GetRedacted());
883 bool CFile::Rename(const std::string
& strFileName
, const std::string
& strNewFileName
)
885 const CURL
pathToUrl(strFileName
);
886 const CURL
pathToUrlNew(strNewFileName
);
887 return Rename(pathToUrl
, pathToUrlNew
);
890 bool CFile::Rename(const CURL
& file
, const CURL
& newFile
)
894 CURL
url(URIUtils::SubstitutePath(file
));
895 CURL
urlnew(URIUtils::SubstitutePath(newFile
));
898 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
899 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
900 CURL authUrlNew
= urlnew
;
901 if (CPasswordManager::GetInstance().IsURLSupported(authUrlNew
) && authUrlNew
.GetUserName().empty())
902 CPasswordManager::GetInstance().AuthenticateURL(authUrlNew
);
904 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
908 if(pFile
->Rename(authUrl
, authUrlNew
))
910 g_directoryCache
.ClearFile(url
.Get());
911 g_directoryCache
.AddFile(urlnew
.Get());
915 XBMCCOMMONS_HANDLE_UNCHECKED
916 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception ", __FUNCTION__
); }
917 CLog::Log(LOGERROR
, "{} - Error renaming file {}", __FUNCTION__
, file
.GetRedacted());
921 bool CFile::SetHidden(const std::string
& fileName
, bool hidden
)
923 const CURL
pathToUrl(fileName
);
924 return SetHidden(pathToUrl
, hidden
);
927 bool CFile::SetHidden(const CURL
& file
, bool hidden
)
931 CURL
url(URIUtils::SubstitutePath(file
));
933 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
934 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
936 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
940 return pFile
->SetHidden(authUrl
, hidden
);
944 CLog::Log(LOGERROR
, "{}({}) - Unhandled exception", __FUNCTION__
, file
.GetRedacted());
949 int CFile::IoControl(EIoControl request
, void* param
)
954 result
= m_pFile
->IoControl(request
, param
);
956 if(result
== -1 && request
== IOCTRL_SEEK_POSSIBLE
)
958 if(m_pFile
->GetLength() >= 0 && m_pFile
->Seek(0, SEEK_CUR
) >= 0)
967 int CFile::GetChunkSize()
970 return m_pFile
->GetChunkSize();
974 const std::string
CFile::GetProperty(XFILE::FileProperty type
, const std::string
&name
) const
978 return m_pFile
->GetProperty(type
, name
);
981 const std::vector
<std::string
> CFile::GetPropertyValues(XFILE::FileProperty type
, const std::string
&name
) const
985 return std::vector
<std::string
>();
987 return m_pFile
->GetPropertyValues(type
, name
);
990 ssize_t
CFile::LoadFile(const std::string
& filename
, std::vector
<uint8_t>& outputBuffer
)
992 const CURL
pathToUrl(filename
);
993 return LoadFile(pathToUrl
, outputBuffer
);
996 ssize_t
CFile::LoadFile(const CURL
& file
, std::vector
<uint8_t>& outputBuffer
)
999 static const size_t max_file_size
= 0x7FFFFFFF;
1000 static const size_t min_chunk_size
= 64 * 1024U;
1001 static const size_t max_chunk_size
= 2048 * 1024U;
1003 outputBuffer
.clear();
1005 if (!Open(file
, READ_TRUNCATED
))
1009 GetLength() will typically return values that fall into three cases:
1010 1. The real filesize. This is the typical case.
1011 2. Zero. This is the case for some http:// streams for example.
1012 3. Some value smaller than the real filesize. This is the case for an expanding file.
1014 In order to handle all three cases, we read the file in chunks, relying on Read()
1015 returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
1016 cases 1 and 3 is set to one byte larger than the value returned by GetLength().
1017 The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
1020 We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
1021 as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
1023 To minimize reallocation, we double the chunksize each read while chunksize is lower
1024 than max_chunk_size.
1026 int64_t filesize
= GetLength();
1027 if (filesize
> (int64_t)max_file_size
)
1028 return 0; /* file is too large for this function */
1030 size_t chunksize
= (filesize
> 0) ? static_cast<size_t>(filesize
+ 1)
1031 : static_cast<size_t>(DetermineChunkSize(GetChunkSize(),
1033 size_t total_read
= 0;
1036 if (total_read
== outputBuffer
.size())
1038 if (outputBuffer
.size() + chunksize
> max_file_size
)
1040 outputBuffer
.clear();
1043 outputBuffer
.resize(outputBuffer
.size() + chunksize
);
1044 if (chunksize
< max_chunk_size
)
1047 ssize_t read
= Read(outputBuffer
.data() + total_read
, outputBuffer
.size() - total_read
);
1050 outputBuffer
.clear();
1058 outputBuffer
.resize(total_read
);
1062 catch (const std::bad_alloc
&)
1064 outputBuffer
.clear();
1065 CLog::LogF(LOGERROR
, "Failed to load {}: out of memory", file
.GetFileName());
1069 double CFile::GetDownloadSpeed()
1072 return m_pFile
->GetDownloadSpeed();
1076 //*********************************************************************************************
1077 //*************** Stream IO for CFile objects *************************************************
1078 //*********************************************************************************************
1079 CFileStreamBuffer::~CFileStreamBuffer()
1085 CFileStreamBuffer::CFileStreamBuffer(int backsize
)
1089 , m_backsize(backsize
)
1093 void CFileStreamBuffer::Attach(IFile
*file
)
1097 m_frontsize
= CFile::DetermineChunkSize(m_file
->GetChunkSize(), 64 * 1024);
1099 m_buffer
= new char[m_frontsize
+m_backsize
];
1104 void CFileStreamBuffer::Detach()
1112 CFileStreamBuffer::int_type
CFileStreamBuffer::underflow()
1114 if(gptr() < egptr())
1115 return traits_type::to_int_type(*gptr());
1118 return traits_type::eof();
1120 size_t backsize
= 0;
1123 backsize
= (size_t)std::min
<ptrdiff_t>((ptrdiff_t)m_backsize
, egptr()-eback());
1124 memmove(m_buffer
, egptr()-backsize
, backsize
);
1127 ssize_t size
= m_file
->Read(m_buffer
+backsize
, m_frontsize
);
1130 return traits_type::eof();
1133 CLog::LogF(LOGWARNING
, "Error reading file - assuming eof");
1134 return traits_type::eof();
1137 setg(m_buffer
, m_buffer
+backsize
, m_buffer
+backsize
+size
);
1138 return traits_type::to_int_type(*gptr());
1141 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekoff(
1143 std::ios_base::seekdir way
,
1144 std::ios_base::openmode mode
)
1146 // calculate relative offset
1147 off_type aheadbytes
= (egptr() - gptr());
1148 off_type pos
= m_file
->GetPosition() - aheadbytes
;
1150 if(way
== std::ios_base::cur
)
1152 else if(way
== std::ios_base::beg
)
1153 offset2
= offset
- pos
;
1154 else if(way
== std::ios_base::end
)
1155 offset2
= offset
+ m_file
->GetLength() - pos
;
1157 return std::streampos(-1);
1159 // a non seek shouldn't modify our buffer
1163 // try to seek within buffer
1164 if(gptr()+offset2
>= eback() && gptr()+offset2
< egptr())
1167 return pos
+ offset2
;
1170 // reset our buffer pointer, will
1171 // start buffering on next read
1175 int64_t position
= -1;
1176 if(way
== std::ios_base::cur
)
1177 position
= m_file
->Seek(offset
- aheadbytes
, SEEK_CUR
);
1178 else if(way
== std::ios_base::end
)
1179 position
= m_file
->Seek(offset
, SEEK_END
);
1181 position
= m_file
->Seek(offset
, SEEK_SET
);
1184 return std::streampos(-1);
1189 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekpos(
1191 std::ios_base::openmode mode
)
1193 return seekoff(pos
, std::ios_base::beg
, mode
);
1196 std::streamsize
CFileStreamBuffer::showmanyc()
1199 return egptr() - gptr();
1202 CFileStream::CFileStream(int backsize
/*= 0*/) : std::istream(&m_buffer
), m_buffer(backsize
)
1206 CFileStream::~CFileStream()
1212 bool CFileStream::Open(const CURL
& filename
)
1216 CURL
url(URIUtils::SubstitutePath(filename
));
1217 m_file
.reset(CFileFactory::CreateLoader(url
));
1220 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
1221 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
1223 if(m_file
&& m_file
->Open(authUrl
))
1225 m_buffer
.Attach(m_file
.get());
1233 int64_t CFileStream::GetLength()
1235 return m_file
->GetLength();
1238 void CFileStream::Close()
1246 bool CFileStream::Open(const std::string
& filename
)
1248 const CURL
pathToUrl(filename
);
1249 return Open(pathToUrl
);