Index: src/ch/TSubTaskCopyMove.cpp =================================================================== diff -u -N -rb7709acbab26fdb108b77d3e08d3872f54248af2 -r8c87d4185fbe5b952c49f72afcfd5f9fca338fb4 --- src/ch/TSubTaskCopyMove.cpp (.../TSubTaskCopyMove.cpp) (revision b7709acbab26fdb108b77d3e08d3872f54248af2) +++ src/ch/TSubTaskCopyMove.cpp (.../TSubTaskCopyMove.cpp) (revision 8c87d4185fbe5b952c49f72afcfd5f9fca338fb4) @@ -22,3 +22,1072 @@ // ============================================================================ #include "stdafx.h" #include "TSubTaskCopyMove.h" +#include "TSubTaskContext.h" +#include "TTaskConfiguration.h" +#include "TTaskDefinition.h" +#include "task.h" +#include "TLocalFilesystem.h" +#include "FeedbackHandler.h" +#include "..\Common\FileSupport.h" +#include "Device IO.h" + +// assume max sectors of 4kB (for rounding) +#define MAXSECTORSIZE 4096 + +struct CUSTOM_COPY_PARAMS +{ + CFileInfoPtr spSrcFile; // CFileInfo - src file + chcore::TSmartPath pathDstFile; // dest path with filename + + CDataBuffer dbBuffer; // buffer handling + bool bOnlyCreate; // flag from configuration - skips real copying - only create + bool bProcessed; // has the element been processed ? (false if skipped) +}; + +TSubTaskCopyMove::TSubTaskCopyMove(TSubTaskContext& tSubTaskContext) : + TSubTaskBase(tSubTaskContext) +{ +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::Exec() +{ + icpf::log_file& rLog = GetContext().GetLog(); + CFileInfoArray& rFilesCache = GetContext().GetFilesCache(); + TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition(); + TTaskConfigTracker& rCfgTracker = GetContext().GetCfgTracker(); + TTaskBasicProgressInfo& rBasicProgressInfo = GetContext().GetTaskBasicProgressInfo(); + TWorkerThreadController& rThreadController = GetContext().GetThreadController(); + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + TTaskLocalStats& rLocalStats = GetContext().GetTaskLocalStats(); + + BOOST_ASSERT(piFeedbackHandler != NULL); + if(piFeedbackHandler == NULL) + return eSubResult_Error; + + // log + rLog.logi(_T("Processing files/folders (ProcessFiles)")); + + // count how much has been done (updates also a member in TSubTaskCopyMoveArray) + rLocalStats.SetProcessedSize(rFilesCache.CalculatePartialSize(rBasicProgressInfo.GetCurrentIndex())); + + // begin at index which wasn't processed previously + size_t stSize = rFilesCache.GetSize(); + bool bIgnoreFolders = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bool bForceDirectories = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + + // create a buffer of size m_nBufferSize + CUSTOM_COPY_PARAMS ccp; + ccp.bProcessed = false; + ccp.bOnlyCreate = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + + // remove changes in buffer sizes to avoid re-creation later + rCfgTracker.RemoveModificationSet(TOptionsSet() % eTO_DefaultBufferSize % eTO_OneDiskBufferSize % eTO_TwoDisksBufferSize % eTO_CDBufferSize % eTO_LANBufferSize % eTO_UseOnlyDefaultBuffer); + + BUFFERSIZES bs; + bs.m_bOnlyDefault = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiDefaultSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiOneDiskSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiTwoDisksSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiCDSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiLANSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + + ccp.dbBuffer.Create(&bs); + + // helpers + DWORD dwLastError = 0; + + // log + const BUFFERSIZES* pbs = ccp.dbBuffer.GetSizes(); + + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Processing files/folders (ProcessFiles):\r\n\tOnlyCreate: %create\r\n\tBufferSize: [Def:%defsize, One:%onesize, Two:%twosize, CD:%cdsize, LAN:%lansize]\r\n\tFiles/folders count: %filecount\r\n\tIgnore Folders: %ignorefolders\r\n\tDest path: %dstpath\r\n\tCurrent index (0-based): %currindex")); + fmt.SetParam(_t("%create"), ccp.bOnlyCreate); + fmt.SetParam(_t("%defsize"), pbs->m_uiDefaultSize); + fmt.SetParam(_t("%onesize"), pbs->m_uiOneDiskSize); + fmt.SetParam(_t("%twosize"), pbs->m_uiTwoDisksSize); + fmt.SetParam(_t("%cdsize"), pbs->m_uiCDSize); + fmt.SetParam(_t("%lansize"), pbs->m_uiLANSize); + fmt.SetParam(_t("%filecount"), stSize); + fmt.SetParam(_t("%ignorefolders"), bIgnoreFolders); + fmt.SetParam(_t("%dstpath"), rTaskDefinition.GetDestinationPath().ToString()); + fmt.SetParam(_t("%currindex"), rBasicProgressInfo.GetCurrentIndex()); + + rLog.logi(fmt); + + for(size_t stIndex = rBasicProgressInfo.GetCurrentIndex(); stIndex < stSize; stIndex++) + { + // should we kill ? + if(rThreadController.KillRequested()) + { + // log + rLog.logi(_T("Kill request while processing file in ProcessFiles")); + return TSubTaskBase::eSubResult_KillRequest; + } + + // update m_stNextIndex, getting current CFileInfo + CFileInfoPtr spFileInfo = rFilesCache.GetAt(rBasicProgressInfo.GetCurrentIndex()); + + // set dest path with filename + ccp.pathDstFile = CalculateDestinationPath(spFileInfo, rTaskDefinition.GetDestinationPath(), ((int)bForceDirectories) << 1 | (int)bIgnoreFolders); + + // are the files/folders lie on the same partition ? + int iDstDriveNumber = 0; + bool bMove = rTaskDefinition.GetOperationType() == eOperation_Move; + if(bMove) + TLocalFilesystem::GetDriveData(rTaskDefinition.GetDestinationPath(), &iDstDriveNumber, NULL); + if(bMove && iDstDriveNumber != -1 && iDstDriveNumber == GetDriveNumber(spFileInfo) && GetMove(spFileInfo)) + { + bool bRetry = true; + if(bRetry && !MoveFile(spFileInfo->GetFullFilePath().ToString(), ccp.pathDstFile.ToString())) + { + dwLastError=GetLastError(); + //log + fmt.SetFormat(_T("Error %errno while calling MoveFile %srcpath -> %dstpath (ProcessFiles)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%srcpath"), spFileInfo->GetFullFilePath().ToString()); + fmt.SetParam(_t("%dstpath"), ccp.pathDstFile.ToString()); + rLog.loge(fmt); + + FEEDBACK_FILEERROR ferr = { spFileInfo->GetFullFilePath().ToString(), ccp.pathDstFile.ToString(), eFastMoveError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); + switch(frResult) + { + case CFeedbackHandler::eResult_Cancel: + return TSubTaskBase::eSubResult_CancelRequest; + + case CFeedbackHandler::eResult_Retry: + continue; + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Skip: + bRetry = false; + break; // just do nothing + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + else + spFileInfo->SetFlags(FIF_PROCESSED, FIF_PROCESSED); + } + else + { + // if folder - create it + if(spFileInfo->IsDirectory()) + { + bool bRetry = true; + if(bRetry && !CreateDirectory(ccp.pathDstFile.ToString(), NULL) && (dwLastError=GetLastError()) != ERROR_ALREADY_EXISTS ) + { + // log + fmt.SetFormat(_T("Error %errno while calling CreateDirectory %path (ProcessFiles)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), ccp.pathDstFile.ToString()); + rLog.loge(fmt); + + FEEDBACK_FILEERROR ferr = { ccp.pathDstFile.ToString(), NULL, eCreateError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); + switch(frResult) + { + case CFeedbackHandler::eResult_Cancel: + return TSubTaskBase::eSubResult_CancelRequest; + + case CFeedbackHandler::eResult_Retry: + continue; + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Skip: + bRetry = false; + break; // just do nothing + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + + rLocalStats.IncreaseProcessedSize(spFileInfo->GetLength64()); + spFileInfo->SetFlags(FIF_PROCESSED, FIF_PROCESSED); + } + else + { + // start copying/moving file + ccp.spSrcFile = spFileInfo; + ccp.bProcessed = false; + + // kopiuj dane + TSubTaskBase::ESubOperationResult eResult = CustomCopyFileFB(&ccp); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + + spFileInfo->SetFlags(ccp.bProcessed ? FIF_PROCESSED : 0, FIF_PROCESSED); + + // if moving - delete file (only if config flag is set) + if(bMove && spFileInfo->GetFlags() & FIF_PROCESSED && !GetTaskPropValue(rTaskDefinition.GetConfiguration())) + { + if(!GetTaskPropValue(rTaskDefinition.GetConfiguration())) + SetFileAttributes(spFileInfo->GetFullFilePath().ToString(), FILE_ATTRIBUTE_NORMAL); + DeleteFile(spFileInfo->GetFullFilePath().ToString()); // there will be another try later, so I don't check + // if succeeded + } + } + + // set a time + if(GetTaskPropValue(rTaskDefinition.GetConfiguration())) + TLocalFilesystem::SetFileDirectoryTime(ccp.pathDstFile.ToString(), spFileInfo->GetCreationTime(), spFileInfo->GetLastAccessTime(), spFileInfo->GetLastWriteTime()); // no error checking (but most probably it should be checked) + + // attributes + if(GetTaskPropValue(rTaskDefinition.GetConfiguration())) + SetFileAttributes(ccp.pathDstFile.ToString(), spFileInfo->GetAttributes()); // as above + } + + rBasicProgressInfo.SetCurrentIndex(stIndex + 1); + } + + // delete buffer - it's not needed + ccp.dbBuffer.Delete(); + + // to look better (as 100%) - increase current index by 1 + rBasicProgressInfo.SetCurrentIndex(stSize); + + // log + rLog.logi(_T("Finished processing in ProcessFiles")); + + return TSubTaskBase::eSubResult_Continue; +} + +bool TSubTaskCopyMove::GetMove(const CFileInfoPtr& spFileInfo) +{ + if(!spFileInfo) + THROW(_T("Invalid pointer"), 0, 0, 0); + if(spFileInfo->GetSrcIndex() == std::numeric_limits::max()) + THROW(_T("Received non-relative (standalone) path info"), 0, 0, 0); + + // check if this information has already been stored + size_t stBaseIndex = spFileInfo->GetSrcIndex(); + if(stBaseIndex >= GetContext().GetBasePathDataContainer().GetCount()) + THROW(_T("Index out of bounds"), 0, 0, 0); + + TBasePathDataPtr spPathData = GetContext().GetBasePathDataContainer().GetAt(stBaseIndex); + return spPathData->GetMove(); +} + +int TSubTaskCopyMove::GetBufferIndex(const CFileInfoPtr& spFileInfo) +{ + TBasePathDataContainer& rSrcPathsInfo = GetContext().GetBasePathDataContainer(); + TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition(); + + if(!spFileInfo) + THROW(_T("Invalid pointer"), 0, 0, 0); + if(spFileInfo->GetSrcIndex() == std::numeric_limits::max()) + THROW(_T("Received non-relative (standalone) path info"), 0, 0, 0); + + // check if this information has already been stored + size_t stBaseIndex = spFileInfo->GetSrcIndex(); + if(stBaseIndex >= rSrcPathsInfo.GetCount()) + THROW(_T("Index out of bounds"), 0, 0, 0); + + TBasePathDataPtr spPathData = rSrcPathsInfo.GetAt(stBaseIndex); + if(spPathData->IsBufferIndexSet()) + return spPathData->GetBufferIndex(); + + // buffer index wasn't cached previously - read it now + int iDriveNumber = 0; + UINT uiDriveType = 0; + int iDstDriveNumber = 0; + UINT uiDstDriveType = 0; + TLocalFilesystem::GetDriveData(rTaskDefinition.GetSourcePathAt(stBaseIndex), &iDriveNumber, &uiDriveType); + TLocalFilesystem::GetDriveData(rTaskDefinition.GetDestinationPath(), &iDstDriveNumber, &uiDstDriveType); + + // what kind of buffer + int iBufferIndex = BI_DEFAULT; + if(uiDriveType == DRIVE_REMOTE || uiDstDriveType == DRIVE_REMOTE) + iBufferIndex = BI_LAN; + else if(uiDriveType == DRIVE_CDROM || uiDstDriveType == DRIVE_CDROM) + iBufferIndex = BI_CD; + else if(uiDriveType == DRIVE_FIXED && uiDstDriveType == DRIVE_FIXED) + { + // two hdd's - is this the same physical disk ? + if(iDriveNumber == iDstDriveNumber || IsSamePhysicalDisk(iDriveNumber, iDstDriveNumber)) + iBufferIndex = BI_ONEDISK; + else + iBufferIndex = BI_TWODISKS; + } + else + iBufferIndex = BI_DEFAULT; + + spPathData->SetBufferIndex(iBufferIndex); + + return iBufferIndex; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::CustomCopyFileFB(CUSTOM_COPY_PARAMS* pData) +{ + TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition(); + TTaskBasicProgressInfo& rBasicProgressInfo = GetContext().GetTaskBasicProgressInfo(); + TWorkerThreadController& rThreadController = GetContext().GetThreadController(); + TTaskLocalStats& rLocalStats = GetContext().GetTaskLocalStats(); + icpf::log_file& rLog = GetContext().GetLog(); + TTaskConfigTracker& rCfgTracker = GetContext().GetCfgTracker(); + + TAutoFileHandle hSrc = INVALID_HANDLE_VALUE, + hDst = INVALID_HANDLE_VALUE; + ictranslate::CFormat fmt; + TSubTaskBase::ESubOperationResult eResult = TSubTaskBase::eSubResult_Continue; + bool bSkip = false; + + // calculate if we want to disable buffering for file transfer + // NOTE: we are using here the file size read when scanning directories for files; it might be + // outdated at this point, but at present we don't want to re-read file size since it + // will cost additional disk access + bool bNoBuffer = (GetTaskPropValue(rTaskDefinition.GetConfiguration()) && + pData->spSrcFile->GetLength64() >= GetTaskPropValue(rTaskDefinition.GetConfiguration())); + + // first open the source file and handle any failures + eResult = OpenSourceFileFB(hSrc, pData->spSrcFile, bNoBuffer); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(hSrc == INVALID_HANDLE_VALUE) + { + // invalid handle = operation skipped by user + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + // 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(rTaskDefinition.GetConfiguration())) + SetFileAttributes(pData->pathDstFile.ToString(), FILE_ATTRIBUTE_NORMAL); + + // open destination file, handle the failures and possibly existence of the destination file + unsigned long long ullSeekTo = 0; + bool bDstFileFreshlyCreated = false; + + if(rBasicProgressInfo.GetCurrentFileProcessedSize() == 0) + { + // open destination file for case, when we start operation on this file (i.e. it is not resume of the + // old operation) + eResult = OpenDestinationFileFB(hDst, pData->pathDstFile, bNoBuffer, pData->spSrcFile, ullSeekTo, bDstFileFreshlyCreated); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(hDst == INVALID_HANDLE_VALUE) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + } + else + { + // we are resuming previous operation + eResult = OpenExistingDestinationFileFB(hDst, pData->pathDstFile, bNoBuffer); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(hDst == INVALID_HANDLE_VALUE) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + ullSeekTo = rBasicProgressInfo.GetCurrentFileProcessedSize(); + } + + if(!pData->bOnlyCreate) + { + // seek to the position where copying will start + if(ullSeekTo != 0) // src and dst files exists, requested resume at the specified index + { + // try to move file pointers to the end + ULONGLONG ullMove = (bNoBuffer ? ROUNDDOWN(ullSeekTo, MAXSECTORSIZE) : ullSeekTo); + + eResult = SetFilePointerFB(hSrc, ullMove, pData->spSrcFile->GetFullFilePath(), bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + eResult = SetFilePointerFB(hDst, ullMove, pData->pathDstFile, bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + // with either first or second seek we got 'skip' answer... + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + rBasicProgressInfo.IncreaseCurrentFileProcessedSize(ullMove); + rLocalStats.IncreaseProcessedSize(ullMove); + } + + // if the destination file already exists - truncate it to the current file position + if(!bDstFileFreshlyCreated) + { + // if destination file was opened (as opposed to newly created) + eResult = SetEndOfFileFB(hDst, pData->pathDstFile, bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + } + + // copying + unsigned long ulToRead = 0; + unsigned long ulRead = 0; + unsigned long ulWritten = 0; + int iBufferIndex = 0; + bool bLastPart = false; + + do + { + // kill flag checks + if(rThreadController.KillRequested()) + { + // log + fmt.SetFormat(_T("Kill request while main copying file %srcpath -> %dstpath")); + fmt.SetParam(_t("%srcpath"), pData->spSrcFile->GetFullFilePath().ToString()); + fmt.SetParam(_t("%dstpath"), pData->pathDstFile.ToString()); + rLog.logi(fmt); + return TSubTaskBase::eSubResult_KillRequest; + } + + // recreate buffer if needed + if(rCfgTracker.IsModified() && rCfgTracker.IsModified(TOptionsSet() % eTO_DefaultBufferSize % eTO_OneDiskBufferSize % eTO_TwoDisksBufferSize % eTO_CDBufferSize % eTO_LANBufferSize % eTO_UseOnlyDefaultBuffer, true)) + { + BUFFERSIZES bs; + bs.m_bOnlyDefault = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiDefaultSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiOneDiskSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiTwoDisksSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiCDSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + bs.m_uiLANSize = GetTaskPropValue(rTaskDefinition.GetConfiguration()); + + // log + const BUFFERSIZES* pbs1 = pData->dbBuffer.GetSizes(); + + fmt.SetFormat(_T("Changing buffer size from [Def:%defsize, One:%onesize, Two:%twosize, CD:%cdsize, LAN:%lansize] to [Def:%defsize2, One:%onesize2, Two:%twosize2, CD:%cdsize2, LAN:%lansize2] wile copying %srcfile -> %dstfile (CustomCopyFileFB)")); + + fmt.SetParam(_t("%defsize"), pbs1->m_uiDefaultSize); + fmt.SetParam(_t("%onesize"), pbs1->m_uiOneDiskSize); + fmt.SetParam(_t("%twosize"), pbs1->m_uiTwoDisksSize); + fmt.SetParam(_t("%cdsize"), pbs1->m_uiCDSize); + fmt.SetParam(_t("%lansize"), pbs1->m_uiLANSize); + fmt.SetParam(_t("%defsize2"), bs.m_uiDefaultSize); + fmt.SetParam(_t("%onesize2"), bs.m_uiOneDiskSize); + fmt.SetParam(_t("%twosize2"), bs.m_uiTwoDisksSize); + fmt.SetParam(_t("%cdsize2"), bs.m_uiCDSize); + fmt.SetParam(_t("%lansize2"), bs.m_uiLANSize); + fmt.SetParam(_t("%srcfile"), pData->spSrcFile->GetFullFilePath().ToString()); + fmt.SetParam(_t("%dstfile"), pData->pathDstFile.ToString()); + + rLog.logi(fmt); + pData->dbBuffer.Create(&bs); + } + + // establish count of data to read + if(GetTaskPropValue(rTaskDefinition.GetConfiguration())) + iBufferIndex = BI_DEFAULT; + else + iBufferIndex = GetBufferIndex(pData->spSrcFile); + rLocalStats.SetCurrentBufferIndex(iBufferIndex); + + ulToRead = bNoBuffer ? ROUNDUP(pData->dbBuffer.GetSizes()->m_auiSizes[iBufferIndex], MAXSECTORSIZE) : pData->dbBuffer.GetSizes()->m_auiSizes[iBufferIndex]; + + // read data from file to buffer + eResult = ReadFileFB(hSrc, pData->dbBuffer, ulToRead, ulRead, pData->spSrcFile->GetFullFilePath(), bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + if(ulRead > 0) + { + // determine if this is the last chunk of data we could get from the source file (EOF condition) + bLastPart = (ulToRead != ulRead); + + // handle not aligned part at the end of file when no buffering is enabled + if(bNoBuffer && bLastPart) + { + // count of data read from the file is less than requested - we're at the end of source file + // and this is the operation with system buffering turned off + + // write as much as possible to the destination file with no buffering + // NOTE: as an alternative, we could write more data to the destination file and then truncate the file + unsigned long ulDataToWrite = ROUNDDOWN(ulRead, MAXSECTORSIZE); + if(ulDataToWrite > 0) + { + eResult = WriteFileFB(hDst, pData->dbBuffer, ulDataToWrite, ulWritten, pData->pathDstFile, bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + // increase count of processed data + rBasicProgressInfo.IncreaseCurrentFileProcessedSize(ulWritten); + rLocalStats.IncreaseProcessedSize(ulWritten); + + // calculate count of bytes left to be written + ulRead -= ulWritten; + + // now remove part of data from buffer (ulWritten bytes) + pData->dbBuffer.CutDataFromBuffer(ulWritten); + } + + // close and re-open the destination file with buffering option for append + hDst.Close(); + + // are there any more data to be written? + if(ulRead != 0) + { + // re-open the destination file, this time with standard buffering to allow writing not aligned part of file data + eResult = OpenExistingDestinationFileFB(hDst, pData->pathDstFile, false); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(hDst == INVALID_HANDLE_VALUE) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + // move file pointer to the end of destination file + eResult = SetFilePointerFB(hDst, rBasicProgressInfo.GetCurrentFileProcessedSize(), pData->pathDstFile, bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + // with either first or second seek we got 'skip' answer... + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + } + } + + // write + if(ulRead != 0) + { + eResult = WriteFileFB(hDst, pData->dbBuffer, ulRead, ulWritten, pData->pathDstFile, bSkip); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(bSkip) + { + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + pData->bProcessed = false; + return TSubTaskBase::eSubResult_Continue; + } + + // increase count of processed data + rBasicProgressInfo.IncreaseCurrentFileProcessedSize(ulRead); + rLocalStats.IncreaseProcessedSize(ulRead); + } + } + } + while(ulRead != 0 && !bLastPart); + } + else + { + // we don't copy contents, but need to increase processed size + rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize()); + } + + pData->bProcessed = true; + rBasicProgressInfo.SetCurrentFileProcessedSize(0); + + return TSubTaskBase::eSubResult_Continue; +} + + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::OpenSourceFileFB(TAutoFileHandle& hOutFile, const CFileInfoPtr& spSrcFileInfo, bool bNoBuffering) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + BOOST_ASSERT(spSrcFileInfo); + if(!spSrcFileInfo) + THROW(_T("Invalid argument"), 0, 0, 0); + + bool bRetry = false; + CString strPath = spSrcFileInfo->GetFullFilePath().ToString(); + + hOutFile = INVALID_HANDLE_VALUE; + + TAutoFileHandle hFile; + do + { + bRetry = false; + + hFile = CreateFile(strPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | (bNoBuffering ? FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH : 0), NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + DWORD dwLastError = GetLastError(); + + FEEDBACK_FILEERROR feedStruct = { (PCTSTR)strPath, NULL, eCreateError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &feedStruct); + + switch(frResult) + { + case CFeedbackHandler::eResult_Skip: + break; // will return INVALID_HANDLE_VALUE + + case CFeedbackHandler::eResult_Cancel: + { + // log + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Cancel request [error %errno] while opening source file %path (OpenSourceFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), strPath); + rLog.loge(fmt); + + return TSubTaskBase::eSubResult_CancelRequest; + } + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Retry: + { + // log + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Retrying [error %errno] to open source file %path (OpenSourceFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), strPath); + rLog.loge(fmt); + + bRetry = true; + break; + } + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + while(bRetry); + + hOutFile = hFile.Detach(); + + return TSubTaskBase::eSubResult_Continue; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::OpenDestinationFileFB(TAutoFileHandle& hOutFile, const chcore::TSmartPath& pathDstFile, bool bNoBuffering, const CFileInfoPtr& spSrcFileInfo, unsigned long long& ullSeekTo, bool& bFreshlyCreated) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + bool bRetry = false; + TAutoFileHandle hFile; + + ullSeekTo = 0; + bFreshlyCreated = true; + hOutFile = INVALID_HANDLE_VALUE; + + do + { + bRetry = false; + + hFile = ::CreateFile(pathDstFile.ToString(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | (bNoBuffering ? FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH : 0), NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + DWORD dwLastError = GetLastError(); + if(dwLastError == ERROR_FILE_EXISTS) + { + bFreshlyCreated = false; + + // pass it to the specialized method + TSubTaskBase::ESubOperationResult eResult = OpenExistingDestinationFileFB(hFile, pathDstFile, bNoBuffering); + if(eResult != TSubTaskBase::eSubResult_Continue) + return eResult; + else if(hFile == INVALID_HANDLE_VALUE) + return TSubTaskBase::eSubResult_Continue; + + // read info about the existing destination file, + // NOTE: it is not known which one would be faster - reading file parameters + // by using spDstFileInfo->Create() (which uses FindFirstFile()) or by + // reading parameters using opened handle; need to be tested in the future + CFileInfoPtr spDstFileInfo(boost::make_shared()); + if(!spDstFileInfo->Create(pathDstFile, std::numeric_limits::max())) + THROW(_T("Cannot get information about file which has already been opened!"), 0, GetLastError(), 0); + + // src and dst files are the same + FEEDBACK_ALREADYEXISTS feedStruct = { spSrcFileInfo, spDstFileInfo }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileAlreadyExists, &feedStruct); + // check for dialog result + switch(frResult) + { + case CFeedbackHandler::eResult_Overwrite: + ullSeekTo = 0; + break; + + case CFeedbackHandler::eResult_CopyRest: + ullSeekTo = spDstFileInfo->GetLength64(); + break; + + case CFeedbackHandler::eResult_Skip: + return TSubTaskBase::eSubResult_Continue; + + case CFeedbackHandler::eResult_Cancel: + { + // log + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Cancel request while checking result of dialog before opening source file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%path"), pathDstFile.ToString()); + rLog.logi(fmt); + + return TSubTaskBase::eSubResult_CancelRequest; + } + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + else + { + FEEDBACK_FILEERROR feedStruct = { pathDstFile.ToString(), NULL, eCreateError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &feedStruct); + switch (frResult) + { + case CFeedbackHandler::eResult_Retry: + { + // log + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Retrying [error %errno] to open destination file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), pathDstFile.ToString()); + rLog.loge(fmt); + + bRetry = true; + + break; + } + case CFeedbackHandler::eResult_Cancel: + { + // log + ictranslate::CFormat fmt; + + fmt.SetFormat(_T("Cancel request [error %errno] while opening destination file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), pathDstFile.ToString()); + rLog.loge(fmt); + + return TSubTaskBase::eSubResult_CancelRequest; + } + + case CFeedbackHandler::eResult_Skip: + break; // will return invalid handle value + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + } + while(bRetry); + + hOutFile = hFile.Detach(); + + return TSubTaskBase::eSubResult_Continue; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::OpenExistingDestinationFileFB(TAutoFileHandle& hOutFile, const chcore::TSmartPath& pathDstFile, bool bNoBuffering) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + bool bRetry = false; + TAutoFileHandle hFile; + + hOutFile = INVALID_HANDLE_VALUE; + + do + { + bRetry = false; + + hFile = CreateFile(pathDstFile.ToString(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | (bNoBuffering ? FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH : 0), NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + DWORD dwLastError = GetLastError(); + FEEDBACK_FILEERROR feedStruct = { pathDstFile.ToString(), NULL, eCreateError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &feedStruct); + switch (frResult) + { + case CFeedbackHandler::eResult_Retry: + { + // log + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Retrying [error %errno] to open destination file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), pathDstFile.ToString()); + rLog.loge(fmt); + + bRetry = true; + + break; + } + case CFeedbackHandler::eResult_Cancel: + { + // log + ictranslate::CFormat fmt; + + fmt.SetFormat(_T("Cancel request [error %errno] while opening destination file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), pathDstFile.ToString()); + rLog.loge(fmt); + + return TSubTaskBase::eSubResult_CancelRequest; + } + + case CFeedbackHandler::eResult_Skip: + break; // will return invalid handle value + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + while(bRetry); + + hOutFile = hFile.Detach(); + + return TSubTaskBase::eSubResult_Continue; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::SetFilePointerFB(HANDLE hFile, long long llDistance, const chcore::TSmartPath& pathFile, bool& bSkip) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + bSkip = false; + bool bRetry = false; + do + { + bRetry = false; + + if(SetFilePointer64(hFile, llDistance, FILE_BEGIN) == -1) + { + DWORD dwLastError = GetLastError(); + + // log + ictranslate::CFormat fmt; + + fmt.SetFormat(_T("Error %errno while moving file pointer of %path to %pos")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), pathFile.ToString()); + fmt.SetParam(_t("%pos"), llDistance); + rLog.loge(fmt); + + FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eSeekError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); + switch(frResult) + { + case CFeedbackHandler::eResult_Cancel: + return TSubTaskBase::eSubResult_CancelRequest; + + case CFeedbackHandler::eResult_Retry: + bRetry = true; + break; + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Skip: + bSkip = true; + return TSubTaskBase::eSubResult_Continue; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + while(bRetry); + + return TSubTaskBase::eSubResult_Continue; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::SetEndOfFileFB(HANDLE hFile, const chcore::TSmartPath& pathFile, bool& bSkip) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + bSkip = false; + + bool bRetry = false; + do + { + if(!SetEndOfFile(hFile)) + { + // log + DWORD dwLastError = GetLastError(); + + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Error %errno while setting size of file %path to 0")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%path"), pathFile.ToString()); + rLog.loge(fmt); + + FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eResizeError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); + switch(frResult) + { + case CFeedbackHandler::eResult_Cancel: + return TSubTaskBase::eSubResult_CancelRequest; + + case CFeedbackHandler::eResult_Retry: + bRetry = true; + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Skip: + bSkip = true; + return TSubTaskBase::eSubResult_Continue; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + while(bRetry); + + return TSubTaskBase::eSubResult_Continue; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::ReadFileFB(HANDLE hFile, CDataBuffer& rBuffer, DWORD dwToRead, DWORD& rdwBytesRead, const chcore::TSmartPath& pathFile, bool& bSkip) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + bSkip = false; + bool bRetry = false; + do + { + bRetry = false; + + if(!ReadFile(hFile, rBuffer, dwToRead, &rdwBytesRead, NULL)) + { + // log + DWORD dwLastError = GetLastError(); + + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Error %errno while trying to read %count bytes from source file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%count"), dwToRead); + fmt.SetParam(_t("%path"), pathFile.ToString()); + rLog.loge(fmt); + + FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eReadError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); + switch(frResult) + { + case CFeedbackHandler::eResult_Cancel: + return TSubTaskBase::eSubResult_CancelRequest; + + case CFeedbackHandler::eResult_Retry: + bRetry = true; + break; + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Skip: + bSkip = true; + return TSubTaskBase::eSubResult_Continue; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + while(bRetry); + + return TSubTaskBase::eSubResult_Continue; +} + +TSubTaskBase::ESubOperationResult TSubTaskCopyMove::WriteFileFB(HANDLE hFile, CDataBuffer& rBuffer, DWORD dwToWrite, DWORD& rdwBytesWritten, const chcore::TSmartPath& pathFile, bool& bSkip) +{ + chcore::IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler(); + icpf::log_file& rLog = GetContext().GetLog(); + + bSkip = false; + + bool bRetry = false; + do + { + bRetry = false; + + if(!WriteFile(hFile, rBuffer, dwToWrite, &rdwBytesWritten, NULL) || dwToWrite != rdwBytesWritten) + { + // log + DWORD dwLastError = GetLastError(); + + ictranslate::CFormat fmt; + fmt.SetFormat(_T("Error %errno while trying to write %count bytes to destination file %path (CustomCopyFileFB)")); + fmt.SetParam(_t("%errno"), dwLastError); + fmt.SetParam(_t("%count"), dwToWrite); + fmt.SetParam(_t("%path"), pathFile.ToString()); + rLog.loge(fmt); + + FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eWriteError, dwLastError }; + CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); + switch(frResult) + { + case CFeedbackHandler::eResult_Cancel: + return TSubTaskBase::eSubResult_CancelRequest; + + case CFeedbackHandler::eResult_Retry: + bRetry = true; + break; + + case CFeedbackHandler::eResult_Pause: + return TSubTaskBase::eSubResult_PauseRequest; + + case CFeedbackHandler::eResult_Skip: + bSkip = true; + return TSubTaskBase::eSubResult_Continue; + + default: + BOOST_ASSERT(FALSE); // unknown result + THROW(_T("Unhandled case"), 0, 0, 0); + } + } + } + while(bRetry); + + return TSubTaskBase::eSubResult_Continue; +}