summaryrefslogtreecommitdiff
path: root/plugins-alternative/CopyEngine/Rsync/TransferThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins-alternative/CopyEngine/Rsync/TransferThread.cpp')
-rw-r--r--plugins-alternative/CopyEngine/Rsync/TransferThread.cpp1252
1 files changed, 1252 insertions, 0 deletions
diff --git a/plugins-alternative/CopyEngine/Rsync/TransferThread.cpp b/plugins-alternative/CopyEngine/Rsync/TransferThread.cpp
new file mode 100644
index 0000000..5557a1e
--- /dev/null
+++ b/plugins-alternative/CopyEngine/Rsync/TransferThread.cpp
@@ -0,0 +1,1252 @@
+//presume bug linked as multple paralelle inode to resume after "overwrite"
+//then do overwrite node function to not re-set the file name
+
+#include "TransferThread.h"
+
+#ifdef Q_CC_GNU
+//this next header is needed to change file time/date under gcc
+#include <utime.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+
+#ifndef Q_OS_UNIX
+#include <windows.h>
+#endif
+
+TransferThread::TransferThread()
+{
+ start();
+ moveToThread(this);
+ needSkip = false;
+ transfer_stat = TransferStat_Idle;
+ stopIt = false;
+ fileExistsAction = FileExists_NotSet;
+ alwaysDoFileExistsAction= FileExists_NotSet;
+ readError = false;
+ writeError = false;
+ rsync = false;
+ this->mkpathTransfer = mkpathTransfer;
+ readThread.setWriteThread(&writeThread);
+
+ connect(&clockForTheCopySpeed, SIGNAL(timeout()), this, SLOT(timeOfTheBlockCopyFinished()));
+ maxTime=QDateTime(QDate(ULTRACOPIER_PLUGIN_MINIMALYEAR,1,1));
+}
+
+TransferThread::~TransferThread()
+{
+ exit();
+ disconnect(&readThread);
+ disconnect(&writeThread);
+ disconnect(this);
+ wait();
+}
+
+void TransferThread::run()
+{
+ //ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start: "+QString::number((qint64)QThread::currentThreadId()));
+ transfer_stat = TransferStat_Idle;
+ stopIt = false;
+ fileExistsAction = FileExists_NotSet;
+ alwaysDoFileExistsAction= FileExists_NotSet;
+ //the error push
+ connect(&readThread,SIGNAL(error()), this, SLOT(getReadError()), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(error()), this, SLOT(getWriteError()), Qt::QueuedConnection);
+ //the thread change operation
+ connect(this,SIGNAL(internalStartPreOperation()), this, SLOT(preOperation()), Qt::QueuedConnection);
+ connect(this,SIGNAL(internalStartPostOperation()), this, SLOT(postOperation()), Qt::QueuedConnection);
+ //the state change operation
+ //connect(&readThread,SIGNAL(readIsStopped()), &readThread, SLOT(postOperation()), Qt::QueuedConnection);//commented to do the checksum
+ connect(&readThread,SIGNAL(opened()), this, SLOT(readIsReady()), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(opened()), this, SLOT(writeIsReady()), Qt::QueuedConnection);
+ connect(&readThread,SIGNAL(readIsStopped()), this, SLOT(readIsStopped()), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(writeIsStopped()), this, SLOT(writeIsStopped()), Qt::QueuedConnection);
+ connect(&readThread,SIGNAL(readIsStopped()), &writeThread, SLOT(endIsDetected()), Qt::QueuedConnection);
+ //connect(&writeThread,SIGNAL(writeIsStopped()), &writeThread, SLOT(postOperation()), Qt::QueuedConnection);//commented to do the checksum
+ connect(&readThread,SIGNAL(closed()), this, SLOT(readIsClosed()), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(closed()), this, SLOT(writeIsClosed()), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(reopened()), this, SLOT(writeThreadIsReopened()), Qt::QueuedConnection);
+ connect(&readThread,SIGNAL(checksumFinish(QByteArray)), this, SLOT(readChecksumFinish(QByteArray)), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(checksumFinish(QByteArray)),this, SLOT(writeChecksumFinish(QByteArray)), Qt::QueuedConnection);
+ //error management
+ connect(&readThread,SIGNAL(isSeekToZeroAndWait()), this, SLOT(readThreadIsSeekToZeroAndWait()), Qt::QueuedConnection);
+ connect(&readThread,SIGNAL(resumeAfterErrorByRestartAtTheLastPosition()), this, SLOT(readThreadResumeAfterError()), Qt::QueuedConnection);
+ connect(&readThread,SIGNAL(resumeAfterErrorByRestartAll()), &writeThread, SLOT(flushAndSeekToZero()), Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(flushedAndSeekedToZero()), this, SLOT(readThreadResumeAfterError()), Qt::QueuedConnection);
+ connect(this,SIGNAL(internalTryStartTheTransfer()), this, SLOT(internalStartTheTransfer()), Qt::QueuedConnection);
+
+ #ifdef ULTRACOPIER_PLUGIN_DEBUG
+ connect(&readThread,SIGNAL(debugInformation(DebugLevel,QString,QString,QString,int)),this,SIGNAL(debugInformation(DebugLevel,QString,QString,QString,int)),Qt::QueuedConnection);
+ connect(&writeThread,SIGNAL(debugInformation(DebugLevel,QString,QString,QString,int)),this,SIGNAL(debugInformation(DebugLevel,QString,QString,QString,int)),Qt::QueuedConnection);
+ #endif
+
+ exec();
+}
+
+TransferStat TransferThread::getStat()
+{
+ return transfer_stat;
+}
+
+void TransferThread::startTheTransfer()
+{
+ emit internalTryStartTheTransfer();
+}
+
+void TransferThread::internalStartTheTransfer()
+{
+ if(transfer_stat==TransferStat_Idle)
+ {
+ if(mode!=Move)
+ {
+ /// \bug can pass here because in case of direct move on same media, it return to idle stat directly
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] can't start transfert at idle");
+ }
+ return;
+ }
+ if(transfer_stat==TransferStat_PostOperation)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] can't start transfert at PostOperation");
+ return;
+ }
+ if(transfer_stat==TransferStat_Transfer)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] can't start transfert at Transfer");
+ return;
+ }
+ if(canStartTransfer)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] canStartTransfer is already set to true");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] check how start the transfer");
+ canStartTransfer=true;
+ if(readIsReadyVariable && writeIsReadyVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start directly the transfer");
+ ifCanStartTransfer();
+ }
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start the transfer as delayed");
+}
+
+void TransferThread::setFiles(const QString &source,const qint64 &size,const QString &destination,const CopyMode &mode)
+{
+ if(transfer_stat!=TransferStat_Idle)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] already used, source: "+source+", destination: "+destination);
+ return;
+ }
+ //to prevent multiple file alocation into ListThread::doNewActions_inode_manipulation()
+ transfer_stat = TransferStat_PreOperation;
+ //emit pushStat(stat,transferId);
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start, source: "+source+", destination: "+destination);
+ this->source = source;
+ this->destination = destination;
+ this->mode = mode;
+ this->size = size;
+ fileExistsAction = FileExists_NotSet;
+ canStartTransfer = false;
+ sended_state_preOperationStopped= false;
+ canBeMovedDirectlyVariable = false;
+ fileContentError = false;
+ resetExtraVariable();
+ emit internalStartPreOperation();
+}
+
+void TransferThread::setFileExistsAction(const FileExistsAction &action)
+{
+ if(transfer_stat!=TransferStat_PreOperation)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] already used, source: "+source+", destination: "+destination);
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] action: "+QString::number(action));
+ if(action!=FileExists_Rename)
+ fileExistsAction = action;
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] rename at the wrong part, source: "+source+", destination: "+destination);
+ if(action==FileExists_Skip)
+ {
+ skip();
+ return;
+ }
+ resetExtraVariable();
+ emit internalStartPreOperation();
+}
+
+void TransferThread::setFileRename(const QString &nameForRename)
+{
+ if(transfer_stat!=TransferStat_PreOperation)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] already used, source: "+source+", destination: "+destination);
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] nameForRename: "+nameForRename);
+ destinationInfo.setFile(destination);
+ destination=destinationInfo.absolutePath();
+ destination+=QDir::separator()+nameForRename;
+ destinationInfo.setFile(destination);
+ fileExistsAction = FileExists_NotSet;
+ resetExtraVariable();
+ emit internalStartPreOperation();
+}
+
+void TransferThread::setAlwaysFileExistsAction(const FileExistsAction &action)
+{
+ //ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] action to do always: "+QString::number(action));
+ alwaysDoFileExistsAction=action;
+}
+
+void TransferThread::resetExtraVariable()
+{
+ sended_state_readStopped = false;
+ sended_state_writeStopped = false;
+ writeError = false;
+ readError = false;
+ readIsReadyVariable = false;
+ writeIsReadyVariable = false;
+ readIsFinishVariable = false;
+ writeIsFinishVariable = false;
+ readIsClosedVariable = false;
+ writeIsClosedVariable = false;
+ needSkip = false;
+ retry = false;
+ readIsOpenVariable = false;
+ writeIsOpenVariable = false;
+}
+
+void TransferThread::preOperation()
+{
+ if(transfer_stat!=TransferStat_PreOperation)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] already used, source: "+source+", destination: "+destination);
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ needRemove=false;
+ sourceInfo.setFile(source);
+ destinationInfo.setFile(destination);
+ if(isSame())
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] is same"+source);
+ return;
+ }
+ if(destinationExists())
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] destination exists: "+source);
+ return;
+ }
+ if(canBeMovedDirectly())
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] need moved directly: "+source);
+ canBeMovedDirectlyVariable=true;
+ readThread.fakeOpen();
+ writeThread.fakeOpen();
+ return;
+ }
+ tryOpen();
+}
+
+void TransferThread::tryOpen()
+{
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start source and destination: "+source+" and "+destination);
+ if(!readIsOpenVariable)
+ {
+ readError=false;
+ readThread.open(source,mode);
+ }
+ if(!writeIsOpenVariable)
+ {
+ writeError=false;
+ writeThread.open(destination,size,osBuffer && (!osBufferLimited || (osBufferLimited && size<osBufferLimit)));
+ }
+}
+
+bool TransferThread::isSame()
+{
+ //check if source and destination is not the same
+ if(sourceInfo==destinationInfo)
+ {
+ emit fileAlreadyExists(sourceInfo,destinationInfo,true);
+ return true;
+ }
+ return false;
+}
+
+bool TransferThread::destinationExists()
+{
+ //check if destination exists
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] overwrite: "+QString::number(fileExistsAction)+", always action: "+QString::number(alwaysDoFileExistsAction));
+ if(alwaysDoFileExistsAction==FileExists_Overwrite || rsync || readError || writeError)
+ return false;
+ if(destinationInfo.exists())
+ {
+ if(fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_Skip)
+ {
+ transfer_stat=TransferStat_Idle;
+ emit postOperationStopped();
+ return true;
+ }
+ if(alwaysDoFileExistsAction==FileExists_Rename)
+ {
+ QString absolutePath=destinationInfo.absolutePath();
+ QString fileName=destinationInfo.fileName();
+ QString suffix="";
+ QString newFileName;
+ //resolv the suffix
+ if(fileName.contains(QRegExp("^(.*)(\\.[a-z0-9]+)$")))
+ {
+ suffix=fileName;
+ suffix.replace(QRegExp("^(.*)(\\.[a-z0-9]+)$"),"\\2");
+ fileName.replace(QRegExp("^(.*)(\\.[a-z0-9]+)$"),"\\1");
+ }
+ //resolv the new name
+ int num=1;
+ do
+ {
+ if(num==1)
+ {
+ if(firstRenamingRule=="")
+ newFileName=tr("%1 - copy").arg(fileName);
+ else
+ {
+ newFileName=firstRenamingRule;
+ newFileName.replace("%name%",fileName);
+ }
+ }
+ else
+ {
+ if(otherRenamingRule=="")
+ newFileName=tr("%1 - copy (%2)").arg(fileName).arg(num);
+ else
+ {
+ newFileName=otherRenamingRule;
+ newFileName.replace("%name%",fileName);
+ newFileName.replace("%number%",QString::number(num));
+ }
+ }
+ destination=absolutePath+QDir::separator()+newFileName+suffix;
+ destinationInfo.setFile(destination);
+ num++;
+ }
+ while(destinationInfo.exists());
+ return false;
+ }
+ if(fileExistsAction==FileExists_OverwriteIfNewer || (fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_OverwriteIfNewer))
+ {
+ if(destinationInfo.lastModified()<sourceInfo.lastModified())
+ return false;
+ else
+ {
+ transfer_stat=TransferStat_Idle;
+ emit postOperationStopped();
+ return true;
+ }
+ }
+ if(fileExistsAction==FileExists_OverwriteIfNotSameModificationDate || (fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_OverwriteIfNotSameModificationDate))
+ {
+ if(destinationInfo.lastModified()!=sourceInfo.lastModified())
+ return false;
+ else
+ {
+ transfer_stat=TransferStat_Idle;
+ emit postOperationStopped();
+ return true;
+ }
+ }
+ if(fileExistsAction==FileExists_NotSet)
+ {
+ emit fileAlreadyExists(sourceInfo,destinationInfo,false);
+ return true;
+ }
+ }
+ return false;
+}
+
+void TransferThread::tryMoveDirectly()
+{
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start the system move");
+
+ //move if on same mount point
+ QFile sourceFile(sourceInfo.absoluteFilePath());
+ QFile destinationFile(destinationInfo.absoluteFilePath());
+ if(destinationFile.exists() && !destinationFile.remove())
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] "+destinationFile.fileName()+", error: "+destinationFile.errorString());
+ emit errorOnFile(destinationInfo,destinationFile.errorString());
+ return;
+ }
+ QDir dir(destinationInfo.absolutePath());
+ {
+ mkpathTransfer->acquire();
+ if(!dir.exists())
+ dir.mkpath(destinationInfo.absolutePath());
+ mkpathTransfer->release();
+ }
+ if(!sourceFile.rename(destinationFile.fileName()))
+ {
+ if(sourceFile.exists())
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] "+QString("file not not exists %1: %2, error: %3").arg(sourceFile.fileName()).arg(destinationFile.fileName()).arg(sourceFile.errorString()));
+ else if(!dir.exists())
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] "+QString("destination folder not exists %1: %2, error: %3").arg(sourceFile.fileName()).arg(destinationFile.fileName()).arg(sourceFile.errorString()));
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] "+QString("unable to do real move %1: %2, error: %3").arg(sourceFile.fileName()).arg(destinationFile.fileName()).arg(sourceFile.errorString()));
+ emit errorOnFile(sourceFile,sourceFile.errorString());
+ return;
+ }
+ readThread.fakeReadIsStarted();
+ writeThread.fakeWriteIsStarted();
+ readThread.fakeReadIsStopped();
+ writeThread.fakeWriteIsStopped();
+}
+
+bool TransferThread::canBeMovedDirectly()
+{
+ //move if on same mount point
+ #if defined (Q_OS_LINUX) || defined (Q_OS_WIN32)
+ if(mode!=Move)
+ return false;
+ if(mountSysPoint.size()==0)
+ return false;
+ if(getDrive(destinationInfo.fileName())==getDrive(sourceInfo.fileName()))
+ return true;
+ #endif
+ return false;
+}
+
+void TransferThread::readIsReady()
+{
+ if(readIsReadyVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ readIsReadyVariable=true;
+ readIsOpenVariable=true;
+ readIsClosedVariable=false;
+ ifCanStartTransfer();
+}
+
+void TransferThread::ifCanStartTransfer()
+{
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] readIsReadyVariable: "+QString::number(readIsReadyVariable)+", writeIsReadyVariable: "+QString::number(writeIsReadyVariable));
+ if(readIsReadyVariable && writeIsReadyVariable)
+ {
+ transfer_stat=TransferStat_WaitForTheTransfer;
+ sended_state_readStopped = false;
+ sended_state_writeStopped = false;
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] stat=WaitForTheTransfer");
+ if(!sended_state_preOperationStopped)
+ {
+ sended_state_preOperationStopped=true;
+ emit preOperationStopped();
+ }
+ if(canStartTransfer)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] stat=Transfer");
+ transfer_stat=TransferStat_Transfer;
+ if(!canBeMovedDirectlyVariable)
+ {
+ needRemove=true;
+ readThread.startRead();
+ }
+ else
+ tryMoveDirectly();
+ emit pushStat(transfer_stat,transferId);
+ }
+ //else
+ //emit pushStat(stat,transferId);
+ }
+}
+
+void TransferThread::writeIsReady()
+{
+ if(writeIsReadyVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ writeIsReadyVariable=true;
+ writeIsOpenVariable=true;
+ writeIsClosedVariable=false;
+ ifCanStartTransfer();
+}
+
+
+//set the copy info and options before runing
+void TransferThread::setRightTransfer(const bool doRightTransfer)
+{
+ this->doRightTransfer=doRightTransfer;
+}
+
+//set keep date
+void TransferThread::setKeepDate(const bool keepDate)
+{
+ this->keepDate=keepDate;
+}
+
+//set the current max speed in KB/s
+void TransferThread::setMaxSpeed(int maxSpeed)
+{
+ int interval=readThread.setMaxSpeed(maxSpeed);
+ writeThread.setMaxSpeed(maxSpeed);
+ if(maxSpeed>0)
+ {
+ clockForTheCopySpeed.setInterval(interval);
+ if(!clockForTheCopySpeed.isActive())//seam useless !this->isFinished()
+ clockForTheCopySpeed.start();
+ }
+ else
+ {
+ if(clockForTheCopySpeed.isActive())
+ clockForTheCopySpeed.stop();
+ }
+}
+
+//set block size in KB
+bool TransferThread::setBlockSize(const unsigned int blockSize)
+{
+ this->blockSize=blockSize;
+ return readThread.setBlockSize(blockSize) && writeThread.setBlockSize(blockSize);
+}
+
+//pause the copy
+void TransferThread::pause()
+{
+ if(transfer_stat==TransferStat_Idle)
+ return;
+ readThread.pause();
+}
+
+//resume the copy
+void TransferThread::resume()
+{
+ if(transfer_stat==TransferStat_Idle)
+ return;
+ readThread.resume();
+}
+
+//stop the current copy
+void TransferThread::stop()
+{
+ stopIt=true;
+ if(transfer_stat==TransferStat_Idle)
+ return;
+ readThread.stop();
+ writeThread.stop();
+}
+
+void TransferThread::readIsFinish()
+{
+ if(readIsFinishVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ readIsFinishVariable=true;
+ canStartTransfer=false;
+ //check here if need start checksuming or not
+ real_doChecksum=doChecksum && (!checksumOnlyOnError || fileContentError);
+ if(real_doChecksum)
+ {
+ readIsFinishVariable=false;
+ transfer_stat=TransferStat_Checksum;
+ sourceChecksum=QByteArray();
+ destinationChecksum=QByteArray();
+ readThread.startCheckSum();
+ }
+ else
+ {
+ transfer_stat=TransferStat_PostTransfer;
+ readThread.postOperation();
+ }
+ emit pushStat(transfer_stat,transferId);
+}
+
+void TransferThread::writeIsFinish()
+{
+ if(writeIsFinishVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ writeIsFinishVariable=true;
+ //check here if need start checksuming or not
+ if(real_doChecksum)
+ {
+ writeIsFinishVariable=false;
+ transfer_stat=TransferStat_Checksum;
+ writeThread.startCheckSum();
+ }
+ else
+ writeThread.postOperation();
+}
+
+void TransferThread::readChecksumFinish(const QByteArray& checksum)
+{
+ sourceChecksum=checksum;
+ compareChecksum();
+}
+
+void TransferThread::writeChecksumFinish(const QByteArray& checksum)
+{
+ destinationChecksum=checksum;
+ compareChecksum();
+}
+
+void TransferThread::compareChecksum()
+{
+ if(sourceChecksum.size()==0)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] the checksum of source is missing");
+ return;
+ }
+ if(destinationChecksum.size()==0)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] the checksum of destination is missing");
+ return;
+ }
+ if(sourceChecksum==destinationChecksum)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] the checksum match");
+ readThread.postOperation();
+ writeThread.postOperation();
+ transfer_stat=TransferStat_PostTransfer;
+ emit pushStat(transfer_stat,transferId);
+ }
+ else
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] the checksum not match");
+ //emit error here, and wait to resume
+ emit errorOnFile(destinationInfo,tr("The checksums not match"));
+ }
+}
+
+void TransferThread::readIsClosed()
+{
+ if(readIsClosedVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ readIsClosedVariable=true;
+ checkIfAllIsClosed();
+}
+
+void TransferThread::writeIsClosed()
+{
+ if(writeIsClosedVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ writeIsClosedVariable=true;
+ checkIfAllIsClosed();
+}
+
+bool TransferThread::checkIfAllIsClosed()
+{
+ if((readError || writeError) && !needSkip)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] resolve error before progress");
+ return false;
+ }
+ if((!readIsReadyVariable || readIsClosedVariable) && (!writeIsReadyVariable || writeIsClosedVariable))
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] emit internalStartPostOperation() to do the real post operation");
+ transfer_stat=TransferStat_PostOperation;
+ //emit pushStat(stat,transferId);
+ emit internalStartPostOperation();
+ return true;
+ }
+ else
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] "+QString("wait self close: readIsReadyVariable: %1, readIsClosedVariable: %2, writeIsReadyVariable: %3, writeIsClosedVariable: %4")
+ .arg(readIsReadyVariable)
+ .arg(readIsClosedVariable)
+ .arg(writeIsReadyVariable)
+ .arg(writeIsClosedVariable)
+ );
+ return false;
+ }
+}
+
+/// \todo found way to retry that's
+/// \todo the rights copy
+void TransferThread::postOperation()
+{
+ if(transfer_stat!=TransferStat_PostOperation)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] need be in transfer, source: "+source+", destination: "+destination+", stat:"+QString::number(transfer_stat));
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ //all except closing
+ if((readError || writeError) && !needSkip)//normally useless by checkIfAllIsFinish()
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] resume after error");
+ return;
+ }
+
+ if(!needSkip)
+ {
+ if(!doFilePostOperation())
+ return;
+
+ //remove source in moving mode
+ if(mode==Move && !canBeMovedDirectlyVariable)
+ {
+ if(QFile::exists(destination))
+ {
+ QFile sourceFile(source);
+ if(!sourceFile.remove())
+ {
+ emit errorOnFile(sourceInfo,sourceFile.errorString());
+ return;
+ }
+ }
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] try remove source but destination not exists!");
+ }
+ }
+ else//do difference skip a file and skip this error case
+ {
+ if(needRemove && QFile::exists(destination))
+ {
+ QFile destinationFile(destination);
+ if(!destinationFile.remove())
+ {
+ //emit errorOnFile(sourceInfo,destinationFile.errorString());
+ //return;
+ }
+ }
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] try remove destination but not exists!");
+ }
+ transfer_stat=TransferStat_Idle;
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] emit postOperationStopped()");
+ emit postOperationStopped();
+}
+
+bool TransferThread::doFilePostOperation()
+{
+ //do operation needed by copy
+ if(!canBeMovedDirectlyVariable)
+ {
+ //set the time if no write thread used
+ if(keepDate)
+ changeFileDateTime(destination,source);//can't do that's after move because after move the source not exist
+ /*
+ ignore it, because need correct management, mainly with move
+ if(!)
+ {
+ emit errorOnFile(destinationInfo,tr("Unable to change the date"));//destination.errorString()
+ return false;
+ }*/
+ }
+
+ if(stopIt)
+ return false;
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////
+/////////////////////// Error management /////////////////////////
+//////////////////////////////////////////////////////////////////
+
+void TransferThread::getWriteError()
+{
+ if(writeError)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] already in write error!");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ fileContentError = true;
+ writeError = true;
+ writeIsReadyVariable = false;
+ writeError_source_seeked = false;
+ writeError_destination_reopened = false;
+ emit errorOnFile(destinationInfo,writeThread.errorString());
+}
+
+void TransferThread::getReadError()
+{
+ if(readError)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] already in read error!");
+ return;
+ }
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ fileContentError = true;
+ readError = true;
+ writeIsReadyVariable = false;
+ readIsReadyVariable = false;
+ emit errorOnFile(sourceInfo,readThread.errorString());
+}
+
+//retry after error
+void TransferThread::retryAfterError()
+{
+ //opening error
+ if(transfer_stat==TransferStat_PreOperation)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] is not idle, source: "+source+", destination: "+destination+", stat: "+QString::number(transfer_stat));
+ tryOpen();
+ return;
+ }
+ //data streaming error
+ if(transfer_stat!=TransferStat_PostOperation && transfer_stat!=TransferStat_Transfer && transfer_stat!=TransferStat_Checksum)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Critical,"["+QString::number(id)+"] is not idle, source: "+source+", destination: "+destination+", stat: "+QString::number(transfer_stat));
+ return;
+ }
+ if(canBeMovedDirectlyVariable)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] retry the system move");
+ tryMoveDirectly();
+ return;
+ }
+ if(transfer_stat==TransferStat_Checksum)
+ {
+ if(writeError)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start and resume the write error");
+ writeThread.reopen();
+ }
+ else if(readError)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start and resume the read error");
+ readThread.reopen();
+ }
+ else //only checksum difference
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] retry all the transfer");
+ canStartTransfer=true;
+ ifCanStartTransfer();
+ }
+ return;
+ }
+ if(writeError)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start and resume the write error");
+ readThread.seekToZeroAndWait();
+ writeThread.reopen();
+ }
+ else if(readError)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start and resume the read error");
+ readThread.reopen();
+ }
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] unknow error resume");
+}
+
+void TransferThread::writeThreadIsReopened()
+{
+ if(writeError_destination_reopened)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ writeError_destination_reopened=true;
+ if(transfer_stat==TransferStat_Checksum)
+ {
+ writeThread.startCheckSum();
+ return;
+ }
+ if(writeError_source_seeked && writeError_destination_reopened)
+ resumeTransferAfterWriteError();
+}
+
+void TransferThread::readThreadIsSeekToZeroAndWait()
+{
+ if(writeError_source_seeked)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] double event dropped");
+ return;
+ }
+ writeError_source_seeked=true;
+ if(writeError_source_seeked && writeError_destination_reopened)
+ resumeTransferAfterWriteError();
+}
+
+void TransferThread::resumeTransferAfterWriteError()
+{
+ writeError=false;
+/********************************
+ if(canStartTransfer)
+ readThread.startRead();
+useless, because the open destination event
+will restart the transfer as normal
+*********************************/
+/*********************************
+if(!canStartTransfer)
+ stat=WaitForTheTransfer;
+useless because already do at open event
+**********************************/
+ //if is in wait
+ if(!canStartTransfer)
+ emit checkIfItCanBeResumed();
+}
+
+void TransferThread::readThreadResumeAfterError()
+{
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ readError=false;
+ writeIsReady();
+ readIsReady();
+}
+
+//////////////////////////////////////////////////////////////////
+///////////////////////// Normal event ///////////////////////////
+//////////////////////////////////////////////////////////////////
+
+void TransferThread::readIsStopped()
+{
+ if(!sended_state_readStopped)
+ {
+ sended_state_readStopped=true;
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] emit readIsStopped()");
+ emit readStopped();
+ }
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] drop dual read stopped");
+ readIsFinish();
+}
+
+void TransferThread::writeIsStopped()
+{
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start");
+ if(!sended_state_writeStopped)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] emit writeStopped()");
+ sended_state_writeStopped=true;
+ emit writeStopped();
+ }
+ else
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] double event dropped");
+ writeIsFinish();
+}
+
+void TransferThread::timeOfTheBlockCopyFinished()
+{
+ readThread.timeOfTheBlockCopyFinished();
+}
+
+//get drive of an file or folder
+QString TransferThread::getDrive(QString fileOrFolder)
+{
+ for (int i = 0; i < mountSysPoint.size(); ++i) {
+ if(fileOrFolder.startsWith(mountSysPoint.at(i)))
+ return mountSysPoint.at(i);
+ }
+ //if unable to locate the right mount point
+ return "";
+}
+
+//set drive list, used in getDrive()
+void TransferThread::setDrive(QStringList drives)
+{
+ mountSysPoint=drives;
+}
+
+//fonction to edit the file date time
+bool TransferThread::changeFileDateTime(const QString &source,const QString &destination)
+{
+ /*
+ if(maxTime>=sourceInfo.lastModified())
+ return;
+ */
+ /** Why not do it with Qt? Because it not support setModificationTime(), and get the time with Qt, that's mean use local time where in C is UTC time */
+ #ifdef Q_OS_UNIX
+ #ifdef Q_OS_LINUX
+ struct stat info;
+ stat(source.toLatin1().data(),&info);
+ time_t ctime=info.st_ctim.tv_sec;
+ time_t actime=info.st_atim.tv_sec;
+ time_t modtime=info.st_mtim.tv_sec;
+ //this function avalaible on unix and mingw
+ utimbuf butime;
+ butime.actime=actime;
+ butime.modtime=modtime;
+ //creation time not exists into unix world
+ Q_UNUSED(ctime)
+ return utime(destination.toLatin1().data(),&butime)==0;
+ #else //mainly for mac
+ QFileInfo fileInfo(destination);
+ time_t ctime=fileInfo.created().toTime_t();
+ time_t actime=fileInfo.lastRead().toTime_t();
+ time_t modtime=fileInfo.lastModified().toTime_t();
+ //this function avalaible on unix and mingw
+ utimbuf butime;
+ butime.actime=actime;
+ butime.modtime=modtime;
+ //creation time not exists into unix world
+ Q_UNUSED(ctime)
+ return utime(destination.toLatin1().data(),&butime)==0;
+ #endif
+ #else
+ #ifdef Q_OS_WIN32
+ #ifdef ULTRACOPIER_PLUGIN_SET_TIME_UNIX_WAY
+ struct __stat64 info;
+ _stat64(source.toLatin1().data(),&info);
+ time_t ctime=info.st_ctime;
+ time_t actime=info.st_atime;
+ time_t modtime=info.st_mtime;
+ //this function avalaible on unix and mingw
+ utimbuf butime;
+ butime.actime=actime;
+ butime.modtime=modtime;
+ //creation time not exists into unix world
+ Q_UNUSED(ctime)
+ return utime(destination.toLatin1().data(),&butime)==0;
+ #else
+ wchar_t filePath[65535];
+ source.toWCharArray(filePath);
+ HANDLE hFileSouce = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ destination.toWCharArray(filePath);
+ HANDLE hFileDestination = CreateFile(filePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if(hFileSouce == INVALID_HANDLE_VALUE)
+ {
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] open failed to read");
+ return false;
+ }
+ if(hFileDestination == INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hFileSouce);
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] open failed to write");
+ return false;
+ }
+ FILETIME ftCreate, ftAccess, ftWrite;
+ if(!GetFileTime(hFileSouce, &ftCreate, &ftAccess, &ftWrite))
+ {
+ CloseHandle(hFileSouce);
+ CloseHandle(hFileDestination);
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] unable to get the file time");
+ return false;
+ }
+ if(!SetFileTime(hFileDestination, &ftCreate, &ftAccess, &ftWrite))
+ {
+ CloseHandle(hFileSouce);
+ CloseHandle(hFileDestination);
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] unable to set the file time");
+ return false;
+ }
+ CloseHandle(hFileSouce);
+ CloseHandle(hFileDestination);
+ return true;
+ #endif
+ #else
+ return false;
+ #endif
+ #endif
+ return false;
+}
+
+//skip the copy
+void TransferThread::skip()
+{
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] start with stat: "+QString::number(transfer_stat));
+ switch(transfer_stat)
+ {
+ case TransferStat_PreOperation:
+ case TransferStat_WaitForTheTransfer:
+ needSkip=true;
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] case WaitForTheTransfer or PreOperation, readIsReadyVariable: "+QString::number(readIsReadyVariable)+", readIsClosedVariable: "+QString::number(readIsClosedVariable)+", writeIsReadyVariable: "+QString::number(writeIsReadyVariable)+", writeIsClosedVariable: "+QString::number(writeIsClosedVariable));
+ //check if all is source and destination is closed
+ if(!checkIfAllIsClosed())
+ {
+ if(readIsReadyVariable && !readIsClosedVariable)
+ readThread.stop();
+ if(writeIsReadyVariable && !writeIsClosedVariable)
+ writeThread.stop();
+ }
+ break;
+ case TransferStat_Transfer:
+ needSkip=true;
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] case Transfer, readIsReadyVariable: "+QString::number(readIsReadyVariable)+", readIsClosedVariable: "+QString::number(readIsClosedVariable)+", writeIsReadyVariable: "+QString::number(writeIsReadyVariable)+", writeIsClosedVariable: "+QString::number(writeIsClosedVariable));
+ if(!checkIfAllIsClosed())
+ {
+ if(readIsReadyVariable && !readIsClosedVariable)
+ readThread.stop();
+ if(writeIsReadyVariable && !writeIsClosedVariable)
+ writeThread.stop();
+ }
+ break;
+ case TransferStat_Checksum:
+ needSkip=true;
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Notice,"["+QString::number(id)+"] case Transfer, readIsReadyVariable: "+QString::number(readIsReadyVariable)+", readIsClosedVariable: "+QString::number(readIsClosedVariable)+", writeIsReadyVariable: "+QString::number(writeIsReadyVariable)+", writeIsClosedVariable: "+QString::number(writeIsClosedVariable));
+ if(!checkIfAllIsClosed())
+ {
+ if(readIsReadyVariable && !readIsClosedVariable)
+ readThread.stop();
+ if(writeIsReadyVariable && !writeIsClosedVariable)
+ writeThread.stop();
+ }
+ break;
+ case TransferStat_PostOperation:
+ //do nothing because here is closing...
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] is already in post op");
+ break;
+ default:
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] can skip in this state!");
+ return;
+ }
+}
+
+//return info about the copied size
+qint64 TransferThread::copiedSize()
+{
+ switch(transfer_stat)
+ {
+ case TransferStat_Transfer:
+ case TransferStat_PostOperation:
+ return readThread.getLastGoodPosition();
+ case TransferStat_Checksum:
+ return transferSize;
+ default:
+ return 0;
+ }
+}
+
+//retry after error
+void TransferThread::putAtBottom()
+{
+ emit tryPutAtBottom();
+}
+
+/// \brief set rsync
+void TransferThread::setRsync(const bool rsync)
+{
+ this->rsync=rsync;
+}
+
+void TransferThread::set_osBufferLimit(unsigned int osBufferLimit)
+{
+ this->osBufferLimit=osBufferLimit;
+}
+
+#ifdef ULTRACOPIER_PLUGIN_DEBUG
+//to set the id
+void TransferThread::setId(int id)
+{
+ this->id=id;
+ readThread.setId(id);
+ writeThread.setId(id);
+}
+
+QChar TransferThread::readingLetter()
+{
+ switch(readThread.stat)
+ {
+ case ReadThread::Idle:
+ return '_';
+ break;
+ case ReadThread::InodeOperation:
+ return 'I';
+ break;
+ case ReadThread::Read:
+ return 'R';
+ break;
+ case ReadThread::WaitWritePipe:
+ return 'W';
+ break;
+ case ReadThread::Checksum:
+ return 'S';
+ break;
+ default:
+ return '?';
+ }
+}
+
+QChar TransferThread::writingLetter()
+{
+ switch(writeThread.stat)
+ {
+ case WriteThread::Idle:
+ return '_';
+ break;
+ case WriteThread::InodeOperation:
+ return 'I';
+ break;
+ case WriteThread::Write:
+ return 'W';
+ break;
+ case WriteThread::Close:
+ return 'C';
+ break;
+ case WriteThread::Read:
+ return 'R';
+ break;
+ case WriteThread::Checksum:
+ return 'S';
+ break;
+ default:
+ return '?';
+ }
+}
+
+#endif
+
+void TransferThread::setMkpathTransfer(QSemaphore *mkpathTransfer)
+{
+ this->mkpathTransfer=mkpathTransfer;
+ writeThread.setMkpathTransfer(mkpathTransfer);
+}
+
+void TransferThread::set_doChecksum(bool doChecksum)
+{
+ this->doChecksum=doChecksum;
+}
+
+void TransferThread::set_checksumIgnoreIfImpossible(bool checksumIgnoreIfImpossible)
+{
+ this->checksumIgnoreIfImpossible=checksumIgnoreIfImpossible;
+}
+
+void TransferThread::set_checksumOnlyOnError(bool checksumOnlyOnError)
+{
+ this->checksumOnlyOnError=checksumOnlyOnError;
+}
+
+void TransferThread::set_osBuffer(bool osBuffer)
+{
+ this->osBuffer=osBuffer;
+}
+
+void TransferThread::set_osBufferLimited(bool osBufferLimited)
+{
+ this->osBufferLimited=osBufferLimited;
+}
+
+//not copied size, because that's count to the checksum, ...
+quint64 TransferThread::realByteTransfered()
+{
+ switch(transfer_stat)
+ {
+ case TransferStat_Transfer:
+ case TransferStat_PostOperation:
+ case TransferStat_Checksum:
+ return readThread.getLastGoodPosition();
+ ULTRACOPIER_DEBUGCONSOLE(DebugLevel_Warning,"["+QString::number(id)+"] transferSize: "+QString::number(transferSize));
+ return transferSize;
+ default:
+ return 0;
+ }
+}
+
+void TransferThread::setRenamingRules(QString firstRenamingRule,QString otherRenamingRule)
+{
+ this->firstRenamingRule=firstRenamingRule;
+ this->otherRenamingRule=otherRenamingRule;
+}