userlibandfileserver/fileserver/sfsrv/cl_scan.cpp
changeset 0 a41df078684a
child 41 0ffb4e86fcc9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfsrv/cl_scan.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,747 @@
+// 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"
+#include "cl_scan.h"
+
+const TInt KDirStackGranularity=8;
+
+/** Replace long names in path and filename with their sohrter version (if exists). Optionally appends
+filename to path name creating fully qualified file name.
+@param aFs (connected) File system session
+@param aCurrentPath  on input contains current full path name, 
+upon return contains the shortest version (using either shor or long names) of the same path
+@param aItem on input contains item with long name in the current path, 
+upon return contains shorter name of either long or short name
+@param aAppend if ETrue aItem will be appended to aCurrentPath before successful return
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+*/
+TInt ShrinkNames(RFs& aFs, TFileName& aCurrentPath, TFileName& aItem, TBool aAppend)
+	{
+	TInt ret = KErrNone;
+	TBuf<8+1+3> shortname;
+	TFileName* current = NULL;
+    TRAPD(r,current = new (ELeave) TFileName);
+    if (r)
+        return r;
+
+    TInt pos = 0;
+	TInt lastslash = KErrNotFound;
+	TInt lastnewslash = KErrNotFound;
+	while(ret == KErrNone && pos < aCurrentPath.Length())
+		{
+		if(aCurrentPath[pos] == KPathDelimiter)
+			{
+			if(lastslash != KErrNotFound)
+				{
+				ret = aFs.GetShortName(*current, shortname);
+				if(ret == KErrNone && pos-lastslash > shortname.Length())
+					{
+					current->SetLength(lastnewslash);
+					current->Append(shortname);
+					}
+				}
+			lastslash = pos+1;
+			lastnewslash = current->Length()+1;
+			}
+		current->Append(aCurrentPath[pos++]);
+		}
+	if(ret == KErrNone && current->Length() + aItem.Length() <= KMaxFileName)
+		{
+		aCurrentPath = *current;
+		TInt lenBefore = aCurrentPath.Length();
+		aCurrentPath.Append(aItem);
+		ret = aFs.GetShortName(aCurrentPath, shortname);
+		aCurrentPath.SetLength(lenBefore);
+
+		if(ret == KErrNone && aItem.Length() > shortname.Length())
+			{
+			aItem = shortname;
+			}
+		}
+	if(aAppend && ret == KErrNone && aCurrentPath.Length() + aItem.Length() <= KMaxFileName)
+		{
+		aCurrentPath.Append(aItem); 
+		}
+    delete current;
+	return ret;
+}
+  
+LOCAL_C TPtrC LeafDir(const TDesC& aPath)
+//
+// Returns the leaf directory of a path
+//
+	{
+
+	TInt end=aPath.LocateReverse('\\');
+	__ASSERT_DEBUG(end!=KErrNotFound,Panic(EDirListError));
+	TPtrC ret(aPath.Ptr(),end);
+	TInt start=ret.LocateReverse('\\');
+	if (start==KErrNotFound)
+		start=end-1;
+	return ret.Right(end-start-1);
+	}
+
+CDirScan::CDirScan(RFs& aFs)
+//
+// Constructor
+//
+	: iFs(&aFs)
+	{
+	}
+
+
+
+
+EXPORT_C CDirScan* CDirScan::NewLC(RFs& aFs)
+/**
+Constructs and allocates memory for a new CDirScan object, putting a pointer
+to the object onto the cleanup stack.
+
+@param aFs The file server session.
+
+@return A pointer to the new directory scan object.
+*/
+	{
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanNewLC, MODULEUID, aFs.Handle());
+
+	CDirScan* scan=new(ELeave) CDirScan(aFs);
+	CleanupStack::PushL(scan);
+	scan->iStack=CDirStack::NewL();
+
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanNewLCReturn, MODULEUID, scan);
+	return scan;
+	}
+
+
+
+
+EXPORT_C CDirScan* CDirScan::NewL(RFs& aFs)
+/**
+Constructs and allocates memory for a new CDirScan object.
+
+@param aFs The file server session.
+
+@return A pointer to the new directory scan object.
+*/
+	{
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanNewL, MODULEUID, aFs.Handle());
+
+	CDirScan* scan=CDirScan::NewLC(aFs);
+	CleanupStack::Pop();
+
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanNewLReturn, MODULEUID, scan);
+	return scan;
+	}
+
+
+
+
+EXPORT_C CDirScan::~CDirScan()
+/**
+Desctructor.
+
+Frees all resources owned by the object, prior to its destruction.
+*/
+	{
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanDestructor, MODULEUID, this);
+
+	delete iStack;
+
+	TRACE0(UTF::EBorder, UTraceModuleEfsrv::ECDirScanDestructorReturn, MODULEUID);
+	}
+
+
+
+
+EXPORT_C void CDirScan::SetScanDataL(const TDesC& aMatchName,TUint anEntryAttMask,TUint anEntrySortKey,TScanDirection aScanDir)
+/**
+Initialises the scan.
+
+This involves specifying:
+
+1. the directory at the top of the structure to be scanned
+
+2. a filter for the entry types of interest
+
+3. the order in which the entries in each directory in the structure are to be sorted
+
+4. the scan direction.
+
+Whether the scan direction is upwards or downwards, the directories that are
+scanned are those in the part of the hierarchy below the directory
+specified in aMatchName. By default, the scan direction is downwards.
+If the scan direction is set to CDirScan::EScanUpTree, then all branches of
+the tree are explored starting at the lowest level directory in
+the tree below aMatchName, and ending at aMatchName.
+This option is provided for deleting a directory structure.
+
+@param aMatchName     The top level directory for the scan. Any path components
+                      that are not specified here are taken from the session path. 
+                      Note that the trailing backslash is required to specify the directory.
+                      I.e. path x:\\dir1\\dir2\\ means that the scan will start from dir2, while
+                      path x:\\dir1\\dir2 assumes scan starting from x:\\dir1\\
+
+@param anEntryAttMask A bit mask that filters the entry types which should be returned by
+		              NextL(). The mask works as follows:
+		              To match files only, specify KEntryAttNormal.
+		              To match both files and directories,
+		              specify KEntryAttDir.
+		              To match directories only,
+		              specify KEntryAttDir|KEntryAttMatchExclusive.
+		              To match files with a	specific attribute,
+		              then OR the attribute involved with
+                      KEntryAttMatchExclusive.
+                      For example, to match read-only files,
+                      specify KEntryAttReadOnly|KEntryAttMatchExclusive.
+                      For more information,
+                      see KEntryAttNormal or
+                      the other file/directory attributes.
+@param anEntrySortKey The order in which the directories are scanned by
+                      NextL(). This flag is defined in TEntryKey.
+@param aScanDir       The direction of the scan. The default is downwards.
+*/
+	{
+	TRACEMULT5(UTF::EBorder, UTraceModuleEfsrv::ECDirScanSetScanDataL, MODULEUID, (TUint) 
+		this, aMatchName, anEntryAttMask, anEntrySortKey, (TUint) aScanDir);
+
+	TInt r = Fs().Parse(aMatchName,iFullPath);
+	if (r != KErrNone)
+		{
+		TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanSetScanDataLReturn, MODULEUID, r);
+		User::Leave(r);
+		}
+
+	iScanning = ETrue;
+	iEntryAttMask=anEntryAttMask;
+	iEntrySortMask=anEntrySortKey;
+	iStack->ResetL(LeafDir(iFullPath.FullName()));
+	iAbbreviatedPathPos=iFullPath.DriveAndPath().Length()-1;
+	iAbbreviatedPath.Set(_L("\\"));
+	iScanDir=aScanDir;
+	if (aScanDir==EScanDownTree)
+		iFullPath.PopDir();
+
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanSetScanDataLReturn, MODULEUID, KErrNone);
+	}
+
+void CDirScan::UpdateAbbreviatedPath()
+//
+// Set the abbreviated path based on the full path
+//
+	{
+
+	TInt length=iFullPath.DriveAndPath().Length();
+	TPtrC fullName=iFullPath.FullName();
+	if (length>iAbbreviatedPathPos)
+		iAbbreviatedPath.Set(&fullName[0]+iAbbreviatedPathPos,length-iAbbreviatedPathPos);
+	else
+		iAbbreviatedPath.Set(_L("\\"));
+	}
+
+
+
+
+EXPORT_C void CDirScan::NextL(CDir*& aDirEntries)
+/**
+Scans the next directory entry in the structure.
+
+The order in which the structure is scanned is determined by the scan
+direction and the entry sort mask. These values are specified when setting up
+the scan. The type of entries retrieved by this function is determined by the
+entry attribute mask. This is also specified when setting up the scan.
+
+Notes:
+
+1. The function first sets aDirEntries to NULL, and then allocates memory for
+   it before appending entries to it. Therefore, aDirEntries should have no
+   memory allocated to it before this function is called, otherwise this
+   memory will become orphaned.
+	 
+2. The caller of this function is responsible for deleting aDirEntries after
+   the function has returned.
+   
+@param aDirEntries On return, a pointer to an array containing filtered entries
+                   from the next directory in the structure. NULL if there are
+                   no more directories in the structure.
+*/
+	{
+	TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanNextL, MODULEUID, this);
+
+	if (iScanDir==EScanUpTree)
+		ScanUpTreeL(aDirEntries);
+	else
+		ScanDownTreeL(aDirEntries);
+
+	TRACE2(UTF::EBorder, UTraceModuleEfsrv::ECDirScanNextLReturn, MODULEUID, 
+		KErrNone, aDirEntries ? (*aDirEntries).Count() : 0);
+	}
+
+void CDirScan::ScanUpTreeL(CDir*& aDirEntries)
+//
+// Get the next directory starting from the bottom of the tree
+// eg: for deleting a directory structure
+//
+	{
+	TInt r;
+	iFullPath.PopDir();
+	CDirList* list=iStack->Peek();
+	if (!list->MoreEntries())
+		{
+		iStack->Pop();
+		if (iStack->IsEmpty())
+			{
+			aDirEntries=NULL;
+			return;
+			}
+		UpdateAbbreviatedPath();
+		GetDirEntriesL(aDirEntries);
+		return;
+		}
+			
+	TFileName* next = new (ELeave) TFileName;
+    CleanupStack::PushL(next);
+	TFileName* current = new (ELeave) TFileName;
+    CleanupStack::PushL(current);
+			
+	FOREVER
+		{
+		TPtrC dirName=list->Next().iName;
+		r = iFullPath.AddDir(dirName);	
+		if (r==KErrGeneral)	//	adding dirName would make iFullPath > 256 characters
+			{
+			current->Copy(iFullPath.DriveAndPath());
+			next->Copy(dirName);
+			
+			r = ShrinkNames(Fs(), *current, *next, EFalse);
+			if(r == KErrNone)
+				{
+				r = iFullPath.Set(*current, NULL, NULL);
+				if(r == KErrNone)
+					{
+					r = iFullPath.AddDir(*next);
+					}
+				}
+			}
+		if (r != KErrNone)
+			{
+			TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+			User::LeaveIfError(r);
+			}
+
+		CDir* dirList;
+		//	Start by searching for directories only	from top to bottom
+		r = Fs().GetDir(iFullPath.DriveAndPath(),
+						KEntryAttDir|KEntryAttMatchExclusive|(iEntryAttMask&KEntryAttMatchMask),
+						iEntrySortMask,
+						dirList);
+		if (r == KErrPermissionDenied && !iScanning)
+			{
+			UpdateAbbreviatedPath();
+			aDirEntries = CDirFactory::NewL();
+			}
+		else if (r != KErrNone)
+			{
+			iScanning = EFalse;
+			TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+			User::Leave(r);
+			}
+		
+		iScanning= EFalse;
+		
+		// Permission denied case. No entry
+		if(!dirList)
+			break;
+
+		if (dirList->Count()==0)//	No more directory entries - bottom of tree reached
+			{
+			delete dirList;
+			break;
+			}
+		iStack->PushL(*dirList);
+		list=iStack->Peek();
+		} //END OF FOREVER
+		
+	CleanupStack::PopAndDestroy(2); // current and next pointers
+
+	UpdateAbbreviatedPath();
+	//	Now get all valid entries for the lowest level directory encountered	
+	
+	if(r!=KErrPermissionDenied )
+		{
+		GetDirEntriesL(aDirEntries);
+		}	
+	}
+	
+void CDirScan::GetDirEntriesL(CDir*& aDirEntries)
+//
+// Call GetDir.
+//
+	{
+	TInt r = Fs().GetDir(iFullPath.FullName(),iEntryAttMask,iEntrySortMask,aDirEntries);
+	if (r != KErrNone)
+		{
+		TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+		User::Leave(r);
+		}
+	}
+
+void CDirScan::ScanDownTreeL(CDir*& aDirEntries)
+//
+// Get the next directory starting from the top of the tree 
+// eg: for copying a directory structure
+//
+	{
+	CDir* dirEntries = NULL;
+	TInt r;
+	aDirEntries=NULL;
+	CDirList* list=iStack->Peek();
+	while (!list->MoreEntries())
+		{
+		iStack->Pop();
+		if (iStack->IsEmpty())
+			{
+			aDirEntries=NULL;
+			return;
+			}
+		iFullPath.PopDir();
+		UpdateAbbreviatedPath();
+		list=iStack->Peek();
+		}
+
+	TPtrC dirName=list->Next().iName;
+	r=iFullPath.AddDir(dirName);
+	if (r==KErrGeneral)	//	Adding dirName makes iFullPath>256 characters
+		{
+		TRACE1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, KErrTooBig);
+		User::Leave(KErrTooBig);
+		}
+
+	//	Get all valid entries in this directory
+	
+	// coverity[alloc_arg]
+	TRAP(r, GetDirEntriesL(dirEntries));
+
+	if (r == KErrNone)
+		{
+		iScanning = EFalse;
+		CleanupStack::PushL(dirEntries);
+		//	Get all directories within this directory - the next level down in the tree
+		CDir* dirList;
+
+		// coverity[alloc_fn]
+		r = Fs().GetDir(iFullPath.DriveAndPath(),
+									   KEntryAttDir|KEntryAttMatchExclusive|(iEntryAttMask&KEntryAttMatchMask),
+									   iEntrySortMask,dirList);
+		if (r != KErrNone)
+			{
+			TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+			User::Leave(r);
+			}
+		iStack->PushL(*dirList);
+		CleanupStack::Pop();	// dirEntries
+		UpdateAbbreviatedPath();
+		aDirEntries=dirEntries;
+		}
+	else if (r == KErrPermissionDenied && !iScanning)
+		{
+		CDir* dirList = CDirFactory::NewL();
+		iStack->PushL(*dirList);
+		aDirEntries = CDirFactory::NewL();
+		}
+	else
+		{
+		iScanning = EFalse;
+		TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+		User::Leave(r);
+		}
+	}
+
+
+
+
+EXPORT_C TPtrC CDirScan::AbbreviatedPath()
+/**
+Gets the abbreviated path of the entry currently being scanned.
+
+The abbreviated path is the path relative to the top level directory
+in the scan.
+
+@return A non modifiable pointer descriptor for the abbreviated path of
+        the entry currently being scanned.
+*/
+	{
+	
+	return iAbbreviatedPath;
+	}
+
+
+
+
+EXPORT_C TPtrC CDirScan::FullPath()
+/**
+Gets the full path of the entry currently being scanned.
+
+The full path includes the drive letter.
+
+@return A non modifiable pointer descriptor for the full path of the entry
+        currently being scanned.
+*/
+	{
+	
+	return iFullPath.DriveAndPath();
+	} 
+
+
+
+
+CDirStack* CDirStack::NewL()
+// 
+// Create new directory stack
+//
+	{
+
+	return new(ELeave) CDirStack;
+	}
+
+CDirStack::CDirStack()
+//
+// Constructor
+//
+	: iDirStack(KDirStackGranularity)
+	{
+	}
+
+CDirStack::~CDirStack()
+//
+// Destructor
+//
+	{
+
+	
+	iDirStack.ResetAndDestroy();
+	}
+	
+TInt CDirStack::IsEmpty()
+//
+// Return number of directories stacked
+//
+	{
+
+	return (iDirStack.Count()==0);
+	}
+
+void CDirStack::ResetL(const TDesC& aStartDir)
+//
+// Reset stack to containing only aStartDir
+//
+	{
+
+	iDirStack.ResetAndDestroy();
+	CDir* dir=CDirFactory::NewL(aStartDir);
+	PushL(*dir);
+	}
+
+void CDirStack::PushL(CDir& aDirContents)
+//
+// Push a list of directories onto the stack
+//
+	{
+
+	CleanupStack::PushL(&aDirContents);
+	CDirList* nextLevel=CDirList::NewL(aDirContents);
+	CleanupStack::Pop(); // aDirContents now owned by CDirList
+
+	TInt r=iDirStack.Append(nextLevel);
+	if (r!=KErrNone)
+		{
+		delete nextLevel;
+		TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+		User::Leave(r);
+		}
+	}
+
+void CDirStack::Pop()
+//
+// Pop subdirectory list off the stack
+//
+	{
+
+	__ASSERT_DEBUG(iDirStack.Count(),Panic(EDirListError));
+	TInt tos=iDirStack.Count()-1;
+	delete iDirStack[tos];
+	iDirStack.Remove(tos);
+	}
+
+CDirList* CDirStack::Peek()
+//
+// Return current subdirectory
+//
+	{
+
+	__ASSERT_DEBUG(iDirStack.Count(),Panic(EDirListError));
+	return iDirStack[iDirStack.Count()-1];
+	}
+
+CDirList* CDirList::NewL(CDir& aDirList)
+//
+// Create a new directory list - takes ownership of aDirList
+//
+	{
+
+	CDirList* dirLevel=new(ELeave) CDirList;
+	dirLevel->iDirList=&aDirList;
+	return dirLevel;
+	}
+
+CDirList::CDirList()
+//
+// Construct directory list
+//
+	{
+	}
+
+CDirList::~CDirList()
+//
+// Destroy directory list
+//
+	{
+
+	delete iDirList;
+	}
+
+const TEntry& CDirList::Next()
+//
+// Return next directory in list.
+//
+	{
+
+	__ASSERT_DEBUG(iCurrentPos>=0 && iCurrentPos<iDirList->Count(),Panic(EDirListError));
+	const TEntry& entry=(*iDirList)[iCurrentPos];
+	iCurrentPos++;
+	return entry;
+	}
+
+TBool CDirList::MoreEntries() const
+//
+// Return EFalse if the entire list has been read
+//
+	{
+
+	__ASSERT_DEBUG(iCurrentPos>=0 && iCurrentPos<=iDirList->Count(),Panic(EDirListError));
+	return (iCurrentPos!=iDirList->Count());
+	}
+
+CDir* CDirFactory::NewL(const TDesC& anEntryName)
+//
+// Create a CDir containing a single entry. Used to initialize the scanner
+//
+	{
+
+	CDirFactory* dir=(CDirFactory*)CDir::NewL();
+	CleanupStack::PushL(dir);
+	TEntry entry;
+	entry.iName=anEntryName;
+	dir->AddL(entry);
+	CleanupStack::Pop();
+	return dir;
+	}
+
+CDir* CDirFactory::NewL()
+//
+// Create a CDir with nothing in it
+//
+	{
+
+	CDirFactory* dir=(CDirFactory*)CDir::NewL();
+	return dir;
+	}
+
+
+
+
+EXPORT_C TOpenFileScan::TOpenFileScan(RFs& aFs)
+/**
+Constructs the object with the specified file server session.
+
+@param aFs The file server session.
+*/
+	: iFs(&aFs), iScanPos(0), iEntryListPos(0)
+	{}
+
+
+
+
+EXPORT_C void TOpenFileScan::NextL(CFileList*& aFileList)
+/**
+Gets a list of entries for the open files in the file server session.
+
+@param aFileList On return, contains a list of entries for all open files
+                 in the file server session.
+*/
+	{
+
+
+	aFileList=NULL;
+	if (iScanPos==KErrNotFound)
+		return;
+	TEntryArray* pArray=new(ELeave) TEntryArray;
+	CleanupStack::PushL(pArray);
+	TEntryArray& array=*pArray;
+	FOREVER
+		{
+		TThreadId theId;
+		TInt r = iFs->GetOpenFileList(iScanPos,iEntryListPos,theId,array);
+		if (r != KErrNone)
+			{
+			TRACERET1(UTF::EBorder, UTraceModuleEfsrv::ECDirScanLeave, MODULEUID, r);
+			User::Leave(r);
+			}
+		TInt count=array.Count();
+		if (count==0)
+			{
+			if (aFileList==NULL)
+				iScanPos=KErrNotFound;
+			else
+				CleanupStack::Pop(); // aFileList
+			iEntryListPos=0;
+			CleanupStack::PopAndDestroy(); // pArray
+			return;
+			}
+		iThreadId = theId;
+		if (aFileList==NULL)
+			{
+			aFileList=CFileList::NewL();
+			CleanupStack::PushL(aFileList);
+			}
+		TInt i=0;
+		while (i<count)
+			aFileList->AddL(array[i++]);
+		}
+	}
+
+
+
+
+EXPORT_C TThreadId TOpenFileScan::ThreadId() const
+/**
+Gets the ID of the thread that opened the files retrieved by NextL().
+
+@return The ID of the thread that opened the files in the file list.
+*/
+	{
+
+	return(iThreadId);
+	}