diff -r 000000000000 -r 96e5fb8b040d userlibandfileserver/fileserver/sfsrv/cl_fman.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfsrv/cl_fman.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,2779 @@ +// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "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: +// + +#include "cl_std.h" + +#define RETURNIFERROR(a,b,t) \ + { \ + if ((a=b)!=KErrNone) \ + { \ + if(iStatus) \ + User::RequestComplete(iStatus,a); \ + TInt _t = t; \ + if (_t) {TRACE1(UTF::EBorder, t, MODULEUID, a);} \ + return(a); \ + } \ + } + +#define RETURNIFERRORD(a,b,t) \ + TInt a; \ + RETURNIFERROR(a,b,t) + +const TUint KRecurseFlag = 0x40000000; +const TUint KScanDownFlag = 0x20000000; +const TUint KFManBusyFlag = 0x10000000; +const TUint KOverWriteFlag = 0x08000000; +const TUint KMoveRenameFlag = 0x04000000; +const TUint KCopyFromHandle = 0x00000001; + +const TInt KPathIncGran=32; + +const TUint KMovingFilesMask = KEntryAttMatchExclude | KEntryAttDir; + +TInt ShrinkNames(RFs& aFs, TFileName& aParent, TFileName& aItem, TBool aAppend); + +LOCAL_C HBufC8* AllocateBuffer(TInt64 aLength) + { +const TInt KBigBufSize = 512 * 1024; +const TInt KMediumBufSize = 32 * 1024; +const TInt KSmallBufSize = 4 * 1024; + // Min result shall be of TInt size + // Hence to suppress warning + TInt big = (TInt)(Min(aLength,(TInt64)KBigBufSize)); + HBufC8* bufPtr=HBufC8::New(big); + if (bufPtr==NULL) + bufPtr=HBufC8::New(KMediumBufSize); + if (bufPtr==NULL) + bufPtr=HBufC8::New(KSmallBufSize); + return(bufPtr); + } + +LOCAL_C TInt IncPathLength(TInt aLen) + { + return(((aLen+KPathIncGran-1)/KPathIncGran)*KPathIncGran); + } + +LOCAL_C TInt CreateTargetNameFromSource(TDes& aTrgName, const TDesC& aTrgMask, const TDesC& aSrcName) +// Replace the wildcards with letters from the matched file. + { + TFileName destName; + TParsePtrC trg(aTrgMask); + TParsePtrC src(aSrcName); + TPtrC mask(trg.NameAndExt()); + TPtrC source(src.NameAndExt()); + TInt steps = 1; + TBool starCharFound = EFalse; + if(mask.LocateReverse('.')!=KErrNotFound || aTrgMask.Right(1)==_L("*")) + { + mask.Set(trg.Name()); + source.Set(src.Name()); + steps = 2; + } + for(TInt i = 0; i < steps; + i++, mask.Set(trg.ExtPresent() ? trg.Ext() : _L(".*")) , source.Set(src.Ext())) + { + TInt offset = 0; + starCharFound = EFalse; + while(offset < mask.Length()) + { + TChar currentChar = mask[offset]; + switch(currentChar) + { + case KMatchAny: + if(offset < source.Length() && !starCharFound) + { + destName.Append(source.Mid(offset)); + starCharFound = ETrue; + } + break; + case KMatchOne: + if(offset < source.Length()) + { + destName.Append(source[offset]); + } + break; + default: + destName.Append(currentChar); + break; + } + offset++; + } + } + if(destName.Right(1) == _L(".")) + { + destName.SetLength(destName.Length()-1); + } + if(aTrgName.Length()+destName.Length() > KMaxFileName) + { + return KErrBadName; + } + aTrgName.Append(destName); + return KErrNone; + } + +EXPORT_C MFileManObserver::TControl MFileManObserver::NotifyFileManStarted() +/** +Inform the observer that an operation is about to start. + +This is done immediately before each entry is processed. + +@return The implementation of this function should return: + MFileManObserver::EContinue, to allow processing of the current file + to proceed; + MFileManObserver::ECancel, to skip processing the current file and move + to the next file; + MFileManObserver::EAbort to abort the entire operation. + The default return value is MFileManObserver::EContinue. +*/ + { + + return(EContinue); + } + + + + +EXPORT_C MFileManObserver::TControl MFileManObserver::NotifyFileManOperation() +/** +Informs the observer that an operation, i.e. a copy or a move, is proceeding. + +Large files are copied and moved in stages. +After each portion of the source file has been copied to the target, this +function is called. + +It may be useful to call CFileMan::BytesTransferredByCopyStep() from this +function to retrieve the current status of the operation. + +@return The implementation of this function should return: + MFileManObserver::ECancel, to cancel the current operation, closing the current source + and target files, the current target file is deleted. + If the operation is performed on several files, cancelling one will not abort whole batch. + + MFileManObserver::EContinue, to continue with the operation. + The default return value is MFileManObserver::EContinue. + +@see CFileMan +*/ + { + + return(EContinue); + } + + + + +EXPORT_C MFileManObserver::TControl MFileManObserver::NotifyFileManEnded() +/** +Informs the observer that an operation is complete. + +This is done immediately after a directory entry has been processed. + +It may be useful to call CFileBase::GetLastError() +and CFileBase::GetMoreInfoAboutError() from this function to retrieve +information about how the operation ended. + +@return The implementation of this function should return: + MFileManObserver::EContinue or MFileManObserver::ECancel, to proceed + with processing the next entry. MFileManObserver::ECancel will not + cancel processing the current entry; + MFileManObserver::ERetry, to re-attempt processing the previous file; + MFileManObserver::EAbort, to abort the entire operation. + The default return value is MFileManObserver::EContinue. + +@see CFileBase +*/ + { + + return(EContinue); + } + + + + +EXPORT_C CFileBase::CFileBase(RFs& aFs) +/** +Protected default constructor. + +Note that the class is intended only as an abstract base for other classes. + +@param aFs File server session. +*/ + : iFs(aFs) + { + } + + + + +EXPORT_C void CFileBase::ConstructL() +/** +Second phase constructor. +*/ + { + + iScanner=CDirScan::NewL(iFs); + User::LeaveIfError(iSynchronizer.CreateLocal(0)); + } + + + + +EXPORT_C CFileBase::~CFileBase() +/** +Destructor. + +Frees resources prior to destruction of the object. +*/ + { + + delete iScanner; + delete iDirList; + iSynchronizer.Close(); + User::Free(iSessionPath); + } + + + + +GLDEF_C void DoFManBaseOperationL(TAny* aPtr) +// +// File manager asynchronous thread +// + { + + CFileBase& fMan=*(CFileBase*)aPtr; + User::LeaveIfError(fMan.iFs.Connect()); + User::LeaveIfError(fMan.iFs.SetSessionPath(*fMan.iSessionPath)); + fMan.iNumberOfFilesProcessed = 0; + fMan.RunL(); + } + +GLDEF_C TInt FManBaseThreadFunction(TAny* aPtr) +// +// Initialise New thread +// + { + + CTrapCleanup* cleanup=CTrapCleanup::New(); + if (cleanup==NULL) + return(KErrNoMemory); + CFileBase& fMan=*(CFileBase*)aPtr; + fMan.iSynchronizer.Wait(); + TRAPD(ret,DoFManBaseOperationL(aPtr)); + if (ret==KErrNone) + ret=fMan.iLastError; + delete cleanup; + fMan.iSwitches=0; + fMan.iFs=fMan.iFsOld; + fMan.iStatus=NULL; + fMan.iFManThread.Close(); + return(ret); + } + + + + +EXPORT_C void CFileBase::RunInSeparateThreadL(TThreadFunction aThreadFunction) +/** +Creates a separate thread to run the command. + +@param aThreadFunction The thread function. +*/ + { + iSwitches|=KFManBusyFlag; + User::LeaveIfError(iFManThread.Create(KNullDesC,aThreadFunction,KDefaultStackSize,NULL,this)); + iFManThread.SetPriority(EPriorityMuchLess); + TFileName sessionPath; + User::LeaveIfError(iFs.SessionPath(sessionPath)); + if (iSessionPath==NULL) + iSessionPath=HBufC::NewL((sessionPath.Length())); + else if (iSessionPath->Des().MaxLength()ReAllocL(IncPathLength(sessionPath.Length())); + iSessionPath->Des()=sessionPath; + iFsOld=iFs; + iLastError=KErrNone; + iFManThread.Logon(*iStatus); + iFManThread.Resume(); + } + + + + +EXPORT_C void CFileBase::RunL() +/** +Executes a command. + +@capability Dependent the capabilities required by this method, of the abstract class CFileBase, + will be dependent on and provided by the concrete-class implementation of + the DoOperationL method + +*/ + { + if (iStatus && (iSwitches&KFManBusyFlag)==EFalse) + { + RunInSeparateThreadL(FManBaseThreadFunction); + return; + } + + TBool copyFromHandle = (iSwitches & KCopyFromHandle)?(TBool)ETrue:(TBool)EFalse; + + CDirScan::TScanDirection scanDir=(iSwitches&KScanDownFlag) ? CDirScan::EScanDownTree : CDirScan::EScanUpTree; + + if (!copyFromHandle) + { + TRAP(iLastError,iScanner->SetScanDataL(iSrcFile.FullName(),iMatchEntry,ESortByName|EAscending,scanDir)); + if (iLastError==KErrNone) + TRAP(iLastError,iScanner->NextL(iDirList)); + + if (iLastError!=KErrNone) + { + iErrorInfo=EInitializationFailed; + User::Leave(iLastError); + } + } + + FOREVER + { + if (copyFromHandle || iDirList->Count()) + { + iLastError=KErrNone; + iErrorInfo=ENoExtraInformation; + TInt action=(iObserver) ? iObserver->NotifyFileManStarted() : MFileManObserver::EContinue; + // Check if NotifyFileManStarted returned ECancel. + if ( action == MFileManObserver::ECancel) + iLastError=KErrCancel; + if (action==MFileManObserver::EContinue) + { + iNumberOfFilesProcessed++; + TRAP(iLastError,DoOperationL()); + action=(iObserver) ? iObserver->NotifyFileManEnded() : MFileManObserver::EContinue; + } + else if(action==MFileManObserver::ERetry) + { + Panic(EFManBadValueFromObserver); + } + + + switch(action) + { + case MFileManObserver::EContinue: + case MFileManObserver::ECancel: + break; + case MFileManObserver::ERetry: + continue; + case MFileManObserver::EAbort: + delete iDirList; + iDirList=NULL; + iCurrentEntry = 0; + User::Leave(KErrCancel); + default: + Panic(EFManBadValueFromObserver); + } + } + iCurrentEntry++; + if (copyFromHandle || iCurrentEntry>=iDirList->Count()) + { + delete iDirList; + iDirList=NULL; + iCurrentEntry=0; + + if (iSwitches&KRecurseFlag) + { + TRAPD(ret,iScanner->NextL(iDirList)); + if (ret!=KErrNone && ret!=KErrPathNotFound) + { + iErrorInfo=EScanNextDirectoryFailed; + iLastError = ret; + User::Leave(iLastError); + } + } + if (iDirList==NULL) + { + CompleteOperationL(); + return; + } + } + } + } + + + + +EXPORT_C void CFileBase::SetObserver(MFileManObserver* anObserver) +/** +Sets the observer. + +Use this function to provide CFileMan with an MFileManObserver, or, if one +already exists, to change the observer. + +@param anObserver File management observer. + +@see CFileMan +@see MFileManObserver +*/ + { + + iObserver=anObserver; + } + + + + +EXPORT_C const TEntry& CFileBase::CurrentEntry() +/** +Gets the entry currently being processed. + +@return Contains the current entry. +*/ + { + + __ASSERT_ALWAYS(iDirList && iCurrentEntry>=0 && iCurrentEntryCount(),Panic(EFManCurrentEntryInvalid)); + return (*iDirList)[iCurrentEntry]; + } + + + + +EXPORT_C TPtrC CFileBase::AbbreviatedPath() +/** +Gets the abbreviated path of the file or directory currently being processed. + +The abbreviated path is its path relative to the top level directory +specified in the operation. + +@return The abbreviated path. +*/ + { + + return iScanner->AbbreviatedPath(); + } + + + + +EXPORT_C TPtrC CFileBase::FullPath() +/** +Gets the full path of the file or directory currently being processed. + +The full path includes the drive letter, path and filename. + +@return The full path. +*/ + { + + return iScanner->FullPath(); + } + + + + +EXPORT_C TInt CFileBase::GetLastError() +/** +Gets the latest error code returned during a CFileMan +operation. + +This function may be called from MFileManObserver::NotifyFileManEnded(). + +@return KErrNone, or another error code that might have been + returned by a CFileMan operation. +*/ + { + + return(iLastError); + } + + + + +EXPORT_C TFileManError CFileBase::GetMoreInfoAboutError() +/** +Gets additional information about the latest error returned during +a CFileMan operation. + +For example, if a rename fails, this function +can be used to report whether the source or target name caused the problem. +This information supplements that provided GetLastError(). + +@return The extra information about the last CFileMan error. + +@see CFileMan +@see CFileBase::GetLastError() +*/ + { + + return(iErrorInfo); + } + + + + +EXPORT_C CFileMan* CFileMan::NewL(RFs& aFs) +/** +Constructs and allocates memory for a new CFileMan object. + +@param aFs File server session. + +@return Newly created CFileMan object. +*/ + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManNewL1, MODULEUID, aFs.Handle()); + + CFileMan* fileMan=new(ELeave) CFileMan(aFs); + CleanupStack::PushL(fileMan); + fileMan->CFileBase::ConstructL(); + CleanupStack::Pop(); + + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManNewL1Return, MODULEUID, fileMan); + return fileMan; + } + + + + +EXPORT_C CFileMan* CFileMan::NewL(RFs& aFs,MFileManObserver* anObserver) +/** +Constructs and allocates memory for a new CFileMan object with an observer. + +@param aFs File server session. +@param anObserver File management observer. + +@return Newly created CFileMan object. +*/ + { + TRACE2(UTF::EBorder, UTraceModuleEfsrv::ECFileManNewL2, MODULEUID, aFs.Handle(), anObserver); + + CFileMan* fileMan=new(ELeave) CFileMan(aFs); + CleanupStack::PushL(fileMan); + fileMan->CFileBase::ConstructL(); + CleanupStack::Pop(); + fileMan->SetObserver(anObserver); + + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManNewL2Return, MODULEUID, fileMan); + return fileMan; + } + + + + +CFileMan::CFileMan(RFs& aFs) +// +// Constructor and destructor +// + : CFileBase(aFs) + { + } +CFileMan::~CFileMan() + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManDestructor, MODULEUID, this); + + TRACE0(UTF::EBorder, UTraceModuleEfsrv::ECFileManDestructorReturn, MODULEUID); + } + + +EXPORT_C CFileMan::TAction CFileMan::CurrentAction() +/** +Gets the action which CFileMan is currently carrying out. + +@return The action which CFileMan is carrying out. +*/ + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCurrentAction, MODULEUID, this); + + TAction action = ENone; + + // Mapping table between internal and external action indicators. + switch(iAction) + { + case EInternalNone: + action = ENone; + break; + case EInternalAttribs: + action = EAttribs; + break; + case EInternalCopy: + action = ECopy; + break; + case EInternalCopyForMove: + action = EMove; + break; + case EInternalDelete: + action = EDelete; + break; + case EInternalRenameInvalidEntry: + action = ERenameInvalidEntry; + break; + case EInternalRenameForMove: + case EInternalRename: + action = ERename; + break; + case EInternalRmDir: + action = ERmDir; + break; + case EInternalCopyFromHandle: + action = ECopyFromHandle; + break; + default: + Panic(EFManUnknownAction); + } + + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCurrentActionReturn, MODULEUID, action); + return (action); + } + + + + +EXPORT_C void CFileMan::GetCurrentTarget(TFileName& aTrgName) +/** +Gets the name of the target file for the CFileMan operation currently +being carried out. + +This function is relevant when copying, moving or renaming files. + +@param aTrgName The full path and filename of the target file for + the current CFileMan operation +*/ + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManGetCurrentTarget, MODULEUID, this); + + GetSrcAndTrg(iTmpParse, aTrgName); + + TRACEMULT1(UTF::EBorder, UTraceModuleEfsrv::ECFileManGetCurrentTargetReturn, MODULEUID, aTrgName); + } + + + + +EXPORT_C void CFileMan::GetCurrentSource(TFileName& aSrcName) +/** +Gets the name of the source file or directory for the CFileMan operation +currently being carried out. + +The source is the file or directory which is being copied, moved or deleted. + +@param aSrcName The full path and filename of the source file for the current + CFileMan operation. +*/ + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManGetCurrentSource, MODULEUID, this); + + TPtrC fullPath(FullPath()); + iTmpParse.Set(CurrentEntry().iName, &fullPath, NULL); + aSrcName = iTmpParse.FullName(); + + TRACEMULT1(UTF::EBorder, UTraceModuleEfsrv::ECFileManGetCurrentSourceReturn, MODULEUID, aSrcName); + } + +void CFileMan::GetSrcAndTrg(TParse& aSrcName,TFileName& aTrgName) +// +// Get the current target for the operation +// + { + TFileName fullpath = FullPath(); + TInt ret = aSrcName.Set(CurrentEntry().iName, &fullpath, NULL); + if(ret == KErrBadName) + { + // Try heap variables first + TBool done = EFalse; + TFileName* current = new TFileName; + if (current != NULL) + { + current->Copy(CurrentEntry().iName); + + ret = ShrinkNames(iFs, fullpath, *current, EFalse); + if(ret == KErrNone) + { + ret = aSrcName.Set(*current, &fullpath, NULL); + done = ETrue; + } + delete current; + } + + if (!done) //heap method failed + { + TFileName current = CurrentEntry().iName; + ret = ShrinkNames(iFs, fullpath, current, EFalse); + if(ret == KErrNone) + { + ret = aSrcName.Set(current, &fullpath, NULL); + } + } + } + __ASSERT_DEBUG(ret == KErrNone, Panic(EBadLength)); + aTrgName=iTrgFile.DriveAndPath(); + TPtrC relPath=iScanner->AbbreviatedPath(); + aTrgName.Append(relPath.Right(relPath.Length()-1)); + CreateTargetNameFromSource(aTrgName,iTrgFile.NameAndExt(),aSrcName.NameAndExt()); + } + + + + +EXPORT_C TInt CFileMan::BytesTransferredByCopyStep() +/** +Gets the number of bytes transferred during a copy or move operation. + +Large files are copied and moved in stages. After each portion of +the source file has been copied to the target, the number of bytes +transferred is updated. This function may be called +from MFileManObserver::NotifyFileManOperation() +and may be used to support the increment of progress bars. + +@return The number of bytes transferred. +*/ + { + TRACE2(UTF::EBorder, UTraceModuleEfsrv::ECFileManBytesTransferredByCopyStep, MODULEUID, this, iBytesTransferred); + + return(iBytesTransferred); + } + + + + +LOCAL_C void MakeParseWild(TParse& aParse, TFileName& aName) +// +// Append _L("\\*") or _L("*") to aParse +// + { + if(!aParse.IsWild()) + { + aName = aParse.FullName(); + if (aParse.NamePresent() || aParse.ExtPresent()) + { + if (aName.Length()<=254) + aName.Append(_L("\\*")); + } + else + { + if (aName.Length()<=255) + aName.Append(_L("*")); + } + aParse.Set(aName,NULL,NULL); + } + } + + +void CFileMan::CheckForDirectory() +// +// If iTrgFile is a directory set target to iTrgFile\\* +// + { + TInt trg = iFs.Entry(iTrgFile.FullName(), iTmpEntry); + if (trg==KErrNone && iTmpEntry.iAtt&KEntryAttDir) + MakeParseWild(iTrgFile, iTmpName1); + TInt src = iFs.Entry(iSrcFile.FullName(), iTmpEntry); + if (src==KErrNone && iTmpEntry.iAtt&KEntryAttDir) + { + MakeParseWild(iSrcFile, iTmpName1); + if (trg==KErrNotFound && (iSwitches&KRecurseFlag)) + MakeParseWild(iTrgFile, iTmpName1); + } + } + +void CFileMan::DoSynchronize(TInt aRetValue) +// +// Synchronise with fmanthread +// + { + + if (iStatus && aRetValue==KErrNone) + iSynchronizer.Signal(); // FManThread started + if (iStatus && aRetValue!=KErrNone) + iStatus=NULL; // FManThread failed to start + } + +LOCAL_C void NextInPath(const TDesC& aPath,TPtrC& anEntry,TInt& aPos) +// +// Returns the next entry in the path +// + { + + anEntry.Set(NULL,0); + if ((aPos+1)>=aPath.Length()) + return; + TPtrC path(aPath.Mid(aPos+1)); // Skip delimiter + TInt delimiterPos=path.Locate(KPathDelimiter); + if (delimiterPos==KErrNotFound) + delimiterPos=aPath.Length()-(aPos+1); + if (delimiterPos<=0) + return; + + if (path[delimiterPos-1]==KExtDelimiter) // return "F32." as "F32" + anEntry.Set(aPath.Mid(aPos+1,delimiterPos-1)); + else + anEntry.Set(aPath.Mid(aPos+1,delimiterPos)); + aPos+=delimiterPos+1; + } + +LOCAL_C TBool ComparePaths(const TDesC& aPath1,const TDesC& aPath2) +// +// Return ETrue if the paths are identical +// To catch case "\\F32.\\GROUP\\" == "\\F32\\GROUP\\" +// + { + + TPtrC entry1(NULL,0); + TPtrC entry2(NULL,0); + TInt pos1=0; + TInt pos2=0; + + do { + NextInPath(aPath1,entry1,pos1); + NextInPath(aPath2,entry2,pos2); + if (entry1.MatchF(entry2)==KErrNotFound) + return(EFalse); + } while (entry1.Length() && entry2.Length()); + + return(ETrue); + } + +EXPORT_C TBool FileNamesIdentical(const TDesC& aFileName1,const TDesC& aFileName2) +// +// Return ETrue if the filenames (and paths) are identical +// NB "Agenda" == "AGENda." +// + { + + TParsePtrC file1(aFileName1); + TParsePtrC file2(aFileName2); + if (file1.Drive().MatchF(file2.Drive())==KErrNotFound) + return(EFalse); + if (file1.Name().MatchF(file2.Name())==KErrNotFound) + return(EFalse); + if (ComparePaths(file1.Path(),file2.Path())==EFalse) + return(EFalse); + if (file1.Ext().Length()==0 || file2.Ext().Length()==0) + { // Agenda == Agenda. + if (file1.Ext().Length()==1 || file2.Ext().Length()==1) + return(ETrue); + } + if (file1.Ext().MatchF(file2.Ext())==KErrNotFound && + file1.NameAndExt().MatchF(file2.NameAndExt())==KErrNotFound) + return(EFalse); + return(ETrue); + } + + + + +EXPORT_C TInt CFileMan::Attribs(const TDesC& aName,TUint aSetMask,TUint aClearMask,const TTime& aTime,TUint aSwitches,TRequestStatus& aStatus) +/** +Sets or clears attributes for one or more files using two bitmasks. + +This is an asynchronous function. +Its behaviour is the same as the synchronous overload. + +@param aName Path indicating the file(s) whose attributes are to be + changed. Any path components which are not specified + here will be taken from the session path. + Use wildcards to specify more than one file. +@param aSetMask Bitmask indicating the attributes to be set. +@param aClearMask Bitmask indicating the attributes to be cleared. + For more information, see KEntryAttNormal and the other + file/directory attributes. +@param aTime Contains the new modification date and time for the files, in UTC. + To preserve the file's timestamps, specify a TTime value of 0. +@param aSwitches Specify zero for no recursion; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function operates + non-recursively. +@param aStatus The request status object. On request completion, + indicates how the request completed: + KErrNone, if successful, otherwise one of the other system-wide error + codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@capability Dependent If aName is /Sys then Tcb capability is required. +@capability Dependent If aName begins with /Private and does not match + this process' SID then AllFiles capability is required. +@capability Dependent If aName is /Resource then Tcb capability is required. + +*/ + { + TRACEMULT8(UTF::EBorder, UTraceModuleEfsrv::ECFileManAttribs1, MODULEUID, + (TUint) this, aName, aSetMask, aClearMask, I64LOW(aTime.Int64()), I64HIGH(aTime.Int64()), aSwitches, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + { + r = KErrInUse; + } + else + { + iStatus=&aStatus; + r = Attribs(aName,aSetMask,aClearMask,aTime,aSwitches); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManAttribs1Return, MODULEUID, r); + return r; + } + + + + +EXPORT_C TInt CFileMan::Attribs(const TDesC& aName,TUint aSetMask,TUint aClearMask,const TTime& aTime,TUint aSwitches) +/** +Sets or clears attributes for one or more files using two bitmasks + +This is a synchronous function. + +The first bitmask specifies the attributes to be set. +The second specifies the attributes to be cleared. +The date and time of the files' last modification can also be changed. + +The function can operate recursively or non-recursively. +When operating non-recursively, only the matching files located in the directory +specified in aName are affected. When operating recursively, all matching files +in the directory hierarchy below the directory specified in aName will be affected. + +Notes: + +1. A panic is raised if any attribute is specified in both bitmasks. + +2. Attempting to change the attributes for an open file results in an error + for that file, as retrieved by CFileBase::GetLastError(). + +3. An attempt to set or clear the KEntryAttDir or KEntryAttVolume attribute + for a file or directory will have no effect. + +@param aName Path indicating the file(s) whose attributes are to be + changed. Any path components which are not specified + here will be taken from the session path. + Use wildcards to specify more than one file. +@param aSetMask Bitmask indicating the attributes to be set. +@param aClearMask Bitmask indicating the attributes to be cleared. + For more information, see KEntryAttNormal and the other + file/directory attributes. +@param aTime Contains the new modification date and time for the files, in UTC. + To preserve the file's timestamps, specify a TTime value of 0. +@param aSwitches Specify zero for no recursion; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function operates + non-recursively. + +@return KErrNone if successful, otherwise one of the other system-wide error codes. + +@capability Dependent If aName is /Sys then Tcb capability is required. +@capability Dependent If aName begins with /Private and does not match + this process' SID then AllFiles capability is required. +@capability Dependent If aName is /Resource then Tcb capability is required. + +*/ + { + TRACEMULT7(UTF::EBorder, UTraceModuleEfsrv::ECFileManAttribs2, MODULEUID, + (TUint) this, aName, aSetMask, aClearMask, I64LOW(aTime.Int64()), I64HIGH(aTime.Int64()), aSwitches); + + TInt ret; + if (iSwitches&KFManBusyFlag) + { + ret = KErrInUse; + } + else + { + SetFlags(aSwitches&EOverWrite,aSwitches&ERecurse,ETrue,EFalse); + RETURNIFERRORD(r,iFs.Parse(aName,iSrcFile),UTraceModuleEfsrv::ECFileManAttribs2Return); + iSetMask=aSetMask; + iClearMask=aClearMask; + iTime=aTime; + iAction = EInternalAttribs; + iMatchEntry=KEntryAttMaskSupported; // all entries + iNumberOfFilesProcessed = 0; + TRAP(r,RunL()); + ret=(r==KErrNone) ? iLastError : r; + DoSynchronize(r); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManAttribs2Return, MODULEUID, ret); + return(ret); + } + + + + +EXPORT_C TInt CFileMan::Copy(const TDesC& anOld,const TDesC& aNew,TUint aSwitches,TRequestStatus& aStatus) +/** +Copies one or more files. + +This is an asynchronous function. +Its behaviour is the same as the synchronous overload. + +@param anOld Path indicating the file(s) to be copied. + Any path components which are not specified here will be + taken from the session path. +@param aNew Path indicating the directory into which the file(s) are to be copied. + Any path components which are not specified here will be + taken from the session path +@param aSwitches Specify zero for no overwriting and no recursion; + CFileMan::EOverWrite to overwrite files with the same name; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function operates + non-recursively and with overwriting. +@param aStatus The request status object. On request completion, + indicates how the request completed: + KErrNone, if successful, otherwise one of the other system-wide error + codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@capability AllFiles + +@capability Dependent If the path for aNew begins with /Sys then Tcb capability is required. +@capability Dependent If the path for aNew begins with /Resource then Tcb capability is required + +*/ + { + TRACEMULT5(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy1, MODULEUID, + (TUint) this, anOld, aNew, aSwitches, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + r = KErrInUse; + else + { + iStatus=&aStatus; + r = Copy(anOld,aNew,aSwitches); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy1Return, MODULEUID, r); + return(r); + } + + + + +EXPORT_C TInt CFileMan::Copy(const TDesC& anOld,const TDesC& aNew,TUint aSwitches) +/** +Copies one or more files. + +This is a synchronous function. + +NB the following applies to files greater than or equal to 2GBytes in size +(2,147,483,648 bytes) : +- Only files smaller than 2GBytes will be copied; any larger files will be skipped and +processing will continue with the next file. +- If at least one file is smaller than 2GBytes, then KErrNone will be returned. +- If all files are greater than or equal to 2GBytes ,then KErrTooBig will be returned. + +One way to detect the presence of any large file(s) is to use an observer: calling +CFileBase::GetLastError() from MFileManObserver::NotifyFileManEnded() will return +KErrToBig for any file >= 2GBytes in size. + +Note: the copy operation behaves differently when MFileManObserver is used. +MFileManObserver should be used with multiple files as it enables you to capture the results of all file copy operations. + +If MFileManObserver is NOT used then only the result of the last +file copy operation is returned because the results of previous file copy operations are overwritten. + +Optionally, this function can be set to overwrite any files with the same name +which exist in the target directory. If the flag is set for no overwriting, +then any files with the same name will not be overwritten, and an error +(KErrAlreadyExists) will be returned for that file. Error codes may be retrieved +using CFileBase::GetLastError(). + +If recursive operation is set, all intermediate directories will be created, +including any directories in the path specified by aNew which do not +already exist. + +If recursive operation is not set, only the matching files located in +the single directory specified in anOld are copied. +No intermediate directories will be created; if any directories in +the destination path do not exist, no files will be copied, and this function +will return KErrPathNotFound. + + Notes: + 1. This function operates on files only, therefore: + 1.1 In contrast to the way CFileMan::Move() and CFileMan::Rename() + behave, the behaviour of the copy operation does not depend on the presence + or absence of a trailing backslash ("\") character. Therefore it is only + possible to copy the content of the source path. It is NOT + possible by use of a trailing backslash ("\") character to request that the + last directory level plus its content be copied to the target path. + + This means that the following two example copy operations will behave + identically + + @code + CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle + ... + fm->Copy(_L("C:\\SRC\\"), _L("C:\\TRG\\"), CFileMan::ERecurse); + fm->Copy(_L("C:\\SRC"), _L("C:\\TRG\\"), CFileMan::ERecurse); + @endcode + + because they will be interpreted as follows: + @code + fm->Copy(_L("C:\\SRC\\*"),_L("C:\\TRG\\"), CFileMan::ERecurse); + @endcode + + 1.2 If there is no file to operate on i.e. if source directory is empty, the + function will do nothing and return error code KErrNotFound. + + 2. Files can be copied across drives. + + 3. Open files can be copied if they have been opened using + the EFileShareReadersOnly file share mode. + + 4. Read-only, hidden and system files can be copied and + the source file's attributes are preserved in the target file. + +@param anOld Path indicating the file(s) to be copied. + Any path components which are not specified here will be + taken from the session path. +@param aNew Path indicating the directory into which the file(s) are to be copied. + Any path components which are not specified here will be + taken from the session path +@param aSwitches Specify zero for no overwriting and no recursion; + CFileMan::EOverWrite to overwrite files with the same name; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function operates + non-recursively and with overwriting. + +@return KErrNone if successful, KErrNotFound if source directory is empty, otherwise one of the other system-wide error codes. + +@see CFileBase::GetLastError() + +@capability AllFiles + +@capability Dependent If the path for anOld begins with /Sys then Tcb capability is required. +@capability Dependent If the path for anOld begins with /Resource then Tcb capability is required + +*/ + { + TRACEMULT4(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy2, MODULEUID, (TUint) this, anOld, aNew, aSwitches); + + if (iSwitches&KFManBusyFlag) + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy2Return, MODULEUID, KErrInUse); + return(KErrInUse); + } + SetFlags(aSwitches&EOverWrite,aSwitches&ERecurse,ETrue,EFalse); + RETURNIFERRORD(r,iFs.Parse(anOld,iSrcFile),UTraceModuleEfsrv::ECFileManCopy2Return); + RETURNIFERROR(r,iFs.Parse(aNew,_L("*"),iTrgFile),UTraceModuleEfsrv::ECFileManCopy2Return); + CheckForDirectory(); + + if((iSwitches&KRecurseFlag) && iTrgFile.DriveAndPath().MatchF(iSrcFile.FullName()) != KErrNotFound) + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy2Return, MODULEUID, KErrArgument); + return(KErrArgument); + } + + iAction = EInternalCopy; + iMatchEntry=KEntryAttMaskSupported; + iNumberOfFilesProcessed = 0; + TRAP(r,RunL()); + TInt ret=(r==KErrNone) ? iLastError : r; + DoSynchronize(r); + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy2Return, MODULEUID, ret); + return(ret); + } + + + + +EXPORT_C TInt CFileMan::Delete(const TDesC& aName,TUint aSwitches,TRequestStatus& aStatus) +/** +Deletes one or more files. + +This is an asynchronous function. +Its behaviour is the same as the synchronous overload. + +@param aName Path indicating the file(s) to be deleted. + May either be a full path, or relative to the session path. + Use wildcards to specify more than one file. + NOTE: if you pass KNullDesC, the empty (or null) descriptor, + then the function interprets this to mean \\*.* +@param aSwitches Specify: + zero for no recursion; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function + operates non-recursively. +@param aStatus The request status object. On request completion, + indicates how the request completed: + KErrNone, if successful, otherwise one of the other system-wide error + codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@capability Dependent If aName is /Sys then Tcb capability is required. +@capability Dependent If aName begins with /Private and does not match this process' SID + then AllFiles capability is required. +@capability Dependent If aName is /Resource then Tcb capability is required. + +@see KNullDesC +*/ + { + TRACEMULT4(UTF::EBorder, UTraceModuleEfsrv::ECFileManDelete1, MODULEUID, (TUint) this, aName, aSwitches, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + { + r = KErrInUse; + } + else + { + iStatus=&aStatus; + r = Delete(aName,aSwitches); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManDelete1Return, MODULEUID, r); + return(r); + } + + + + +EXPORT_C TInt CFileMan::Delete(const TDesC& aName,TUint aSwitches) +/** +Deletes one or more files. + +This is a synchronous function. + +This function can operate recursively or non-recursively. +When operating non-recursively, only the matching files located in +the directory specified in aName are affected. +When operating recursively, all matching files in the directory hierarchy +below the directory specified in aName will be deleted. + +Note that read-only and open files may not be deleted. +Attempting to do so will return an error for that file. +Error codes may be retrieved using CFileBase::GetLastError(). + +@param aName Path indicating the file(s) to be deleted. + May either be a full path, or relative to the session path. + Use wildcards to specify more than one file. + NOTE: if you pass KNullDesC, the empty (or null) descriptor, + then the function interprets this to mean \\*.* +@param aSwitches Specify: + zero for no recursion; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function + operates non-recursively. + +@return KErrNone if successful, otherwise one of the other system-wide error + codes. + +@see CFileBase::GetLastError() + +@capability Dependent If aName is /Sys then Tcb capability is required. +@capability Dependent If aName begins with /Private and does not match this process' SID + then AllFiles capability is required. +@capability Dependent If aName is /Resource then Tcb capability is required. + +@see KNullDesC +*/ + { + TRACEMULT3(UTF::EBorder, UTraceModuleEfsrv::ECFileManDelete2, MODULEUID, (TUint) this, aName, aSwitches); + + TInt ret; + if (iSwitches&KFManBusyFlag) + { + ret = KErrInUse; + } + else + { + SetFlags(aSwitches&EOverWrite,aSwitches&ERecurse,ETrue,EFalse); + RETURNIFERRORD(r,iFs.Parse(aName,iSrcFile),UTraceModuleEfsrv::ECFileManDelete2Return); + iAction = EInternalDelete; + iMatchEntry=KEntryAttHidden|KEntryAttMatchExclude|KEntryAttDir; + // Exclude directories and system files - include hidden files + iNumberOfFilesProcessed = 0; + TRAP(r,RunL()); + ret=(r==KErrNone) ? iLastError : r; + DoSynchronize(r); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManDelete2Return, MODULEUID, ret); + return(ret); + } + + + + +EXPORT_C TInt CFileMan::Move(const TDesC& anOld,const TDesC& aNew,TUint aSwitches,TRequestStatus& aStatus) +/** +Moves one or more files. + +This is an asynchronous function. +Its behaviour is the same as the synchronous overload. + +@param anOld Path indicating the files to be moved. May be either + a full path, or relative to the session path. Any path + components which are not specified here will be taken + from the session path. +@param aNew Path indicating the directory into which the file(s) are + to be moved. Any path components which are not specified + here will be taken from the session path. +@param aSwitches Specify zero for no overwriting and no recursion; + CFileMan::EOverWrite to overwrite files with the same name; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function operates + non-recursively and with overwriting. +@param aStatus The request status object. On request completion, + indicates how the request completed: + KErrNone, if successful, otherwise one of the other system-wide error + codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@capability Dependent If the path in aNew starts with /Sys then capability Tcb is required +@capability Dependent If the path in aNew starts with /Resource then capability Tcb is required + +@capability AllFiles + +@capability Dependent If the path in anOld starts with /Sys then Tcb capability is required. +@capability Dependent If the path in anOld starts with /Resource then Tcb capability is required. + +*/ + { + TRACEMULT5(UTF::EBorder, UTraceModuleEfsrv::ECFileManMove1, MODULEUID, + (TUint) this, anOld, aNew, aSwitches, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + { + r = KErrInUse; + } + else + { + iStatus=&aStatus; + r = Move(anOld,aNew,aSwitches); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManMove1Return, MODULEUID, r); + return r; + } + + + + +EXPORT_C TInt CFileMan::Move(const TDesC& anOld,const TDesC& aNew,TUint aSwitches) +/** +Moves one or more files. + +This is a synchronous function. + +Optionally, this function can be set to overwrite any files with the same name +which exist in the target directory. If the flag is set for no overwriting, +then any files with the same name will not be overwritten, and +an error (KErrAlreadyExists) will be returned for that file. +Error codes may be retrieved using CFileBase::GetLastError(). +By default, when the function is operating synchronously, files are overwritten. + +When this function is operating recursively, all intermediate directories will +be created, including all directories in the destination path specified +by aNew which do not already exist. + +If recursive operation is not set, only the matching files located in +the single directory specified in anOld are moved. No intermediate directories +will be created; if any directories in the destination path do not exist, +no files will be moved, and this function will return KErrPathNotFound. + +The behaviour of the move operation is sensitive to the presence (or absence) +of a trailing backslash ("\") character on the end of the source path: +- if there is a trailing backslash ("\") character, then the operation moves + the content of the last directory level only. +- if there is no trailing backslash ("\") character, then the operation behaves + recursively by default and moves both the last directory level and all of its content. + Notice that no trailing backslash ("\") implies moving files recursively automatically. + +For example, if the directory level "b" contains the files F1,F2 and F3, then: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Move(_L("C:\\a\\b\\"), _L("C:\\x\\y\\"), CFileMan::ERecurse); +@endcode + +results in files F1, F2 and F3 being moved from C:\\a\\b to C:\\x\\y, leaving the +path C:\\a\\b unchanged, except that it no longer contains the files +F1, F2 and F3. + +If there is no trailing backslash character, for example: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Move(_L("C:\\a\\b"), _L("C:\\x\\y\\"), CFileMan::ERecurse); +@endcode + +then both the directory level "b" and its contents are moved. This means that +there is no longer a directory "b" under C:\\a. Instead there is a new +directory structure C:\\x\\y\\b and the files F1, F2, and F3 now exist +under C:\\x\\y\\b. Also if "b" contains subdirectories, then these are also +moved along with "b". + +If there is no trailing backslash character and the switch is not set, i.e. +0 is passed as an argument, the operation behaves the same way as by passing +CFileMan::ERecurse flag. + +for example: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Move(_L("C:\\a\\b"), _L("C:\\x\\y\\"), 0); +@endcode + +The example above produces the same output as: + +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Move(_L("C:\\a\\b"), _L("C:\\x\\y\\"), CFileMan::ERecurse); +@endcode + +Notes: + +-# Read-only, hidden and system files can be moved and the source file's + attributes are preserved in the target file, but open files cannot + be moved. Attempting to move an open file will return an error for + that file, as retrieved by CFileBase::GetLastError(). + +@param anOld Path indicating the files to be moved. May be either a full path, or + relative to the session path. Note that if you specify a directory level, + then the behaviour of the move operation is sensitive to the presence + (or absence) of a trailing backslash ("\") character. Any path components + which are not specified here will be taken from the session path. See the + main description for the detailed explanation. +@param aNew Path indicating the directory into which the file(s) are to be moved. + Any path components which are not specified here will be taken from the session path. +@param aSwitches CFileMan::EOverWrite to overwrite files with the same name; + CFileMan::ERecurse for recursion. + By default, the synchronous variant of this function operates non-recursively and + with overwriting. And no trailing backslash ("\") character at the end of source path + always means CFileMan::ERecurse. + +@return KErrNone if successful, otherwise one of the other system-wide error + codes. + +@capability Dependent If the path in aNew starts with /Sys then capability Tcb is required +@capability Dependent If the path in aNew starts with /Resource then capability Tcb is required + +@capability AllFiles + +@capability Dependent If the path in anOld starts with /Sys then Tcb capability is required. +@capability Dependent If the path in anOld starts with /Resource then Tcb capability is required. + +@see CFileBase::GetLastError() +*/ + { + TRACEMULT4(UTF::EBorder, UTraceModuleEfsrv::ECFileManMove2, MODULEUID, + (TUint) this, anOld, aNew, aSwitches); + + + if (iSwitches&KFManBusyFlag) + { + TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECFileManMove2Return, MODULEUID, KErrInUse); + return(KErrInUse); + } + + iNumberOfFilesProcessed = 0; + + RETURNIFERRORD(r,iFs.Parse(anOld,iSrcFile),UTraceModuleEfsrv::ECFileManMove2Return); + RETURNIFERROR(r,iFs.Parse(aNew,_L("*"),iTrgFile),UTraceModuleEfsrv::ECFileManMove2Return); + + TInt ret = KErrNone; + TBool aComplete = EFalse; + if(SrcTrgDrivesIdentical()) + { + ret = SetupMoveOnSameDrive(aSwitches, aComplete); + } + else + { + ret = SetupMoveAcrossDrives(aSwitches); + } + + if(ret != KErrNone || aComplete) + { + if (iStatus) + { + User::RequestComplete(iStatus, ret); + } + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManMove2Return, MODULEUID, ret); + return(ret); + } + + iMatchEntry = KEntryAttMaskSupported; + if((aSwitches&ERecurse)==0 && iMovingContents) + { + iMatchEntry = KMovingFilesMask; + } + + // Do the Move or Rename Operation + TRAP(r,RunL()); + ret = (r==KErrNone) ? iLastError : r; + DoSynchronize(r); + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManMove2Return, MODULEUID, ret); + return(ret); + } + + +TBool CFileMan::SrcTrgDrivesIdentical() +// +// Returns ETrue if the source and target drives are the same +// - Used by CFileMan::Move operations to determine whether to rename or move the files +// + { + return iSrcFile.Drive().MatchF(iTrgFile.Drive()) != KErrNotFound; + } + + +TInt CFileMan::SetupDirectoryForMove(TBool& aSrcIsDir) +/** + * Sets up the target specification to include the new target directory if required. + * + * @param aSrcIsDir Set to ETrue if the source specifies that the directory is to be moved in its entirety, + * or EFalse if only the contents of the directory need to be moved. + * + * @return KErrNone if successful, otherwise one of the system wide error codes. + */ + { + iMovingContents = ETrue; + aSrcIsDir = EFalse; + + TPtrC nameAndExt(iSrcFile.NameAndExt()); + if (nameAndExt == _L("*") || nameAndExt == _L("*.*")) + { + // Wildcard specification - Move the entire contents of the directory to the target + aSrcIsDir = ETrue; + } + else + { + TInt src = iFs.Entry(iSrcFile.FullName(), iTmpEntry); + if ((src == KErrNone && iTmpEntry.iAtt&KEntryAttDir) || (!iSrcFile.NamePresent() && iSrcFile.IsRoot())) + { + aSrcIsDir = ETrue; + + // A directory is specified. + // - Mandatory recursion with Wildcard Copy + // - Target is a directory (Enforced by MakeParseWild) + + MakeParseWild(iTrgFile, iTmpName1); + + // Construct the target name by parsing + // the source path for the directory name. + TPtrC srcPath(iSrcFile.FullName()); + TInt srcPathLen = srcPath.Length() - 1; + + iMovingContents = (srcPath[srcPathLen] == KPathDelimiter); // Moving the directory itself, or the contents? + + if(!iMovingContents) + { + // No path delimiter specified + // - move the whole directory (if specified) + TInt len = srcPath.Length(); + + TInt idx = srcPath.Left(len).LocateReverse(KPathDelimiter); + + if((idx >= 2) && (idx != KErrNotFound)) + { + // Source path is a directory (not just the drive) + TPtrC mid(srcPath.Left(len).Mid(1+idx)); + TInt r = iTrgFile.AddDir(mid); + if (r != KErrNone) + return r; + } + } + } + } + + return KErrNone; + } + + +TInt CFileMan::SetupTargetDirectory(TBool aOverWrite, TBool& aComplete) + { + aComplete = EFalse; + + TInt trgErr = iFs.Entry(iTrgFile.DriveAndPath(), iTmpEntry); + + TEntry srcEntry; + TInt srcErr = iFs.Entry(iSrcFile.FullName(), srcEntry); + + if(srcErr == KErrNone && trgErr == KErrNone) + { + if ((srcEntry.iAtt&KEntryAttDir) != (iTmpEntry.iAtt&KEntryAttDir)) + { + // return KErrAccessDenied if it is trying to overwrite a file to a dir or vice versa. + return KErrAccessDenied; + } + } + + if(trgErr == KErrNone) + { + // Already Exists - Overwrite if flags set + if(!aOverWrite) + { + trgErr = KErrAlreadyExists; + } + else + { + iNumberOfFilesProcessed++; + } + } + else if((trgErr == KErrNotFound) || (trgErr == KErrPathNotFound)) + { + if(SrcTrgDrivesIdentical()) + { + // When moving a directory on the same drive, the directory can simply be renamed... + TParse& midDir = iTmpParse; + midDir = iTrgFile; + if(midDir.PopDir() == KErrNone) + { + // ...before renaming, ensure that all intermediate directories exist + trgErr = iFs.MkDirAll(midDir.DriveAndPath()); + if(trgErr == KErrAlreadyExists) + { + trgErr = KErrNone; + } + } + + if (trgErr == KErrNone) + { + // ...and finally rename the source directory + trgErr = iFs.Rename(iSrcFile.FullName(),iTrgFile.DriveAndPath()); + aComplete = ETrue; + } + } + else + { + trgErr = iFs.MkDirAll(iTrgFile.FullName()); + } + iNumberOfFilesProcessed++; + } + + return(trgErr); + } + + +TInt CFileMan::SetupMoveOnSameDrive(TUint aSwitches, TBool& aComplete) + { + // Moving on the same drive. + + aComplete = EFalse; + + TBool srcIsDir = EFalse; + TInt ret = SetupDirectoryForMove(srcIsDir); + if (ret != KErrNone) + { + return ret; + } + + TBool scanDown = ETrue; + TBool recurse = (aSwitches & ERecurse); + + iAction = EInternalRenameForMove; + + TFileName& srcpath = iTmpName1; + srcpath.Copy(iSrcFile.FullName()); + if(srcpath.Length()1 && srcpath[srcpath.Length()-1]!=KPathDelimiter) + { + srcpath.Append(KPathDelimiter); + } + + // If the source path is a subset of the target path then Move operation is not allowed + if((srcIsDir && recurse) || (srcIsDir && !iTrgFile.IsRoot() && !iMovingContents)) + { + if(iTrgFile.FullName().Left(srcpath.Length()).MatchF(srcpath)==0) + { + aComplete = ETrue; + return KErrInUse; + } + } + // if any of the SRC folders already existing in TRG, scan upwards + if(iMovingContents) + { + CDirScan* srcScanDir = NULL; + CDirScan* trgScanDir = NULL; + CDir* srcEntryList = NULL; + CDir* trgEntryList = NULL; + TInt trgCnt = 0; + TInt srcCnt = 0; + + TRAP(ret,(srcScanDir = CDirScan::NewL(iFs))); + if (ret!=KErrNone) + { + goto CleanUp; + } + TRAP(ret,srcScanDir->SetScanDataL(iSrcFile.FullName(),KEntryAttMaskSupported,ESortByName)); + if (ret!=KErrNone) + { + goto CleanUp; + } + TRAP(ret,srcScanDir->NextL(srcEntryList)); + if(ret!=KErrNone) + { + goto CleanUp; + } + TRAP(ret,(trgScanDir=CDirScan::NewL(iFs))); + if (ret!=KErrNone) + { + goto CleanUp; + } + TRAP(ret,trgScanDir->SetScanDataL(iTrgFile.FullName(),KEntryAttMaskSupported,ESortByName)); + if (ret!=KErrNone) + { + goto CleanUp; + } + TRAP(ret,trgScanDir->NextL(trgEntryList)); + if(ret!=KErrNone) + { + goto CleanUp; + } + for(trgCnt=trgEntryList->Count()-1; trgCnt>-1; trgCnt--) + { + for(srcCnt=srcEntryList->Count()-1; srcCnt>-1; srcCnt--) + { + if( (*srcEntryList)[srcCnt].iName == (*trgEntryList)[trgCnt].iName + && ((*srcEntryList)[srcCnt].iAtt & KEntryAttDir) + && ((*trgEntryList)[trgCnt].iAtt & KEntryAttDir)) + { + // Set scan upwards + scanDown = EFalse; + goto CleanUp; + } + }// end inner for loop + } // end outer for loop +CleanUp: + // clean up + if(srcEntryList!=NULL) + delete srcEntryList; + if(trgEntryList!=NULL) + delete trgEntryList; + if(srcScanDir!=NULL) + delete srcScanDir; + if(trgScanDir!=NULL) + delete trgScanDir; + }// end if(iMovingContents) + + if(srcIsDir && !iTrgFile.IsRoot() && !iMovingContents) + { + ret = SetupTargetDirectory(aSwitches & EOverWrite, aComplete); + if(ret != KErrNone || aComplete) + { + return(ret); + } + } + if(!iMovingContents) + { + recurse = ETrue; + scanDown = EFalse; + } + if(srcIsDir) + { + MakeParseWild(iSrcFile, iTmpName1); + } + + SetFlags(aSwitches & EOverWrite, recurse, scanDown, ETrue); + return(KErrNone); + } + + +TInt CFileMan::SetupMoveAcrossDrives(TUint aSwitches) + { + // Moving across drives. We may need to recurse, + // depending on the supplied source path. + + TBool srcIsDir = EFalse; + TInt ret = SetupDirectoryForMove(srcIsDir); + if (ret != KErrNone) + { + return ret; + } + + TBool recurse = (aSwitches & ERecurse); + TBool scanDown = (recurse) ? (TBool)EFalse : (TBool)ETrue; + + if(srcIsDir) + { + if(!iMovingContents) + { + recurse = ETrue; + if(!iTrgFile.IsRoot()) + { + TBool complete = EFalse; + ret = SetupTargetDirectory(aSwitches & EOverWrite, complete); + if(ret != KErrNone || complete) + { + return(ret); + } + } + } + } + + CheckForDirectory(); + iAction = EInternalCopyForMove; + SetFlags(aSwitches & EOverWrite, recurse, scanDown, EFalse); + return(KErrNone); + } + +TInt CFileMan::RenameInvalidEntry(const TDesC& /*aName*/,const TDesC& /*aNewName*/,TUint /*aSwitches*/) +// +// Start rename operation +// + { + return KErrNotSupported; + } + + + + +EXPORT_C TInt CFileMan::Rename(const TDesC& aName,const TDesC& aNewName,TUint aSwitches,TRequestStatus& aStatus) +/** +Renames one or more files. + +This is an asynchronous function. +Its behaviour is the same as the synchronous overload. + +@param aName Path specifying the file(s) to be renamed. Any path components + which are not specified + here will be taken from the session path. +@param aNewName Path specifying the new name for the files and/or + the new directory. Any directories specified in this path + that do not exist, will be created. Any path components + which are not specified here will be taken from the session path. +@param aSwitches Specify zero for no overwriting; + CFileMan::EOverWrite to overwrite files with the same name. + This function cannot operate recursively. +@param aStatus The request status object. On request completion, + indicates how the request completed: + KErrNone, if successful, otherwise one of the other system-wide error + codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@capability Dependent If either aName or aNewName is /Sys then Tcb capability is required. +@capability Dependent If either aName or aNewName begins with /Private and does not match + this process' SID then AllFiles capability is required. +@capability Dependent If either aName or aNewName is /Resource then Tcb capability is required. + +*/ + { + TRACEMULT5(UTF::EBorder, UTraceModuleEfsrv::ECFileManRename1, MODULEUID, + (TUint) this, aName, aNewName, aSwitches, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + { + r = KErrInUse; + } + else + { + iStatus=&aStatus; + r = Rename(aName,aNewName,aSwitches); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManRename1Return, MODULEUID, r); + return(r); + } + + + + +EXPORT_C TInt CFileMan::Rename(const TDesC& aName,const TDesC& aNewName,TUint aSwitches) +/** +Renames one or more files, or a directory + +This is a synchronous function. + +The function can also be used to move files by specifying different destination +and source directories. + +Some rules for using CFileMan::Rename(): + +1. General rules: + +1.1. Trailing backslash ("\") in either source path (aName) or target path (aNewName) +will be interpreted to "\*.*"; + +For example, following code should behave identically: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC\\"), _L("C:\\TRG\\")); +fm->Rename(_L("C:\\SRC\\*.*"), _L("C:\\TRG\\")); +fm->Rename(_L("C:\\SRC\\"), _L("C:\\TRG\\*.*")); +fm->Rename(_L("C:\\SRC\\*.*"), _L("C:\\TRG\\*.*")); +@endcode + +1.2 The behaviour of the rename operation is sensitive to the presence (or absence) of +a trailing backslash ("\") character on the end of the target path (aNewName); + +For example, under all other constraints (see rules 2. and 3.), +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG\")); +@endcode +will result in renaming "C:\\SRC" to "C:\\TRG\\SRC", while +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG")); +@endcode +will result in renaming "C:\\SRC" to "C:\\TRG". + +2. Renaming file(s): + +2.1 Wildcards: + +A file's name and extension are interpreted separately, for example: + +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC\\1234.567"), _L("C:\\TRG\\AB*CD.TXT")); +@endcode +renames the source file to file "C:\\TRG\\AB34CD.TXT". + +Wildcards can be used for renaming multiple files, for example; +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC\\*.567"), _L("C:\\TRG\\*.TXT")); +@endcode +renames all the file under "C:\\SRC\\" having extension ".567" to the files under +"C:\\TRG\\" having extension ".TXT". + +2.2 An option is provided to allow the user to overwrite any files with the same +name which may exist in the target directory; If the flag is set for no overwriting, +any files with the same name will not be overwritten, and an error (KErrAlreadyExists) +will be returned for that file, as retrieved by CFileBase::GetLastError(). + +2.3 It can only operate non-recursively, so that only the matching files located +in the single directory specified by anOld may be renamed. + +2.4 Trying to rename file(s) to existing directory(ies) will fail; + +For example, giving following directory structure: +@code +C:\SRC\ITEM01 +C:\SRC\ITEM02 +C:\TRG\ITEM01\ +C:\TRG\ITEM02\ +@endcode + +Following code will fail: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC\\ITEM01"), _L("C:\\TRG\\ITEM01")); +fm->Rename(_L("C:\\SRC\\ITEM*"), _L("C:\\TRG\\ITEM*")); +fm->Rename(_L("C:\\SRC\\"), _L("C:\\TRG\\")); +@endcode + +3. When renamnig a directory: + +3.1. Only when the trailing backslash ("\") is missing from the source path (aName), +will the source directory be renamed, otherwise, see rule 1.1. + +For example, following code will result in moving "C:\SRC" directory including all +its contents: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG")); +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG\\")); +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG\\*.*")); +@endcode + +3.2. Wildcards can not be used for moving directories; + +3.3. Overwriting is not permitted; + +For example, giving directory structure as following: +@code +C:\SRC\FILE.TXT +C:\TRG\ +C:\TRG\SRC\ +@endcode + +following code will fail: +@code +CFileMan* fm(CFileMan::NewL(iFs)); // Where iFs is an RFs handle +... +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG")); +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG\\")); +fm->Rename(_L("C:\\SRC"), _L("C:\\TRG\\*.*")); +@endcode + +4. Notes: + +4.1. The target and source directories must be on the same drive. + +4.2. Read-only, hidden and system files can be moved and the source file's +attributes are preserved in the target file, but open files cannot +be moved. Attempting to move an open file will return an error for +that file, as retrieved by CFileBase::GetLastError(). + +@param aName Path specifying the file(s) to be renamed. Any path components + which are not specified + here will be taken from the session path. +@param aNewName Path specifying the new name for the files and/or + the new directory. Any directories specified in this path + that do not exist, will be created. Any path components which + are not specified here will be taken from the session path. +@param aSwitches Specify zero for no overwriting; + CFileMan::EOverWrite to overwrite files with the same name. + This function cannot operate recursively. + +@return KErrNone if successful, otherwise one of the other system-wide error + codes. + +@see CFileBase::GetLastError() + +@capability Dependent If either aName or aNewName is /Sys then Tcb capability is required. +@capability Dependent If either aName or aNewName begins with /Private and does not match + this process' SID then AllFiles capability is required. +@capability Dependent If either aName or aNewName is /Resource then Tcb capability is required. + +*/ + { + TRACEMULT4(UTF::EBorder, UTraceModuleEfsrv::ECFileManRename2, MODULEUID, + (TUint) this, aName, aNewName, aSwitches); + + TInt ret; + if (iSwitches&KFManBusyFlag) + { + ret = KErrInUse; + } + else + { + SetFlags(aSwitches&EOverWrite,EFalse,ETrue,EFalse); + RETURNIFERRORD(r,iFs.Parse(aName,iSrcFile),UTraceModuleEfsrv::ECFileManRename2Return); + RETURNIFERROR(r,iFs.Parse(aNewName,_L("*"),iTrgFile),UTraceModuleEfsrv::ECFileManRename2Return); + + iAction = EInternalRename; + iMatchEntry=KEntryAttMaskSupported; + iNumberOfFilesProcessed = 0; + TRAP(r,RunL()); + ret=(r==KErrNone) ? iLastError : r; + DoSynchronize(r); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManRename2Return, MODULEUID, ret); + return(ret); + } + + +EXPORT_C TInt CFileMan::RmDir(const TDesC& aDirName,TRequestStatus& aStatus) +/** +Deletes a directory and all files and directories contained in the +directory structure below it. + +Other than being asynchronous, the behaviour of this function is the same +as is documented in its synchronous overload. + +@param aDirName Path specifying the directory to be deleted. Any path components + which are not specified here will be taken from the session path. +@param aStatus The request status object. On request completion, indicates how + the request completed: + KErrNone if successful, otherwise one of the other system-wide + error codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@capability Dependent If aDirName starts with /Sys then Tcb capability is required. +@capability Dependent If aDirName begins with /Private and does not match this process' SID + then AllFiles capability is required. +@capability Dependent If aDirName starts with /Resource then Tcb capability is required. + +*/ + { + TRACEMULT3(UTF::EBorder, UTraceModuleEfsrv::ECFileManRmDir1, MODULEUID, (TUint) this, aDirName, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + { + r = KErrInUse; + } + else + { + iStatus=&aStatus; + r = RmDir(aDirName); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManRmDir1Return, MODULEUID, r); + return r; + } + + +EXPORT_C TInt CFileMan::RmDir(const TDesC& aDirName) +/** +Deletes a directory and all files and directories contained in the +directory structure below it. + +This is a synchronous function. + +The function cannot be used non-recursively. For a non-recursive +directory deletion, use RFs::RmDir(). + +Note: + +1. All files in the directory hierarchy to be deleted must be closed and + none may have the read-only attribute. Otherwise, not all of the hierarchy will + be deleted, and this function will return KErrInUse. + +@param aDirName Path specifying the directory to be deleted. Any path components + which are not specified here will be taken from the session path. + +@return KErrNone if successful, otherwise one of the other system-wide error + codes. + +@capability Dependent If aDirName starts with /Sys then Tcb capability is required. +@capability Dependent If aDirName begins with /Private and does not match this process' SID + then AllFiles capability is required. +@capability Dependent If aDirName starts with /Resource then Tcb capability is required. + + +*/ + { + TRACEMULT2(UTF::EBorder, UTraceModuleEfsrv::ECFileManRmDir2, MODULEUID, (TUint) this, aDirName); + + TInt ret; + if (iSwitches&KFManBusyFlag) + { + ret = KErrInUse; + } + else + { + SetFlags(ETrue,ETrue,EFalse,EFalse); + RETURNIFERRORD(r,iFs.Parse(aDirName,iTrgFile),UTraceModuleEfsrv::ECFileManRmDir2Return); + iSrcFile.Set(iTrgFile.DriveAndPath(),NULL,NULL); + iAction = EInternalRmDir; + iMatchEntry=KEntryAttMaskSupported; + iNumberOfFilesProcessed = 0; + TRAP(r,RunL()); + DoSynchronize(r); + ret = (r!=KErrNone) ? iLastError : KErrNone; + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManRmDir2Return, MODULEUID, ret); + return ret; + } + + +void CFileMan::DoOperationL() +// Call the action in progress. + { + switch (iAction) + { + case EInternalAttribs: + DoAttribsL(); + break; + case EInternalCopy: + case EInternalCopyForMove: + DoCopyOrMoveL(); + break; + case EInternalDelete: + DoDeleteL(); + break; + case EInternalRenameInvalidEntry: + case EInternalRenameForMove: + case EInternalRename: + DoRenameL(); + break; + case EInternalRmDir: + DoRmDirL(); + break; + case EInternalCopyFromHandle: + DoCopyFromHandleL(); + break; + default: + Panic(EFManUnknownAction); + } + } + +void CFileMan::DoAttribsL() +// +// Do attribs operation step +// + { + TPtrC fullPath(FullPath()); + iTmpParse.Set(CurrentEntry().iName, &fullPath, NULL); + User::LeaveIfError(iFs.SetEntry(iTmpParse.FullName(), iTime, iSetMask, iClearMask)); + } + +void CFileMan::DoCopyOrMoveL() +// +// Do copy or move operation +// + { + // Following 'if' statements are to prevent incorrect recursive Move() or Copy() from "destination" + // to "destination", this problem occurs when the initial source directory contains destination + // directory. + // (e.g. CFileMan::Move(_L("C:\\SRC\\*.TXT"), _L("C:\\SRC\\Sub\\"), CFileMan::ERecurse);) + // Note that CFileMan::Rename() does not suffer from this particular case, as CFileMan::Rename() API + // can only operate non-recursively. + if (iSrcFile.DriveAndPath().Length() < iTrgFile.DriveAndPath().Length()) + { + if (iTrgFile.DriveAndPath().Left(iSrcFile.DriveAndPath().Length()) == iSrcFile.DriveAndPath()) + // If source directory path contains destination directory path, including drive number, we consider + // this is "...\\ROOT\\" -> "...\\ROOT\\SUB\\" type of operation. Therefore skips all the items we + // found in "...\\ROOT\\SUB\\". We achieve this by checking current scanning directory path: + { + if (iTrgFile.DriveAndPath() == iScanner->FullPath().Left(iTrgFile.DriveAndPath().Length())) + { + return; + } + } + } + + TParse& srcName = iTmpParse; + TFileName& trgName = iTmpName1; + GetSrcAndTrg(srcName,trgName); + + // Handle case when source is directory + if (CurrentEntry().iAtt&KEntryAttDir) + { + if(!(iSwitches&KRecurseFlag)) + { + User::Leave(KErrNone); + } + trgName.Append(KPathDelimiter); + TInt r = iFs.MkDirAll(trgName); + if (r!=KErrNone && r!=KErrAlreadyExists) + User::Leave(r); + + if(iAction == EInternalCopyForMove) + { + // Move operation - Attempt to delete the source directory. + if((iMatchEntry & KMovingFilesMask) != KMovingFilesMask) + { + iTmpName2 = srcName.FullName(); + iTmpName2.Append(KPathDelimiter); + TInt rdErr = iFs.RmDir(iTmpName2); + if(rdErr != KErrNone && rdErr != KErrInUse) + { + User::Leave(rdErr); + } + } + } + return; + } + +#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + RFile srcFile,trgFile; +#else + RFile64 srcFile,trgFile; +#endif + TInt r=KErrNone; + if (FileNamesIdentical(srcName.FullName(),trgName)) + { + if (iSwitches & KOverWriteFlag) + // Source and target are identical, KOverWriteFlag makes copying + // having no effect. + return; + else + User::Leave(KErrAlreadyExists); + } + + r=srcFile.Open(iFs, srcName.FullName(), + iAction==EInternalCopy ? EFileRead|EFileShareReadersOnly // Copy access + : EFileWrite|EFileWriteDirectIO|EFileShareExclusive); // Move access + TBool isRO = EFalse; + if(r==KErrAccessDenied && iAction==EInternalCopyForMove) + { + TEntry& entry = iTmpEntry; + r = iFs.Entry(srcName.FullName(), entry); + if(r==KErrNone && (entry.iAtt&KEntryAttReadOnly)) + { + isRO = ETrue; + r = iFs.SetAtt(srcName.FullName(), 0, KEntryAttReadOnly); + if(r==KErrNone) + { + r = srcFile.Open(iFs, srcName.FullName(), EFileWrite|EFileWriteDirectIO|EFileShareExclusive); + } + } + } + if (r!=KErrNone) + { + iErrorInfo=ESrcOpenFailed; + if(isRO) + { + iFs.SetAtt(srcName.FullName(), KEntryAttReadOnly, 0); + } + User::Leave(r); + } + + if ((iSwitches&KOverWriteFlag)==0) + r=trgFile.Create(iFs,trgName,EFileWrite|EFileWriteDirectIO|EFileShareExclusive); + else + r=trgFile.Replace(iFs,trgName,EFileWrite|EFileWriteDirectIO|EFileShareExclusive); + + if (r==KErrPathNotFound && (iSwitches&KRecurseFlag)) + { + r=iFs.MkDirAll(trgName); + if (r==KErrNone) + r=trgFile.Create(iFs,trgName,EFileWrite|EFileWriteDirectIO|EFileShareExclusive); + } + + if (r!=KErrNone) + iErrorInfo=ETrgOpenFailed; + + TInt ret=0; + if (r == KErrNone) + r = DoCopy(srcFile, trgFile, ret); + + srcFile.Close(); + trgFile.Close(); + if ((r!=KErrNone && (r!=KErrAlreadyExists && iErrorInfo!=ETrgOpenFailed)) || (ret==MFileManObserver::ECancel)) + iFs.Delete(trgName); + if(r==KErrNone && isRO) + { + r = iFs.SetAtt(trgName, KEntryAttReadOnly, 0); + } + User::LeaveIfError(r); + + // + // Move operation + // + if (iAction == EInternalCopyForMove && ret != MFileManObserver::ECancel) + { + r=iFs.Delete(srcName.FullName()); + if (r==KErrNone) + return; + iFs.Delete(trgName); + User::Leave(r); + } + } + +void CFileMan::DoDeleteL() +// +// Do delete operation step +// + { + TFileName& pathname = iTmpName1; + TFileName& filename = iTmpName2; + pathname.Copy(FullPath()); + filename.Copy(CurrentEntry().iName); + if(CurrentEntry().iName.Length() + pathname.Length() > KMaxFileName) + { + User::LeaveIfError(ShrinkNames(iFs, pathname, filename, EFalse)); + } + iTmpParse.Set(filename, &pathname, NULL); + User::LeaveIfError(iFs.Delete(iTmpParse.FullName())); + } + +void CFileMan::DoRenameL() +// +// Do rename operation step +// + { + // Following 'if' statements are to prevent incorrect recursive Move() or Copy() from "destination" + // to "destination", this problem occurs when the initial source directory contains destination + // directory. + // (e.g. CFileMan::Move(_L("C:\\SRC\\*.TXT"), _L("C:\\SRC\\Sub\\"), CFileMan::ERecurse);) + // Note that CFileMan::Rename() does not suffer from this particular case, as CFileMan::Rename() API + // can only operate non-recursively. + if (iSrcFile.DriveAndPath().Length() < iTrgFile.DriveAndPath().Length()) + { + if (iTrgFile.DriveAndPath().Left(iSrcFile.DriveAndPath().Length()) == iSrcFile.DriveAndPath()) + // If source directory path contains destination directory path, including drive number, we consider + // this is "...\\ROOT\\" -> "...\\ROOT\\SUB\\" type of operation. Therefore skips all the items we + // found in "...\\ROOT\\SUB\\". We achieve this by checking current scanning directory path: + { + if (iTrgFile.DriveAndPath() == iScanner->FullPath().Left(iTrgFile.DriveAndPath().Length())) + { + return; + } + } + } + + TParse& srcName = iTmpParse; + TFileName& trgName = iTmpName1; + GetSrcAndTrg(srcName, trgName); + + TInt r = iFs.Rename(srcName.FullName(),trgName); + if (r==KErrAlreadyExists && (iSwitches&KOverWriteFlag)!=0) + { + // Target already exists, with the overwrite flag enabled + if((CurrentEntry().iAtt & KEntryAttDir) == 0) + { + // Renaming a file + r=iFs.Replace(srcName.FullName(),trgName); + } + else if (iAction == EInternalRenameForMove) + { + trgName = srcName.FullName(); + trgName.Append(KPathDelimiter); + r = iFs.RmDir(trgName); // remove empty directory after move + if(r == KErrInUse) + { + r = KErrNone; + } + } + } + + if (r==KErrPathNotFound) + { + if((iSwitches&KMoveRenameFlag) && !(iSwitches&KRecurseFlag)) + User::Leave(r); + r=iFs.MkDirAll(trgName); + if (r==KErrNone) + r=iFs.Rename(srcName.FullName(),trgName); + } + if (r==KErrBadName) + { + TEntry& entry = iTmpEntry; + TInt retcode=iFs.Entry(srcName.FullName(), entry); + if (retcode!=KErrNone) + iErrorInfo=ESrcOpenFailed; + else + iErrorInfo=ETrgOpenFailed; + } + User::LeaveIfError(r); + } + +void CFileMan::DoRmDirL() +// +// Do rmdir operation step +// + { + TFileName& srcName = iTmpName1; + srcName.Copy(FullPath()); + if (srcName.Length() + CurrentEntry().iName.Length() > KMaxFileName) + { + TFileName& current = iTmpName2; + current.Copy(CurrentEntry().iName); + User::LeaveIfError(ShrinkNames(iFs, srcName, current, ETrue)); + } + else + { + srcName.Append(CurrentEntry().iName); + } + + if ((CurrentEntry().iAtt&KEntryAttDir)==0) + User::LeaveIfError(iFs.Delete(srcName)); + else + { + srcName.Append(KPathDelimiter); + User::LeaveIfError(iFs.RmDir(srcName)); + } + } + + +void CFileMan::CompleteOperationL() +// +// Tidy up after an operation +// The last step to remove directory or to a move directory operation +// is to remove the source directory... +// + { + TInt r=KErrNotFound; + if (iAction == EInternalRmDir || + (iAction == EInternalCopyForMove && ((iMatchEntry & KMovingFilesMask) != KMovingFilesMask) && !iMovingContents && !iSrcFile.IsRoot()) || + iAction == EInternalRenameForMove && !iMovingContents && iNumberOfFilesProcessed) + { + r=iFs.RmDir(iSrcFile.FullName()); + if ((r!=KErrNone && r!=KErrNotFound && iAction!=EInternalRenameForMove && r!=KErrInUse) || (iAction == EInternalRmDir && r == KErrInUse)) + { + iLastError=r; + User::Leave(r); + } + } + + if (iLastError == KErrCancel && iNumberOfFilesProcessed==0 ) + { + iLastError=KErrCancel; + iErrorInfo=ENoFilesProcessed; + User::Leave(KErrCancel); + } + + if (iLastError==KErrNone && r==KErrNotFound && iNumberOfFilesProcessed==0) + { + iLastError=KErrNotFound; + iErrorInfo=ENoFilesProcessed; + User::Leave(KErrNotFound); + } + } + +void CFileMan::SetFlags(TBool anOverWrite,TBool aRecurse,TBool aScanDownTree,TBool aMoveRename) +// +// Set or clear flags +// + { + + iSwitches=0; + if (aRecurse) + iSwitches|=KRecurseFlag; + if (anOverWrite) + iSwitches|=KOverWriteFlag; + if (aScanDownTree) + iSwitches|=KScanDownFlag; + if (aMoveRename) + iSwitches|=KMoveRenameFlag; + } + + +EXPORT_C TInt CFileMan::Copy(const RFile& anOld, const TDesC& aNew, TUint aSwitches) +/** +Copies from an open file handle to a destination file name. + +This is a synchronous function. + +Optionally, this function can be set to overwrite the target file. +If the flag is set for no overwriting and the target file already exists, +then the target file will not be overwritten, and an error (KErrAlreadyExists) +will be returned. +Error codes may be retrieved using CFileBase::GetLastError(). + +Notes: + +-# The file can be copied across drives. +-# Read-only, hidden and system files can be copied and + the source file's attributes are preserved in the target file. + +@param anOld Open file handle indicating the file to be copied. +@param aNew Path indicating the directory (and optionally the filename) + into which the file is to be copied. + Any path components which are not specified here will be + taken from the session path +@param aSwitches Specify zero for no overwriting; + CFileMan::EOverWrite to overwrite files with the same name; + Any other flags are illegal + By default, the synchronous variant of this function operates + with overwriting. + +@return KErrNone if successful, otherwise one of the other system-wide error codes. + +@see CFileBase::GetLastError() +@see CFileMan::Move() + +@capability Dependent If the path for aNew begins with /Sys then Tcb capability is required. +@capability Dependent If the path for aNew begins with /Private and does not match + this process' SID then AllFiles capability is required. +@capability Dependent If the path for aNew begins with /Resource then Tcb capability is required. +*/ + { + TRACEMULT4(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy3, MODULEUID, + (TUint) this, anOld.SubSessionHandle(), aNew, aSwitches); + + TInt ret; + if (iSwitches&KFManBusyFlag) + { + ret = KErrInUse; + } + // The only switch that is legal for single file copies is EOverWrite + else if ((aSwitches & ~EOverWrite) != 0) + { + ret = KErrArgument; + } + else + { + + SetFlags(aSwitches & EOverWrite, EFalse, EFalse, EFalse); + + // need to signal to CFileBase that we're copying from a handle + // and that iSrcFile is invalid + iSwitches|= KCopyFromHandle; + + TInt r; + RETURNIFERROR(r, iFs.Parse(aNew, iTrgFile),UTraceModuleEfsrv::ECFileManCopy3Return); + + // Need to duplicate the RFile handle so that any threads owned + // by this process can use it - i.e. the worker thread + RETURNIFERROR(r, iSrcFileHandle.Duplicate(anOld, EOwnerProcess),UTraceModuleEfsrv::ECFileManCopy3Return); + + iAction = EInternalCopyFromHandle; + iNumberOfFilesProcessed = 0; + TRAP(r,RunL()); + ret=(r==KErrNone) ? iLastError : r; + DoSynchronize(r); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy3Return, MODULEUID, ret); + return(ret); + } + +EXPORT_C TInt CFileMan::Copy(const RFile& anOld,const TDesC& aNew,TUint aSwitches,TRequestStatus& aStatus) +/** +Copies from an open file handle to a destination file name. + +This is an asynchronous function. +Its behaviour is the same as the synchronous overload. + +@param anOld Open file handle indicating the file to be copied. +@param aNew Path indicating the directory (and optionally the filename) + into which the file is to be copied. + Any path components which are not specified here will be + taken from the session path +@param aSwitches Specify zero for no overwriting; + CFileMan::EOverWrite to overwrite files with the same name; + Any other flags are illegal. + +@param aStatus The request status object. On request completion, + indicates how the request completed: + KErrNone, if successful, otherwise one of the other system-wide error + codes. + +@return KErrNone if the asynchronous request is made successfully; KErrInUse if an asynchronous request + is still pending; otherwise one of the other system-wide error codes + +@see CFileBase::GetLastError() + +@capability Dependent If the path for aNew begins with /Sys then Tcb capability is required. +@capability Dependent If the path for aNew begins with /Private and does not match + this process' SID then AllFiles capability is required. +@capability Dependent If the path for aNew begins with /Resource then Tcb capability is required. +*/ + { + TRACEMULT5(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy4, MODULEUID, + (TUint) this, anOld.SubSessionHandle(), aNew, aSwitches, (TUint) &aStatus); + + TInt r; + if (iSwitches&KFManBusyFlag) + { + r = KErrInUse; + } + else + { + iStatus=&aStatus; + r = Copy(anOld,aNew,aSwitches); + } + + TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECFileManCopy4Return, MODULEUID, r); + return(r); + } + +void CFileMan::DoCopyFromHandleL() +// +// Copy from open file handle +// + { + TInt ret=0; + TFileName& trgName = iTmpName1; + + if (iTrgFile.NamePresent()) + { + trgName = iTrgFile.FullName(); + } + else + { + iSrcFileHandle.Name(trgName); + if ((trgName.Length() + iTrgFile.DriveAndPath().Length()) > KMaxFileName) + { + iSrcFileHandle.Close(); + User::Leave(KErrBadName); + } + trgName.Insert(0, iTrgFile.DriveAndPath()); + } + +#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + RFile trgFile; +#else + RFile64 trgFile; +#endif + TInt r=KErrNone; + + if ((iSwitches&KOverWriteFlag)==0) + r=trgFile.Create(iFs,trgName,EFileWrite|EFileWriteDirectIO|EFileShareExclusive); + else + r=trgFile.Replace(iFs,trgName,EFileWrite|EFileWriteDirectIO|EFileShareExclusive); + if (r!=KErrNone) + iErrorInfo = ETrgOpenFailed; + + if (r == KErrNone) + r = DoCopy(iSrcFileHandle, trgFile, ret); + + // close the (duplicated) source file handle + iSrcFileHandle.Close(); + + trgFile.Close(); + if (ret == MFileManObserver::ECancel || (r!=KErrNone && r!=KErrAlreadyExists && iErrorInfo!=ETrgOpenFailed)) + iFs.Delete(trgName); + User::LeaveIfError(r); + } + +#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API +TInt CFileMan::DoCopy(const RFile& aSrcFile, RFile& aDstFile, TInt& aRet) + { + TInt rem; +#else +TInt CFileMan::DoCopy(const RFile64& aSrcFile, RFile64& aDstFile, TInt& aRet) + { + TInt64 rem; +#endif + RETURNIFERRORD(r,aSrcFile.Size(rem),EFalse); + RETURNIFERROR(r, aDstFile.SetSize(rem),EFalse); + + HBufC8* bufPtr = NULL; + bufPtr = AllocateBuffer(rem); + if (bufPtr == NULL) + return KErrNoMemory; + TPtr8 copyBuf=bufPtr->Des(); + +#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + TInt pos=0; +#else + TInt64 pos=0; +#endif + aRet = MFileManObserver::EContinue; + while(rem && aRet == MFileManObserver::EContinue) + { +#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + TInt s=Min(rem,copyBuf.MaxSize()); +#else + // Min result shall be of TInt size + TInt s=(TInt)(Min(rem,(TInt64)copyBuf.MaxSize())); +#endif + r=aSrcFile.Read(pos,copyBuf,s); + if (r==KErrNone && copyBuf.Length()!=s) + r = KErrCorrupt; + if (r==KErrNone) + r=aDstFile.Write(pos,copyBuf,s); + if (r!=KErrNone) + break; + pos+= s; + rem-= s; + iBytesTransferred = s; + aRet = (iObserver) ? iObserver->NotifyFileManOperation() : MFileManObserver::EContinue; + if (aRet != MFileManObserver::EContinue && aRet != MFileManObserver::ECancel) + Panic(EFManBadValueFromObserver); + } + + // need to flush the target file - otherwise if there is any dirty data this will be flushed + // when the file is closed and this will set the archive attribute, resulting in the file + // having potentially a different attribute from the source file + if (r == KErrNone) + r = aDstFile.Flush(); + + if (aRet != MFileManObserver::ECancel) + { + TTime lastMod; + if (r == KErrNone) + r = aSrcFile.Modified(lastMod); + if (r == KErrNone) + r = aDstFile.SetModified(lastMod); + + TUint fileAttributes=0; + if (r == KErrNone) + r = aSrcFile.Att(fileAttributes); + if (r == KErrNone) + r = aDstFile.SetAtt(fileAttributes,(~fileAttributes)&KEntryAttMaskSupported); + + if(r == KErrNone) + r = aDstFile.Flush(); + } + + delete bufPtr; + + return r; + }