//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 #include #include #include #endif #ifndef Q_OS_UNIX #include #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 && sizeacquire(); 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; }