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__
); }
768 bool XFILE::CFile::ReadString(std::vector
<char>& line
)
777 using traits
= CFileStreamBuffer::traits_type
;
778 CFileStreamBuffer::int_type aByte
= m_pBuffer
->sgetc();
780 while (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 line
.emplace_back(traits::to_char_type(aByte
));
809 // Read by buffer chuncks until to EOL or EOF
812 char bufferLine
[1025];
813 if (!m_pFile
->ReadString(bufferLine
, sizeof(bufferLine
))) // EOF or error
814 return !line
.empty();
816 const size_t length
= std::strlen(bufferLine
);
817 line
.insert(line
.end(), bufferLine
, bufferLine
+ length
);
818 } while (line
.back() != '\n' && line
.back() != '\r');
822 XBMCCOMMONS_HANDLE_UNCHECKED
825 CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
);
830 //*********************************************************************************************
831 bool CFile::ReadString(char *szLine
, int iLineLength
)
833 if (!m_pFile
|| !szLine
)
838 typedef CFileStreamBuffer::traits_type traits
;
839 CFileStreamBuffer::int_type aByte
= m_pBuffer
->sgetc();
841 if(aByte
== traits::eof())
846 aByte
= m_pBuffer
->sbumpc();
848 if(aByte
== traits::eof())
851 if(aByte
== traits::to_int_type('\n'))
853 if(m_pBuffer
->sgetc() == traits::to_int_type('\r'))
858 if(aByte
== traits::to_int_type('\r'))
860 if(m_pBuffer
->sgetc() == traits::to_int_type('\n'))
865 *szLine
= traits::to_char_type(aByte
);
870 // if we have no space for terminating character we failed
881 return m_pFile
->ReadString(szLine
, iLineLength
);
883 XBMCCOMMONS_HANDLE_UNCHECKED
884 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
888 ssize_t
CFile::Write(const void* lpBuf
, size_t uiBufSize
)
892 if (lpBuf
== NULL
&& uiBufSize
!= 0)
897 if (uiBufSize
== 0 && lpBuf
== NULL
)
898 { // "test" write with zero size
899 // some VFSs don't handle correctly null buffer pointer
900 // provide valid buffer pointer for them
901 const char dummyBuf
= 0;
902 return m_pFile
->Write(&dummyBuf
, 0);
905 return m_pFile
->Write(lpBuf
, uiBufSize
);
907 XBMCCOMMONS_HANDLE_UNCHECKED
908 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
912 bool CFile::Delete(const std::string
& strFileName
)
914 const CURL
pathToUrl(strFileName
);
915 return Delete(pathToUrl
);
918 bool CFile::Delete(const CURL
& file
)
922 CURL
url(URIUtils::SubstitutePath(file
));
924 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
925 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
927 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
931 if(pFile
->Delete(authUrl
))
933 g_directoryCache
.ClearFile(url
.Get());
937 XBMCCOMMONS_HANDLE_UNCHECKED
938 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
940 CLog::Log(LOGERROR
, "{} - Error deleting file {}", __FUNCTION__
, file
.GetRedacted());
944 bool CFile::Rename(const std::string
& strFileName
, const std::string
& strNewFileName
)
946 const CURL
pathToUrl(strFileName
);
947 const CURL
pathToUrlNew(strNewFileName
);
948 return Rename(pathToUrl
, pathToUrlNew
);
951 bool CFile::Rename(const CURL
& file
, const CURL
& newFile
)
955 CURL
url(URIUtils::SubstitutePath(file
));
956 CURL
urlnew(URIUtils::SubstitutePath(newFile
));
959 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
960 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
961 CURL authUrlNew
= urlnew
;
962 if (CPasswordManager::GetInstance().IsURLSupported(authUrlNew
) && authUrlNew
.GetUserName().empty())
963 CPasswordManager::GetInstance().AuthenticateURL(authUrlNew
);
965 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
969 if(pFile
->Rename(authUrl
, authUrlNew
))
971 g_directoryCache
.ClearFile(url
.Get());
972 g_directoryCache
.AddFile(urlnew
.Get());
976 XBMCCOMMONS_HANDLE_UNCHECKED
977 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception ", __FUNCTION__
); }
978 CLog::Log(LOGERROR
, "{} - Error renaming file {}", __FUNCTION__
, file
.GetRedacted());
982 bool CFile::SetHidden(const std::string
& fileName
, bool hidden
)
984 const CURL
pathToUrl(fileName
);
985 return SetHidden(pathToUrl
, hidden
);
988 bool CFile::SetHidden(const CURL
& file
, bool hidden
)
992 CURL
url(URIUtils::SubstitutePath(file
));
994 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
995 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
997 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
1001 return pFile
->SetHidden(authUrl
, hidden
);
1005 CLog::Log(LOGERROR
, "{}({}) - Unhandled exception", __FUNCTION__
, file
.GetRedacted());
1010 int CFile::IoControl(EIoControl request
, void* param
)
1015 result
= m_pFile
->IoControl(request
, param
);
1017 if(result
== -1 && request
== IOCTRL_SEEK_POSSIBLE
)
1019 if(m_pFile
->GetLength() >= 0 && m_pFile
->Seek(0, SEEK_CUR
) >= 0)
1028 int CFile::GetChunkSize()
1031 return m_pFile
->GetChunkSize();
1035 const std::string
CFile::GetProperty(XFILE::FileProperty type
, const std::string
&name
) const
1039 return m_pFile
->GetProperty(type
, name
);
1042 const std::vector
<std::string
> CFile::GetPropertyValues(XFILE::FileProperty type
, const std::string
&name
) const
1046 return std::vector
<std::string
>();
1048 return m_pFile
->GetPropertyValues(type
, name
);
1051 ssize_t
CFile::LoadFile(const std::string
& filename
, std::vector
<uint8_t>& outputBuffer
)
1053 const CURL
pathToUrl(filename
);
1054 return LoadFile(pathToUrl
, outputBuffer
);
1057 ssize_t
CFile::LoadFile(const CURL
& file
, std::vector
<uint8_t>& outputBuffer
)
1060 static const size_t max_file_size
= 0x7FFFFFFF;
1061 static const size_t min_chunk_size
= 64 * 1024U;
1062 static const size_t max_chunk_size
= 2048 * 1024U;
1064 outputBuffer
.clear();
1066 if (!Open(file
, READ_TRUNCATED
))
1070 GetLength() will typically return values that fall into three cases:
1071 1. The real filesize. This is the typical case.
1072 2. Zero. This is the case for some http:// streams for example.
1073 3. Some value smaller than the real filesize. This is the case for an expanding file.
1075 In order to handle all three cases, we read the file in chunks, relying on Read()
1076 returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
1077 cases 1 and 3 is set to one byte larger than the value returned by GetLength().
1078 The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
1081 We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
1082 as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
1084 To minimize reallocation, we double the chunksize each read while chunksize is lower
1085 than max_chunk_size.
1087 int64_t filesize
= GetLength();
1088 if (filesize
> (int64_t)max_file_size
)
1089 return 0; /* file is too large for this function */
1091 size_t chunksize
= (filesize
> 0) ? static_cast<size_t>(filesize
+ 1)
1092 : static_cast<size_t>(DetermineChunkSize(GetChunkSize(),
1094 size_t total_read
= 0;
1097 if (total_read
== outputBuffer
.size())
1099 if (outputBuffer
.size() + chunksize
> max_file_size
)
1101 outputBuffer
.clear();
1104 outputBuffer
.resize(outputBuffer
.size() + chunksize
);
1105 if (chunksize
< max_chunk_size
)
1108 ssize_t read
= Read(outputBuffer
.data() + total_read
, outputBuffer
.size() - total_read
);
1111 outputBuffer
.clear();
1119 outputBuffer
.resize(total_read
);
1123 catch (const std::bad_alloc
&)
1125 outputBuffer
.clear();
1126 CLog::LogF(LOGERROR
, "Failed to load {}: out of memory", file
.GetFileName());
1130 double CFile::GetDownloadSpeed()
1133 return m_pFile
->GetDownloadSpeed();
1137 //*********************************************************************************************
1138 //*************** Stream IO for CFile objects *************************************************
1139 //*********************************************************************************************
1140 CFileStreamBuffer::~CFileStreamBuffer()
1146 CFileStreamBuffer::CFileStreamBuffer(int backsize
)
1150 , m_backsize(backsize
)
1154 void CFileStreamBuffer::Attach(IFile
*file
)
1158 m_frontsize
= CFile::DetermineChunkSize(m_file
->GetChunkSize(), 64 * 1024);
1160 m_buffer
= new char[m_frontsize
+m_backsize
];
1165 void CFileStreamBuffer::Detach()
1173 CFileStreamBuffer::int_type
CFileStreamBuffer::underflow()
1175 if(gptr() < egptr())
1176 return traits_type::to_int_type(*gptr());
1179 return traits_type::eof();
1181 size_t backsize
= 0;
1184 backsize
= (size_t)std::min
<ptrdiff_t>((ptrdiff_t)m_backsize
, egptr()-eback());
1185 memmove(m_buffer
, egptr()-backsize
, backsize
);
1188 ssize_t size
= m_file
->Read(m_buffer
+backsize
, m_frontsize
);
1191 return traits_type::eof();
1194 CLog::LogF(LOGWARNING
, "Error reading file - assuming eof");
1195 return traits_type::eof();
1198 setg(m_buffer
, m_buffer
+backsize
, m_buffer
+backsize
+size
);
1199 return traits_type::to_int_type(*gptr());
1202 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekoff(
1204 std::ios_base::seekdir way
,
1205 std::ios_base::openmode mode
)
1207 // calculate relative offset
1208 off_type aheadbytes
= (egptr() - gptr());
1209 off_type pos
= m_file
->GetPosition() - aheadbytes
;
1211 if(way
== std::ios_base::cur
)
1213 else if(way
== std::ios_base::beg
)
1214 offset2
= offset
- pos
;
1215 else if(way
== std::ios_base::end
)
1216 offset2
= offset
+ m_file
->GetLength() - pos
;
1218 return std::streampos(-1);
1220 // a non seek shouldn't modify our buffer
1224 // try to seek within buffer
1225 if(gptr()+offset2
>= eback() && gptr()+offset2
< egptr())
1228 return pos
+ offset2
;
1231 // reset our buffer pointer, will
1232 // start buffering on next read
1236 int64_t position
= -1;
1237 if(way
== std::ios_base::cur
)
1238 position
= m_file
->Seek(offset
- aheadbytes
, SEEK_CUR
);
1239 else if(way
== std::ios_base::end
)
1240 position
= m_file
->Seek(offset
, SEEK_END
);
1242 position
= m_file
->Seek(offset
, SEEK_SET
);
1245 return std::streampos(-1);
1250 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekpos(
1252 std::ios_base::openmode mode
)
1254 return seekoff(pos
, std::ios_base::beg
, mode
);
1257 std::streamsize
CFileStreamBuffer::showmanyc()
1260 return egptr() - gptr();
1263 CFileStream::CFileStream(int backsize
/*= 0*/) : std::istream(&m_buffer
), m_buffer(backsize
)
1267 CFileStream::~CFileStream()
1273 bool CFileStream::Open(const CURL
& filename
)
1277 CURL
url(URIUtils::SubstitutePath(filename
));
1278 m_file
.reset(CFileFactory::CreateLoader(url
));
1281 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
1282 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
1284 if(m_file
&& m_file
->Open(authUrl
))
1286 m_buffer
.Attach(m_file
.get());
1294 int64_t CFileStream::GetLength()
1296 return m_file
->GetLength();
1299 void CFileStream::Close()
1307 bool CFileStream::Open(const std::string
& filename
)
1309 const CURL
pathToUrl(filename
);
1310 return Open(pathToUrl
);