Index: src/libchcore/IFilesystem.h =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/IFilesystem.h (.../IFilesystem.h) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/IFilesystem.h (.../IFilesystem.h) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -57,7 +57,7 @@ virtual void FastMove(const TSmartPath& pathSource, const TSmartPath& pathDestination) = 0; virtual IFilesystemFindPtr CreateFinderObject(const TSmartPath& pathDir, const TSmartPath& pathMask) = 0; - virtual IFilesystemFilePtr CreateFileObject(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering) = 0; + virtual IFilesystemFilePtr CreateFileObject(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, bool bProtectReadOnlyFiles) = 0; virtual EPathsRelation GetPathsRelation(const TSmartPath& pathFirst, const TSmartPath& pathSecond) = 0; Index: src/libchcore/IFilesystemFile.h =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/IFilesystemFile.h (.../IFilesystemFile.h) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/IFilesystemFile.h (.../IFilesystemFile.h) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -43,21 +43,20 @@ public: virtual ~IFilesystemFile(); - virtual void CreateNewForWriting() = 0; - virtual void OpenExistingForWriting() = 0; - virtual void Truncate(file_size_t fsNewSize) = 0; virtual void ReadFile(TOverlappedDataBuffer& rBuffer) = 0; virtual void WriteFile(TOverlappedDataBuffer& rBuffer) = 0; virtual void FinalizeFile(TOverlappedDataBuffer& rBuffer) = 0; + virtual void Close() = 0; + virtual bool IsOpen() const = 0; + virtual bool IsFreshlyCreated() = 0; + virtual file_size_t GetFileSize() = 0; virtual void GetFileInfo(TFileInfo& tFileInfo) = 0; - virtual void Close() = 0; - virtual TSmartPath GetFilePath() const = 0; virtual file_size_t GetSeekPositionForResume(file_size_t fsLastAvailablePosition) = 0; }; Index: src/libchcore/TFilesystemFileFeedbackWrapper.cpp =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TFilesystemFileFeedbackWrapper.cpp (.../TFilesystemFileFeedbackWrapper.cpp) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TFilesystemFileFeedbackWrapper.cpp (.../TFilesystemFileFeedbackWrapper.cpp) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -43,219 +43,47 @@ throw TCoreException(eErr_InvalidArgument, L"spFilesystem is NULL", LOCATION); } - TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::OpenExistingDestinationFileFB(bool bProtectReadOnlyFiles) + TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::HandleFileAlreadyExistsFB(const TFileInfoPtr& spSrcFileInfo, bool& bShouldAppend, bool& bSkip) { - bool bRetry = false; - bool bAttributesChanged = false; + bSkip = false; + bShouldAppend = false; - m_spFile->Close(); + // read info about the existing destination file, + TFileInfo tDstFileInfo; + m_spFile->GetFileInfo(tDstFileInfo); - do + // src and dst files are the same + TFeedbackResult frResult = m_spFeedbackHandler->FileAlreadyExists(*spSrcFileInfo, tDstFileInfo); + switch(frResult.GetResult()) { - bRetry = false; + case EFeedbackResult::eResult_Overwrite: + bShouldAppend = false; + return TSubTaskBase::eSubResult_Continue; - DWORD dwLastError = ERROR_SUCCESS; - try - { - m_spFile->OpenExistingForWriting(); - return TSubTaskBase::eSubResult_Continue; - } - catch (const TFileException& e) - { - dwLastError = e.GetNativeError(); - } + case EFeedbackResult::eResult_CopyRest: + bShouldAppend = true; + return TSubTaskBase::eSubResult_Continue; - // when access is denied it might mean the read-only attribute prevents from opening file for writing; - // try to remove the attribute and retry (attributes are changed only once) - if(dwLastError == ERROR_ACCESS_DENIED && !bProtectReadOnlyFiles && !bAttributesChanged) - { - try - { - TFileInfoPtr spDstFileInfo(std::make_shared()); - m_spFilesystem->GetFileInfo(m_spFile->GetFilePath(), spDstFileInfo); + case EFeedbackResult::eResult_Skip: + bSkip = true; + return TSubTaskBase::eSubResult_Continue; - if(spDstFileInfo->IsReadOnly()) - { - m_spFilesystem->SetAttributes(m_spFile->GetFilePath(), spDstFileInfo->GetAttributes() & ~FILE_ATTRIBUTE_READONLY); - bRetry = true; - bAttributesChanged = true; - continue; - } - } - catch(const TFileException& e) - { - dwLastError = e.GetErrorCode(); - } - } - - TFeedbackResult frResult = m_spFeedbackHandler->FileError(m_spFile->GetFilePath().ToWString(), TString(), EFileError::eCreateError, dwLastError); - switch (frResult.GetResult()) - { - case EFeedbackResult::eResult_Retry: - { - // log - TString strFormat = _T("Retrying [error %errno] to open destination file %path (CustomCopyFileFB)"); - strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str()); - strFormat.Replace(_T("%path"), m_spFile->GetFilePath().ToString()); - LOG_ERROR(m_spLog) << strFormat.c_str(); - - bRetry = true; - break; - } - case EFeedbackResult::eResult_Cancel: - { - // log - TString strFormat = _T("Cancel request [error %errno] while opening destination file %path (CustomCopyFileFB)"); - strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str()); - strFormat.Replace(_T("%path"), m_spFile->GetFilePath().ToString()); - LOG_ERROR(m_spLog) << strFormat.c_str(); - - return TSubTaskBase::eSubResult_CancelRequest; - } - - case EFeedbackResult::eResult_Skip: - return TSubTaskBase::eSubResult_Continue; - - case EFeedbackResult::eResult_Pause: - return TSubTaskBase::eSubResult_PauseRequest; - - default: - BOOST_ASSERT(FALSE); // unknown result - throw TCoreException(eErr_UnhandledCase, L"Feedback result unknown", LOCATION); - } - - if(WasKillRequested(frResult)) - return TSubTaskBase::eSubResult_KillRequest; - } - while(bRetry); - - return TSubTaskBase::eSubResult_Continue; - } - - TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::OpenDestinationFileFB(const TFileInfoPtr& spSrcFileInfo, - unsigned long long& ullSeekTo, - bool& bFreshlyCreated, - bool& bSkip, - bool bProtectReadOnlyFiles) - { - bool bRetry = false; - bSkip = false; - - ullSeekTo = 0; - bFreshlyCreated = true; - - m_spFile->Close(); - do + case EFeedbackResult::eResult_Cancel: { - bRetry = false; + // log + TString strFormat = _T("Cancel request while checking result of dialog before opening source file %path (CustomCopyFileFB)"); + strFormat.Replace(_T("%path"), m_spFile->GetFilePath().ToString()); + LOG_INFO(m_spLog) << strFormat.c_str(); - DWORD dwLastError = ERROR_SUCCESS; - try - { - m_spFile->CreateNewForWriting(); - return TSubTaskBase::eSubResult_Continue; - } - catch (const TFileException& e) - { - dwLastError = e.GetNativeError(); - } - - if (dwLastError == ERROR_FILE_EXISTS) - { - bFreshlyCreated = false; - - // pass it to the specialized method - TSubTaskBase::ESubOperationResult eResult = OpenExistingDestinationFileFB(bProtectReadOnlyFiles); - if (eResult != TSubTaskBase::eSubResult_Continue) - return eResult; - else if (!m_spFile->IsOpen()) - { - bSkip = true; - return TSubTaskBase::eSubResult_Continue; - } - - // read info about the existing destination file, - TFileInfoPtr spDstFileInfo(std::make_shared()); - m_spFile->GetFileInfo(*spDstFileInfo); - - // src and dst files are the same - TFeedbackResult frResult = m_spFeedbackHandler->FileAlreadyExists(*spSrcFileInfo, *spDstFileInfo); - switch (frResult.GetResult()) - { - case EFeedbackResult::eResult_Overwrite: - ullSeekTo = 0; - return TSubTaskBase::eSubResult_Continue; - - case EFeedbackResult::eResult_CopyRest: - ullSeekTo = spDstFileInfo->GetLength64(); - return TSubTaskBase::eSubResult_Continue; - - case EFeedbackResult::eResult_Skip: - bSkip = true; - return TSubTaskBase::eSubResult_Continue; - - case EFeedbackResult::eResult_Cancel: - { - // log - TString strFormat = _T("Cancel request while checking result of dialog before opening source file %path (CustomCopyFileFB)"); - strFormat.Replace(_T("%path"), m_spFile->GetFilePath().ToString()); - LOG_INFO(m_spLog) << strFormat.c_str(); - - return TSubTaskBase::eSubResult_CancelRequest; - } - case EFeedbackResult::eResult_Pause: - return TSubTaskBase::eSubResult_PauseRequest; - - default: - BOOST_ASSERT(FALSE); // unknown result - throw TCoreException(eErr_UnhandledCase, L"Feedback result unknown", LOCATION); - } - } - - TFeedbackResult frResult = m_spFeedbackHandler->FileError(m_spFile->GetFilePath().ToWString(), TString(), EFileError::eCreateError, dwLastError); - switch (frResult.GetResult()) - { - case EFeedbackResult::eResult_Retry: - { - // log - TString strFormat = _T("Retrying [error %errno] to open destination file %path (CustomCopyFileFB)"); - strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str()); - strFormat.Replace(_T("%path"), m_spFile->GetFilePath().ToString()); - LOG_ERROR(m_spLog) << strFormat.c_str(); - - bRetry = true; - - break; - } - case EFeedbackResult::eResult_Cancel: - { - // log - TString strFormat = _T("Cancel request [error %errno] while opening destination file %path (CustomCopyFileFB)"); - strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str()); - strFormat.Replace(_T("%path"), m_spFile->GetFilePath().ToString()); - LOG_ERROR(m_spLog) << strFormat.c_str(); - - return TSubTaskBase::eSubResult_CancelRequest; - } - - case EFeedbackResult::eResult_Skip: - bSkip = true; - return TSubTaskBase::eSubResult_Continue; - - case EFeedbackResult::eResult_Pause: - return TSubTaskBase::eSubResult_PauseRequest; - - default: - BOOST_ASSERT(FALSE); // unknown result - throw TCoreException(eErr_UnhandledCase, L"Feedback result unknown", LOCATION); - } - - if(WasKillRequested(frResult)) - return TSubTaskBase::eSubResult_KillRequest; + return TSubTaskBase::eSubResult_CancelRequest; } - while(bRetry); + case EFeedbackResult::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; - return TSubTaskBase::eSubResult_Continue; + default: + BOOST_ASSERT(FALSE); // unknown result + throw TCoreException(eErr_UnhandledCase, L"Feedback result unknown", LOCATION); + } } TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::TruncateFileFB(file_size_t fsNewSize, bool& bSkip) Index: src/libchcore/TFilesystemFileFeedbackWrapper.h =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TFilesystemFileFeedbackWrapper.h (.../TFilesystemFileFeedbackWrapper.h) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TFilesystemFileFeedbackWrapper.h (.../TFilesystemFileFeedbackWrapper.h) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -37,9 +37,7 @@ const IFilesystemPtr& spFilesystem); TFilesystemFileFeedbackWrapper& operator=(const TFilesystemFileFeedbackWrapper&) = delete; - TSubTaskBase::ESubOperationResult OpenExistingDestinationFileFB(bool bProtectReadOnlyFiles); - TSubTaskBase::ESubOperationResult OpenDestinationFileFB(const TFileInfoPtr& spSrcFileInfo, - unsigned long long& ullSeekTo, bool& bFreshlyCreated, bool& bSkip, bool bProtectReadOnlyFiles); + TSubTaskBase::ESubOperationResult HandleFileAlreadyExistsFB(const TFileInfoPtr& spSrcFileInfo, bool& bShouldAppend, bool& bSkip); TSubTaskBase::ESubOperationResult TruncateFileFB(file_size_t fsNewSize, bool& bSkip); @@ -51,6 +49,7 @@ TSubTaskBase::ESubOperationResult HandleReadError(TOverlappedDataBuffer& rBuffer, bool& bSkip); TSubTaskBase::ESubOperationResult HandleWriteError(TOverlappedDataBuffer& rBuffer, bool& bSkip); + bool IsFreshlyCreated() const { return m_spFile->IsFreshlyCreated(); } TSmartPath GetFilePath() const { return m_spFile->GetFilePath(); } file_size_t GetFileSize() const { return m_spFile->GetFileSize(); } file_size_t GetSeekPositionForResume(file_size_t fsLastAvailablePosition) { return m_spFile->GetSeekPositionForResume(fsLastAvailablePosition); } Index: src/libchcore/TLocalFilesystem.cpp =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TLocalFilesystem.cpp (.../TLocalFilesystem.cpp) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TLocalFilesystem.cpp (.../TLocalFilesystem.cpp) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -320,10 +320,10 @@ return std::shared_ptr(new TLocalFilesystemFind(pathDir, pathMask, m_spLog->GetLogFileData())); } - IFilesystemFilePtr TLocalFilesystem::CreateFileObject(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering) + IFilesystemFilePtr TLocalFilesystem::CreateFileObject(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, bool bProtectReadOnlyFiles) { - LOG_TRACE(m_spLog) << L"Creating file object for path " << pathFile << L" with no-buffering set to " << bNoBuffering; - return std::shared_ptr(new TLocalFilesystemFile(eMode, pathFile, bNoBuffering, m_spLog->GetLogFileData())); + LOG_TRACE(m_spLog) << L"Creating file object for path " << pathFile << L" with no-buffering set to " << bNoBuffering << L" and protect-read-only set to " << bProtectReadOnlyFiles; + return std::shared_ptr(new TLocalFilesystemFile(eMode, pathFile, bNoBuffering, bProtectReadOnlyFiles, m_spLog->GetLogFileData())); } TSmartPath TLocalFilesystem::PrependPathExtensionIfNeeded(const TSmartPath& pathInput) Index: src/libchcore/TLocalFilesystem.h =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TLocalFilesystem.h (.../TLocalFilesystem.h) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TLocalFilesystem.h (.../TLocalFilesystem.h) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -58,7 +58,7 @@ virtual void FastMove(const TSmartPath& pathSource, const TSmartPath& pathDestination) override; virtual IFilesystemFindPtr CreateFinderObject(const TSmartPath& pathDir, const TSmartPath& pathMask) override; - virtual IFilesystemFilePtr CreateFileObject(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering) override; + virtual IFilesystemFilePtr CreateFileObject(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, bool bProtectReadOnlyFiles) override; virtual EPathsRelation GetPathsRelation(const TSmartPath& pathFirst, const TSmartPath& pathSecond) override; Index: src/libchcore/TLocalFilesystemFile.cpp =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TLocalFilesystemFile.cpp (.../TLocalFilesystemFile.cpp) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TLocalFilesystemFile.cpp (.../TLocalFilesystemFile.cpp) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -35,10 +35,11 @@ // compile-time check - ensure the buffer granularity used for transfers are bigger than expected sector size static_assert(TLocalFilesystemFile::MaxSectorSize <= TBufferSizes::BufferGranularity, "Buffer granularity must be equal to or bigger than the max sector size"); - TLocalFilesystemFile::TLocalFilesystemFile(EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, const logger::TLogFileDataPtr& spLogFileData) : + TLocalFilesystemFile::TLocalFilesystemFile(EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, bool bProtectReadOnlyFiles, const logger::TLogFileDataPtr& spLogFileData) : m_pathFile(TLocalFilesystem::PrependPathExtensionIfNeeded(pathFile)), m_hFile(INVALID_HANDLE_VALUE), m_eMode(eMode), + m_bProtectReadOnlyFiles(bProtectReadOnlyFiles), m_bNoBuffering(bNoBuffering), m_spLog(logger::MakeLogger(spLogFileData, L"Filesystem-File")) { @@ -68,20 +69,14 @@ return; if(m_eMode == eMode_Read) - OpenExistingForReading(); + OpenFileForReading(); else - { - if(!IsOpen()) - { - LOG_ERROR(m_spLog) << L"File not open" << GetFileInfoForLog(m_bNoBuffering); - throw TFileException(eErr_FileNotOpen, ERROR_INVALID_HANDLE, m_pathFile, L"File not open yet. Cannot truncate.", LOCATION); - } - } + OpenFileForWriting(); } - void TLocalFilesystemFile::OpenExistingForReading() + void TLocalFilesystemFile::OpenFileForReading() { - LOG_DEBUG(m_spLog) << L"Opening existing file for reading" << GetFileInfoForLog(m_bNoBuffering); + LOG_DEBUG(m_spLog) << L"Opening file for reading" << GetFileInfoForLog(m_bNoBuffering); m_hFile = ::CreateFile(m_pathFile.ToString(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, GetFlagsAndAttributes(m_bNoBuffering), nullptr); if (m_hFile == INVALID_HANDLE_VALUE) @@ -95,25 +90,56 @@ LOG_DEBUG(m_spLog) << "Opening file for reading succeeded. New handle: " << m_hFile << GetFileInfoForLog(m_bNoBuffering); } - void TLocalFilesystemFile::CreateNewForWriting() + void TLocalFilesystemFile::OpenFileForWriting() { Close(); - LOG_DEBUG(m_spLog) << "Creating new file for writing" << GetFileInfoForLog(m_bNoBuffering); + LOG_DEBUG(m_spLog) << L"Opening file for writing" << GetFileInfoForLog(m_bNoBuffering); - m_hFile = ::CreateFile(m_pathFile.ToString(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_NEW, GetFlagsAndAttributes(m_bNoBuffering), nullptr); - if (m_hFile == INVALID_HANDLE_VALUE) + bool bAttributesChanged = false; + + do { + m_hFile = ::CreateFile(m_pathFile.ToString(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, GetFlagsAndAttributes(m_bNoBuffering), nullptr); DWORD dwLastError = GetLastError(); - LOG_ERROR(m_spLog) << "CreateNewForWriting failed with error: " << dwLastError << GetFileInfoForLog(m_bNoBuffering); - throw TFileException(eErr_CannotOpenFile, dwLastError, m_pathFile, L"Cannot create file.", LOCATION); + if(m_hFile == INVALID_HANDLE_VALUE) + { + // failed + if(dwLastError == ERROR_ACCESS_DENIED && !m_bProtectReadOnlyFiles && !bAttributesChanged) + { + // handle read-only files + DWORD dwAttributes = GetFileAttributes(m_pathFile.ToString()); + if(dwAttributes == INVALID_FILE_ATTRIBUTES) + { + LOG_ERROR(m_spLog) << "Retrieving file attributes failed while opening file for writing. Error: " << dwLastError << GetFileInfoForLog(m_bNoBuffering); + throw TFileException(eErr_CannotOpenFile, dwLastError, m_pathFile, L"Cannot retrieve file attributes.", LOCATION); + } + + if(dwAttributes & FILE_ATTRIBUTE_READONLY) + { + if(!SetFileAttributes(m_pathFile.ToString(), dwAttributes & ~FILE_ATTRIBUTE_READONLY)) + { + LOG_ERROR(m_spLog) << "Error while trying to reset read-only attribute. Error: " << dwLastError << GetFileInfoForLog(m_bNoBuffering); + throw TFileException(eErr_CannotOpenFile, dwLastError, m_pathFile, L"Cannot reset read-only attribute.", LOCATION); + } + + bAttributesChanged = true; + continue; + } + } + + // all other errors + LOG_ERROR(m_spLog) << "Encountered an error while opening file for writing. Error: " << dwLastError << GetFileInfoForLog(m_bNoBuffering); + throw TFileException(eErr_CannotOpenFile, dwLastError, m_pathFile, L"Cannot open file.", LOCATION); + } + + // succeeded + m_bFreshlyCreated = !(dwLastError == ERROR_ALREADY_EXISTS); + break; } - LOG_DEBUG(m_spLog) << "CreateNewForWriting succeeded. New handle: " << m_hFile << GetFileInfoForLog(m_bNoBuffering); - } + while(bAttributesChanged); - void TLocalFilesystemFile::OpenExistingForWriting() - { - OpenExistingForWriting(m_bNoBuffering); + LOG_DEBUG(m_spLog) << "Opening file for writing succeeded. New handle: " << m_hFile << GetFileInfoForLog(m_bNoBuffering); } void TLocalFilesystemFile::OpenExistingForWriting(bool bNoBuffering) @@ -299,6 +325,17 @@ } } + bool TLocalFilesystemFile::IsOpen() const + { + return m_hFile != INVALID_HANDLE_VALUE; + } + + bool TLocalFilesystemFile::IsFreshlyCreated() + { + EnsureOpen(); + return m_bFreshlyCreated; + } + void TLocalFilesystemFile::InternalClose() { if (m_hFile != INVALID_HANDLE_VALUE) Index: src/libchcore/TLocalFilesystemFile.h =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TLocalFilesystemFile.h (.../TLocalFilesystemFile.h) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TLocalFilesystemFile.h (.../TLocalFilesystemFile.h) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -34,16 +34,15 @@ public: virtual ~TLocalFilesystemFile(); - virtual void CreateNewForWriting() override; - virtual void OpenExistingForWriting() override; - virtual void Truncate(file_size_t fsNewSize) override; virtual void ReadFile(TOverlappedDataBuffer& rBuffer) override; virtual void WriteFile(TOverlappedDataBuffer& rBuffer) override; virtual void FinalizeFile(TOverlappedDataBuffer& rBuffer) override; - virtual bool IsOpen() const override { return m_hFile != INVALID_HANDLE_VALUE; } + virtual bool IsOpen() const override; + virtual bool IsFreshlyCreated() override; + virtual file_size_t GetFileSize() override; virtual void GetFileInfo(TFileInfo& tFileInfo) override; @@ -53,24 +52,30 @@ virtual file_size_t GetSeekPositionForResume(file_size_t fsLastAvailablePosition) override; private: - TLocalFilesystemFile(EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, const logger::TLogFileDataPtr& spLogFileData); + TLocalFilesystemFile(EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, bool bProtectReadOnlyFiles, const logger::TLogFileDataPtr& spLogFileData); void EnsureOpen(); - void OpenExistingForReading(); + void OpenFileForReading(); + void OpenFileForWriting(); - DWORD GetFlagsAndAttributes(bool bNoBuffering) const; void OpenExistingForWriting(bool bNoBuffering); + DWORD GetFlagsAndAttributes(bool bNoBuffering) const; + void InternalClose(); std::wstring TLocalFilesystemFile::GetFileInfoForLog(bool bNoBuffering) const; private: TSmartPath m_pathFile; - HANDLE m_hFile; - EOpenMode m_eMode; - bool m_bNoBuffering; + HANDLE m_hFile = INVALID_HANDLE_VALUE; + EOpenMode m_eMode = eMode_Read; + bool m_bProtectReadOnlyFiles = false; + bool m_bNoBuffering = false; + + bool m_bFreshlyCreated = false; + #pragma warning(push) #pragma warning(disable: 4251) logger::TLoggerPtr m_spLog; Index: src/libchcore/TSubTaskCopyMove.cpp =================================================================== diff -u -r6b927672a652279a203f6465ead20ffb9fe6bde1 -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/TSubTaskCopyMove.cpp (.../TSubTaskCopyMove.cpp) (revision 6b927672a652279a203f6465ead20ffb9fe6bde1) +++ src/libchcore/TSubTaskCopyMove.cpp (.../TSubTaskCopyMove.cpp) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -348,8 +348,8 @@ bool bNoBuffer = (GetTaskPropValue(rConfig) && pData->spSrcFile->GetLength64() >= GetTaskPropValue(rConfig)); - IFilesystemFilePtr fileSrc = spFilesystem->CreateFileObject(IFilesystemFile::eMode_Read, pData->spSrcFile->GetFullFilePath(), bNoBuffer); - IFilesystemFilePtr fileDst = spFilesystem->CreateFileObject(IFilesystemFile::eMode_Write, pData->pathDstFile, bNoBuffer); + IFilesystemFilePtr fileSrc = spFilesystem->CreateFileObject(IFilesystemFile::eMode_Read, pData->spSrcFile->GetFullFilePath(), bNoBuffer, GetTaskPropValue(rConfig)); + IFilesystemFilePtr fileDst = spFilesystem->CreateFileObject(IFilesystemFile::eMode_Write, pData->pathDstFile, bNoBuffer, GetTaskPropValue(rConfig)); TFilesystemFileFeedbackWrapperPtr spSrcFileWrapper(std::make_shared(fileSrc, spFeedbackHandler, GetContext().GetLogFileData(), rThreadController, spFilesystem)); TFilesystemFileFeedbackWrapperPtr spDstFileWrapper(std::make_shared(fileDst, spFeedbackHandler, GetContext().GetLogFileData(), rThreadController, spFilesystem)); @@ -393,93 +393,68 @@ TSubTaskCopyMove::ESubOperationResult TSubTaskCopyMove::OpenSrcAndDstFilesFB(TFilesystemFileFeedbackWrapper& rSrcFile, TFilesystemFileFeedbackWrapper& rDstFile, CUSTOM_COPY_PARAMS* pData, bool& bSkip) { - const TConfig& rConfig = GetContext().GetConfig(); IFilesystemPtr spFilesystem = GetContext().GetLocalFilesystem(); bSkip = false; - unsigned long long ullProcessedSize = m_spSubTaskStats->GetCurrentItemProcessedSize(); - // first open the source file and handle any failures - TSubTaskCopyMove::ESubOperationResult eResult = eSubResult_Continue; + ESubOperationResult eResult = eSubResult_Continue; // update the source file size (it might differ from the time this file was originally scanned). // NOTE: this kind of update could be also done when copying chunks of data beyond the original end-of-file, // but it would require frequent total size updates and thus - serializations). // NOTE2: the by-chunk corrections of stats are still applied when copying to ensure even further size // matching; this update however still allows for better serialization management. - file_size_t fsNewSize = rSrcFile.GetFileSize(); file_size_t fsOldSize = pData->spSrcFile->GetLength64(); + file_size_t fsNewSize = rSrcFile.GetFileSize(); if(fsNewSize != fsOldSize) { m_spSubTaskStats->AdjustTotalSize(fsOldSize, fsNewSize); pData->spSrcFile->SetLength64(fsNewSize); } // open destination file, handle the failures and possibly existence of the destination file + unsigned long long ullProcessedSize = m_spSubTaskStats->GetCurrentItemProcessedSize(); unsigned long long ullSeekTo = ullProcessedSize; - bool bDstFileFreshlyCreated = false; + bool bDstFileFreshlyCreated = rDstFile.IsFreshlyCreated(); + unsigned long long ullDstFileSize = rDstFile.GetFileSize(); + // try to resume if possible - bool bResumeSucceeded = false; + bool bCanSilentResume = false; if (m_spSubTaskStats->CanCurrentItemSilentResume()) { - bool bContinue = true; - TFileInfoPtr spDstFileInfo(std::make_shared()); - // verify that the file qualifies for silent resume - try + if(ullDstFileSize == ullProcessedSize && ullDstFileSize >= fsNewSize) { - spFilesystem->GetFileInfo(rDstFile.GetFilePath(), spDstFileInfo); + ullSeekTo = ullDstFileSize; + bCanSilentResume = true; } - catch(const TFileException&) - { - bContinue = false; - } - - if (bContinue && spDstFileInfo->GetLength64() != ullProcessedSize) - bContinue = false; - - // we are resuming previous operation - if(bContinue) - { - eResult = rDstFile.OpenExistingDestinationFileFB(GetTaskPropValue(rConfig)); - if (eResult != TSubTaskBase::eSubResult_Continue) - return eResult; - else if (!rDstFile.IsOpen()) - { - AdjustProcessedSizeForSkip(pData->spSrcFile); - - pData->bProcessed = false; - bSkip = true; - return TSubTaskBase::eSubResult_Continue; - } - - bResumeSucceeded = true; - } } - if(!bResumeSucceeded) + if(!bCanSilentResume && !bDstFileFreshlyCreated && ullDstFileSize > 0) { - // open destination file for case, when we start operation on this file (i.e. it is not resume of the - // old operation) - eResult = rDstFile.OpenDestinationFileFB(pData->spSrcFile, ullSeekTo, bDstFileFreshlyCreated, bSkip, GetTaskPropValue(rConfig)); - if(eResult != TSubTaskBase::eSubResult_Continue) + bool bShouldAppend = false; + eResult = rDstFile.HandleFileAlreadyExistsFB(pData->spSrcFile, bShouldAppend, bSkip); + if(eResult != eSubResult_Continue) return eResult; else if(bSkip) { - AdjustProcessedSizeForSkip(pData->spSrcFile); - pData->bProcessed = false; - return TSubTaskBase::eSubResult_Continue; + return eSubResult_Continue; } + + if(bShouldAppend) + ullSeekTo = std::min(ullDstFileSize, fsNewSize); + else + ullSeekTo = 0; } if(pData->bOnlyCreate) { // we don't copy contents, but need to increase processed size AdjustProcessedSizeForSkip(pData->spSrcFile); - return TSubTaskBase::eSubResult_Continue; + return eSubResult_Continue; } // ullSeekTo contains the seek position in destination file; in case the destination is already Index: src/libchcore/Tests/TestsTDestinationPathProvider.cpp =================================================================== diff -u -r7e0900602b9ab2e8a8f95d613f062c79d86f416c -r789d0908abf8db57e27cfeac7045d9962f4b522a --- src/libchcore/Tests/TestsTDestinationPathProvider.cpp (.../TestsTDestinationPathProvider.cpp) (revision 7e0900602b9ab2e8a8f95d613f062c79d86f416c) +++ src/libchcore/Tests/TestsTDestinationPathProvider.cpp (.../TestsTDestinationPathProvider.cpp) (revision 789d0908abf8db57e27cfeac7045d9962f4b522a) @@ -23,7 +23,7 @@ MOCK_METHOD2(FastMove, void(const TSmartPath& pathSource, const TSmartPath& pathDestination)); MOCK_METHOD2(CreateFinderObject, IFilesystemFindPtr(const TSmartPath& pathDir, const TSmartPath& pathMask)); - MOCK_METHOD3(CreateFileObject, IFilesystemFilePtr(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering)); + MOCK_METHOD4(CreateFileObject, IFilesystemFilePtr(IFilesystemFile::EOpenMode eMode, const TSmartPath& pathFile, bool bNoBuffering, bool bProtectReadOnlyFiles)); MOCK_METHOD2(GetPathsRelation, EPathsRelation(const TSmartPath& pathFirst, const TSmartPath& pathSecond));