diff -r 491b3ed49290 -r 65326cf895ed filemanager/Engine/src/CFileManagerActiveExecute.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/filemanager/Engine/src/CFileManagerActiveExecute.cpp Wed Sep 01 12:31:07 2010 +0100 @@ -0,0 +1,1178 @@ +/* +* Copyright (c) 2002-2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0"" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Handles the copy/move operation +* +*/ + + +// INCLUDE FILES +#include +//#include +//#include +#include "CFileManagerActiveExecute.h" +#include "MFileManagerProcessObserver.h" +#include "CFileManagerEngine.h" +#include "CFileManagerFileSystemIterator.h" +#include "Cfilemanagerindexiterator.h" +#include "CFileManagerCommonDefinitions.h" +#include "CFileManagerUtils.h" +#include "FileManagerDebug.h" +#include "CFileManagerThreadWrapper.h" + +// CONSTANTS +const TInt KFileManagerNotificationArrayGranularity = 64; + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::NewL +// +// ----------------------------------------------------------------------------- +// +EXPORT_C CFileManagerActiveExecute* CFileManagerActiveExecute::NewL( + CFileManagerEngine& aEngine, + MFileManagerProcessObserver::TFileManagerProcess aOperation, + MFileManagerProcessObserver& aObserver, + CArrayFixFlat& aIndexList, + const TDesC& aToFolder ) + { + CFileManagerActiveExecute* self = + new( ELeave ) CFileManagerActiveExecute( + aEngine, + aOperation, + aObserver ); + CleanupStack::PushL( self ); + self->ConstructL( aIndexList, aToFolder ); + CleanupStack::Pop( self ); + return self; + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::~CFileManagerActiveExecute +// +// ----------------------------------------------------------------------------- +// +EXPORT_C CFileManagerActiveExecute::~CFileManagerActiveExecute() + { + Cancel(); + delete iThreadWrapper; + delete iItemIterator; + delete iFullPath; + delete iDestination; + delete iToFolder; + delete iIndexList; + delete iChangedSrcItems; + delete iChangedDstItems; + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::ExecuteL +// +// ----------------------------------------------------------------------------- +// +EXPORT_C void CFileManagerActiveExecute::ExecuteL( TFileManagerSwitch aOverWrite ) + { + TInt error( KErrNone ); + + if ( iCancelled ) + { + TFileName newName; + TParsePtrC parse( *iFullPath ); + if ( parse.NameOrExtPresent() ) + { + newName.Copy( parse.NameAndExt() ); + } + else + { + TPtrC name( iEngine.LocalizedName( *iFullPath ) ); + if ( name.Length() > 0 ) + { + newName.Copy( name ); + } + else + { + newName = BaflUtils::FolderNameFromFullName( *iFullPath ); + } + } + iObserver.ProcessFinishedL( iError, newName ); + return; + } + + if ( aOverWrite == ENoOverWrite ) + { + iItemIterator->CurrentL( &iSrc, &iDst, iItemType ); + } + + if ( iDst && iDst->Length() > KMaxFileName ) + { + error = KErrBadName; + TRequestStatus* status = &iStatus; + User::RequestComplete( status, error ); + } + else if ( iItemType == EFileManagerFile ) + { +#ifdef __KEEP_DRM_CONTENT_ON_PHONE + if ( iSrc && iDst && + CFileManagerUtils::IsFromInternalToRemovableDrive( iFs, *iSrc, *iDst ) ) + { + TBool protectedFile( EFalse ); + + // silently ignore this file if it is protected, or if there + // was an error in checking. + // Did consider leaving, but what about eg KErrNotFound - eg another + // process moving/deleting the file... + TInt ret( iEngine.IsDistributableFile( *iSrc, protectedFile ) ); + if( protectedFile || ret != KErrNone ) + { + TRequestStatus* status = &iStatus; + User::RequestComplete( status, KErrNone ); + SetActive(); + return; + } + } +#endif + DoOperation( aOverWrite ); + return; + } + else if ( iItemType == EFileManagerFolder ) + { + if ( !iIsDstRemoteDrive && iEngine.IsNameFoundL( *iDst ) ) + { + TRequestStatus* status = &iStatus; + User::RequestComplete( status, KErrAlreadyExists ); + SetActive(); + return; + } + DoOperation( aOverWrite ); + return; + } + else + { + // We must complete this + TRequestStatus* status = &iStatus; + User::RequestComplete( status, error ); + } + SetActive(); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::CancelExecution +// +// ----------------------------------------------------------------------------- +// +EXPORT_C void CFileManagerActiveExecute::CancelExecution() + { + FUNC_LOG + + iCancelled = ETrue; + + if ( iSrc ) + { + iEngine.CancelTransfer( *iSrc ); + } + if ( iDst ) + { + iEngine.CancelTransfer( *iDst ); + } + delete iThreadWrapper; // Cancel thread + iThreadWrapper = NULL; + Cancel(); + TRAP_IGNORE( CompleteL( KErrCancel ) ); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::DoCancel +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::DoCancel() + { + iCancelled = ETrue; + + if ( iSrc ) + { + iEngine.CancelTransfer( *iSrc ); + } + if ( iDst ) + { + iEngine.CancelTransfer( *iDst ); + } + delete iThreadWrapper; // Cancel thread + iThreadWrapper = NULL; + } + +// ------------------------------------------------------------------------------ +// CFileManagerActiveExecute::KErrNoneAction +// +// ------------------------------------------------------------------------------ +// +void CFileManagerActiveExecute::KErrNoneActionL() + { + UpdateNotifications( EFalse, KErrNone ); + if ( iItemIterator->NextL() ) + { + ExecuteL( ENoOverWrite ); + } + else + { + if ( iOperation == MFileManagerProcessObserver::EMoveProcess && + iEngine.IsFolder( iIndexList->At( 0 ) ) ) + { + if ( !iFinalizeMove ) + { + // Finalize move in the thread, the finalizing way take time + iFinalizeMove = ETrue; + DoOperation( ENoOverWrite ); + return; + } + } + UpdateNotifications( ETrue, KErrNone ); + iObserver.ProcessFinishedL( KErrNone ); + } + + } + +// ------------------------------------------------------------------------------ +// CFileManagerActiveExecute::KErrAlreadyExistsAction +// +// ------------------------------------------------------------------------------ +// +void CFileManagerActiveExecute::KErrAlreadyExistsActionL() + { + TParsePtrC dstParse( *iDst ); + HBufC* name = HBufC::NewLC( KMaxFileName ); + TPtr ptrName( name->Des() ); + TBool doContinue( EFalse ); + + // Depending on target file can it be delete, we ask overwrite or rename. + // If source and target is same, then rename is only possible choice. + if ( iItemType == EFileManagerFile && iEngine.CanDelete( *iDst ) && iSrc->FindF( *iDst ) ) + { + if ( iObserver.ProcessQueryOverWriteL( *iDst, ptrName, iOperation ) ) + { + ExecuteL( EOverWrite ); + } + else + { + // user does not want to overwrite item and ptrName should now contain + // user given new name + if ( ptrName.Length() > 0 ) + { + ptrName.Insert( 0, dstParse.DriveAndPath() ); + if ( !iDst->CompareF( ptrName ) ) + { + DoOperation( ENoOverWrite ); + } + else + { + iDst->Des().Copy( ptrName ); + // Overwrite, because user already queried by overwrite + DoOperation( EOverWrite ); + } + } + else + { + // User is not willing to rename item, continue current operation + doContinue = ETrue; + } + } + } + else + { + // item can't be overwrite + if ( iObserver.ProcessQueryRenameL( *iDst, ptrName, iOperation ) ) + { + if ( ptrName.Length() > 0 ) + { + if ( iItemType == EFileManagerFile ) + { + ptrName.Insert( 0, dstParse.DriveAndPath() ); + iDst->Des().Copy( ptrName ); + } + else if ( iItemType == EFileManagerFolder ) + { + TPtr ptr( iDst->Des() ); + AddLastFolder( ptr, ptrName, *iToFolder ); + iDestination->Des().Copy( ptr ); + } + // Overwrite, because user already queried by rename + ExecuteL( EOverWrite ); + } + else if ( iItemType == EFileManagerFolder ) + { + iCancelled = ETrue; + iError = KErrCancel; + ExecuteL( ENoOverWrite ); + } + else + { + // User is not willing to rename item, continue current operation + doContinue = ETrue; + } + } + else if ( iItemType == EFileManagerFolder ) + { + iCancelled = ETrue; + iError = KErrCancel; + ExecuteL( ENoOverWrite ); + } + else + { + // User is not willing to rename item, continue current operation + doContinue = ETrue; + } + } + + CleanupStack::PopAndDestroy( name ); + + if ( doContinue ) + { + if ( iItemIterator->NextL() ) + { + ExecuteL( ENoOverWrite ); + } + else + { + UpdateNotifications( ETrue, KErrNone ); + iObserver.ProcessFinishedL( KErrNone ); + } + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::RunL +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::RunL() + { + CompleteL( iStatus.Int() ); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::CompleteL +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::CompleteL( TInt aError ) + { + if ( iCancelled || !iItemIterator ) + { + UpdateNotifications( ETrue, iError ); + TParsePtrC parse( CFileManagerUtils::StripFinalBackslash( + *iDestination ) ); + if ( parse.NameOrExtPresent() ) + { + iObserver.ProcessFinishedL( iError, parse.NameAndExt() ); + } + else if ( parse.DrivePresent() ) + { + iObserver.ProcessFinishedL( iError, parse.Drive() ); + } + else + { + iObserver.ProcessFinishedL( iError ); + } + return; + } + + // Symbian returns KErrNone if source and destination + // in moving is same. Here we have to treat it as error. + if( iSrc && iDst ) + { + if ( !iSrc->CompareF( *iDst ) && aError == KErrNone ) + { + aError = KErrInUse; + } + } + + ERROR_LOG1( "CFileManagerActiveExecute::CompleteL()-aError=%d", aError ) + + switch ( aError ) + { + case KErrNone: + { + KErrNoneActionL(); + break; + } + case KErrAlreadyExists: + { + KErrAlreadyExistsActionL(); + break; + } + case KErrCancel: // Suppressed errors + { + UpdateNotifications( ETrue, KErrNone ); + iObserver.ProcessFinishedL( KErrNone ); + break; + } + default: + { + if ( iSrc ) + { + // Try rename when moving and the target file exists and is in use + if ( iOperation == MFileManagerProcessObserver::EMoveProcess && + aError == KErrInUse && + iDst && + iEngine.CanDelete( *iSrc ) && + iEngine.IsNameFoundL( *iDst ) ) + { + KErrAlreadyExistsActionL(); + } + else + { + UpdateNotifications( ETrue, aError ); + TParsePtrC parse( *iSrc ); + iObserver.ProcessFinishedL( aError, parse.NameAndExt() ); + } + } + else + { + UpdateNotifications( ETrue, aError ); + iObserver.ProcessFinishedL( aError ); + } + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::RunError +// +// ----------------------------------------------------------------------------- +// +TInt CFileManagerActiveExecute::RunError(TInt aError) + { + return aError; + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::CFileManagerActiveExecute +// +// ----------------------------------------------------------------------------- +// +CFileManagerActiveExecute::CFileManagerActiveExecute( + CFileManagerEngine& aEngine, + MFileManagerProcessObserver::TFileManagerProcess aOperation, + MFileManagerProcessObserver& aObserver ) : + CActive( CActive::EPriorityLow ), // Use low to avoid progress note mess up + iEngine( aEngine ), + iFs( aEngine.Fs() ), + iOperation( aOperation ), + iObserver( aObserver ) + { + CActiveScheduler::Add( this ); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::ConstructL +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::ConstructL( CArrayFixFlat& aIndexList, + const TDesC& aToFolder ) + { + iChangedSrcItems = new( ELeave ) CDesCArrayFlat( + KFileManagerNotificationArrayGranularity ); + iChangedDstItems = new( ELeave ) CDesCArrayFlat( + KFileManagerNotificationArrayGranularity ); + + iToFolder = aToFolder.AllocL(); + TInt count( aIndexList.Count() ); + iIndexList = new( ELeave ) CArrayFixFlat< TInt >( count ); + for( TInt i( 0 ); i < count; ++i ) + { + // make own copy of index list because caller may + // destroy the original one. + iIndexList->AppendL( aIndexList.At( i ) ); + } + + TInt index( iIndexList->At( iCurrentIndex ) ); + + iFullPath = iEngine.IndexToFullPathL( index ); + + TBool isDirectory( iEngine.IsFolder( index ) ); + + iDestination = HBufC::NewL( KFmgrDoubleMaxFileName ); + + if ( isDirectory ) + { + TPtr ptr( iDestination->Des() ); + AddLastFolder( ptr, *iFullPath, *iToFolder ); + } + else + { + iDestination->Des().Copy( aToFolder ); + } + + // Check that we are not copying/moving folder to inside it ( recursive copy ) + if ( isDirectory && !iDestination->FindF( *iFullPath ) && + iDestination->Length() > iFullPath->Length() ) + { + iCancelled = ETrue; + iError = KErrAccessDenied; + } + // Is destination path too long for file system + else if ( iDestination->Length() > KMaxFileName ) + { + iCancelled = ETrue; + iError = KErrBadName; + } + else if ( isDirectory ) + { + iItemIterator = CFileManagerFileSystemIterator::NewL( + iFs, *iFullPath, *iDestination, iEngine ); + } + else + { + iItemIterator = CFileManagerIndexIterator::NewL( + iEngine, aIndexList, *iDestination ); + } + + // MG2 notification object + //iMgxFileManager = &iEngine.MGXFileManagerL(); + + // Check are operation source and target on the same drive + TParsePtrC srcParse( *iFullPath ); + TParsePtrC dstParse( *iDestination ); + TPtrC srcDrv( srcParse.Drive() ); + TPtrC dstDrv( dstParse.Drive() ); + iOperationOnSameDrive = !( srcDrv.CompareF( dstDrv ) ); + iIsSrcRemoteDrive = CFileManagerUtils::IsRemoteDrive( iFs, srcDrv ); + iIsDstRemoteDrive = CFileManagerUtils::IsRemoteDrive( iFs, dstDrv ); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::AddLastFolder +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::AddLastFolder( TDes& aResult, + const TDesC& aSrc, + const TDesC& aDst ) + { + TInt lastBackslash = aSrc.LocateReverse( KFmgrBackslash()[0] ); + if ( lastBackslash != KErrNotFound ) + { + // source is full path + aResult.Copy( aSrc.Left( lastBackslash - 1 ) ); + // Last backslash is now temporary removed check next last backslash + TInt secondLastBackslash( aResult.LocateReverse( KFmgrBackslash()[0] ) ); + // Now we know the coordinates of the last path + aResult.Copy( iEngine.LocalizedName( aSrc ) ); + if ( aResult.Length() > 0 ) + { + aResult.Insert( 0, aDst ); + aResult.Append( KFmgrBackslash ); + } + else + { + aResult.Append( aDst ); + // Skip '\\' + TInt startingPoint( secondLastBackslash + 1 ); + aResult.Append( aSrc.Mid( startingPoint, + lastBackslash - secondLastBackslash ) ); + } + } + else + { + // source is only one folder name + aResult.Copy( aDst ); + aResult.Append( aSrc ); + aResult.Append( KFmgrBackslash ); + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::DoOperation +// +// ----------------------------------------------------------------------------- +// +TInt CFileManagerActiveExecute::DoOperation( TInt aSwitch ) + { + // Source and destination must be different + if ( iSrc && iDst && !iDst->CompareF( *iSrc ) ) + { + TRequestStatus* status = &iStatus; + User::RequestComplete( status, KErrAlreadyExists ); + SetActive(); + return KErrAlreadyExists; + } + TInt err( KErrNone ); + + iSwitch = aSwitch; + + if ( !iThreadWrapper ) + { + TRAP( err, iThreadWrapper = CFileManagerThreadWrapper::NewL() ); + if ( err != KErrNone ) + { + TRequestStatus* status = &iStatus; + User::RequestComplete( status, err ); + SetActive(); + return err; + } + } + if ( iThreadWrapper->IsThreadStarted() ) + { + iThreadWrapper->ResumeThread(); + } + else + { + err = iThreadWrapper->StartThread( + *this, ENotifyStepFinished, EPriorityLess ); + if ( err != KErrNone ) + { + TRequestStatus* status = &iStatus; + User::RequestComplete( status, err ); + SetActive(); + return err; + } + } + return err; + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::IsEmptyDir +// +// ----------------------------------------------------------------------------- +// +TBool CFileManagerActiveExecute::IsEmptyDir( const TDesC& aDir ) + { + return !CFileManagerUtils::HasAny( + iFs, aDir, KEntryAttMatchMask | KEntryAttNormal ); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::ThreadCopyOrMoveStepL() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::ThreadCopyOrMoveStepL() + { + FUNC_LOG + + INFO_LOG2( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-%S=>%S", + iSrc, iDst ) + + TInt err( KErrNone ); + + if ( iItemType == EFileManagerFolder ) + { + // Handle folders + err = iFs.MkDir( *iDst ); // Try faster way first + LOG_IF_ERROR1( + err, + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-MkDir,err=%d", + err ) + if ( err != KErrNone && err != KErrCancel ) + { + err = iFs.MkDirAll( *iDst ); + LOG_IF_ERROR1( + err, + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-MkDirAll,err=%d", + err ) + } + if ( err == KErrNone ) + { + TEntry entry; + err = iFs.Entry( *iSrc, entry ); + if ( err == KErrNone ) + { + iFs.SetEntry( *iDst, entry.iModified, entry.iAtt, 0 ); // Ignore error + } + } + User::LeaveIfError( err ); + return; + } + + // Handle files + if ( iOperationOnSameDrive && + iOperation == MFileManagerProcessObserver::EMoveProcess ) + { + INFO_LOG( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-MoveInsideDrive" ) + + if ( iSwitch == EOverWrite ) + { + INFO_LOG( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-Overwrite" ) + + err = iFs.Replace( *iSrc, *iDst ); + } + else + { + INFO_LOG( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-NoOverwrite" ) + + err = iFs.Rename( *iSrc, *iDst ); + } + + LOG_IF_ERROR1( + err, + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-OnSameDrive,err=%d", + err ) + + if ( err == KErrNone || err == KErrCancel ) + { + // Optimized move was successful or cancelled + User::LeaveIfError( err ); + // If move the files in the same drive, the application + // just calculate the amount of the files, so it should + // notify the observer that how many files have been moved. + iBytesTransferredTotal++; + TRAP_IGNORE( iObserver.ProcessAdvanceL( + iBytesTransferredTotal ) ); + return; + } + } + + TInt64 fileSize( 0 ); + RFile64 srcFile; + + // Open source file + if ( iOperation == MFileManagerProcessObserver::EMoveProcess ) + { + INFO_LOG( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-Move" ) + + User::LeaveIfError( srcFile.Open( + iFs, *iSrc, EFileRead | EFileShareExclusive ) ); + } + else + { + INFO_LOG( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-Copy" ) + + User::LeaveIfError( srcFile.Open( + iFs, *iSrc, EFileRead | EFileShareReadersOnly ) ); + } + CleanupClosePushL( srcFile ); + User::LeaveIfError( srcFile.Size( fileSize ) ); + + // Open destination file + RFile64 dstFile; + if ( iSwitch == EOverWrite ) + { + INFO_LOG( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-Overwrite" ) + + User::LeaveIfError( dstFile.Replace( + iFs, *iDst, EFileWrite | EFileShareExclusive ) ); + } + else + { + INFO_LOG( "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-NoOverwrite" ) + + User::LeaveIfError( dstFile.Create( + iFs, *iDst, EFileWrite | EFileShareExclusive ) ); + } + CleanupClosePushL( dstFile ); + + dstFile.SetSize( fileSize ); // Setting the size first speeds up operation + + INFO_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-FileSize=%d", + fileSize ) + + // Create buffer and copy file data using it. + // Note that buffer size should not be too big to make it is possible + // to cancel the operation in reasonable time from the main thread. + + // Move these to CenRep to make configuration and fine tuning easier. + const TInt64 KBigBufSize = 0x40000; // 256KB + const TInt64 KMediumBufSize = 0x10000; // 64KB + const TInt64 KSmallBufSize = 0x2000; // 8KB + + HBufC8* buf = HBufC8::New( + Max( KSmallBufSize, Min( fileSize, KBigBufSize ) ) ); + if ( !buf ) + { + buf = HBufC8::New( KMediumBufSize ); + } + if ( !buf ) + { + buf = HBufC8::New( KSmallBufSize ); + } + if ( !buf ) + { + User::Leave( KErrNoMemory ); + } + CleanupStack::PushL( buf ); + + TPtr8 block( buf->Des() ); + + INFO_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-BlockSize=%d", + block.MaxSize() ) + + while ( err == KErrNone && fileSize > 0 ) + { + if ( iThreadWrapper->IsThreadCanceled() ) + { + err = KErrCancel; + break; + } + TInt blockSize( Min( fileSize, static_cast(block.MaxSize() ) ) ); + err = srcFile.Read( block, blockSize ); + if ( err == KErrNone ) + { + if ( block.Length() == blockSize ) + { + err = dstFile.Write( block, blockSize ); + if ( err == KErrNone ) + { + fileSize -= blockSize; + + // Do not update the latest file transfer progress here. + // Flushing file below may take a long time and + // progress indicator should not get full before it. + TRAP_IGNORE( iObserver.ProcessAdvanceL( + iBytesTransferredTotal ) ); + iBytesTransferredTotal += blockSize; + } + } + else + { + err = KErrCorrupt; + } + } + } + + INFO_LOG2( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-BytesNotWritten=%d,err=%d", + fileSize, err ) + + CleanupStack::PopAndDestroy( buf ); + + // Copy attributes + TTime mod; + if ( err == KErrNone ) + { + err = srcFile.Modified( mod ); + + INFO_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-ModifiedRead,err=%d", + err ) + + } + if ( err == KErrNone ) + { + err = dstFile.SetModified( mod ); + + INFO_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-ModifiedWritten,err=%d", + err ) + } + TUint att( 0 ); + if ( err == KErrNone ) + { + err = srcFile.Att( att ); + + INFO_LOG2( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-AttributesRead,err=%d,att=%d", + err, att ) + } + if ( err == KErrNone ) + { + // Ignore fail, because some drives like remote drives + // do not support attributes at all + dstFile.SetAtt( att, ( ~att ) & KEntryAttMaskSupported ); + + INFO_LOG( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-AttributesWritten" ) + } + // Flush file and finalize transfer progress of this file. + // Don't flush if copying failed because it causes save dialog to appear + // when remote drives are involved. + if ( err == KErrNone ) + { + err = dstFile.Flush(); + + INFO_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-Flushed,err=%d", + err ) + } + TRAP_IGNORE( iObserver.ProcessAdvanceL( iBytesTransferredTotal ) ); + + CleanupStack::PopAndDestroy( &dstFile ); + CleanupStack::PopAndDestroy( &srcFile ); + + // Delete source if move was succesful so far + if ( err == KErrNone && + iOperation == MFileManagerProcessObserver::EMoveProcess ) + { + // Ensure that read-only is removed before delete + if ( att & KEntryAttReadOnly ) + { + CFileManagerUtils::RemoveReadOnlyAttribute( iFs, *iSrc ); + } + err = iFs.Delete( *iSrc ); + + INFO_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-MoveSourceDeleted,err=%d", + err ) + } + + // Delete incomplete destination if error + if ( err != KErrNone ) + { + // Ensure that read-only is removed before delete + if ( att & KEntryAttReadOnly ) + { + CFileManagerUtils::RemoveReadOnlyAttribute( iFs, *iDst ); + } + iFs.Delete( *iDst ); + + ERROR_LOG1( + "CFileManagerActiveExecute::ThreadCopyOrMoveStepL-FailedDstDeleted,fail=%d", + err ) + } + + User::LeaveIfError( err ); + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::IsThreadDone() +// +// ----------------------------------------------------------------------------- +// +TBool CFileManagerActiveExecute::IsThreadDone() + { + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::NotifyThreadClientL() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::NotifyThreadClientL( + TNotifyType aType, TInt aValue ) + { + switch ( aType ) + { + case ENotifyStepFinished: + { + CompleteL( aValue ); + break; + } + default: + { + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::AppendArrayIfNotFound() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::AppendArrayIfNotFound( + CDesCArray& aArray, const TDesC& aFullPath ) + { + // Append if not already appended to the last item + TBool append( ETrue ); + TInt count( aArray.MdcaCount() ); + if ( count > 0 ) + { + TPtrC ptr( aArray.MdcaPoint( count - 1 ) ); + if ( !ptr.Compare( aFullPath ) ) + { + append = EFalse; + } + } + if ( append ) + { + TRAPD( err, aArray.AppendL( aFullPath ) ); + if ( err != KErrNone ) + { + ERROR_LOG1( + "CFileManagerActiveExecute::AppendArrayIfNotFound-err=%d", + err ) + } + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::FlushArray() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::FlushArray( CDesCArray& aArray ) + { + if ( aArray.MdcaCount() > 0 ) + { +// TRAP_IGNORE( iMgxFileManager->UpdateL( aArray ) ); + aArray.Reset(); + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::UpdateNotifications() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::UpdateNotifications( + TBool aFlush, TInt aError ) + { + // Append notification item if operation was successful and + // item does not already exist + if ( aError == KErrNone && iSrc && iDst && iItemType == EFileManagerFile ) + { + // Notifications are relevant only for local drives + if ( iOperation == MFileManagerProcessObserver::EMoveProcess && + !iIsSrcRemoteDrive ) + { + AppendArrayIfNotFound( *iChangedSrcItems, *iSrc ); + } + if ( !iIsDstRemoteDrive ) + { + AppendArrayIfNotFound( *iChangedDstItems, *iDst ); + } + } + if ( aFlush ) + { + FlushArray( *iChangedSrcItems ); + FlushArray( *iChangedDstItems ); + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::ThreadFinalizeMoveStepL() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::ThreadFinalizeMoveStepL() + { + FUNC_LOG + + HBufC* folderToDelete = HBufC::NewLC( KMaxFileName ); + TPtr ptrFolderToDelete( folderToDelete->Des() ); + CDirScan* dirScan = CDirScan::NewLC( iFs ); + dirScan->SetScanDataL( + *iFullPath, + KEntryAttNormal|KEntryAttHidden|KEntryAttSystem| + KEntryAttDir|KEntryAttMatchExclusive, + ESortNone, + CDirScan::EScanUpTree ); + CDir* dir = NULL; + dirScan->NextL( dir ); + while( dir ) + { + CFileManagerUtils::RemoveReadOnlyAttribute( + iFs, dirScan->FullPath() ); + CleanupStack::PushL( dir ); + TInt count( dir->Count() ); + for( TInt i( 0 ); i < count; ++i ) + { + if ( iThreadWrapper->IsThreadCanceled() ) + { + User::Leave( KErrCancel ); + } + TPtrC abbrPath( dirScan->AbbreviatedPath() ); + const TEntry& entry( ( *dir )[ i ] ); + ptrFolderToDelete.Copy( *iFullPath ); + ptrFolderToDelete.Append( + abbrPath.Right( abbrPath.Length() - 1 ) ); + ptrFolderToDelete.Append( entry.iName ); + ptrFolderToDelete.Append( KFmgrBackslash ); +#ifdef __KEEP_DRM_CONTENT_ON_PHONE + if ( iSrc && iDst && + CFileManagerUtils::IsFromInternalToRemovableDrive( iFs, *iSrc, *iDst ) ) + { + HBufC* targetFolderToDelete = HBufC::NewLC( KMaxFileName ); + TPtr ptrTargetFolderToDelete( targetFolderToDelete->Des() ); + ptrTargetFolderToDelete.Append( *iDestination ); + ptrTargetFolderToDelete.Append( + abbrPath.Right( abbrPath.Length() - 1 ) ); + ptrTargetFolderToDelete.Append( entry.iName ); + ptrTargetFolderToDelete.Append( KFmgrBackslash ); + + if ( IsEmptyDir( ptrFolderToDelete ) ) + { + User::LeaveIfError( iFs.RmDir( ptrFolderToDelete ) ); + } + else if ( IsEmptyDir( ptrTargetFolderToDelete )) + { + User::LeaveIfError( iFs.RmDir( ptrTargetFolderToDelete ) ); + } + CleanupStack::PopAndDestroy( targetFolderToDelete ); + } + else + { + User::LeaveIfError( iFs.RmDir( ptrFolderToDelete ) ); + } +#else + User::LeaveIfError( iFs.RmDir( ptrFolderToDelete ) ); +#endif + } + if ( iThreadWrapper->IsThreadCanceled() ) + { + User::Leave( KErrCancel ); + } + CleanupStack::PopAndDestroy( dir ); + dir = NULL; + dirScan->NextL( dir ); + } + CleanupStack::PopAndDestroy( dirScan ); + CleanupStack::PopAndDestroy( folderToDelete ); +#ifdef __KEEP_DRM_CONTENT_ON_PHONE + if ( iSrc && iDst && + CFileManagerUtils::IsFromInternalToRemovableDrive( iFs, *iSrc, *iDst ) ) + { + if ( IsEmptyDir( *iFullPath ) ) + { + User::LeaveIfError( iFs.RmDir( *iFullPath ) ); + } + } + else + { + User::LeaveIfError( iFs.RmDir( *iFullPath ) ); + } +#else + User::LeaveIfError( iFs.RmDir( *iFullPath ) ); +#endif + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::ThreadStepL() +// +// ----------------------------------------------------------------------------- +// +void CFileManagerActiveExecute::ThreadStepL() + { + FUNC_LOG + + if ( !iFinalizeMove ) + { + ThreadCopyOrMoveStepL(); + } + else + { + ThreadFinalizeMoveStepL(); + } + } + +// ----------------------------------------------------------------------------- +// CFileManagerActiveExecute::ToFolder() +// +// ----------------------------------------------------------------------------- +// +EXPORT_C TPtrC CFileManagerActiveExecute::ToFolder() + { + if ( iToFolder ) + { + return iToFolder->Des(); + } + return TPtrC( KNullDesC ); + } + +// End of File