//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" #include #include #include #ifdef WIDESTRING #include //#include #endif #ifdef Q_OS_WIN32 #include #include #endif #include "../../../cpp11addition.h" TransferThread::TransferThread() : mode(Ultracopier::CopyMode::Copy), transfer_stat (TransferStat_Idle), doRightTransfer (false), #ifdef ULTRACOPIER_PLUGIN_RSYNC rsync (false), #endif keepDate (false), mkFullPath (false), stopIt (false), fileExistsAction (FileExists_NotSet), alwaysDoFileExistsAction (FileExists_NotSet), needSkip (false), needRemove (false), deletePartiallyTransferredFiles (true), renameTheOriginalDestination (false), writeError (false), readError (false), havePermission (false), haveTransferTime (false) { id=0; renameRegex=std::regex("^(.*)(\\.[a-zA-Z0-9]+)$"); #ifdef Q_OS_WIN32 regRead=std::regex("^[a-zA-Z]:"); #endif transferSize = 0;//external set by ListThread #ifndef Q_OS_UNIX PSecurityD=NULL; dacl=NULL; #endif #ifdef Q_OS_Win32 stopItWin=0; #endif //if not QThread run(); } TransferThread::~TransferThread() { #ifdef Q_OS_WIN32 stopItWin=1; #endif stopIt=true; //else cash without this disconnect //disconnect(&readThread); //disconnect(&writeThread); #ifndef Q_OS_UNIX if(PSecurityD!=NULL) { free(PSecurityD); PSecurityD=NULL; } if(dacl!=NULL) { //free(dacl); dacl=NULL; } #endif } void TransferThread::run() { moveToThread(this); //ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+QStringLiteral("] start: ")+QString::number((qint64)QThread::currentThreadId()))); transfer_stat = TransferStat_Idle; stopIt = false; fileExistsAction = FileExists_NotSet; alwaysDoFileExistsAction= FileExists_NotSet; #ifdef ULTRACOPIER_PLUGIN_DEBUG if(!connect(&driveManagement,&DriveManagement::debugInformation,this, &TransferThread::debugInformation, Qt::QueuedConnection)) abort(); #endif } TransferStat TransferThread::getStat() const { return transfer_stat; } #ifdef WIDESTRING INTERNALTYPEPATH TransferThread::stringToInternalString(const std::string& utf8) { /* buggy on MXE std::wstring_convert> converter; return converter.from_bytes(utf8);*/ return QString::fromUtf8(utf8.data(),utf8.size()).toStdWString(); //return widen(utf8); } std::string TransferThread::internalStringTostring(const INTERNALTYPEPATH& utf16) { /* buggy on MXE std::wstring_convert> conv1; return conv1.to_bytes(utf16);*/ const QByteArray &data=QString::fromStdWString(utf16).toUtf8(); return std::string(data.constData(),data.size()); //return narrow(utf16); } #else std::string TransferThread::stringToInternalString(const std::string& utf8) { return utf8; } std::string TransferThread::internalStringTostring(const std::string& utf16) { return utf16; } #endif bool TransferThread::setFiles(const INTERNALTYPEPATH& source, const int64_t &size, const INTERNALTYPEPATH& destination, const Ultracopier::CopyMode &mode) { if(transfer_stat!=TransferStat_Idle) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"["+std::to_string(id)+("] already used, source: ")+ TransferThread::internalStringTostring(source)+", destination: "+TransferThread::internalStringTostring(destination)); return false; } //to prevent multiple file alocation into ListThread::doNewActions_inode_manipulation() transfer_stat = TransferStat_PreOperation; //emit pushStat(stat,transferId); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] start, source: "+TransferThread::internalStringTostring(source)+", destination: "+TransferThread::internalStringTostring(destination)); this->source = source; this->destination = destination; this->mode = mode; this->size = size; stopIt = false; #ifdef Q_OS_WIN32 stopItWin=0; #endif fileExistsAction = FileExists_NotSet; canStartTransfer = false; sended_state_preOperationStopped= false; fileContentError = false; writeError = false; readError = false; haveTransferTime = false; canStartTransfer = false; resetExtraVariable(); emit internalStartPreOperation(); startTransferTime.restart(); return true; } void TransferThread::setFileRename(const std::string &nameForRename) { if(transfer_stat!=TransferStat_PreOperation) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"["+std::to_string(id)+("] already used, source: ")+ TransferThread::internalStringTostring(source)+(", destination: ")+TransferThread::internalStringTostring(destination)); return; } if(QString::fromStdString(nameForRename).contains(QRegularExpression(QStringLiteral("[/\\\\\\*]")))) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"["+std::to_string(id)+("] can't use this kind of name, internal error")); emit errorOnFile(destination,tr("Try rename with using special characters").toStdString()); return; } #ifdef WIDESTRING std::string::size_type n=destination.rfind(L'/'); #else std::string::size_type n=destination.rfind('/'); #endif #ifdef Q_OS_WIN32 const std::wstring::size_type n2=destination.rfind(L'\\'); if(n2>n && (n2!=std::wstring::npos || n==std::wstring::npos)) n=n2; #endif #ifdef WIDESTRING if(n != std::wstring::npos) #else if(n != std::string::npos) #endif { INTERNALTYPEPATH destinationPath=destination.substr(0,n);//+1 ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] nameForRename: "+nameForRename+", destinationPath: "+internalStringTostring(destinationPath)); if(stringEndsWith(destinationPath,'/') #ifdef Q_OS_WIN32 || stringEndsWith(destinationPath,'\\') #endif ) destination=destinationPath+TransferThread::stringToInternalString(nameForRename); else destination=destinationPath+TransferThread::stringToInternalString("/")+TransferThread::stringToInternalString(nameForRename); } else destination=TransferThread::stringToInternalString(nameForRename); /*why all this code? if(!renameTheOriginalDestination) destination=destination+TransferThread::stringToInternalString("/")+TransferThread::stringToInternalString(nameForRename); else { //INTERNALTYPEPATH tempDestination=destination; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Information,"["+std::to_string(id)+"] rename "+TransferThread::internalStringTostring(destination)+ ": to: "+TransferThread::internalStringTostring(destination)+"/"+nameForRename); if(!rename(destination,(destination+TransferThread::stringToInternalString("/")+TransferThread::stringToInternalString(nameForRename)))) { if(!is_file(destination)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] source not exists "+TransferThread::internalStringTostring(destination)+ ": destination: "+TransferThread::internalStringTostring(destination)+", error: "+std::to_string(errno)); emit errorOnFile(destination,tr("File not found").toStdString()); return; } else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] unable to do real move "+TransferThread::internalStringTostring(destination)+ ": "+TransferThread::internalStringTostring(destination)+", error: "+std::to_string(errno)); emit errorOnFile(destination,"errno: "+std::string(strerror(errno))); return; } if(source==destination) source=destination+TransferThread::stringToInternalString("/")+TransferThread::stringToInternalString(nameForRename); destination=tempDestination; }*/ fileExistsAction = FileExists_NotSet; resetExtraVariable(); emit internalStartPreOperation(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] destination is: "+TransferThread::internalStringTostring(destination)); } bool TransferThread::rename(const INTERNALTYPEPATH &source, const INTERNALTYPEPATH &destination) { #ifdef Q_OS_WIN32 return MoveFileExW( TransferThread::toFinalPath(source).c_str(), TransferThread::toFinalPath(destination).c_str(), MOVEFILE_REPLACE_EXISTING); #else return ::rename(TransferThread::internalStringTostring(source).c_str(), TransferThread::internalStringTostring(destination).c_str())==0; #endif } void TransferThread::setAlwaysFileExistsAction(const FileExistsAction &action) { //ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+QStringLiteral("] action to do always: ")+QString::number(action))); alwaysDoFileExistsAction=action; } void TransferThread::resetExtraVariable() { sended_state_preOperationStopped=false; writeError = false; readError = false; needRemove = false; needSkip = false; retry = false; havePermission = false; } bool TransferThread::isSame() { //check if source and destination is not the same //source.absoluteFilePath()==destination.absoluteFilePath() not work is source don't exists if(source==destination) { #ifdef ULTRACOPIER_PLUGIN_DEBUG if(!is_file(destination)) ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] start source: "+TransferThread::internalStringTostring(source)+" not exists"); if(is_symlink(source)) ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] start source: "+TransferThread::internalStringTostring(source)+" isSymLink"); if(is_symlink(destination)) ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] start destination: "+TransferThread::internalStringTostring(destination)+" isSymLink"); #endif if(fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_Skip) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] is same but skip"); transfer_stat=TransferStat_Idle; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] transfer_stat=TransferStat_Idle"); emit postOperationStopped(); //quit return true; } if(checkAlwaysRename()) return false; emit fileAlreadyExists(source,destination,true); return true; } return false; } bool TransferThread::destinationExists() { //check if destination exists ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] "+QStringLiteral("overwrite: %1, alwaysDoFileExistsAction: %2, readError: %3, writeError: %4") .arg(fileExistsAction) .arg(alwaysDoFileExistsAction) .arg(readError) .arg(writeError) .toStdString() ); if(alwaysDoFileExistsAction==FileExists_Overwrite || readError || writeError #ifdef ULTRACOPIER_PLUGIN_RSYNC || rsync #endif ) return false; bool destinationExists=false; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] time to first FS access"); destinationExists=is_file(destination); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] finish first FS access: "+std::to_string(destinationExists)); if(destinationExists) { if(fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_Skip) { transfer_stat=TransferStat_Idle; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] transfer_stat=TransferStat_Idle"); emit postOperationStopped(); //quit return true; } ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] FS access"); if(checkAlwaysRename()) return false; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] FS access"); if(is_file(source)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] FS access"); if(fileExistsAction==FileExists_OverwriteIfNewer || (fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_OverwriteIfNewer)) { const int64_t &sm=readFileMDateTime(source); const int64_t &sd=readFileMDateTime(destination); if(sm==-1) { #ifndef Q_OS_WIN32 const int e=errno; emit errorOnFile(source,"Unable to read modification time: "+std::string(strerror(e))+" ("+std::to_string(e)+")"); #else emit errorOnFile(source,"Unable to read modification time: "+GetLastErrorStdStr()); #endif return true; } if(sd==-1) { #ifndef Q_OS_WIN32 const int e=errno; emit errorOnFile(destination,"Unable to read modification time: "+std::string(strerror(e))+" ("+std::to_string(e)+")"); #else emit errorOnFile(destination,"Unable to read modification time: "+GetLastErrorStdStr()); #endif return true; } ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] readFileMDateTime(source): "+std::to_string(sm)); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] readFileMDateTime(destination): "+std::to_string(sd)); if(sm>sd || sm==-1 || sd==-1) return false; else { transfer_stat=TransferStat_Idle; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] transfer_stat=TransferStat_Idle;"); emit postOperationStopped(); return true; } } ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] FS access"); if(fileExistsAction==FileExists_OverwriteIfOlder || (fileExistsAction==FileExists_NotSet && alwaysDoFileExistsAction==FileExists_OverwriteIfOlder)) { const int64_t &sm=readFileMDateTime(source); const int64_t &sd=readFileMDateTime(destination); if(sm==-1) { #ifndef Q_OS_WIN32 const int e=errno; emit errorOnFile(source,"Unable to read modification time: "+std::string(strerror(e))+" ("+std::to_string(e)+")"); #else emit errorOnFile(source,"Unable to read modification time: "+GetLastErrorStdStr()); #endif return true; } if(sd==-1) { #ifndef Q_OS_WIN32 const int e=errno; emit errorOnFile(destination,"Unable to read modification time: "+std::string(strerror(e))+" ("+std::to_string(e)+")"); #else emit errorOnFile(destination,"Unable to read modification time: "+GetLastErrorStdStr()); #endif return true; } ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] readFileMDateTime(source): "+std::to_string(sm)); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] readFileMDateTime(destination): "+std::to_string(sd)); if(sm file * /file -> file * /dir1/dir2/ -> dir2 * /dir1/ -> dir1 * / -> root */ #ifdef Q_OS_WIN32 std::string TransferThread::resolvedName(std::string inode) #else std::string TransferThread::resolvedName(const std::string &inode) #endif { #ifdef Q_OS_WIN32 stringreplaceAll(inode,"\\","/"); #endif const std::string::size_type &lastPos=inode.rfind('/'); if(lastPos == std::string::npos || (lastPos==0 && inode.size()==1)) return "root"; if((lastPos+1)!=inode.size()) return inode.substr(lastPos+1); if(inode.size()==1) return "root"; const std::string::size_type &previousLastPos=inode.rfind('/',inode.size()-2); if((lastPos-2)==previousLastPos || previousLastPos == std::string::npos) return "root"; return inode.substr(previousLastPos+1,lastPos-previousLastPos-1); } #ifdef Q_OS_WIN32 std::wstring TransferThread::resolvedName(std::wstring inode) #else std::wstring TransferThread::resolvedName(const std::wstring &inode) #endif { #ifdef Q_OS_WIN32 stringreplaceAll(inode,L"\\",L"/"); #endif const std::wstring::size_type &lastPos=inode.rfind(L'/'); if(lastPos == std::wstring::npos || (lastPos==0 && inode.size()==1)) return L"root"; if((lastPos+1)!=inode.size()) return inode.substr(lastPos+1); if(inode.size()==1) return L"root"; const std::wstring::size_type &previousLastPos=inode.rfind(L'/',inode.size()-2); if((lastPos-2)==previousLastPos || previousLastPos == std::wstring::npos) return L"root"; return inode.substr(previousLastPos+1,lastPos-previousLastPos-1); } INTERNALTYPEPATH TransferThread::getSourcePath() const { return source; } INTERNALTYPEPATH TransferThread::getDestinationPath() const { return destination; } Ultracopier::CopyMode TransferThread::getMode() const { return mode; } //return true if has been renamed bool TransferThread::checkAlwaysRename() { if(alwaysDoFileExistsAction==FileExists_Rename) { INTERNALTYPEPATH newDestination=destination; std::string fileName=resolvedName(TransferThread::internalStringTostring(newDestination)); std::string suffix; std::string newFileName; //resolv the suffix if(std::regex_match(fileName,renameRegex)) { suffix=fileName; suffix=std::regex_replace(suffix,renameRegex,"$2"); fileName=std::regex_replace(fileName,renameRegex,"$1"); } //resolv the new name int num=1; do { if(num==1) { if(firstRenamingRule.empty()) newFileName=tr("%name% - copy%suffix%").toStdString(); else newFileName=firstRenamingRule; } else { if(otherRenamingRule.empty()) newFileName=tr("%name% - copy (%number%)%suffix%").toStdString(); else newFileName=otherRenamingRule; stringreplaceAll(newFileName,"%number%",std::to_string(num)); } stringreplaceAll(newFileName,"%name%",fileName); stringreplaceAll(newFileName,"%suffix%",suffix); newDestination=FSabsolutePath(newDestination); if(!stringEndsWith(newDestination,'/') #ifdef Q_OS_WIN32 && !stringEndsWith(destination,'\\') #endif ) newDestination+=TransferThread::stringToInternalString("/"); newDestination+=TransferThread::stringToInternalString(newFileName); num++; } while(is_file(newDestination)); if(!renameTheOriginalDestination) destination=newDestination; else { if(rename(destination.c_str(),newDestination.c_str())!=0) { if(!is_file(destination)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] source not exists "+TransferThread::internalStringTostring(destination)+ ": destination: "+TransferThread::internalStringTostring(newDestination)+", error: "+std::to_string(errno)); emit errorOnFile(destination,tr("File not found").toStdString()); readError=true; return true; } else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] unable to do real move "+TransferThread::internalStringTostring(destination)+ ": "+TransferThread::internalStringTostring(newDestination)+", error: "+std::to_string(errno)); readError=true; emit errorOnFile(destination,std::string(strerror(errno))+", errno: "+std::string(strerror(errno))); return true; } } return true; } return false; } /// \warning not check if path have double //, if have do bug return failed /// \warning check mkpath() call should not exists because only existing dest is allowed now #ifdef Q_OS_UNIX bool TransferThread::mkpath(const INTERNALTYPEPATH &path, const mode_t &mode) #else bool TransferThread::mkpath(const INTERNALTYPEPATH &path) #endif { #ifdef Q_OS_WIN32 if(!mkdir(path)) if(errno==EEXIST) return true; printf("%i",errno); #ifndef WIDESTRING #error if windows, WIDESTRING need be enabled #endif //use reverse method to performance, more complex to code but less system call/fs call std::wstring::size_type previouspos=path.size(); std::wstring::size_type lastpos=std::string::npos; const wchar_t *pathC=path.c_str(); wchar_t pathCedit[32000]; wcscpy(pathCedit,pathC); std::vector pathSplit; pathSplit.push_back(path.size()); do { lastpos=path.rfind(L'/',previouspos-1); if(lastpos == std::wstring::npos) return false; while(pathC[lastpos-1]==L'/') if(lastpos>0) lastpos--; else return false; //buggy case? #ifdef Q_OS_UNIX if(lastpos<2) return false; #else if(lastpos<4) return false; #endif pathCedit[lastpos]=L'\0'; previouspos=lastpos; errno=0; if(!mkdir(pathCedit)) if(errno!=EEXIST && errno!=ENOENT) return false; //here errno can be: EEXIST, ENOENT, 0 if(errno==ENOENT) pathSplit.push_back(lastpos); } while(lastpos>0 && errno==ENOENT); do { wcscpy(pathCedit,pathC); lastpos=pathSplit.back(); pathSplit.pop_back(); pathCedit[lastpos]=L'\0'; #ifdef Q_OS_UNIX if(mkdir(pathCedit, mode)==-1) #else if(!mkdir(pathCedit)) #endif if(errno!=EEXIST) return false; } while(!pathSplit.empty()); return true; #else std::string pathC(TransferThread::internalStringTostring(path)); if(!mkdir(path)) if(errno==EEXIST) return true; printf("%i",errno); //use reverse method to performance, more complex to code but less system call/fs call std::string::size_type previouspos=path.size(); std::string::size_type lastpos=std::string::npos; std::string pathCedit(pathC); std::vector pathSplit; pathSplit.push_back(path.size()); do { lastpos=path.rfind('/',previouspos-1); if(lastpos == std::string::npos) return false; while(pathC[lastpos-1]=='/') if(lastpos>0) lastpos--; else return false; //buggy case? #ifdef Q_OS_UNIX if(lastpos<2) return false; #else if(lastpos<4) return false; #endif pathCedit.resize(lastpos); previouspos=lastpos; errno=0; #ifdef Q_OS_UNIX if(::mkdir(pathCedit.c_str(), mode)==-1) #else if(::mkdir(pathCedit.c_str())==-1) #endif if(errno!=EEXIST && errno!=ENOENT) return false; //here errno can be: EEXIST, ENOENT, 0 if(errno==ENOENT) pathSplit.push_back(lastpos); } while(lastpos>0 && errno==ENOENT); do { pathCedit = pathC; lastpos=pathSplit.back(); pathSplit.pop_back(); pathCedit.resize(lastpos); #ifdef Q_OS_UNIX if(::mkdir(pathCedit.c_str(), mode)==-1) #else if(::mkdir(pathCedit.c_str())==-1) #endif if(errno!=EEXIST) return false; } while(!pathSplit.empty()); return true; #endif } #ifdef Q_OS_UNIX bool TransferThread::mkdir(const INTERNALTYPEPATH &file_path, const mode_t &mode) #else bool TransferThread::mkdir(const INTERNALTYPEPATH &file_path) #endif { #ifdef Q_OS_WIN32 const bool r = CreateDirectory(TransferThread::toFinalPath(file_path).c_str(),NULL); const DWORD &t=GetLastError(); if(!r) { if(t==183/*is_dir(file_path) performance impact*/) errno=EEXIST; else if(t==3/*is_dir(file_path) performance impact*/) errno=ENOENT; else errno=t; } return r; #else #ifdef Q_OS_UNIX return ::mkdir(TransferThread::internalStringTostring(file_path).c_str(),mode)==0; #else return ::mkdir(TransferThread::internalStringTostring(file_path).c_str())==0; #endif #endif } bool TransferThread::canBeMovedDirectly() const { if(mode!=Ultracopier::Move) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] mode!=Ultracopier::Move"); return false; } return is_symlink(source) || driveManagement.isSameDrive( TransferThread::internalStringTostring(destination), TransferThread::internalStringTostring(source) ); } bool TransferThread::canBeCopiedDirectly() const { return is_symlink(source); } //set the copy info and options before runing void TransferThread::setRightTransfer(const bool doRightTransfer) { this->doRightTransfer=doRightTransfer; } /// \brief set buffer void TransferThread::setBuffer(const bool buffer) { Q_UNUSED(buffer); } /*void TransferThread::setBufferSize(const int parallelBuffer,const int serialBuffer) { writeThread->setBufferSize(const int parallelBuffer,const int serialBuffer); }*/ //set keep date void TransferThread::setKeepDate(const bool keepDate) { this->keepDate=keepDate; } bool TransferThread::doFilePostOperation() { //do operation needed by copy //set the time if no write thread used if(/*not implied by is_symlink, exist can be false because symlink dest not exists*/ !exists(destination) && !is_symlink(destination)) { if(!stopIt) if(/*true when the destination have been remove but not the symlink:*/!is_symlink(source)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] Unable to change the date: File not found"); emit errorOnFile(destination,tr("Unable to change the date").toStdString()+": "+tr("File not found").toStdString()); return false; } } else { if(doRightTransfer) { //should be never used but... /*source.refresh(); if(source.exists())*/ if(havePermission) { if(!writeDestinationFilePermissions(destination)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] Unable to set the destination file permission"); //emit errorOnFile(destination,tr("Unable to set the destination file permission")); //return false; } } else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] Try doRightTransfer when source not exists"); } //date at end because previous change can touch the file if(doTheDateTransfer) { if(!writeDestinationFileDateTime(destination)) { if(!is_file(destination)) ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] Unable to change the date (is not a file)"); else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] Unable to change the date"); /* error with virtual folder under windows */ #ifndef Q_OS_WIN32 if(keepDate) { emit errorOnFile(destination,tr("Unable to change the date").toStdString()); return false; } #endif } /*else -> need be done at source m time read { #ifndef Q_OS_WIN32 ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"["+std::to_string(id)+"] read the destination time: "+destination.lastModified().toString().toStdString()); if(destination.lastModified()ftCreate=ftCreate; this->ftAccess=ftAccess; this->ftWrite=ftWrite; CloseHandle(hFileSouce); const uint64_t modtime=(uint64_t)ftWrite.dwLowDateTime + ((uint64_t)2^32 * (uint64_t)ftWrite.dwHighDateTime); if(modtimeftCreate; ftAccess=this->ftAccess; ftWrite=this->ftWrite; if(!SetFileTime(hFileDestination, &ftCreate, &ftAccess, &ftWrite)) { CloseHandle(hFileDestination); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] unable to set the file time"); return false; } CloseHandle(hFileDestination); return true; #else return false; #endif #endif return false; } bool TransferThread::readSourceFilePermissions(const INTERNALTYPEPATH &source) { #ifdef Q_OS_UNIX if(stat(TransferThread::internalStringTostring(source).c_str(), &permissions)!=0) return false; else return true; #else HANDLE hFile = CreateFileW(source.c_str(), GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] CreateFile() failed. Error: INVALID_HANDLE_VALUE: "+TransferThread::GetLastErrorStdStr()); return false; } DWORD lasterror = GetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &PSecurityD); if (lasterror != ERROR_SUCCESS) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] GetSecurityInfo() failed. Error"+std::to_string(lasterror)); return false; } CloseHandle(hFile); return true; #endif } bool TransferThread::writeDestinationFilePermissions(const INTERNALTYPEPATH &destination) { #ifdef Q_OS_UNIX if(chmod(TransferThread::internalStringTostring(destination).c_str(), permissions.st_mode)!=0) return false; if(chown(TransferThread::internalStringTostring(destination).c_str(), permissions.st_uid, permissions.st_gid)!=0) return false; return true; #else HANDLE hFile = CreateFileW(destination.c_str(),READ_CONTROL | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY,0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] CreateFile() failed. Error: INVALID_HANDLE_VALUE: "+TransferThread::GetLastErrorStdStr()); return false; } DWORD lasterror = SetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION , NULL, NULL, dacl, NULL); if (lasterror != ERROR_SUCCESS) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"["+std::to_string(id)+"] SetSecurityInfo() failed. Error"+std::to_string(lasterror)); return false; } CloseHandle(hFile); if(PSecurityD!=NULL) { //free(PSecurityD);//crash on some NAS, it's why is commented, http://forum-ultracopier.first-world.info/viewtopic.php?f=8&t=903&p=3689#p3689 PSecurityD=NULL; } if(dacl!=NULL) { //free(dacl); dacl=NULL; } return true; #endif } //retry after error void TransferThread::putAtBottom() { emit tryPutAtBottom(); } #ifdef ULTRACOPIER_PLUGIN_RSYNC /// \brief set rsync void TransferThread::setRsync(const bool rsync) { this->rsync=rsync; } #endif #ifdef ULTRACOPIER_PLUGIN_DEBUG //to set the id void TransferThread::setId(int id) { this->id=id; } #endif void TransferThread::setRenamingRules(const std::string &firstRenamingRule, const std::string &otherRenamingRule) { this->firstRenamingRule=firstRenamingRule; this->otherRenamingRule=otherRenamingRule; } void TransferThread::setDeletePartiallyTransferredFiles(const bool &deletePartiallyTransferredFiles) { this->deletePartiallyTransferredFiles=deletePartiallyTransferredFiles; } void TransferThread::setRenameTheOriginalDestination(const bool &renameTheOriginalDestination) { this->renameTheOriginalDestination=renameTheOriginalDestination; } void TransferThread::set_updateMount() { driveManagement.tryUpdate(); } bool TransferThread::is_symlink(const INTERNALTYPEPATH &filename) { #ifdef Q_OS_WIN32 WIN32_FILE_ATTRIBUTE_DATA fileInfo; BOOL r = GetFileAttributesExW(TransferThread::toFinalPath(filename).c_str(), GetFileExInfoStandard, &fileInfo); if(r != FALSE) { return fileInfo.dwFileAttributes != INVALID_FILE_ATTRIBUTES && (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); } else return false; #else return is_symlink(TransferThread::internalStringTostring(filename).c_str()); #endif } bool TransferThread::is_symlink(const char * const filename) { #ifdef Q_OS_WIN32 WIN32_FILE_ATTRIBUTE_DATA fileInfo; BOOL r = GetFileAttributesExA(TransferThread::toFinalPath(filename).c_str(), GetFileExInfoStandard, &fileInfo); if(r != FALSE) { return fileInfo.dwFileAttributes != INVALID_FILE_ATTRIBUTES && (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); } else return false; #else struct stat p_statbuf; if (lstat(filename, &p_statbuf) < 0) //if error or file not exists, considere as regular file return false; if (S_ISLNK(p_statbuf.st_mode)) return true; #endif return false; } bool TransferThread::is_file(const INTERNALTYPEPATH &filename) { #ifdef Q_OS_WIN32 DWORD dwAttrib = GetFileAttributesW(TransferThread::toFinalPath(filename).c_str()); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_NORMAL || dwAttrib & FILE_ATTRIBUTE_ARCHIVE) ); #else return is_file(TransferThread::internalStringTostring(filename).c_str()); #endif } bool TransferThread::is_file(const char * const filename) { #ifdef Q_OS_WIN32 DWORD dwAttrib = GetFileAttributesA(TransferThread::toFinalPath(filename).c_str()); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); #else struct stat p_statbuf; if (lstat(filename, &p_statbuf) < 0) //if error or file not exists, considere as regular file return false; if (S_ISREG(p_statbuf.st_mode)) return true; return false; #endif } bool TransferThread::is_dir(const INTERNALTYPEPATH &filename) { #ifdef Q_OS_WIN32 DWORD dwAttrib = GetFileAttributesW(TransferThread::toFinalPath(filename).c_str()); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) && !(dwAttrib & FILE_ATTRIBUTE_REPARSE_POINT)); #else return is_dir(TransferThread::internalStringTostring(filename).c_str()); #endif } bool TransferThread::is_dir(const char * const filename) { #ifdef Q_OS_WIN32 DWORD dwAttrib = GetFileAttributesA(TransferThread::toFinalPath(filename).c_str()); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) && !(dwAttrib & FILE_ATTRIBUTE_REPARSE_POINT)); #else struct stat p_statbuf; if (lstat(filename, &p_statbuf) < 0) //if error or file not exists, considere as regular file return false; if (S_ISDIR(p_statbuf.st_mode)) return true; return false; #endif } bool TransferThread::exists(const INTERNALTYPEPATH &filename) { #ifdef Q_OS_WIN32 DWORD dwAttrib = GetFileAttributesW(TransferThread::toFinalPath(filename).c_str()); return dwAttrib != INVALID_FILE_ATTRIBUTES; #else return exists(TransferThread::internalStringTostring(filename).c_str()); #endif } bool TransferThread::exists(const char * const filename) { #ifdef Q_OS_WIN32 DWORD dwAttrib = GetFileAttributesA(TransferThread::toFinalPath(filename).c_str()); return dwAttrib != INVALID_FILE_ATTRIBUTES; #else struct stat p_statbuf; if (lstat(filename, &p_statbuf) < 0) //if error or file not exists, considere as regular file return false; #endif return true; } int64_t TransferThread::file_stat_size(const INTERNALTYPEPATH &filename) { #ifdef Q_OS_WIN32 WIN32_FILE_ATTRIBUTE_DATA fileInfo; BOOL r = GetFileAttributesExW(TransferThread::toFinalPath(filename).c_str(), GetFileExInfoStandard, &fileInfo); if(r == FALSE) return -1; int64_t size=fileInfo.nFileSizeHigh; size<<=32; size|=fileInfo.nFileSizeLow; return size; #else return file_stat_size(TransferThread::internalStringTostring(filename).c_str()); #endif } int64_t TransferThread::file_stat_size(const char * const filename) { #ifdef Q_OS_WIN32 WIN32_FILE_ATTRIBUTE_DATA fileInfo; BOOL r = GetFileAttributesExA(TransferThread::toFinalPath(filename).c_str(), GetFileExInfoStandard, &fileInfo); if(r == FALSE) return -1; int64_t size=fileInfo.nFileSizeHigh; size<<=32; size|=fileInfo.nFileSizeLow; return size; #else struct stat p_statbuf; if (stat(filename, &p_statbuf) < 0) //if error or file not exists, considere as regular file return -1; if (lstat(filename, &p_statbuf) < 0) //if error or file not exists, considere as regular file return -1; return p_statbuf.st_size; #endif } bool TransferThread::entryInfoList(const INTERNALTYPEPATH &path,std::vector &list) { std::vector listTemp; if(!TransferThread::entryInfoList(path,listTemp)) return false; for(const dirent_uc &u : listTemp) list.push_back(u.d_name); return true; } bool TransferThread::rmdir(const INTERNALTYPEPATH &path) { #ifdef Q_OS_WIN32 return RemoveDirectoryW(TransferThread::toFinalPath(path).c_str()); #else return ::rmdir(TransferThread::internalStringTostring(path).c_str())==0; #endif } #ifdef Q_OS_UNIX bool TransferThread::entryInfoList(const INTERNALTYPEPATH &path,std::vector &list) { DIR *dp; struct dirent *ep; dp=opendir(TransferThread::internalStringTostring(path).c_str()); if(dp!=NULL) { do { ep=readdir(dp); if(ep!=NULL) { const std::string name(ep->d_name); if(name!="." && name!="..") { dirent_uc tempValue; #if defined(__HAIKU__) struct stat sp; memset(&sp,0,sizeof(sp)); if(stat((TransferThread::internalStringTostring(path)+"/"+name).c_str(), &sp)==0) tempValue.isFolder=S_ISDIR(sp.st_mode); else return false; #else tempValue.isFolder=ep->d_type==DT_DIR; #endif tempValue.d_name=TransferThread::stringToInternalString(ep->d_name); list.push_back(tempValue); } } } while(ep!=NULL); (void) closedir(dp); return true; } return false; } #else bool TransferThread::entryInfoList(const INTERNALTYPEPATH &path,std::vector &list) { HANDLE hFind = NULL; #ifdef WIDESTRING WIN32_FIND_DATAW fdFile; if((hFind = FindFirstFileW((TransferThread::toFinalPath(path)+L"\\*").c_str(), &fdFile)) == INVALID_HANDLE_VALUE) #else WIN32_FIND_DATAA fdFile; char finalpath[MAX_PATH]; strcpy(finalpath,path.c_str()); strcat(finalpath,"\\*"); if((hFind = FindFirstFileW(finalpath, &fdFile)) == INVALID_HANDLE_VALUE) #endif return false; do { #ifdef WIDESTRING if(wcscmp(fdFile.cFileName, L".")!=0 && wcscmp(fdFile.cFileName, L"..")!=0) #else if(strcmp(fdFile.cFileName, ".")!=0 && strcmp(fdFile.cFileName, "..")!=0) #endif { dirent_uc tempValue; tempValue.isFolder= (fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(fdFile.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ; tempValue.d_name=fdFile.cFileName; tempValue.size=fdFile.nFileSizeHigh; tempValue.size<<=32; tempValue.size|=fdFile.nFileSizeLow; list.push_back(tempValue); } } while(FindNextFileW(hFind, &fdFile)); FindClose(hFind); return true; } #endif void TransferThread::setMkFullPath(const bool mkFullPath) { this->mkFullPath=mkFullPath; } /*int TransferThread::fseeko64(FILE *__stream, uint64_t __off, int __whence) { #if defined(__HAIKU__) || defined(Q_OS_MAC) || defined(ANDROID) || defined(__ANDROID_API__) return ::fseeko(__stream,__off,__whence); #else return ::fseeko64(__stream,__off,__whence); #endif } int TransferThread::ftruncate64(int __fd, uint64_t __length) { #if defined(__HAIKU__) || defined(Q_OS_MAC) || defined(ANDROID) || defined(__ANDROID_API__) return ::ftruncate(__fd,__length); #else //return ::ftruncate64(__fd,__length); return ::ftruncate(__fd,__length); #endif }*/ int64_t TransferThread::transferTime() const { return startTransferTime.elapsed(); } #ifdef Q_OS_WIN32 std::wstring TransferThread::toFinalPath(std::wstring path) { if(path.size()==2 && path.at(1)==L':') path+=L"\\"; stringreplaceAll(path,L"/",L"\\"); std::wstring pathW; if(path.size()>2 && path.substr(0,2)==L"\\\\")//nas pathW=L"\\\\?\\UNC\\"+path.substr(2); else pathW=L"\\\\?\\"+path; return pathW; } std::string TransferThread::toFinalPath(std::string path) { if(path.size()==2 && path.at(1)==':') path+="\\"; stringreplaceAll(path,"/","\\"); std::string pathW; if(path.size()>2 && path.substr(0,2)=="\\\\")//nas pathW="\\\\?\\UNC\\"+path.substr(2); else pathW="\\\\?\\"+path; return pathW; } bool TransferThread::unlink(const std::wstring &path) { return DeleteFileW(TransferThread::toFinalPath(path).c_str()) || RemoveDirectoryW(TransferThread::toFinalPath(path).c_str()); } std::string TransferThread::GetLastErrorStdStr() { DWORD error = GetLastError(); if (error) { LPVOID lpMsgBuf; DWORD bufLen = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_DEFAULT), (LPSTR) &lpMsgBuf, 0, NULL ); if (bufLen) { std::string result((char *)lpMsgBuf, (int)bufLen); LocalFree(lpMsgBuf); return result+" ("+std::to_string(error)+")"; } return "1: "+std::to_string(error); } return "2: "+std::to_string(error); } #else bool TransferThread::unlink(const INTERNALTYPEPATH &path) { return ::unlink(internalStringTostring(path).c_str())==0; } #endif