Index: src/libchcore/TFilesystemFeedbackWrapper.cpp =================================================================== diff -u -r89f857792bba8752de98ddd477949e45cef5ba5a -rd3b1a109f2ace158e828715205592615d6e33fd0 --- src/libchcore/TFilesystemFeedbackWrapper.cpp (.../TFilesystemFeedbackWrapper.cpp) (revision 89f857792bba8752de98ddd477949e45cef5ba5a) +++ src/libchcore/TFilesystemFeedbackWrapper.cpp (.../TFilesystemFeedbackWrapper.cpp) (revision d3b1a109f2ace158e828715205592615d6e33fd0) @@ -197,21 +197,14 @@ do { bRetry = false; + DWORD dwLastError = ERROR_SUCCESS; - // #bug - changing read-only attribute should only be done when user explicitly requests it (via feedback response) - // for now it is ignored try { - if (!bProtectReadOnlyFiles) - m_spFilesystem->SetAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY); - } - catch (const TFileException&) - { - } + DWORD dwAttributes = spFileInfo->GetAttributes(); + if((dwAttributes & FILE_ATTRIBUTE_READONLY) && !bProtectReadOnlyFiles) + m_spFilesystem->SetAttributes(spFileInfo->GetFullFilePath(), dwAttributes & ~FILE_ATTRIBUTE_READONLY); - DWORD dwLastError = ERROR_SUCCESS; - try - { m_spFilesystem->RemoveDirectory(spFileInfo->GetFullFilePath()); return TSubTaskBase::eSubResult_Continue; } @@ -220,11 +213,11 @@ dwLastError = e.GetNativeError(); } - if (dwLastError == ERROR_PATH_NOT_FOUND || dwLastError == ERROR_FILE_NOT_FOUND) + if(dwLastError == ERROR_PATH_NOT_FOUND || dwLastError == ERROR_FILE_NOT_FOUND) return TSubTaskBase::eSubResult_Continue; // log - TString strFormat = _T("Error #%errno while deleting file/folder %path"); + TString strFormat = _T("Error #%errno while deleting folder %path"); strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str()); strFormat.Replace(_T("%path"), spFileInfo->GetFullFilePath().ToString()); m_rLog.loge(strFormat.c_str()); @@ -266,20 +259,13 @@ { bRetry = false; - // #bug - changing read-only attribute should only be done when user explicitly requests it (via feedback response) - // for now it is ignored - try - { - if (!bProtectReadOnlyFiles) - m_spFilesystem->SetAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY); - } - catch (const TFileException&) - { - } - DWORD dwLastError = ERROR_SUCCESS; try { + DWORD dwAttributes = spFileInfo->GetAttributes(); + if((dwAttributes & FILE_ATTRIBUTE_READONLY) && !bProtectReadOnlyFiles) + m_spFilesystem->SetAttributes(spFileInfo->GetFullFilePath(), dwAttributes & ~FILE_ATTRIBUTE_READONLY); + m_spFilesystem->DeleteFile(spFileInfo->GetFullFilePath()); return TSubTaskBase::eSubResult_Continue; } @@ -292,7 +278,7 @@ return TSubTaskBase::eSubResult_Continue; // log - TString strFormat = _T("Error #%errno while deleting file/folder %path"); + TString strFormat = _T("Error #%errno while deleting file %path"); strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str()); strFormat.Replace(_T("%path"), spFileInfo->GetFullFilePath().ToString()); m_rLog.loge(strFormat.c_str()); Index: src/libchcore/TFilesystemFileFeedbackWrapper.cpp =================================================================== diff -u -r89f857792bba8752de98ddd477949e45cef5ba5a -rd3b1a109f2ace158e828715205592615d6e33fd0 --- src/libchcore/TFilesystemFileFeedbackWrapper.cpp (.../TFilesystemFileFeedbackWrapper.cpp) (revision 89f857792bba8752de98ddd477949e45cef5ba5a) +++ src/libchcore/TFilesystemFileFeedbackWrapper.cpp (.../TFilesystemFileFeedbackWrapper.cpp) (revision d3b1a109f2ace158e828715205592615d6e33fd0) @@ -26,11 +26,14 @@ namespace chcore { - TFilesystemFileFeedbackWrapper::TFilesystemFileFeedbackWrapper(const IFeedbackHandlerPtr& spFeedbackHandler, icpf::log_file& rLog, TWorkerThreadController& rThreadController) : + TFilesystemFileFeedbackWrapper::TFilesystemFileFeedbackWrapper(const IFeedbackHandlerPtr& spFeedbackHandler, icpf::log_file& rLog, TWorkerThreadController& rThreadController, const IFilesystemPtr& spFilesystem) : m_spFeedbackHandler(spFeedbackHandler), m_rLog(rLog), - m_rThreadController(rThreadController) + m_rThreadController(rThreadController), + m_spFilesystem(spFilesystem) { + if(!spFeedbackHandler || !spFilesystem) + THROW_CORE_EXCEPTION_MSG(eErr_InvalidArgument, L"Missing filesystem or feedback handler"); } TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::OpenSourceFileFB(const IFilesystemFilePtr& fileSrc) @@ -100,9 +103,10 @@ return TSubTaskBase::eSubResult_Continue; } - TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::OpenExistingDestinationFileFB(const IFilesystemFilePtr& fileDst) + TSubTaskBase::ESubOperationResult TFilesystemFileFeedbackWrapper::OpenExistingDestinationFileFB(const IFilesystemFilePtr& fileDst, bool bProtectReadOnlyFiles) { bool bRetry = false; + bool bAttributesChanged = false; fileDst->Close(); @@ -121,6 +125,29 @@ dwLastError = e.GetNativeError(); } + // 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(boost::make_shared()); + m_spFilesystem->GetFileInfo(fileDst->GetFilePath(), spDstFileInfo); + + if(spDstFileInfo->IsReadOnly()) + { + m_spFilesystem->SetAttributes(fileDst->GetFilePath(), spDstFileInfo->GetAttributes() & ~FILE_ATTRIBUTE_READONLY); + bRetry = true; + bAttributesChanged = true; + continue; + } + } + catch(const TFileException& e) + { + dwLastError = e.GetErrorCode(); + } + } + TFeedbackResult frResult = m_spFeedbackHandler->FileError(fileDst->GetFilePath().ToWString(), TString(), EFileError::eCreateError, dwLastError); switch (frResult.GetResult()) { @@ -169,7 +196,8 @@ const TFileInfoPtr& spSrcFileInfo, unsigned long long& ullSeekTo, bool& bFreshlyCreated, - bool& bSkip) + bool& bSkip, + bool bProtectReadOnlyFiles) { bool bRetry = false; bSkip = false; @@ -198,7 +226,7 @@ bFreshlyCreated = false; // pass it to the specialized method - TSubTaskBase::ESubOperationResult eResult = OpenExistingDestinationFileFB(fileDst); + TSubTaskBase::ESubOperationResult eResult = OpenExistingDestinationFileFB(fileDst, bProtectReadOnlyFiles); if (eResult != TSubTaskBase::eSubResult_Continue) return eResult; else if (!fileDst->IsOpen()) Index: src/libchcore/TFilesystemFileFeedbackWrapper.h =================================================================== diff -u -rbfc7a8378a96c5b58def559b343918fca32f05a6 -rd3b1a109f2ace158e828715205592615d6e33fd0 --- src/libchcore/TFilesystemFileFeedbackWrapper.h (.../TFilesystemFileFeedbackWrapper.h) (revision bfc7a8378a96c5b58def559b343918fca32f05a6) +++ src/libchcore/TFilesystemFileFeedbackWrapper.h (.../TFilesystemFileFeedbackWrapper.h) (revision d3b1a109f2ace158e828715205592615d6e33fd0) @@ -33,13 +33,13 @@ class TFilesystemFileFeedbackWrapper { public: - TFilesystemFileFeedbackWrapper(const IFeedbackHandlerPtr& spFeedbackHandler, icpf::log_file& rLog, TWorkerThreadController& rThreadController); + TFilesystemFileFeedbackWrapper(const IFeedbackHandlerPtr& spFeedbackHandler, icpf::log_file& rLog, TWorkerThreadController& rThreadController, const IFilesystemPtr& spFilesystem); TFilesystemFileFeedbackWrapper& operator=(const TFilesystemFileFeedbackWrapper&) = delete; TSubTaskBase::ESubOperationResult OpenSourceFileFB(const IFilesystemFilePtr& fileSrc); - TSubTaskBase::ESubOperationResult OpenExistingDestinationFileFB(const IFilesystemFilePtr& fileDst); + TSubTaskBase::ESubOperationResult OpenExistingDestinationFileFB(const IFilesystemFilePtr& fileDst, bool bProtectReadOnlyFiles); TSubTaskBase::ESubOperationResult OpenDestinationFileFB(const IFilesystemFilePtr& fileDst, const TFileInfoPtr& spSrcFileInfo, - unsigned long long& ullSeekTo, bool& bFreshlyCreated, bool& bSkip); + unsigned long long& ullSeekTo, bool& bFreshlyCreated, bool& bSkip, bool bProtectReadOnlyFiles); TSubTaskBase::ESubOperationResult TruncateFileFB(const IFilesystemFilePtr& spFile, file_size_t fsNewSize, const TSmartPath& pathFile, bool& bSkip); Index: src/libchcore/TSubTaskCopyMove.cpp =================================================================== diff -u -r89f857792bba8752de98ddd477949e45cef5ba5a -rd3b1a109f2ace158e828715205592615d6e33fd0 --- src/libchcore/TSubTaskCopyMove.cpp (.../TSubTaskCopyMove.cpp) (revision 89f857792bba8752de98ddd477949e45cef5ba5a) +++ src/libchcore/TSubTaskCopyMove.cpp (.../TSubTaskCopyMove.cpp) (revision d3b1a109f2ace158e828715205592615d6e33fd0) @@ -280,7 +280,7 @@ const TConfig& rConfig = GetContext().GetConfig(); IFilesystemPtr spFilesystem = GetContext().GetLocalFilesystem(); - TFilesystemFileFeedbackWrapper tFileFBWrapper(spFeedbackHandler, rLog, rThreadController); + TFilesystemFileFeedbackWrapper tFileFBWrapper(spFeedbackHandler, rLog, rThreadController, spFilesystem); TString strFormat; TSubTaskBase::ESubOperationResult eResult = TSubTaskBase::eSubResult_Continue; @@ -580,21 +580,6 @@ pData->spSrcFile->SetLength64(fsNewSize); } - // change attributes of a dest file - // NOTE: probably should be removed from here and report problems with read-only files - // directly to the user (as feedback request) - if(!GetTaskPropValue(rConfig)) - { - try - { - spFilesystem->SetAttributes(pData->pathDstFile, FILE_ATTRIBUTE_NORMAL); - } - catch (const TFileException&) - { - // currently ignore that problem; this should be handled with user feedback - } - } - // open destination file, handle the failures and possibly existence of the destination file unsigned long long ullSeekTo = ullProcessedSize; bool bDstFileFreshlyCreated = false; @@ -604,13 +589,13 @@ if (m_tSubTaskStats.CanCurrentItemSilentResume()) { bool bContinue = true; - // verify that the file qualifies for silent resume TFileInfoPtr spDstFileInfo(boost::make_shared()); + // verify that the file qualifies for silent resume try { spFilesystem->GetFileInfo(spFileDst->GetFilePath(), spDstFileInfo); } - catch (const TFileException&) + catch(const TFileException&) { bContinue = false; } @@ -621,7 +606,7 @@ // we are resuming previous operation if(bContinue) { - eResult = rFileFBWrapper.OpenExistingDestinationFileFB(spFileDst); + eResult = rFileFBWrapper.OpenExistingDestinationFileFB(spFileDst, GetTaskPropValue(rConfig)); if (eResult != TSubTaskBase::eSubResult_Continue) return eResult; else if (!spFileDst->IsOpen()) @@ -641,7 +626,7 @@ { // open destination file for case, when we start operation on this file (i.e. it is not resume of the // old operation) - eResult = rFileFBWrapper.OpenDestinationFileFB(spFileDst, pData->spSrcFile, ullSeekTo, bDstFileFreshlyCreated, bSkip); + eResult = rFileFBWrapper.OpenDestinationFileFB(spFileDst, pData->spSrcFile, ullSeekTo, bDstFileFreshlyCreated, bSkip, GetTaskPropValue(rConfig)); if(eResult != TSubTaskBase::eSubResult_Continue) return eResult; else if(bSkip)