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_CHUNKED
|| m_pFile
->GetChunkSize() > 0)
392 // file size > 200 MB but not in optical disk
393 if (m_pFile
->GetLength() > 200 * 1024 * 1024 && !URIUtils::IsDVD(url
.GetShareName()))
399 bool CFile::OpenForWrite(const std::string
& strFileName
, bool bOverWrite
)
401 const CURL
pathToUrl(strFileName
);
402 return OpenForWrite(pathToUrl
, bOverWrite
);
405 bool CFile::OpenForWrite(const CURL
& file
, bool bOverWrite
)
409 CURL url
= URIUtils::SubstitutePath(file
);
411 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
412 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
414 m_pFile
.reset(CFileFactory::CreateLoader(url
));
416 if (m_pFile
&& m_pFile
->OpenForWrite(authUrl
, bOverWrite
))
418 // add this file to our directory cache (if it's stored)
419 g_directoryCache
.AddFile(url
.Get());
424 XBMCCOMMONS_HANDLE_UNCHECKED
427 CLog::Log(LOGERROR
, "{} - Unhandled exception opening {}", __FUNCTION__
, file
.GetRedacted());
429 CLog::Log(LOGERROR
, "{} - Error opening {}", __FUNCTION__
, file
.GetRedacted());
433 int CFile::DetermineChunkSize(const int srcChunkSize
, const int reqChunkSize
)
435 // Determine cache chunk size: if source chunk size is bigger than 1
436 // use source chunk size else use requested chunk size
437 return (srcChunkSize
> 1 ? srcChunkSize
: reqChunkSize
);
440 bool CFile::Exists(const std::string
& strFileName
, bool bUseCache
/* = true */)
442 const CURL
pathToUrl(strFileName
);
443 return Exists(pathToUrl
, bUseCache
);
446 bool CFile::Exists(const CURL
& file
, bool bUseCache
/* = true */)
448 CURL
url(URIUtils::SubstitutePath(file
));
450 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
451 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
458 if (g_directoryCache
.FileExists(url
.Get(), bPathInCache
))
464 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
468 return pFile
->Exists(authUrl
);
470 XBMCCOMMONS_HANDLE_UNCHECKED
471 catch (CRedirectException
*pRedirectEx
)
473 // the file implementation decided this item should use a different implementation.
474 // the exception will contain the new implementation and optional a redirected URL.
475 CLog::Log(LOGDEBUG
, "File::Exists - redirecting implementation for {}", file
.GetRedacted());
476 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
478 std::unique_ptr
<IFile
> pImp(pRedirectEx
->m_pNewFileImp
);
479 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
489 if (g_directoryCache
.FileExists(pNewUrl
->Get(), bPathInCache
))
494 CURL newAuthUrl
= *pNewUrl
;
495 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
496 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
498 return pImp
->Exists(newAuthUrl
);
502 return pImp
->Exists(authUrl
);
507 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
508 CLog::Log(LOGERROR
, "{} - Error checking for {}", __FUNCTION__
, file
.GetRedacted());
512 int CFile::Stat(struct __stat64
*buffer
)
524 return m_pFile
->Stat(buffer
);
527 int CFile::Stat(const std::string
& strFileName
, struct __stat64
* buffer
)
529 const CURL
pathToUrl(strFileName
);
530 return Stat(pathToUrl
, buffer
);
533 int CFile::Stat(const CURL
& file
, struct __stat64
* buffer
)
538 CURL
url(URIUtils::SubstitutePath(file
));
540 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
541 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
545 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
548 return pFile
->Stat(authUrl
, buffer
);
550 XBMCCOMMONS_HANDLE_UNCHECKED
551 catch (CRedirectException
*pRedirectEx
)
553 // the file implementation decided this item should use a different implementation.
554 // the exception will contain the new implementation and optional a redirected URL.
555 CLog::Log(LOGDEBUG
, "File::Stat - redirecting implementation for {}", file
.GetRedacted());
556 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
558 std::unique_ptr
<IFile
> pImp(pRedirectEx
->m_pNewFileImp
);
559 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
566 CURL newAuthUrl
= *pNewUrl
;
567 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
568 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
570 if (!pImp
->Stat(newAuthUrl
, buffer
))
578 if (pImp
.get() && !pImp
->Stat(authUrl
, buffer
))
585 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
586 CLog::Log(LOGERROR
, "{} - Error statting {}", __FUNCTION__
, file
.GetRedacted());
590 ssize_t
CFile::Read(void *lpBuf
, size_t uiBufSize
)
594 if (lpBuf
== NULL
&& uiBufSize
!= 0)
597 if (uiBufSize
> SSIZE_MAX
)
598 uiBufSize
= SSIZE_MAX
;
602 // "test" read with zero size
603 // some VFSs don't handle correctly null buffer pointer
604 // provide valid buffer pointer for them
606 return m_pFile
->Read(&dummy
, 0);
611 if(m_flags
& READ_TRUNCATED
)
613 const ssize_t nBytes
= m_pBuffer
->sgetn(
614 (char *)lpBuf
, std::min
<std::streamsize
>((std::streamsize
)uiBufSize
,
615 m_pBuffer
->in_avail()));
616 if (m_bitStreamStats
&& nBytes
>0)
617 m_bitStreamStats
->AddSampleBytes(nBytes
);
622 const ssize_t nBytes
= m_pBuffer
->sgetn((char*)lpBuf
, uiBufSize
);
623 if (m_bitStreamStats
&& nBytes
>0)
624 m_bitStreamStats
->AddSampleBytes(nBytes
);
631 if(m_flags
& READ_TRUNCATED
)
633 const ssize_t nBytes
= m_pFile
->Read(lpBuf
, uiBufSize
);
634 if (m_bitStreamStats
&& nBytes
>0)
635 m_bitStreamStats
->AddSampleBytes(nBytes
);
641 while((uiBufSize
-done
) > 0)
643 const ssize_t curr
= m_pFile
->Read((char*)lpBuf
+done
, uiBufSize
-done
);
646 if (curr
< 0 && done
== 0)
653 if (m_bitStreamStats
&& done
> 0)
654 m_bitStreamStats
->AddSampleBytes(done
);
658 XBMCCOMMONS_HANDLE_UNCHECKED
661 CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
);
667 //*********************************************************************************************
678 XBMCCOMMONS_HANDLE_UNCHECKED
679 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
689 XBMCCOMMONS_HANDLE_UNCHECKED
690 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
693 //*********************************************************************************************
694 int64_t CFile::Seek(int64_t iFilePosition
, int iWhence
)
701 if(iWhence
== SEEK_CUR
)
702 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::cur
);
703 else if(iWhence
== SEEK_END
)
704 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::end
);
705 else if(iWhence
== SEEK_SET
)
706 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::beg
);
711 return m_pFile
->Seek(iFilePosition
, iWhence
);
713 XBMCCOMMONS_HANDLE_UNCHECKED
714 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
718 //*********************************************************************************************
719 int CFile::Truncate(int64_t iSize
)
726 return m_pFile
->Truncate(iSize
);
728 XBMCCOMMONS_HANDLE_UNCHECKED
729 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
733 //*********************************************************************************************
734 int64_t CFile::GetLength()
739 return m_pFile
->GetLength();
742 XBMCCOMMONS_HANDLE_UNCHECKED
743 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
747 //*********************************************************************************************
748 int64_t CFile::GetPosition() const
754 return m_pBuffer
->pubseekoff(0, std::ios_base::cur
);
758 return m_pFile
->GetPosition();
760 XBMCCOMMONS_HANDLE_UNCHECKED
761 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
766 //*********************************************************************************************
767 bool CFile::ReadString(char *szLine
, int iLineLength
)
769 if (!m_pFile
|| !szLine
)
774 typedef CFileStreamBuffer::traits_type traits
;
775 CFileStreamBuffer::int_type aByte
= m_pBuffer
->sgetc();
777 if(aByte
== traits::eof())
782 aByte
= m_pBuffer
->sbumpc();
784 if(aByte
== traits::eof())
787 if(aByte
== traits::to_int_type('\n'))
789 if(m_pBuffer
->sgetc() == traits::to_int_type('\r'))
794 if(aByte
== traits::to_int_type('\r'))
796 if(m_pBuffer
->sgetc() == traits::to_int_type('\n'))
801 *szLine
= traits::to_char_type(aByte
);
806 // if we have no space for terminating character we failed
817 return m_pFile
->ReadString(szLine
, iLineLength
);
819 XBMCCOMMONS_HANDLE_UNCHECKED
820 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
824 ssize_t
CFile::Write(const void* lpBuf
, size_t uiBufSize
)
828 if (lpBuf
== NULL
&& uiBufSize
!= 0)
833 if (uiBufSize
== 0 && lpBuf
== NULL
)
834 { // "test" write with zero size
835 // some VFSs don't handle correctly null buffer pointer
836 // provide valid buffer pointer for them
837 const char dummyBuf
= 0;
838 return m_pFile
->Write(&dummyBuf
, 0);
841 return m_pFile
->Write(lpBuf
, uiBufSize
);
843 XBMCCOMMONS_HANDLE_UNCHECKED
844 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
848 bool CFile::Delete(const std::string
& strFileName
)
850 const CURL
pathToUrl(strFileName
);
851 return Delete(pathToUrl
);
854 bool CFile::Delete(const CURL
& file
)
858 CURL
url(URIUtils::SubstitutePath(file
));
860 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
861 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
863 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
867 if(pFile
->Delete(authUrl
))
869 g_directoryCache
.ClearFile(url
.Get());
873 XBMCCOMMONS_HANDLE_UNCHECKED
874 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
876 CLog::Log(LOGERROR
, "{} - Error deleting file {}", __FUNCTION__
, file
.GetRedacted());
880 bool CFile::Rename(const std::string
& strFileName
, const std::string
& strNewFileName
)
882 const CURL
pathToUrl(strFileName
);
883 const CURL
pathToUrlNew(strNewFileName
);
884 return Rename(pathToUrl
, pathToUrlNew
);
887 bool CFile::Rename(const CURL
& file
, const CURL
& newFile
)
891 CURL
url(URIUtils::SubstitutePath(file
));
892 CURL
urlnew(URIUtils::SubstitutePath(newFile
));
895 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
896 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
897 CURL authUrlNew
= urlnew
;
898 if (CPasswordManager::GetInstance().IsURLSupported(authUrlNew
) && authUrlNew
.GetUserName().empty())
899 CPasswordManager::GetInstance().AuthenticateURL(authUrlNew
);
901 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
905 if(pFile
->Rename(authUrl
, authUrlNew
))
907 g_directoryCache
.ClearFile(url
.Get());
908 g_directoryCache
.AddFile(urlnew
.Get());
912 XBMCCOMMONS_HANDLE_UNCHECKED
913 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception ", __FUNCTION__
); }
914 CLog::Log(LOGERROR
, "{} - Error renaming file {}", __FUNCTION__
, file
.GetRedacted());
918 bool CFile::SetHidden(const std::string
& fileName
, bool hidden
)
920 const CURL
pathToUrl(fileName
);
921 return SetHidden(pathToUrl
, hidden
);
924 bool CFile::SetHidden(const CURL
& file
, bool hidden
)
928 CURL
url(URIUtils::SubstitutePath(file
));
930 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
931 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
933 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
937 return pFile
->SetHidden(authUrl
, hidden
);
941 CLog::Log(LOGERROR
, "{}({}) - Unhandled exception", __FUNCTION__
, file
.GetRedacted());
946 int CFile::IoControl(EIoControl request
, void* param
)
951 result
= m_pFile
->IoControl(request
, param
);
953 if(result
== -1 && request
== IOCTRL_SEEK_POSSIBLE
)
955 if(m_pFile
->GetLength() >= 0 && m_pFile
->Seek(0, SEEK_CUR
) >= 0)
964 int CFile::GetChunkSize()
967 return m_pFile
->GetChunkSize();
971 const std::string
CFile::GetProperty(XFILE::FileProperty type
, const std::string
&name
) const
975 return m_pFile
->GetProperty(type
, name
);
978 const std::vector
<std::string
> CFile::GetPropertyValues(XFILE::FileProperty type
, const std::string
&name
) const
982 return std::vector
<std::string
>();
984 return m_pFile
->GetPropertyValues(type
, name
);
987 ssize_t
CFile::LoadFile(const std::string
& filename
, std::vector
<uint8_t>& outputBuffer
)
989 const CURL
pathToUrl(filename
);
990 return LoadFile(pathToUrl
, outputBuffer
);
993 ssize_t
CFile::LoadFile(const CURL
& file
, std::vector
<uint8_t>& outputBuffer
)
995 static const size_t max_file_size
= 0x7FFFFFFF;
996 static const size_t min_chunk_size
= 64 * 1024U;
997 static const size_t max_chunk_size
= 2048 * 1024U;
999 outputBuffer
.clear();
1001 if (!Open(file
, READ_TRUNCATED
))
1005 GetLength() will typically return values that fall into three cases:
1006 1. The real filesize. This is the typical case.
1007 2. Zero. This is the case for some http:// streams for example.
1008 3. Some value smaller than the real filesize. This is the case for an expanding file.
1010 In order to handle all three cases, we read the file in chunks, relying on Read()
1011 returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
1012 cases 1 and 3 is set to one byte larger than the value returned by GetLength().
1013 The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
1016 We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
1017 as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
1019 To minimize reallocation, we double the chunksize each read while chunksize is lower
1020 than max_chunk_size.
1022 int64_t filesize
= GetLength();
1023 if (filesize
> (int64_t)max_file_size
)
1024 return 0; /* file is too large for this function */
1026 size_t chunksize
= (filesize
> 0) ? static_cast<size_t>(filesize
+ 1)
1027 : static_cast<size_t>(DetermineChunkSize(GetChunkSize(),
1029 size_t total_read
= 0;
1032 if (total_read
== outputBuffer
.size())
1034 if (outputBuffer
.size() + chunksize
> max_file_size
)
1036 outputBuffer
.clear();
1039 outputBuffer
.resize(outputBuffer
.size() + chunksize
);
1040 if (chunksize
< max_chunk_size
)
1043 ssize_t read
= Read(outputBuffer
.data() + total_read
, outputBuffer
.size() - total_read
);
1046 outputBuffer
.clear();
1054 outputBuffer
.resize(total_read
);
1059 double CFile::GetDownloadSpeed()
1062 return m_pFile
->GetDownloadSpeed();
1066 //*********************************************************************************************
1067 //*************** Stream IO for CFile objects *************************************************
1068 //*********************************************************************************************
1069 CFileStreamBuffer::~CFileStreamBuffer()
1075 CFileStreamBuffer::CFileStreamBuffer(int backsize
)
1079 , m_backsize(backsize
)
1083 void CFileStreamBuffer::Attach(IFile
*file
)
1087 m_frontsize
= CFile::DetermineChunkSize(m_file
->GetChunkSize(), 64 * 1024);
1089 m_buffer
= new char[m_frontsize
+m_backsize
];
1094 void CFileStreamBuffer::Detach()
1102 CFileStreamBuffer::int_type
CFileStreamBuffer::underflow()
1104 if(gptr() < egptr())
1105 return traits_type::to_int_type(*gptr());
1108 return traits_type::eof();
1110 size_t backsize
= 0;
1113 backsize
= (size_t)std::min
<ptrdiff_t>((ptrdiff_t)m_backsize
, egptr()-eback());
1114 memmove(m_buffer
, egptr()-backsize
, backsize
);
1117 ssize_t size
= m_file
->Read(m_buffer
+backsize
, m_frontsize
);
1120 return traits_type::eof();
1123 CLog::LogF(LOGWARNING
, "Error reading file - assuming eof");
1124 return traits_type::eof();
1127 setg(m_buffer
, m_buffer
+backsize
, m_buffer
+backsize
+size
);
1128 return traits_type::to_int_type(*gptr());
1131 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekoff(
1133 std::ios_base::seekdir way
,
1134 std::ios_base::openmode mode
)
1136 // calculate relative offset
1137 off_type aheadbytes
= (egptr() - gptr());
1138 off_type pos
= m_file
->GetPosition() - aheadbytes
;
1140 if(way
== std::ios_base::cur
)
1142 else if(way
== std::ios_base::beg
)
1143 offset2
= offset
- pos
;
1144 else if(way
== std::ios_base::end
)
1145 offset2
= offset
+ m_file
->GetLength() - pos
;
1147 return std::streampos(-1);
1149 // a non seek shouldn't modify our buffer
1153 // try to seek within buffer
1154 if(gptr()+offset2
>= eback() && gptr()+offset2
< egptr())
1157 return pos
+ offset2
;
1160 // reset our buffer pointer, will
1161 // start buffering on next read
1165 int64_t position
= -1;
1166 if(way
== std::ios_base::cur
)
1167 position
= m_file
->Seek(offset
- aheadbytes
, SEEK_CUR
);
1168 else if(way
== std::ios_base::end
)
1169 position
= m_file
->Seek(offset
, SEEK_END
);
1171 position
= m_file
->Seek(offset
, SEEK_SET
);
1174 return std::streampos(-1);
1179 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekpos(
1181 std::ios_base::openmode mode
)
1183 return seekoff(pos
, std::ios_base::beg
, mode
);
1186 std::streamsize
CFileStreamBuffer::showmanyc()
1189 return egptr() - gptr();
1192 CFileStream::CFileStream(int backsize
/*= 0*/) : std::istream(&m_buffer
), m_buffer(backsize
)
1196 CFileStream::~CFileStream()
1202 bool CFileStream::Open(const CURL
& filename
)
1206 CURL
url(URIUtils::SubstitutePath(filename
));
1207 m_file
.reset(CFileFactory::CreateLoader(url
));
1210 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
1211 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
1213 if(m_file
&& m_file
->Open(authUrl
))
1215 m_buffer
.Attach(m_file
.get());
1223 int64_t CFileStream::GetLength()
1225 return m_file
->GetLength();
1228 void CFileStream::Close()
1236 bool CFileStream::Open(const std::string
& filename
)
1238 const CURL
pathToUrl(filename
);
1239 return Open(pathToUrl
);